opensc-0.13.0/0000755000015201777760000000000012057406120010101 500000000000000opensc-0.13.0/config.h.in0000644000015201777760000001372012057406055012056 00000000000000/* config.h.in. Generated from configure.ac by autoheader. */ /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD /* Debug file */ #undef DEBUG_FILE /* Default PC/SC provider */ #undef DEFAULT_PCSC_PROVIDER /* Default SM module */ #undef DEFAULT_SM_MODULE /* Enable CT-API support */ #undef ENABLE_CTAPI /* Enable minidriver support */ #undef ENABLE_MINIDRIVER /* Have OpenCT libraries and header files */ #undef ENABLE_OPENCT /* Have OpenSSL libraries and header files */ #undef ENABLE_OPENSSL /* Define if PC/SC is to be enabled */ #undef ENABLE_PCSC /* Use readline libraries and header files */ #undef ENABLE_READLINE /* Enable secure messaging support */ #undef ENABLE_SM /* Use zlib libraries and header files */ #undef ENABLE_ZLIB /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ #undef HAVE_DOPRNT /* Define to 1 if you have the header file. */ #undef HAVE_ERRNO_H /* Define to 1 if you have the header file. */ #undef HAVE_FCNTL_H /* Define to 1 if you have the header file. */ #undef HAVE_GETOPT_H /* Define to 1 if you have the `getopt_long' function. */ #undef HAVE_GETOPT_LONG /* Define to 1 if you have the `getopt_long_only' function. */ #undef HAVE_GETOPT_LONG_ONLY /* Define to 1 if you have the `getpass' function. */ #undef HAVE_GETPASS /* Define to 1 if you have the `gettimeofday' function. */ #undef HAVE_GETTIMEOFDAY /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the `dl' library (-ldl). */ #undef HAVE_LIBDL /* Define to 1 if you have the header file. */ #undef HAVE_MALLOC_H /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the `memset' function. */ #undef HAVE_MEMSET /* Define to 1 if you have the `mkdir' function. */ #undef HAVE_MKDIR /* Define if you have POSIX threads libraries and header files. */ #undef HAVE_PTHREAD /* Have PTHREAD_PRIO_INHERIT. */ #undef HAVE_PTHREAD_PRIO_INHERIT /* Define to 1 if you have the header file. */ #undef HAVE_READLINE_READLINE_H /* Define to 1 if `stat' has the bug that it succeeds when given the zero-length file name argument. */ #undef HAVE_STAT_EMPTY_STRING_BUG /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the `strdup' function. */ #undef HAVE_STRDUP /* Define to 1 if you have the `strerror' function. */ #undef HAVE_STRERROR /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the `strlcat' function. */ #undef HAVE_STRLCAT /* Define to 1 if you have the `strlcpy' function. */ #undef HAVE_STRLCPY /* Define to 1 if you have the header file. */ #undef HAVE_SYS_MMAN_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TIME_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have that is POSIX.1 compatible. */ #undef HAVE_SYS_WAIT_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the `vprintf' function. */ #undef HAVE_VPRINTF /* Define to 1 if you have the header file. */ #undef HAVE_WINSCARD_H /* Define to 1 if you have the header file. */ #undef HAVE_ZLIB_H /* Define to 1 if `lstat' dereferences a symlink specified with a trailing slash. */ #undef LSTAT_FOLLOWS_SLASHED_SYMLINK /* Define to the sub-directory in which libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* Define to 1 if assertions should be disabled. */ #undef NDEBUG /* Enabled OpenSC features */ #undef OPENSC_FEATURES /* OpenSC version fix component */ #undef OPENSC_VERSION_FIX /* OpenSC version major component */ #undef OPENSC_VERSION_MAJOR /* OpenSC version minor component */ #undef OPENSC_VERSION_MINOR /* Name of package */ #undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to necessary symbol if this constant uses a non-standard name on your system. */ #undef PTHREAD_CREATE_JOINABLE /* The size of `void *', as computed by sizeof. */ #undef SIZEOF_VOID_P /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Define to 1 if you can safely include both and . */ #undef TIME_WITH_SYS_TIME /* Define if you are on Cygwin */ #undef USE_CYGWIN /* Version number of package */ #undef VERSION /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN # undef WORDS_BIGENDIAN # endif #endif /* Define to empty if `const' does not conform to ANSI C. */ #undef const /* Define to `int' if doesn't define. */ #undef gid_t /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #undef inline #endif /* Define to `unsigned int' if does not define. */ #undef size_t /* Define to `int' if doesn't define. */ #undef uid_t opensc-0.13.0/bootstrap0000755000015201777760000000063112057406034011770 00000000000000#!/bin/sh set -e set -x if test -f Makefile; then make distclean fi rm -rf *~ *.cache *.m4 config.guess config.log config.status config.sub depcomp ltmain.sh if test ! -z "$1"; then cp configure.ac configure.ac.orig sed 's/^define(\[PACKAGE_SUFFIX\],\s*\[\([-~]*[0-9a-zA-Z]*\)\])$/define(\[PACKAGE_SUFFIX\], \['$1'\])/g' configure.ac.orig > configure.ac fi autoreconf --verbose --install --force opensc-0.13.0/etc/0000755000015201777760000000000012057406120010654 500000000000000opensc-0.13.0/etc/Makefile.mak0000644000015201777760000000021012057406034013000 00000000000000all: opensc.conf.win opensc.conf.win: opensc.conf.win.in copy /y opensc.conf.win.in opensc.conf.win clean:: del /Q opensc.conf.win opensc-0.13.0/etc/Makefile.am0000644000015201777760000000174212057406034012640 00000000000000MAINTAINERCLEANFILES = $(srcdir)/Makefile.in $(srcdir)/opensc.conf.win DISTCLEANFILES = opensc.conf opensc.conf.win EXTRA_DIST = Makefile.mak SUFFIXES = .in dist_noinst_DATA = opensc.conf.in opensc.conf.win.in nodist_noinst_DATA = opensc.conf opensc.conf.win # For Windows MSVC build # Make sure we build this every time # as there is no dependency for this. # Can be removed if MSVC is not requried. force: opensc.conf: opensc.conf.in force .in: sed \ -e 's|@pkgdatadir[@]|$(pkgdatadir)|g' \ -e 's|@libdir[@]|$(libdir)|g' \ -e 's|@DEFAULT_PCSC_PROVIDER[@]|$(DEFAULT_PCSC_PROVIDER)|g' \ -e 's|@DEFAULT_SM_MODULE[@]|$(DEFAULT_SM_MODULE)|g' \ -e 's|@DEBUG_FILE[@]|$(DEBUG_FILE)|g' \ < $< > $@ install-exec-hook: opensc.conf $(MKDIR_P) "$(DESTDIR)$(sysconfdir)" if [ -f "$(DESTDIR)$(sysconfdir)/opensc.conf" ]; then \ $(INSTALL_DATA) opensc.conf "$(DESTDIR)$(sysconfdir)/opensc.conf.new"; \ else \ $(INSTALL_DATA) opensc.conf "$(DESTDIR)$(sysconfdir)/opensc.conf"; \ fi opensc-0.13.0/etc/opensc.conf.in0000644000015201777760000005326412057406034013355 00000000000000# 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 = 0; # The file to which debug output will be written # # Special values 'stdout' and 'stderr' are recognized. # Default: stderr # # debug_file = @DEBUG_FILE@ # Re-open debug file (used in WIN32) # # In Windows, file handles can not be shared between DLL-s, # each DLL has a separate file handle table. # For that reason reopen debug file before every debug message. # # Default: true # reopen_debug_file = false; # PKCS#15 initialization / personalization # profiles directory for pkcs15-init. # Default: @pkgdatadir@ # # profile_dir = @pkgdatadir@; # Paranoid memory allocation. # # If set to 'true', then refuse to continue when locking of non-pageable # memory fails. This can cause subtle failures but is more secure when # you have a swap disk. # Default: false # # paranoid_memory = false; # CT-API module configuration. reader_driver ctapi { # module @libdir@/libtowitoko.so { # 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. # Default: n/a # max_send_size = 255; # max_recv_size = 256; # # Connect to reader in exclusive mode? # Default: false # connect_exclusive = true; # # What to do when disconnecting from a card (SCardDisconnect) # Valid values: leave, reset, unpower. # Default: reset # disconnect_action = unpower; # # What to do at the end of a transaction (SCardEndTransaction) # Valid values: leave, reset, unpower. # Default: leave # transaction_end_action = reset; # # What to do when reconnection to a card (SCardReconnect) # Valid values: leave, reset, unpower. # Note that this affects only the internal reconnect (after a SCARD_W_RESET_CARD). # A forced reset via sc_reset() always does a full powerup. # Default: leave # reconnect_action = reset; # # Enable pinpad if detected (PC/SC v2.0.2 Part 10) # Default: true # enable_pinpad = false; # # 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; }; # What card drivers to load at start-up # # A special value of 'internal' will load all # statically linked drivers. If an unknown (ie. not # internal) driver is supplied, a separate configuration # configuration block has to be written for the driver. # Default: internal # NOTE: When "internal" keyword is used, must be last entry # # card_drivers = customcos, internal; # Card driver configuration blocks. # For 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@/card_customcos.so; # } # Force using specific card driver # # If this option is present, OpenSC will use the supplied # driver with all inserted cards. # # Default: autodetect # # force_card_driver = customcos; # In addition to the built-in list of known cards in the # card driver, you can configure a new card for the driver # using the card_atr block. The goal is to centralize # everything related to a certain card to card_atr. # # The supported internal card driver names can be retrieved # from the output of: # $ opensc-tool --list-drivers # Generic format: card_atr # New card entry for the flex card driver # card_atr 3b:f0:0d:ca:fe { # All parameters for the context are # optional unless specified otherwise. # Context: global, card driver # # ATR mask value # # The mask is logically AND'd with an # card ATR prior to comparison with the # ATR reference value above. Using mask # allows identifying and configuring # multiple ATRs as the same card model. # atrmask = "ff:ff:ff:ff:ff"; # Context: card driver # # Specify used card driver (REQUIRED). # # When enabled, overrides all possible # settings from the card drivers built-in # card configuration list. # driver = "flex"; # Set card name for card drivers that allows it. # name = "My CryptoFlex card"; # Card type as an integer value. # # Depending on card driver, this allows # tuning the behaviour of the card driver # for your card. # type = "2002"; # Card flags as an hex value. # Multiple values are OR'd together. # # Depending on card driver, this allows # fine-tuning the capabilities in # the card driver for your card. # # Optionally, some known parameters # can be specified as strings: # # rng - On-board random number source # # flags = "rng", "0x80000000"; # # 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"; # } # PIV cards need an entry similar to this one: # card_atr 3B:7D:96:00:00:80:31:80:65:B0:83:11:00:AC:83:00:90:00 { # name = "PIV-II"; # driver = "piv"; # } # Estonian ID card and Micardo driver sometimes only play together with T=0 # In theory only the 'cold' ATR should be specified, as T=0 will # be the preferred protocol once you boot it up with T=0, but be # paranoid. # # Warm ATR v1 card_atr 3b:6e:00:ff:45:73:74:45:49:44:20:76:65:72:20:31:2e:30 { force_protocol = t0; } # Cold ATR v1 card_atr 3b:fe:94:00:ff:80:b1:fa:45:1f:03:45:73:74:45:49:44:20:76:65:72:20:31:2e:30:43 { force_protocol = t0; } # Warm ATR v2 card_atr 3b:5e:11:ff:45:73:74:45:49:44:20:76:65:72:20:31:2e:30 { force_protocol = t0; } # Cold ATR v2 card_atr 3b:de:18:ff:c0:80:b1:fe:45:1f:03:45:73:74:45:49:44:20:76:65:72:20:31:2e:30:2b { force_protocol = t0; } # Digi-ID cold ATR. The same card has the same warm ATR as "Cold ATR v1" above # The card is claimed to only support T=0 but in fact (sometimes) works with T=1, even if not advertised in ATR. card_atr 3b:6e:00:00:45:73:74:45:49:44:20:76:65:72:20:31:2e:30 { force_protocol = t0; } # Cold ATR v3 dev1 card_atr 3b:fe:18:00:00:80:31:fe:45:45:73:74:45:49:44:20:76:65:72:20:31:2e:30:a8 { force_protocol = t0; } # Warm ATR v3 dev1 card_atr 3b:fe:18:00:00:80:31:fe:45:80:31:80:66:40:90:a4:56:1b:16:83:01:90:00:86 { force_protocol = t0; } # Warm ATR v3 dev2 card_atr 3b:fe:18:00:00:80:31:fe:45:80:31:80:66:40:90:a4:16:2a:00:83:01:90:00:e1 { force_protocol = t0; } # Warm ATR v3 (18.01.2011) card_atr 3b:fe:18:00:00:80:31:fe:45:80:31:80:66:40:90:a4:16:2a:00:83:0f:90:00:ef { force_protocol = t0; } # D-Trust cards are also based on micardo and need T=0 for some reason card_atr 3b:ff:94:00:ff:80:b1:fe:45:1f:03:00:68:d2:76:00:00:28:ff:05:1e:31:80:00:90:00:23 { force_protocol = t0; } card_atr 3b:ff:11:00:ff:80:b1:fe:45:1f:03:00:68:d2:76:00:00:28:ff:05:1e:31:80:00:90:00:a6 { force_protocol = t0; } # Oberthur's AuthentIC v3.2.2 card_atr 3B:DD:18:00:81:31:FE:45:80:F9:A0:00:00:00:77:01:00:70:0A:90:00:8B { type = 11100; driver = "authentic"; name = "AuthentIC v3.1"; # Name of SM configuration sub-section # secure_messaging = local_authentic; } # IAS/ECC cards card_atr 3B:7F:96:00:00:00:31:B9:64:40:70:14:10:73:94:01:80:82:90:00 { type = 25001; driver = "iasecc"; name = "Gemalto MultiApp IAS/ECC v1.0.1"; secure_messaging = local_gemalto_iam; # secure_messaging = local_adele; md_read_only = false; md_supports_X509_enrollment = true; } card_atr 3B:7F:96:00:00:00:31:B8:64:40:70:14:10:73:94:01:80:82:90:00 { type = 25001; driver = "iasecc"; name = "Gemalto MultiApp IAS/ECC v1.0.1"; secure_messaging = local_gemalto_iam; md_read_only = false; md_supports_X509_enrollment = true; } #card_atr 3B:DD:18:00:81:31:FE:45:80:F9:A0:00:00:00:77:01:08:00:07:90:00:FE { # type = 25002; # driver = "iasecc"; # name = "Oberthur IAS/ECC v1.0.1"; # # No 'admin' application for this card -- no secure messaging #} #card_atr 3B:7F:18:00:00:00:31:B8:64:50:23:EC:C1:73:94:01:80:82:90:00 { # type = 25003; # driver = "iasecc"; # name = "Morpho YpsID S3 IAS/ECC"; # # secure_messaging = local_morpho_YpsID_S3; #} card_atr 3B:DF:18:FF:81:91:FE:1F:C3:00:31:B8:64:0C:01:EC:C1:73:94:01:80:82:90:00:B3 { type = 25004; driver = "iasecc"; name = "Amos IAS/ECC v1.0.1"; md_read_only = false; md_supports_X509_enrollment = true; secure_messaging = local_amos; } card_atr 3B:DC:18:FF:81:91:FE:1F:C3:80:73:C8:21:13:66:01:0B:03:52:00:05:38 { type = 25004; driver = "iasecc"; name = "Amos IAS/ECC v1.0.1"; md_read_only = false; md_supports_X509_enrollment = true; secure_messaging = local_amos_eid; } secure_messaging local_authentic { #path to ans name of external SM module #module_name = @DEFAULT_SM_MODULE@; #module_path = @libdir@; # 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 = @libdir@; #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 = @libdir@; 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 = @libdir@; 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 = @libdir@; #module_data = ""; type = acl; # transmit, acl ifd_serial = "11:22:33:44:55:66:77:88"; # Keyset values from 'Adele' profiles of the IAS/ECC cards keyset_01_enc = "EMENCECHANTILLON"; keyset_01_mac = "EMMACECHANTILLON"; keyset_02_enc = "AAENCECHANTILLON"; keyset_02_mac = "AAMACECHANTILLON"; keyset_E828BD080FD2500000040301_02_enc = "E2ENCECHANTILLON"; keyset_E828BD080FD2500000040301_02_mac = "E2MACECHANTILLON"; keyset_D2500000044164E86C650101_02_enc = "E1ENCECHANTILLON"; keyset_D2500000044164E86C650101_02_mac = "E1MACECHANTILLON"; keyset_D2500000044164E86C650101_03_enc = "SIENCECHANTILLON"; keyset_D2500000044164E86C650101_03_mac = "SIMACECHANTILLON"; } # Below are the framework specific configuration blocks. # PKCS #15 framework pkcs15 { # Whether to use the cache files in the user's # home directory. # # At the moment you have to 'teach' the card # to the system by running command: pkcs15-tool -L # # WARNING: Caching shouldn't be used in setuid root # applications. # Default: false # use_file_caching = true; # # Use PIN caching? # Default: true # use_pin_caching = false; # # How many times to use a PIN from cache before re-authenticating it? # Default: 10 # pin_cache_counter = 3; # # Older PKCS#11 applications not supporting CKA_ALWAYS_AUTHENTICATE # may need to set this to get signatures to work with some cards. # Default: false # pin_cache_ignore_user_consent = true; # # Enable pkcs15 emulation. # Default: yes # enable_pkcs15_emulation = no; # # Prefer pkcs15 emulation code before # the normal pkcs15 processing. # Some cards (like esteid and pteid) work in emu-only mode, # and do not depend on this option. # # Default: no # try_emulation_first = yes; # Enable builtin emulators. # Default: yes # enable_builtin_emulation = no; # # List of the builtin pkcs15 emulators to test # Default: esteid, openpgp, tcos, starcert, itacns, infocamere, postecert, actalis, atrust-acos, gemsafeGPK, gemsafeV1, tccardos, PIV-II; # builtin_emulators = openpgp; # additional settings per driver # # For pkcs15 emulators loaded from an external shared # library/DLL, you need to specify the path name of the module # and customize the card_atr example above correctly. # # emulate custom { # The location of the driver library # module = @libdir@/p15emu_custom.so; # } # some additional application parameters: # - type (generic, protected) used to distinguish the common access application # and application for which authentication to perform some operation cannot be # obtained with the common procedures (ex. object creation protected by secure messaging). # Used by PKCS#11 module configurated to expose restricted number of slots. # (for ex. configurated to expose only User PIN slot, User and Sign PINs slots, ...) application E828BD080FD25047656E65726963 { type = generic; model = "ECC Generic PKI"; } application E828BD080FD2500000040301 { type = generic; model = "Adèle Générique"; } application E828BD080FD2504543432D654944 { type = protected; model = "ECC eID"; } application E828BD080FD2500000040201 { type = protected; model = "Adèle Admin-2"; } } } # Parameters for the OpenSC PKCS11 module app opensc-pkcs11 { pkcs11 { # Should the module support hotplug of readers as per PKCS#11 v2.20? # This affects slot changes and PC/SC PnP, as v2.11 applications # are not allowed to change the length of the slot list. # Default: true # plug_and_play = false; # Maximum Number of virtual slots. # If there are more slots than defined here, # the remaining slots will be hidden from PKCS#11. # Default: 16 # max_virtual_slots = 32; # Maximum number of slots per smart card. # If the card has fewer keys than defined here, # the remaining number of slots will be empty. # Default: 4 # slots_per_card = 2; # (max_virtual_slots/slots_per_card) limits the number of readers # that can be used on the system. Default is then 16/4=4 readers. # Normally, the pkcs11 module will create # the full number of slots defined above by # num_slots. If there are fewer pins/keys on # the card, the remaining keys will be empty # (and you will be able to create new objects # within them). # Default: true # hide_empty_tokens = false; # By default, the OpenSC PKCS#11 module will not lock your card # once you authenticate to the card via C_Login. # # Thus the other users or other applications is not prevented # from connecting to the card and perform crypto operations # (which may be possible because you have already authenticated # with the card). This setting is not very secure. # # Also, if your card is not locked, you can enconter problems # due to limitation of the OpenSC framework, that still is not # thoroughly tested in the multi threads environment. # # Your settings will be more secure if you choose to lock your # card. Nevertheless this behavior is a known violation of PKCS#11 # specification. Now once one application has started using your # card with C_Login, no other application can use it, until # the first is done and calls C_Logout or C_Finalize. In the case # of many PKCS#11 application this does not happen until you exit # the application. # Thus it is impossible to use several smart card aware applications # at the same time, e.g. you cannot run both Firefox and Thunderbird at # the same time, if both are configured to use your smart card. # # Default: false # lock_login = true; # User PIN unblock style # none: PIN unblock is not possible with PKCS#11 API; # set_pin_in_unlogged_session: C_SetPIN() in unlogged session: # PUK is passed as the 'OldPin' argument of the C_SetPIN() call. # set_pin_in_specific_context: C_SetPIN() in the CKU_SPECIFIC_CONTEXT logged session: # PUK is passed as the 'OldPin' argument of the C_SetPIN() call. # init_pin_in_so_session: C_InitPIN() in CKU_SO logged session: # User PIN 'UNBLOCK' is protected by SOPIN. (PUK == SOPIN). # # Actually this style works only for the PKCS15 contents without SOPIN. # # For those with SOPIN, this mode will be usefull for the cards without # # modes 00 and 01 of ISO command 'RESET RETRY COUNTER'. --vt # # Default: none # user_pin_unblock_style = set_pin_in_unlogged_session; # Create slot for unblocking PIN with PUK # This way PKCS#11 API can be used to login with PUK and # change a PIN. # Warning: causes problems with some applications like # firefox and thunderbird. Thus turned off by default # # Default: false # create_puk_slot = true; # Report as 'zero' the CKA_ID attribute of CA certificate # For the unknown reason the middleware of the manufacturer of gemalto (axalto, gemplus) # card reports as '0' the CKA_ID of CA cartificates. # Maybe someone else will need it. (Would be nice to know who and what for -- VTA) # # Default: false # zero_ckaid_for_ca_certs = true; # List of readers to ignore # If any of the strings listed below is matched (case sensitive) in a reader name, # the reader is ignored by the PKCS#11 module. # # Default: empty # ignored_readers = "CardMan 1021", "SPR 532"; # Symbolic names of PINs for which slots are created # Card can contain more then one PINs or more then one on-card application with # its own PINs. Normally, to access all of them with the PKCS#11 API a slot has to be # created for all of them. Many slots could be ennoying for some of widely used application, # like FireFox. This configuration parameter allows to select the PINs or on-card application # for which PKCS#11 slot will be created. # Actually recognised following symbolic names: # 'user', 'sign', 'application', 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. # 'application' slot created for each on-card application, # even if they use a common global PIN. # '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 = application; # create_slots_for_pins = "application,sign"; } } # 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; } } # XXX: remove cardmod pseudodriver app cardmod { # cardmod app name use special pcsc reader subset # fix options for this reader driver here. reader_driver cardmod { # Enable pinpad if detected (PC/SC v2.0.2 Part 10) # Default: true # enable_pinpad = false; } } opensc-0.13.0/etc/Makefile.in0000644000015201777760000003045712057406055012661 00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program 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. @SET_MAKE@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = etc DIST_COMMON = $(dist_noinst_DATA) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ SOURCES = DIST_SOURCES = DATA = $(dist_noinst_DATA) $(nodist_noinst_DATA) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUG_FILE = @DEBUG_FILE@ DEFAULT_PCSC_PROVIDER = @DEFAULT_PCSC_PROVIDER@ DEFAULT_SM_MODULE = @DEFAULT_SM_MODULE@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GIT = @GIT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBRARY_BITNESS = @LIBRARY_BITNESS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCT_CFLAGS = @OPENCT_CFLAGS@ OPENCT_LIBS = @OPENCT_LIBS@ OPENSC_LT_AGE = @OPENSC_LT_AGE@ OPENSC_LT_CURRENT = @OPENSC_LT_CURRENT@ OPENSC_LT_OLDEST = @OPENSC_LT_OLDEST@ OPENSC_LT_REVISION = @OPENSC_LT_REVISION@ OPENSC_VERSION_FIX = @OPENSC_VERSION_FIX@ OPENSC_VERSION_MAJOR = @OPENSC_VERSION_MAJOR@ OPENSC_VERSION_MINOR = @OPENSC_VERSION_MINOR@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OPTIONAL_OPENCT_CFLAGS = @OPTIONAL_OPENCT_CFLAGS@ OPTIONAL_OPENCT_LIBS = @OPTIONAL_OPENCT_LIBS@ OPTIONAL_OPENSSL_CFLAGS = @OPTIONAL_OPENSSL_CFLAGS@ OPTIONAL_OPENSSL_LIBS = @OPTIONAL_OPENSSL_LIBS@ OPTIONAL_PCSC_CFLAGS = @OPTIONAL_PCSC_CFLAGS@ OPTIONAL_READLINE_CFLAGS = @OPTIONAL_READLINE_CFLAGS@ OPTIONAL_READLINE_LIBS = @OPTIONAL_READLINE_LIBS@ OPTIONAL_ZLIB_CFLAGS = @OPTIONAL_ZLIB_CFLAGS@ OPTIONAL_ZLIB_LIBS = @OPTIONAL_ZLIB_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCSC_CFLAGS = @PCSC_CFLAGS@ PCSC_LIBS = @PCSC_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RC = @RC@ READLINE_CFLAGS = @READLINE_CFLAGS@ READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ WIN_LIBPREFIX = @WIN_LIBPREFIX@ XSLTPROC = @XSLTPROC@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ git = @git@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkcs11dir = @pkcs11dir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ xslstylesheetsdir = @xslstylesheetsdir@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in $(srcdir)/opensc.conf.win DISTCLEANFILES = opensc.conf opensc.conf.win EXTRA_DIST = Makefile.mak SUFFIXES = .in dist_noinst_DATA = opensc.conf.in opensc.conf.win.in nodist_noinst_DATA = opensc.conf opensc.conf.win # For Windows MSVC build all: all-am .SUFFIXES: .SUFFIXES: .in $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign etc/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign etc/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags: TAGS TAGS: ctags: CTAGS CTAGS: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-exec-hook install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-exec-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ distclean distclean-generic distclean-libtool distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-exec-hook install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ uninstall uninstall-am # Make sure we build this every time # as there is no dependency for this. # Can be removed if MSVC is not requried. force: opensc.conf: opensc.conf.in force .in: sed \ -e 's|@pkgdatadir[@]|$(pkgdatadir)|g' \ -e 's|@libdir[@]|$(libdir)|g' \ -e 's|@DEFAULT_PCSC_PROVIDER[@]|$(DEFAULT_PCSC_PROVIDER)|g' \ -e 's|@DEFAULT_SM_MODULE[@]|$(DEFAULT_SM_MODULE)|g' \ -e 's|@DEBUG_FILE[@]|$(DEBUG_FILE)|g' \ < $< > $@ install-exec-hook: opensc.conf $(MKDIR_P) "$(DESTDIR)$(sysconfdir)" if [ -f "$(DESTDIR)$(sysconfdir)/opensc.conf" ]; then \ $(INSTALL_DATA) opensc.conf "$(DESTDIR)$(sysconfdir)/opensc.conf.new"; \ else \ $(INSTALL_DATA) opensc.conf "$(DESTDIR)$(sysconfdir)/opensc.conf"; \ fi # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: opensc-0.13.0/etc/opensc.conf.win.in0000644000015201777760000005341012057406034014142 00000000000000# 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 = 0; # The file to which debug output will be written # # Special values 'stdout' and 'stderr' are recognized. # Default: stderr # # debug_file = %TEMP%\opensc-debug.log # Re-open debug file (used in WIN32) # # In Windows, file handles can not be shared between DLL-s, # each DLL has a separate file handle table. # For that reason reopen debug file before every debug message. # # Default: true # reopen_debug_file = false; # PKCS#15 initialization / personalization # profiles directory for pkcs15-init. # Default: obtained from windows registers # # profile_dir = ""; # Paranoid memory allocation. # # If set to 'true', then refuse to continue when locking of non-pageable # memory fails. This can cause subtle failures but is more secure when # you have a swap disk. # Default: false # # paranoid_memory = false; # CT-API module configuration. reader_driver ctapi { # module @libdir@/libtowitoko.so { # 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. # Default: n/a # max_send_size = 255; # max_recv_size = 256; # # Connect to reader in exclusive mode? # Default: false # connect_exclusive = true; # # What to do when disconnecting from a card (SCardDisconnect) # Valid values: leave, reset, unpower. # Default: reset # disconnect_action = unpower; # # What to do at the end of a transaction (SCardEndTransaction) # Valid values: leave, reset, unpower. # Default: leave # transaction_end_action = reset; # # What to do when reconnection to a card (SCardReconnect) # Valid values: leave, reset, unpower. # Note that this affects only the internal reconnect (after a SCARD_W_RESET_CARD). # A forced reset via sc_reset() always does a full powerup. # Default: leave # reconnect_action = reset; # # Enable pinpad if detected (PC/SC v2.0.2 Part 10) # Default: true # enable_pinpad = false; # # Use specific pcsc provider. # Default: winscard.dll # provider_library = winscard.dll } # 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; }; # What card drivers to load at start-up # # A special value of 'internal' will load all # statically linked drivers. If an unknown (ie. not # internal) driver is supplied, a separate configuration # configuration block has to be written for the driver. # Default: internal # NOTE: When "internal" keyword is used, must be last entry # # card_drivers = customcos, internal; # Card driver configuration blocks. # For 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@/card_customcos.so; # } # Force using specific card driver # # If this option is present, OpenSC will use the supplied # driver with all inserted cards. # # Default: autodetect # # force_card_driver = customcos; # In addition to the built-in list of known cards in the # card driver, you can configure a new card for the driver # using the card_atr block. The goal is to centralize # everything related to a certain card to card_atr. # # The supported internal card driver names can be retrieved # from the output of: # $ opensc-tool --list-drivers # Generic format: card_atr # New card entry for the flex card driver # card_atr 3b:f0:0d:ca:fe { # All parameters for the context are # optional unless specified otherwise. # Context: global, card driver # # ATR mask value # # The mask is logically AND'd with an # card ATR prior to comparison with the # ATR reference value above. Using mask # allows identifying and configuring # multiple ATRs as the same card model. # atrmask = "ff:ff:ff:ff:ff"; # Context: card driver # # Specify used card driver (REQUIRED). # # When enabled, overrides all possible # settings from the card drivers built-in # card configuration list. # driver = "flex"; # Set card name for card drivers that allows it. # name = "My CryptoFlex card"; # Card type as an integer value. # # Depending on card driver, this allows # tuning the behaviour of the card driver # for your card. # type = "2002"; # Card flags as an hex value. # Multiple values are OR'd together. # # Depending on card driver, this allows # fine-tuning the capabilities in # the card driver for your card. # # Optionally, some known parameters # can be specified as strings: # # rng - On-board random number source # # flags = "rng", "0x80000000"; # # 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"; # } # PIV cards need an entry similar to this one: # card_atr 3B:7D:96:00:00:80:31:80:65:B0:83:11:00:AC:83:00:90:00 { # name = "PIV-II"; # driver = "piv"; # } # Estonian ID card and Micardo driver sometimes only play together with T=0 # In theory only the 'cold' ATR should be specified, as T=0 will # be the preferred protocol once you boot it up with T=0, but be # paranoid. # # Warm ATR v1 card_atr 3b:6e:00:ff:45:73:74:45:49:44:20:76:65:72:20:31:2e:30 { force_protocol = t0; } # Cold ATR v1 card_atr 3b:fe:94:00:ff:80:b1:fa:45:1f:03:45:73:74:45:49:44:20:76:65:72:20:31:2e:30:43 { force_protocol = t0; } # Warm ATR v2 card_atr 3b:5e:11:ff:45:73:74:45:49:44:20:76:65:72:20:31:2e:30 { force_protocol = t0; } # Cold ATR v2 card_atr 3b:de:18:ff:c0:80:b1:fe:45:1f:03:45:73:74:45:49:44:20:76:65:72:20:31:2e:30:2b { force_protocol = t0; } # Digi-ID cold ATR. The same card has the same warm ATR as "Cold ATR v1" above # The card is claimed to only support T=0 but in fact (sometimes) works with T=1, even if not advertised in ATR. card_atr 3b:6e:00:00:45:73:74:45:49:44:20:76:65:72:20:31:2e:30 { force_protocol = t0; } # Cold ATR v3 dev1 card_atr 3b:fe:18:00:00:80:31:fe:45:45:73:74:45:49:44:20:76:65:72:20:31:2e:30:a8 { force_protocol = t0; } # Warm ATR v3 dev1 card_atr 3b:fe:18:00:00:80:31:fe:45:80:31:80:66:40:90:a4:56:1b:16:83:01:90:00:86 { force_protocol = t0; } # Warm ATR v3 dev2 card_atr 3b:fe:18:00:00:80:31:fe:45:80:31:80:66:40:90:a4:16:2a:00:83:01:90:00:e1 { force_protocol = t0; } # Warm ATR v3 (18.01.2011) card_atr 3b:fe:18:00:00:80:31:fe:45:80:31:80:66:40:90:a4:16:2a:00:83:0f:90:00:ef { force_protocol = t0; } # D-Trust cards are also based on micardo and need T=0 for some reason card_atr 3b:ff:94:00:ff:80:b1:fe:45:1f:03:00:68:d2:76:00:00:28:ff:05:1e:31:80:00:90:00:23 { force_protocol = t0; } card_atr 3b:ff:11:00:ff:80:b1:fe:45:1f:03:00:68:d2:76:00:00:28:ff:05:1e:31:80:00:90:00:a6 { force_protocol = t0; } # Oberthur's AuthentIC v3.2.2 card_atr 3B:DD:18:00:81:31:FE:45:80:F9:A0:00:00:00:77:01:00:70:0A:90:00:8B { type = 11100; driver = "authentic"; name = "AuthentIC v3.1"; # Name of SM configuration sub-section # secure_messaging = local_authentic; } # IAS/ECC cards card_atr 3B:7F:96:00:00:00:31:B9:64:40:70:14:10:73:94:01:80:82:90:00 { type = 25001; driver = "iasecc"; name = "Gemalto MultiApp IAS/ECC v1.0.1"; secure_messaging = local_gemalto_iam; # secure_messaging = local_adele; md_read_only = false; md_supports_X509_enrollment = true; } card_atr 3B:7F:96:00:00:00:31:B8:64:40:70:14:10:73:94:01:80:82:90:00 { type = 25001; driver = "iasecc"; name = "Gemalto MultiApp IAS/ECC v1.0.1"; secure_messaging = local_gemalto_iam; md_read_only = false; md_supports_X509_enrollment = true; } #card_atr 3B:DD:18:00:81:31:FE:45:80:F9:A0:00:00:00:77:01:08:00:07:90:00:FE { # type = 25002; # driver = "iasecc"; # name = "Oberthur IAS/ECC v1.0.1"; # # No 'admin' application for this card -- no secure messaging #} #card_atr 3B:7F:18:00:00:00:31:B8:64:50:23:EC:C1:73:94:01:80:82:90:00 { # type = 25003; # driver = "iasecc"; # name = "Morpho YpsID S3 IAS/ECC"; # # secure_messaging = local_morpho_YpsID_S3; #} card_atr 3B:DF:18:FF:81:91:FE:1F:C3:00:31:B8:64:0C:01:EC:C1:73:94:01:80:82:90:00:B3 { type = 25004; driver = "iasecc"; name = "Amos IAS/ECC v1.0.1"; md_read_only = false; md_supports_X509_enrollment = true; secure_messaging = local_amos; } card_atr 3B:DC:18:FF:81:91:FE:1F:C3:80:73:C8:21:13:66:01:0B:03:52:00:05:38 { type = 25004; driver = "iasecc"; name = "Amos IAS/ECC v1.0.1"; md_read_only = false; md_supports_X509_enrollment = true; secure_messaging = local_amos_eid; } secure_messaging local_authentic { # name of external SM module # module_name = smm-local.dll; # directory with external SM module # Default: defined by windows register # 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 = smm-local.dll; # 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 = smm-local.dll; # 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 = smm-local.dll; # 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 = smm-local.dll; # module_path = ""; # module_data = ""; type = acl; # transmit, acl ifd_serial = "11:22:33:44:55:66:77:88"; # Keyset values from 'Adele' profiles of the IAS/ECC cards keyset_01_enc = "EMENCECHANTILLON"; keyset_01_mac = "EMMACECHANTILLON"; keyset_02_enc = "AAENCECHANTILLON"; keyset_02_mac = "AAMACECHANTILLON"; keyset_E828BD080FD2500000040301_02_enc = "E2ENCECHANTILLON"; keyset_E828BD080FD2500000040301_02_mac = "E2MACECHANTILLON"; keyset_D2500000044164E86C650101_02_enc = "E1ENCECHANTILLON"; keyset_D2500000044164E86C650101_02_mac = "E1MACECHANTILLON"; keyset_D2500000044164E86C650101_03_enc = "SIENCECHANTILLON"; keyset_D2500000044164E86C650101_03_mac = "SIMACECHANTILLON"; } # Below are the framework specific configuration blocks. # PKCS #15 framework pkcs15 { # Whether to use the cache files in the user's # home directory. # # At the moment you have to 'teach' the card # to the system by running command: pkcs15-tool -L # # WARNING: Caching shouldn't be used in setuid root # applications. # Default: false # use_file_caching = true; # # Use PIN caching? # Default: true # use_pin_caching = false; # # How many times to use a PIN from cache before re-authenticating it? # Default: 10 # pin_cache_counter = 3; # # Older PKCS#11 applications not supporting CKA_ALWAYS_AUTHENTICATE # may need to set this to get signatures to work with some cards. # Default: false # pin_cache_ignore_user_consent = true; # # Enable pkcs15 emulation. # Default: yes # enable_pkcs15_emulation = no; # # Prefer pkcs15 emulation code before # the normal pkcs15 processing. # Some cards (like esteid and pteid) work in emu-only mode, # and do not depend on this option. # # Default: no # try_emulation_first = yes; # Enable builtin emulators. # Default: yes # enable_builtin_emulation = no; # # List of the builtin pkcs15 emulators to test # Default: esteid, openpgp, tcos, starcert, itacns, infocamere, postecert, actalis, atrust-acos, gemsafeGPK, gemsafeV1, tccardos, PIV-II; # builtin_emulators = openpgp; # additional settings per driver # # For pkcs15 emulators loaded from an external shared # library/DLL, you need to specify the path name of the module # and customize the card_atr example above correctly. # # emulate custom { # The location of the driver library # module = p15emu_custom.dll; # } # some additional application parameters: # - type (generic, protected) used to distinguish the common access application # and application for which authentication to perform some operation cannot be # obtained with the common procedures (ex. object creation protected by secure messaging). # Used by PKCS#11 module configurated to expose restricted number of slots. # (for ex. configurated to expose only User PIN slot, User and Sign PINs slots, ...) application E828BD080FD25047656E65726963 { type = generic; model = "ECC Generic PKI"; } application E828BD080FD2500000040301 { type = generic; model = "Adèle Générique"; } application E828BD080FD2504543432D654944 { type = protected; model = "ECC eID"; } application E828BD080FD2500000040201 { type = protected; model = "Adèle Admin-2"; } } } # Parameters for the OpenSC PKCS11 module app opensc-pkcs11 { pkcs11 { # Should the module support hotplug of readers as per PKCS#11 v2.20? # This affects slot changes and PC/SC PnP, as v2.11 applications # are not allowed to change the length of the slot list. # Default: true # plug_and_play = false; # Maximum Number of virtual slots. # If there are more slots than defined here, # the remaining slots will be hidden from PKCS#11. # Default: 16 # max_virtual_slots = 32; # Maximum number of slots per smart card. # If the card has fewer keys than defined here, # the remaining number of slots will be empty. # Default: 4 # slots_per_card = 2; # (max_virtual_slots/slots_per_card) limits the number of readers # that can be used on the system. Default is then 16/4=4 readers. # Normally, the pkcs11 module will create # the full number of slots defined above by # num_slots. If there are fewer pins/keys on # the card, the remaining keys will be empty # (and you will be able to create new objects # within them). # Default: true # hide_empty_tokens = false; # By default, the OpenSC PKCS#11 module will not lock your card # once you authenticate to the card via C_Login. # # Thus the other users or other applications is not prevented # from connecting to the card and perform crypto operations # (which may be possible because you have already authenticated # with the card). This setting is not very secure. # # Also, if your card is not locked, you can enconter problems # due to limitation of the OpenSC framework, that still is not # thoroughly tested in the multi threads environment. # # Your settings will be more secure if you choose to lock your # card. Nevertheless this behavior is a known violation of PKCS#11 # specification. Now once one application has started using your # card with C_Login, no other application can use it, until # the first is done and calls C_Logout or C_Finalize. In the case # of many PKCS#11 application this does not happen until you exit # the application. # Thus it is impossible to use several smart card aware applications # at the same time, e.g. you cannot run both Firefox and Thunderbird at # the same time, if both are configured to use your smart card. # # Default: false # lock_login = true; # User PIN unblock style # none: PIN unblock is not possible with PKCS#11 API; # set_pin_in_unlogged_session: C_SetPIN() in unlogged session: # PUK is passed as the 'OldPin' argument of the C_SetPIN() call. # set_pin_in_specific_context: C_SetPIN() in the CKU_SPECIFIC_CONTEXT logged session: # PUK is passed as the 'OldPin' argument of the C_SetPIN() call. # init_pin_in_so_session: C_InitPIN() in CKU_SO logged session: # User PIN 'UNBLOCK' is protected by SOPIN. (PUK == SOPIN). # # Actually this style works only for the PKCS15 contents without SOPIN. # # For those with SOPIN, this mode will be usefull for the cards without # # modes 00 and 01 of ISO command 'RESET RETRY COUNTER'. --vt # # Default: none # user_pin_unblock_style = set_pin_in_unlogged_session; # Create slot for unblocking PIN with PUK # This way PKCS#11 API can be used to login with PUK and # change a PIN. # Warning: causes problems with some applications like # firefox and thunderbird. Thus turned off by default # # Default: false # create_puk_slot = true; # Report as 'zero' the CKA_ID attribute of CA certificate # For the unknown reason the middleware of the manufacturer of gemalto (axalto, gemplus) # card reports as '0' the CKA_ID of CA cartificates. # Maybe someone else will need it. (Would be nice to know who and what for -- VTA) # # Default: false # zero_ckaid_for_ca_certs = true; # List of readers to ignore # If any of the strings listed below is matched (case sensitive) in a reader name, # the reader is ignored by the PKCS#11 module. # # Default: empty # ignored_readers = "CardMan 1021", "SPR 532"; # Symbolic names of PINs for which slots are created # Card can contain more then one PINs or more then one on-card application with # its own PINs. Normally, to access all of them with the PKCS#11 API a slot has to be # created for all of them. Many slots could be ennoying for some of widely used application, # like FireFox. This configuration parameter allows to select the PINs or on-card application # for which PKCS#11 slot will be created. # Actually recognised following symbolic names: # 'user', 'sign', 'application', 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. # 'application' slot created for each on-card application, # even if they use a common global PIN. # '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 = application; # create_slots_for_pins = "application,sign"; } } # 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; } } # XXX: remove cardmod pseudodriver app cardmod { # cardmod app name use special pcsc reader subset # fix options for this reader driver here. reader_driver cardmod { # Enable pinpad if detected (PC/SC v2.0.2 Part 10) # Default: true # enable_pinpad = false; } } opensc-0.13.0/doc/0000755000015201777760000000000012057406120010646 500000000000000opensc-0.13.0/doc/api.css0000644000015201777760000000101512057406034012052 00000000000000body { font-family: Verdana, Arial; font-size: 0.9em; } .title { font-size: 1.5em; text-align: center; } .toc b { font-size: 1.2em; border-bottom: dashed 1px black; } a { color: blue; text-decoration: none; } a:visited { color: blue; text-decoration: none; } pre.programlisting { font-size: 1.1em; background-color: #EEEEEE ; border: 1px solid #006600 ; padding: 1em; } span.symbol { font-weight: bold; } span.errorname { font-weight: bold; } span.errortext { font-style: italic; } opensc-0.13.0/doc/Makefile.am0000644000015201777760000000020112057406034012617 00000000000000MAINTAINERCLEANFILES = $(srcdir)/Makefile.in SUBDIRS = tools dist_noinst_SCRIPTS = html.xsl man.xsl dist_noinst_DATA = api.css opensc-0.13.0/doc/html.xsl0000644000015201777760000000063512057406034012272 00000000000000 ]> opensc-0.13.0/doc/Makefile.in0000644000015201777760000004464012057406055012652 00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program 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. @SET_MAKE@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = doc DIST_COMMON = $(dist_noinst_DATA) $(dist_noinst_SCRIPTS) \ $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = SCRIPTS = $(dist_noinst_SCRIPTS) AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ html-recursive info-recursive install-data-recursive \ install-dvi-recursive install-exec-recursive \ install-html-recursive install-info-recursive \ install-pdf-recursive install-ps-recursive install-recursive \ installcheck-recursive installdirs-recursive pdf-recursive \ ps-recursive uninstall-recursive DATA = $(dist_noinst_DATA) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ distdir ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUG_FILE = @DEBUG_FILE@ DEFAULT_PCSC_PROVIDER = @DEFAULT_PCSC_PROVIDER@ DEFAULT_SM_MODULE = @DEFAULT_SM_MODULE@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GIT = @GIT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBRARY_BITNESS = @LIBRARY_BITNESS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCT_CFLAGS = @OPENCT_CFLAGS@ OPENCT_LIBS = @OPENCT_LIBS@ OPENSC_LT_AGE = @OPENSC_LT_AGE@ OPENSC_LT_CURRENT = @OPENSC_LT_CURRENT@ OPENSC_LT_OLDEST = @OPENSC_LT_OLDEST@ OPENSC_LT_REVISION = @OPENSC_LT_REVISION@ OPENSC_VERSION_FIX = @OPENSC_VERSION_FIX@ OPENSC_VERSION_MAJOR = @OPENSC_VERSION_MAJOR@ OPENSC_VERSION_MINOR = @OPENSC_VERSION_MINOR@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OPTIONAL_OPENCT_CFLAGS = @OPTIONAL_OPENCT_CFLAGS@ OPTIONAL_OPENCT_LIBS = @OPTIONAL_OPENCT_LIBS@ OPTIONAL_OPENSSL_CFLAGS = @OPTIONAL_OPENSSL_CFLAGS@ OPTIONAL_OPENSSL_LIBS = @OPTIONAL_OPENSSL_LIBS@ OPTIONAL_PCSC_CFLAGS = @OPTIONAL_PCSC_CFLAGS@ OPTIONAL_READLINE_CFLAGS = @OPTIONAL_READLINE_CFLAGS@ OPTIONAL_READLINE_LIBS = @OPTIONAL_READLINE_LIBS@ OPTIONAL_ZLIB_CFLAGS = @OPTIONAL_ZLIB_CFLAGS@ OPTIONAL_ZLIB_LIBS = @OPTIONAL_ZLIB_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCSC_CFLAGS = @PCSC_CFLAGS@ PCSC_LIBS = @PCSC_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RC = @RC@ READLINE_CFLAGS = @READLINE_CFLAGS@ READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ WIN_LIBPREFIX = @WIN_LIBPREFIX@ XSLTPROC = @XSLTPROC@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ git = @git@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkcs11dir = @pkcs11dir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ xslstylesheetsdir = @xslstylesheetsdir@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in SUBDIRS = tools dist_noinst_SCRIPTS = html.xsl man.xsl dist_noinst_DATA = api.css all: all-recursive .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign doc/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run `make' without going through this Makefile. # To change the values of `make' variables: instead of editing Makefiles, # (1) if the variable is set in `config.status', edit `config.status' # (which will cause the Makefiles to be regenerated when you run `make'); # (2) otherwise, pass the desired values on the `make' command line. $(RECURSIVE_TARGETS): @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ *k*) failcom='fail=yes';; \ esac; \ done; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ list='$(SUBDIRS)'; for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" $(RECURSIVE_CLEAN_TARGETS): @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ *k*) failcom='fail=yes';; \ esac; \ done; \ dot_seen=no; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ rev=''; for subdir in $$list; do \ if test "$$subdir" = "."; then :; else \ rev="$$subdir $$rev"; \ fi; \ done; \ rev="$$rev ."; \ target=`echo $@ | sed s/-recursive//`; \ for subdir in $$rev; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done && test -z "$$fail" tags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ done ctags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ done ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: CTAGS CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(SCRIPTS) $(DATA) installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \ install-am install-strip tags-recursive .PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ all all-am check check-am clean clean-generic clean-libtool \ ctags ctags-recursive distclean distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \ uninstall uninstall-am # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: opensc-0.13.0/doc/man.xsl0000644000015201777760000000025312057406034012075 00000000000000 opensc-0.13.0/doc/tools/0000755000015201777760000000000012057406120012006 500000000000000opensc-0.13.0/doc/tools/pkcs15-tool.1.xml0000644000015201777760000002000512057406034014671 00000000000000 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 aid Specify in a hexadecimal form the AID of the on-card PKCS#15 application to bind to. pin, pin 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. , Dump card objects. , Cache PKCS #15 token data to the local filesystem. Subsequent operations are performed on the cached data where possible. If the cache becomes out-of-sync with the token state (eg. new key is generated and stored on the token), the cache should be updated or operations may show stale results. List the on-card PKCS#15 applications , Lists all certificates stored on the token. , Lists 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. , Lists 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. Lists all PINs stored on the token. General information about each PIN is listed (eg. PIN name). Actual PIN values are not shown. Lists all public keys stored on the token, including key name, id, algorithm and length information. Disables token data caching. 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. cert, cert Reads the certificate with the given id. cert, data Reads data object with OID, applicationName or label. 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. num Forces pkcs15-tool to use reader number num for operations. The default is to use reader number 0, the first reader in the system. , 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. Verify PIN after card binding and before issuing any command (without 'auth-id' the first non-SO, non-Unblock PIN will be verified) See also pkcs15-init 1 , pkcs15-crypt 1 opensc-0.13.0/doc/tools/piv-tool.1.xml0000644000015201777760000001615012057406034014367 00000000000000 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 intened 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 or 3DES 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. The key is provided by the card vendor, and the environment variable PIV_EXT_AUTH_KEY must point to 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 th 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 on to 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 on to the card. ref is 9A, 9C, 9D or 9E ref, ref Load a certificate that has been gziped on to 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. num, num Use the given reader number. The default is 0, the first reader in the system. driver, driver Use the given card driver. The default is auto-detected. , 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 opensc-0.13.0/doc/tools/cardos-tool.1.xml0000644000015201777760000000516012057406034015043 00000000000000 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 name, name Use the card driver specified by name. The default is to auto-detect the correct card driver. , Format the card or token. , Display information about the card or token. number, number Specify the reader number number to use. The default is reader 0. , 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. opensc-0.13.0/doc/tools/pkcs11-tool.1.xml0000644000015201777760000002322312057406034014672 00000000000000 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 path Extract information from path (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 , Hash some data. id, id Specify the id of the object to operate on. 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 . Initialize a token: set the token label as well as a Security Officer PIN (the label must be specified using ). path, path Specify the path to a file for input. , Generate a new key pair (public and private pair.) 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. , Display a list of available slots on the token. , Authenticate to the token before performing other operations. This option is not needed if a PIN is provided on the command line. mechanism, mechanism Use the specified mechanism for token operations. See for a list of mechanisms supported by your token. mod Specify a PKCS#11 module (or library) to load. path, path Test a Mozilla-like keypair generation and certificate request. Specify the path to the certificate file. path, path Specify the path to a file for output. pin, pin Use the given pin for token operations. 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. This option will also set the option. id, id Set the CKA_ID of the object. , Display general token information. , Sign some data. id Specify the id of the slot to use. description Specify the description of the slot to use. index Specify the index of the slot to use. 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). The same warning as also applies here. , Perform some tests on the token. This option is most useful when used with either or . type, type Specify the type of object to operate on. Examples are cert, privkey and pubkey. , 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. id, path Write a key or certificate object to the token. path points to the DER-encoded certificate or key file. opensc-0.13.0/doc/tools/sc-hsm-tool.1.xml0000644000015201777760000001763212057406034014771 00000000000000 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. filename, filename Create a DKEK share encrypted under a user supplied password and saved to the file given as parameter. Use to provide a password for encryption rather than prompting for one. 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. 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. value Define SO-PIN for initialization. value Define user PIN for initialization, wrap or unwrap operation. value Define number of PIN retries for user PIN during initialization. Default is 3. value Define password for DKEK share encryption. Force removal of existing key, description and certificate. num, num Use the given reader number. The default is 0, the first reader in the system. , 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 Initialize SmartCard-HSM to use a single DKEK share sc-hsm-tool --initialize --so-pin 3537363231383830 --pin 648219 --dkek-shares 1 Import DKEK share sc-hsm-tool --import-dkek-share dkek-share-1.pbe 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 See also opensc-tool 1 opensc-0.13.0/doc/tools/iasecc-tool.1.xml0000644000015201777760000000474012057406034015022 00000000000000 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 number, Specify the reader number number to use. The default is reader 0. , 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. opensc-0.13.0/doc/tools/tools.xml0000644000015201777760000000206312057406034013615 00000000000000 OpenSC OpenSC tools OpenSC file formats opensc-0.13.0/doc/tools/openpgp-tool.1.xml0000644000015201777760000001070112057406034015235 00000000000000 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 prog, prog Execute the given program with data in environment variables. , Print help message on screen. Print values in raw format, as they are stored on the card. Print values in pretty format. , Show card holder information. num, num Use the given reader. The default is the first reader with a card. pintype Verify PIN (CHV1, CHV2 or CHV3). string The PIN text to verify. ID, ID Generate key. Specify key ID (1, 2 or 3) to generate. bitlength, bitlength Length (default 2048 bit) of the key to be generated. , 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.13.0/doc/tools/pkcs15-crypt.1.xml0000644000015201777760000001456012057406034015066 00000000000000 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 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. id, id Selects the ID of the key to use. file, file Any output will be sent to the specified file. 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. N, N Selects the N-th smart card reader configured by the system. If unspecified, pkcs15-crypt will use the first reader found. This option tells pkcs15-crypt that the input file is the result of an SHA1 hash operation, rather than an MD5 hash. 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 ). , 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 opensc-0.13.0/doc/tools/pkcs15-init.1.xml0000644000015201777760000004542512057406034014674 00000000000000 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 length of the key to be created, such as rsa/512. This will create a 512 bit RSA key. Currently, only RSA key generation is supported. Note that cards usually support just a few different key lengths. Almost all cards will support 512 and 1024 bit keys, some will support 768 or 2048 as well. 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 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 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, SHA1(pub) for DSA, ...). 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. Options 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. , 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. keyspec, keyspec Tells the card to generate new key and store it on the card. keyspec consists of an algorithm name (currently, the only supported name is ), optionally followed by a slash and the length of the key in bits. 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' attribut 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 read additional options from filename. The file is supposed to contain one long option per line, without the leading dashes, for instance: pin frank puk zappa You can specify several times. , , , These options can be used to specify PIN/PUK values on the command line. 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 use these options only on a secured system, or in an options file specified with . 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. 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 intrisic ID will be calculated from the certificate's public key. Look the description of the 'pkcs15-id-style' attribut 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. 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' attribut 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, filename Tells pkcs15-init to update the certificate object with the ID specified via the option with the certificate in . 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! , Tells pkcs15-init to not ask for the transport keys and use default keys, as known by the card driver. , Causes pkcs15-init to be more verbose. Specify this flag several times to enable debug output in the OpenSC library. See also pkcs15-profile 5 opensc-0.13.0/doc/tools/Makefile.am0000644000015201777760000000162012057406034013765 00000000000000MAINTAINERCLEANFILES = $(srcdir)/Makefile.in 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)) man5_MANS = $(patsubst $(srcdir)/%.xml, %, $(wildcard $(srcdir)/*.5.xml)) endif tools.html: $(srcdir)/tools.xml $(wildcard $(srcdir)/*.1.xml) $(wildcard $(srcdir)/*.5.xml) $(XSLTPROC) --nonet --path "$(srcdir)/..:$(xslstylesheetsdir)/html" --xinclude -o $@ html.xsl $< %.1: $(srcdir)/%.1.xml sed -e 's|@pkgdatadir[@]|$(pkgdatadir)|g' < $< \ | $(XSLTPROC) --nonet --path "$(srcdir)/..:$(xslstylesheetsdir)/manpages" --xinclude -o $@ man.xsl $< %.5: $(srcdir)/%.5.xml sed -e 's|@pkgdatadir[@]|$(pkgdatadir)|g' < $< \ | $(XSLTPROC) --nonet --path "$(srcdir)/..:$(xslstylesheetsdir)/manpages" --xinclude -o $@ man.xsl $< clean-local: -rm -rf $(html_DATA) $(man1_MANS) $(man5_MANS) opensc-0.13.0/doc/tools/eidenv.1.xml0000644000015201777760000000570612057406034014075 00000000000000 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. num, num Use the given reader. The default is the first reader with a card. , 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.13.0/doc/tools/opensc-explorer.1.xml0000644000015201777760000004463312057406034015752 00000000000000 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 interactively to perform miscellaneous operations such as exploring the contents of or sending arbitrary APDU commands to a smart card or similar security token. 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 auto-detected. 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. num, num Use the given reader number. The default is 0, the first reader in the system. , Causes 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 The following commands are supported at opensc-explorer's interactive prompt or in script files passed via the command line parameter SCRIPT. apdu hex-data Send a custom APDU command hex-data. asn1 file-id Parse and print the ASN.1 encoded content of the file specified by file-id. cat [file-id | sfi:short-id] Print the contents of the currently selected EF or the contents of a file specified by file-id or the short file id short-id. 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 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 a PIN, where pin-ref is the PIN reference. 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 id number and size is the size of the new file. debug [level] Set OpenSC debug level to level. If level is omitted the current debug level will be shown. delete file-id Remove the EF or DF specified by file-id do_get hex-tag [output] Copy the internal card's 'tagged' data into the local file. The local file is specified by output while the tag of the card's data is specified by hex-tag. If output is omitted, the name of the output file will be derived from hex-tag. do_put hex-tag input Update internal card's 'tagged' data. hex-tag is the tag of the card's data. input is the filename of the source file or the literal data presented as a sequence of hexadecimal values or " enclosed string. 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. 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 printed. ls [pattern ...] List files in the current DF. If no pattern is given, then all files are listed. If one ore 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 (by default from 0000 to FFFF). mkdir file-id size Create a DF. file-id specifies the id number and size is the size of the new file. 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 Generate random sequence of count bytes. 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 set potentially change its value to new pin. PUK and PIN values can be a sequence of hexadecimal values, "-enclosed strings, empty (""), or absent. If they are 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 sequencer of the hex values or as a " enclosed string. 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 the hex values or as a " enclosed string. verify key-type key-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 list of hex values or a " enclosed string. 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 CHV0 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. See also opensc-tool 1 opensc-0.13.0/doc/tools/netkey-tool.1.xml0000644000015201777760000002075412057406034015075 00000000000000 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-value, pin-value Specifies the current value of the global PIN. pin-value, pin-value Specifies the current value of the global PUK. pin-value, pin-value Specifies the current value of the local PIN0 (aka local PIN). pin-value, pin-value Specifies the current value of the local PIN1 (aka local PUK). number, number Use smart card in specified reader. Default is reader 0. 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 consists of exacly 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 exeption 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 succesfull 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.13.0/doc/tools/pkcs15-profile.5.xml0000644000015201777760000000401512057406034015363 00000000000000 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 intialization, such as location of PIN files, key references etc. Profiles currently reside in @pkgdatadir@ Syntax This section should contain information about the profile syntax. Will add this soonishly. See also pkcs15-init 1 , pkcs15-crypt 1 opensc-0.13.0/doc/tools/Makefile.in0000644000015201777760000004340612057406055014011 00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program 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. @SET_MAKE@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = doc/tools DIST_COMMON = $(dist_noinst_DATA) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ SOURCES = DIST_SOURCES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' man1dir = $(mandir)/man1 am__installdirs = "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" \ "$(DESTDIR)$(htmldir)" man5dir = $(mandir)/man5 NROFF = nroff MANS = $(man1_MANS) $(man5_MANS) DATA = $(dist_noinst_DATA) $(html_DATA) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUG_FILE = @DEBUG_FILE@ DEFAULT_PCSC_PROVIDER = @DEFAULT_PCSC_PROVIDER@ DEFAULT_SM_MODULE = @DEFAULT_SM_MODULE@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GIT = @GIT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBRARY_BITNESS = @LIBRARY_BITNESS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCT_CFLAGS = @OPENCT_CFLAGS@ OPENCT_LIBS = @OPENCT_LIBS@ OPENSC_LT_AGE = @OPENSC_LT_AGE@ OPENSC_LT_CURRENT = @OPENSC_LT_CURRENT@ OPENSC_LT_OLDEST = @OPENSC_LT_OLDEST@ OPENSC_LT_REVISION = @OPENSC_LT_REVISION@ OPENSC_VERSION_FIX = @OPENSC_VERSION_FIX@ OPENSC_VERSION_MAJOR = @OPENSC_VERSION_MAJOR@ OPENSC_VERSION_MINOR = @OPENSC_VERSION_MINOR@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OPTIONAL_OPENCT_CFLAGS = @OPTIONAL_OPENCT_CFLAGS@ OPTIONAL_OPENCT_LIBS = @OPTIONAL_OPENCT_LIBS@ OPTIONAL_OPENSSL_CFLAGS = @OPTIONAL_OPENSSL_CFLAGS@ OPTIONAL_OPENSSL_LIBS = @OPTIONAL_OPENSSL_LIBS@ OPTIONAL_PCSC_CFLAGS = @OPTIONAL_PCSC_CFLAGS@ OPTIONAL_READLINE_CFLAGS = @OPTIONAL_READLINE_CFLAGS@ OPTIONAL_READLINE_LIBS = @OPTIONAL_READLINE_LIBS@ OPTIONAL_ZLIB_CFLAGS = @OPTIONAL_ZLIB_CFLAGS@ OPTIONAL_ZLIB_LIBS = @OPTIONAL_ZLIB_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCSC_CFLAGS = @PCSC_CFLAGS@ PCSC_LIBS = @PCSC_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RC = @RC@ READLINE_CFLAGS = @READLINE_CFLAGS@ READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ WIN_LIBPREFIX = @WIN_LIBPREFIX@ XSLTPROC = @XSLTPROC@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ git = @git@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkcs11dir = @pkcs11dir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ xslstylesheetsdir = @xslstylesheetsdir@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in dist_noinst_DATA = $(wildcard $(srcdir)/*.xml) @ENABLE_DOC_TRUE@html_DATA = tools.html @ENABLE_MAN_TRUE@man1_MANS = $(patsubst $(srcdir)/%.xml, %, $(wildcard $(srcdir)/*.1.xml)) @ENABLE_MAN_TRUE@man5_MANS = $(patsubst $(srcdir)/%.xml, %, $(wildcard $(srcdir)/*.5.xml)) all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/tools/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign doc/tools/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-man1: $(man1_MANS) @$(NORMAL_INSTALL) test -z "$(man1dir)" || $(MKDIR_P) "$(DESTDIR)$(man1dir)" @list='$(man1_MANS)'; test -n "$(man1dir)" || exit 0; \ { for i in $$list; do echo "$$i"; done; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ done; } uninstall-man1: @$(NORMAL_UNINSTALL) @list='$(man1_MANS)'; test -n "$(man1dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ test -z "$$files" || { \ echo " ( cd '$(DESTDIR)$(man1dir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(man1dir)" && rm -f $$files; } install-man5: $(man5_MANS) @$(NORMAL_INSTALL) test -z "$(man5dir)" || $(MKDIR_P) "$(DESTDIR)$(man5dir)" @list='$(man5_MANS)'; test -n "$(man5dir)" || exit 0; \ { for i in $$list; do echo "$$i"; done; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man5dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \ done; } uninstall-man5: @$(NORMAL_UNINSTALL) @list='$(man5_MANS)'; test -n "$(man5dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ test -z "$$files" || { \ echo " ( cd '$(DESTDIR)$(man5dir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(man5dir)" && rm -f $$files; } install-htmlDATA: $(html_DATA) @$(NORMAL_INSTALL) test -z "$(htmldir)" || $(MKDIR_P) "$(DESTDIR)$(htmldir)" @list='$(html_DATA)'; test -n "$(htmldir)" || list=; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(htmldir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(htmldir)" || exit $$?; \ done uninstall-htmlDATA: @$(NORMAL_UNINSTALL) @list='$(html_DATA)'; test -n "$(htmldir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ test -n "$$files" || exit 0; \ echo " ( cd '$(DESTDIR)$(htmldir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(htmldir)" && rm -f $$files tags: TAGS TAGS: ctags: CTAGS CTAGS: distdir: $(DISTFILES) @list='$(MANS)'; if test -n "$$list"; then \ list=`for p in $$list; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \ if test -n "$$list" && \ grep 'ab help2man is required to generate this page' $$list >/dev/null; then \ echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \ grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \ echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \ echo " typically \`make maintainer-clean' will remove them" >&2; \ exit 1; \ else :; fi; \ else :; fi @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(MANS) $(DATA) installdirs: for dir in "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(htmldir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libtool clean-local mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-htmlDATA install-man install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-man1 install-man5 install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-htmlDATA uninstall-man uninstall-man: uninstall-man1 uninstall-man5 .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ clean-local distclean distclean-generic distclean-libtool \ distdir dvi dvi-am html html-am info info-am install \ install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-htmlDATA install-info install-info-am \ install-man install-man1 install-man5 install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am \ uninstall-htmlDATA uninstall-man uninstall-man1 uninstall-man5 tools.html: $(srcdir)/tools.xml $(wildcard $(srcdir)/*.1.xml) $(wildcard $(srcdir)/*.5.xml) $(XSLTPROC) --nonet --path "$(srcdir)/..:$(xslstylesheetsdir)/html" --xinclude -o $@ html.xsl $< %.1: $(srcdir)/%.1.xml sed -e 's|@pkgdatadir[@]|$(pkgdatadir)|g' < $< \ | $(XSLTPROC) --nonet --path "$(srcdir)/..:$(xslstylesheetsdir)/manpages" --xinclude -o $@ man.xsl $< %.5: $(srcdir)/%.5.xml sed -e 's|@pkgdatadir[@]|$(pkgdatadir)|g' < $< \ | $(XSLTPROC) --nonet --path "$(srcdir)/..:$(xslstylesheetsdir)/manpages" --xinclude -o $@ man.xsl $< clean-local: -rm -rf $(html_DATA) $(man1_MANS) $(man5_MANS) # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: opensc-0.13.0/doc/tools/cryptoflex-tool.1.xml0000644000015201777760000001237512057406034015775 00000000000000 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 num, num Forces cryptoflex-tool to use reader number num for operations. The default is to use reader number 0, the first reader in the system. , 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 See also pkcs15-tool 1 opensc-0.13.0/doc/tools/opensc-tool.1.xml0000644000015201777760000001007512057406034015060 00000000000000 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 Answer To Reset (ATR) of the card. Output is in hex byte format driver, driver Use the given card driver. The default is auto-detected. , 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). num, num Use the given reader number. The default is 0, the first reader in the system. apdu, apdu Sends an arbitrary APDU to the card in the format AA:BB:CC:DD:EE:FF.... 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 opensc-0.13.0/doc/tools/westcos-tool.1.xml0000644000015201777760000001433712057406034015265 00000000000000 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 are insecure because 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 (ie. the file for ithe PIN must havei been created, see option ). By default the key length is 1536 bits. User authentication is required for 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. value, value Set value of PIN. value, value set value of PUK (or value of new PIN for change PIN command see ). path, path Read the file path from the card. The file is written on disk with name path. User authentication is required for this operation. num, num Use the given reader. The default is the first reader with a card. , 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. path, path Put the file with name path from disk to card. On the card the file is written in path. User authentication is required for this operation. Authors westcos-tool was written by Francois Leblanc francois.leblanc@cev-sa.com. opensc-0.13.0/ltmain.sh0000755000015201777760000073337712057406052011674 00000000000000# Generated from ltmain.m4sh. # ltmain.sh (GNU libtool) 2.2.6b # Written by Gordon Matzigkeit , 1996 # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 2008 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool 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. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that 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 GNU Libtool; see the file COPYING. If not, a copy # can be downloaded from http://www.gnu.org/licenses/gpl.html, # or obtained by writing to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Usage: $progname [OPTION]... [MODE-ARG]... # # Provide generalized library-building support services. # # --config show all configuration variables # --debug enable verbose shell tracing # -n, --dry-run display commands without modifying any files # --features display basic configuration information and exit # --mode=MODE use operation mode MODE # --preserve-dup-deps don't remove duplicate dependency libraries # --quiet, --silent don't print informational messages # --tag=TAG use configuration variables from tag TAG # -v, --verbose print informational messages (default) # --version print version information # -h, --help print short or long help message # # MODE must be one of the following: # # clean remove files from the build directory # compile compile a source file into a libtool object # execute automatically set library path, then run a program # finish complete the installation of libtool libraries # install install libraries or executables # link create a library or an executable # uninstall remove libraries from an installed directory # # MODE-ARGS vary depending on the MODE. # Try `$progname --help --mode=MODE' for a more detailed description of MODE. # # When reporting a bug, please describe a test case to reproduce it and # include the following information: # # host-triplet: $host # shell: $SHELL # compiler: $LTCC # compiler flags: $LTCFLAGS # linker: $LD (gnu? $with_gnu_ld) # $progname: (GNU libtool) 2.2.6b Debian-2.2.6b-2 # automake: $automake_version # autoconf: $autoconf_version # # Report bugs to . PROGRAM=ltmain.sh PACKAGE=libtool VERSION="2.2.6b Debian-2.2.6b-2" TIMESTAMP="" package_revision=1.3017 # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # NLS nuisances: We save the old values to restore during execute mode. # Only set LANG and LC_ALL to C if already set. # These must not be set unconditionally because not all systems understand # e.g. LANG=C (notably SCO). lt_user_locale= lt_safe_locale= for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${$lt_var+set}\" = set; then save_$lt_var=\$$lt_var $lt_var=C export $lt_var lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\" lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\" fi" done $lt_unset CDPATH : ${CP="cp -f"} : ${ECHO="echo"} : ${EGREP="/bin/grep -E"} : ${FGREP="/bin/grep -F"} : ${GREP="/bin/grep"} : ${LN_S="ln -s"} : ${MAKE="make"} : ${MKDIR="mkdir"} : ${MV="mv -f"} : ${RM="rm -f"} : ${SED="/bin/sed"} : ${SHELL="${CONFIG_SHELL-/bin/sh}"} : ${Xsed="$SED -e 1s/^X//"} # Global variables: EXIT_SUCCESS=0 EXIT_FAILURE=1 EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. exit_status=$EXIT_SUCCESS # Make sure IFS has a sensible default lt_nl=' ' IFS=" $lt_nl" dirname="s,/[^/]*$,," basename="s,^.*/,," # func_dirname_and_basename file append nondir_replacement # perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # Implementation must be kept synchronized with func_dirname # and func_basename. For efficiency, we do not delegate to # those functions but instead duplicate the functionality here. func_dirname_and_basename () { # Extract subdirectory from the argument. func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"` if test "X$func_dirname_result" = "X${1}"; then func_dirname_result="${3}" else func_dirname_result="$func_dirname_result${2}" fi func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"` } # Generated shell functions inserted here. # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. progpath="$0" # The name of this program: # In the unlikely event $progname began with a '-', it would play havoc with # func_echo (imagine progname=-n), so we prepend ./ in that case: func_dirname_and_basename "$progpath" progname=$func_basename_result case $progname in -*) progname=./$progname ;; esac # Make sure we have an absolute path for reexecution: case $progpath in [\\/]*|[A-Za-z]:\\*) ;; *[\\/]*) progdir=$func_dirname_result progdir=`cd "$progdir" && pwd` progpath="$progdir/$progname" ;; *) save_IFS="$IFS" IFS=: for progdir in $PATH; do IFS="$save_IFS" test -x "$progdir/$progname" && break done IFS="$save_IFS" test -n "$progdir" || progdir=`pwd` progpath="$progdir/$progname" ;; esac # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. Xsed="${SED}"' -e 1s/^X//' sed_quote_subst='s/\([`"$\\]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\(["`\\]\)/\\\1/g' # Re-`\' parameter expansions in output of double_quote_subst that were # `\'-ed in input to the same. If an odd number of `\' preceded a '$' # in input to double_quote_subst, that '$' was protected from expansion. # Since each input `\' is now two `\'s, look for any number of runs of # four `\'s followed by two `\'s and then a '$'. `\' that '$'. bs='\\' bs2='\\\\' bs4='\\\\\\\\' dollar='\$' sed_double_backslash="\ s/$bs4/&\\ /g s/^$bs2$dollar/$bs&/ s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g s/\n//g" # Standard options: opt_dry_run=false opt_help=false opt_quiet=false opt_verbose=false opt_warning=: # func_echo arg... # Echo program name prefixed message, along with the current mode # name if it has been set yet. func_echo () { $ECHO "$progname${mode+: }$mode: $*" } # func_verbose arg... # Echo program name prefixed message in verbose mode only. func_verbose () { $opt_verbose && func_echo ${1+"$@"} # A bug in bash halts the script if the last line of a function # fails when set -e is in force, so we need another command to # work around that: : } # func_error arg... # Echo program name prefixed message to standard error. func_error () { $ECHO "$progname${mode+: }$mode: "${1+"$@"} 1>&2 } # func_warning arg... # Echo program name prefixed warning message to standard error. func_warning () { $opt_warning && $ECHO "$progname${mode+: }$mode: warning: "${1+"$@"} 1>&2 # bash bug again: : } # func_fatal_error arg... # Echo program name prefixed message to standard error, and exit. func_fatal_error () { func_error ${1+"$@"} exit $EXIT_FAILURE } # func_fatal_help arg... # Echo program name prefixed message to standard error, followed by # a help hint, and exit. func_fatal_help () { func_error ${1+"$@"} func_fatal_error "$help" } help="Try \`$progname --help' for more information." ## default # func_grep expression filename # Check whether EXPRESSION matches any line of FILENAME, without output. func_grep () { $GREP "$1" "$2" >/dev/null 2>&1 } # func_mkdir_p directory-path # Make sure the entire path to DIRECTORY-PATH is available. func_mkdir_p () { my_directory_path="$1" my_dir_list= if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then # Protect directory names starting with `-' case $my_directory_path in -*) my_directory_path="./$my_directory_path" ;; esac # While some portion of DIR does not yet exist... while test ! -d "$my_directory_path"; do # ...make a list in topmost first order. Use a colon delimited # list incase some portion of path contains whitespace. my_dir_list="$my_directory_path:$my_dir_list" # If the last portion added has no slash in it, the list is done case $my_directory_path in */*) ;; *) break ;; esac # ...otherwise throw away the child directory and loop my_directory_path=`$ECHO "X$my_directory_path" | $Xsed -e "$dirname"` done my_dir_list=`$ECHO "X$my_dir_list" | $Xsed -e 's,:*$,,'` save_mkdir_p_IFS="$IFS"; IFS=':' for my_dir in $my_dir_list; do IFS="$save_mkdir_p_IFS" # mkdir can fail with a `File exist' error if two processes # try to create one of the directories concurrently. Don't # stop in that case! $MKDIR "$my_dir" 2>/dev/null || : done IFS="$save_mkdir_p_IFS" # Bail out if we (or some other process) failed to create a directory. test -d "$my_directory_path" || \ func_fatal_error "Failed to create \`$1'" fi } # func_mktempdir [string] # Make a temporary directory that won't clash with other running # libtool processes, and avoids race conditions if possible. If # given, STRING is the basename for that directory. func_mktempdir () { my_template="${TMPDIR-/tmp}/${1-$progname}" if test "$opt_dry_run" = ":"; then # Return a directory name, but don't create it in dry-run mode my_tmpdir="${my_template}-$$" else # If mktemp works, use that first and foremost my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` if test ! -d "$my_tmpdir"; then # Failing that, at least try and use $RANDOM to avoid a race my_tmpdir="${my_template}-${RANDOM-0}$$" save_mktempdir_umask=`umask` umask 0077 $MKDIR "$my_tmpdir" umask $save_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure test -d "$my_tmpdir" || \ func_fatal_error "cannot create temporary directory \`$my_tmpdir'" fi $ECHO "X$my_tmpdir" | $Xsed } # func_quote_for_eval arg # Aesthetically quote ARG to be evaled later. # This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT # is double-quoted, suitable for a subsequent eval, whereas # FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters # which are still active within double quotes backslashified. func_quote_for_eval () { case $1 in *[\\\`\"\$]*) func_quote_for_eval_unquoted_result=`$ECHO "X$1" | $Xsed -e "$sed_quote_subst"` ;; *) func_quote_for_eval_unquoted_result="$1" ;; esac case $func_quote_for_eval_unquoted_result in # Double-quote args containing shell metacharacters to delay # word splitting, command substitution and and variable # expansion for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\"" ;; *) func_quote_for_eval_result="$func_quote_for_eval_unquoted_result" esac } # func_quote_for_expand arg # Aesthetically quote ARG to be evaled later; same as above, # but do not quote variable references. func_quote_for_expand () { case $1 in *[\\\`\"]*) my_arg=`$ECHO "X$1" | $Xsed \ -e "$double_quote_subst" -e "$sed_double_backslash"` ;; *) my_arg="$1" ;; esac case $my_arg in # Double-quote args containing shell metacharacters to delay # word splitting and command substitution for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") my_arg="\"$my_arg\"" ;; esac func_quote_for_expand_result="$my_arg" } # func_show_eval cmd [fail_exp] # Unless opt_silent is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { my_cmd="$1" my_fail_exp="${2-:}" ${opt_silent-false} || { func_quote_for_expand "$my_cmd" eval "func_echo $func_quote_for_expand_result" } if ${opt_dry_run-false}; then :; else eval "$my_cmd" my_status=$? if test "$my_status" -eq 0; then :; else eval "(exit $my_status); $my_fail_exp" fi fi } # func_show_eval_locale cmd [fail_exp] # Unless opt_silent is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. Use the saved locale for evaluation. func_show_eval_locale () { my_cmd="$1" my_fail_exp="${2-:}" ${opt_silent-false} || { func_quote_for_expand "$my_cmd" eval "func_echo $func_quote_for_expand_result" } if ${opt_dry_run-false}; then :; else eval "$lt_user_locale $my_cmd" my_status=$? eval "$lt_safe_locale" if test "$my_status" -eq 0; then :; else eval "(exit $my_status); $my_fail_exp" fi fi } # func_version # Echo version message to standard output and exit. func_version () { $SED -n '/^# '$PROGRAM' (GNU /,/# warranty; / { s/^# // s/^# *$// s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/ p }' < "$progpath" exit $? } # func_usage # Echo short help message to standard output and exit. func_usage () { $SED -n '/^# Usage:/,/# -h/ { s/^# // s/^# *$// s/\$progname/'$progname'/ p }' < "$progpath" $ECHO $ECHO "run \`$progname --help | more' for full usage" exit $? } # func_help # Echo long help message to standard output and exit. func_help () { $SED -n '/^# Usage:/,/# Report bugs to/ { s/^# // s/^# *$// s*\$progname*'$progname'* s*\$host*'"$host"'* s*\$SHELL*'"$SHELL"'* s*\$LTCC*'"$LTCC"'* s*\$LTCFLAGS*'"$LTCFLAGS"'* s*\$LD*'"$LD"'* s/\$with_gnu_ld/'"$with_gnu_ld"'/ s/\$automake_version/'"`(automake --version) 2>/dev/null |$SED 1q`"'/ s/\$autoconf_version/'"`(autoconf --version) 2>/dev/null |$SED 1q`"'/ p }' < "$progpath" exit $? } # func_missing_arg argname # Echo program name prefixed message to standard error and set global # exit_cmd. func_missing_arg () { func_error "missing argument for $1" exit_cmd=exit } exit_cmd=: # Check that we have a working $ECHO. if test "X$1" = X--no-reexec; then # Discard the --no-reexec flag, and continue. shift elif test "X$1" = X--fallback-echo; then # Avoid inline document here, it may be left over : elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t'; then # Yippee, $ECHO works! : else # Restart under the correct shell, and then maybe $ECHO will work. exec $SHELL "$progpath" --no-reexec ${1+"$@"} fi if test "X$1" = X--fallback-echo; then # used as fallback echo shift cat </dev/null 2>&1; then taglist="$taglist $tagname" # Evaluate the configuration. Be careful to quote the path # and the sed script, to avoid splitting on whitespace, but # also don't use non-portable quotes within backquotes within # quotes we have to do it in 2 steps: extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` eval "$extractedcf" else func_error "ignoring unknown tag $tagname" fi ;; esac } # Parse options once, thoroughly. This comes as soon as possible in # the script to make things like `libtool --version' happen quickly. { # Shorthand for --mode=foo, only valid as the first argument case $1 in clean|clea|cle|cl) shift; set dummy --mode clean ${1+"$@"}; shift ;; compile|compil|compi|comp|com|co|c) shift; set dummy --mode compile ${1+"$@"}; shift ;; execute|execut|execu|exec|exe|ex|e) shift; set dummy --mode execute ${1+"$@"}; shift ;; finish|finis|fini|fin|fi|f) shift; set dummy --mode finish ${1+"$@"}; shift ;; install|instal|insta|inst|ins|in|i) shift; set dummy --mode install ${1+"$@"}; shift ;; link|lin|li|l) shift; set dummy --mode link ${1+"$@"}; shift ;; uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) shift; set dummy --mode uninstall ${1+"$@"}; shift ;; esac # Parse non-mode specific arguments: while test "$#" -gt 0; do opt="$1" shift case $opt in --config) func_config ;; --debug) preserve_args="$preserve_args $opt" func_echo "enabling shell trace mode" opt_debug='set -x' $opt_debug ;; -dlopen) test "$#" -eq 0 && func_missing_arg "$opt" && break execute_dlfiles="$execute_dlfiles $1" shift ;; --dry-run | -n) opt_dry_run=: ;; --features) func_features ;; --finish) mode="finish" ;; --mode) test "$#" -eq 0 && func_missing_arg "$opt" && break case $1 in # Valid mode arguments: clean) ;; compile) ;; execute) ;; finish) ;; install) ;; link) ;; relink) ;; uninstall) ;; # Catch anything else as an error *) func_error "invalid argument for $opt" exit_cmd=exit break ;; esac mode="$1" shift ;; --preserve-dup-deps) opt_duplicate_deps=: ;; --quiet|--silent) preserve_args="$preserve_args $opt" opt_silent=: ;; --verbose| -v) preserve_args="$preserve_args $opt" opt_silent=false ;; --tag) test "$#" -eq 0 && func_missing_arg "$opt" && break preserve_args="$preserve_args $opt $1" func_enable_tag "$1" # tagname is set here shift ;; # Separate optargs to long options: -dlopen=*|--mode=*|--tag=*) func_opt_split "$opt" set dummy "$func_opt_split_opt" "$func_opt_split_arg" ${1+"$@"} shift ;; -\?|-h) func_usage ;; --help) opt_help=: ;; --version) func_version ;; -*) func_fatal_help "unrecognized option \`$opt'" ;; *) nonopt="$opt" break ;; esac done case $host in *cygwin* | *mingw* | *pw32* | *cegcc*) # don't eliminate duplications in $postdeps and $predeps opt_duplicate_compiler_generated_deps=: ;; *) opt_duplicate_compiler_generated_deps=$opt_duplicate_deps ;; esac # Having warned about all mis-specified options, bail out if # anything was wrong. $exit_cmd $EXIT_FAILURE } # func_check_version_match # Ensure that we are using m4 macros, and libtool script from the same # release of libtool. func_check_version_match () { if test "$package_revision" != "$macro_revision"; then if test "$VERSION" != "$macro_version"; then if test -z "$macro_version"; then cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from an older release. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from $PACKAGE $macro_version. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF fi else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, $progname: but the definition of this LT_INIT comes from revision $macro_revision. $progname: You should recreate aclocal.m4 with macros from revision $package_revision $progname: of $PACKAGE $VERSION and run autoconf again. _LT_EOF fi exit $EXIT_MISMATCH fi } ## ----------- ## ## Main. ## ## ----------- ## $opt_help || { # Sanity checks first: func_check_version_match if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then func_fatal_configuration "not configured to build any kind of library" fi test -z "$mode" && func_fatal_error "error: you must specify a MODE." # Darwin sucks eval std_shrext=\"$shrext_cmds\" # Only execute mode is allowed to have -dlopen flags. if test -n "$execute_dlfiles" && test "$mode" != execute; then func_error "unrecognized option \`-dlopen'" $ECHO "$help" 1>&2 exit $EXIT_FAILURE fi # Change the help message to a mode-specific one. generic_help="$help" help="Try \`$progname --help --mode=$mode' for more information." } # func_lalib_p file # True iff FILE is a libtool `.la' library or `.lo' object file. # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_lalib_p () { test -f "$1" && $SED -e 4q "$1" 2>/dev/null \ | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 } # func_lalib_unsafe_p file # True iff FILE is a libtool `.la' library or `.lo' object file. # This function implements the same check as func_lalib_p without # resorting to external programs. To this end, it redirects stdin and # closes it afterwards, without saving the original file descriptor. # As a safety measure, use it only where a negative result would be # fatal anyway. Works if `file' does not exist. func_lalib_unsafe_p () { lalib_p=no if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then for lalib_p_l in 1 2 3 4 do read lalib_p_line case "$lalib_p_line" in \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; esac done exec 0<&5 5<&- fi test "$lalib_p" = yes } # func_ltwrapper_script_p file # True iff FILE is a libtool wrapper script # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_script_p () { func_lalib_p "$1" } # func_ltwrapper_executable_p file # True iff FILE is a libtool wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_executable_p () { func_ltwrapper_exec_suffix= case $1 in *.exe) ;; *) func_ltwrapper_exec_suffix=.exe ;; esac $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 } # func_ltwrapper_scriptname file # Assumes file is an ltwrapper_executable # uses $file to determine the appropriate filename for a # temporary ltwrapper_script. func_ltwrapper_scriptname () { func_ltwrapper_scriptname_result="" if func_ltwrapper_executable_p "$1"; then func_dirname_and_basename "$1" "" "." func_stripname '' '.exe' "$func_basename_result" func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper" fi } # func_ltwrapper_p file # True iff FILE is a libtool wrapper script or wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_p () { func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" } # func_execute_cmds commands fail_cmd # Execute tilde-delimited COMMANDS. # If FAIL_CMD is given, eval that upon failure. # FAIL_CMD may read-access the current command in variable CMD! func_execute_cmds () { $opt_debug save_ifs=$IFS; IFS='~' for cmd in $1; do IFS=$save_ifs eval cmd=\"$cmd\" func_show_eval "$cmd" "${2-:}" done IFS=$save_ifs } # func_source file # Source FILE, adding directory component if necessary. # Note that it is not necessary on cygwin/mingw to append a dot to # FILE even if both FILE and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing # `FILE.' does not work on cygwin managed mounts. func_source () { $opt_debug case $1 in */* | *\\*) . "$1" ;; *) . "./$1" ;; esac } # func_infer_tag arg # Infer tagged configuration to use if any are available and # if one wasn't chosen via the "--tag" command line option. # Only attempt this if the compiler in the base compile # command doesn't match the default compiler. # arg is usually of the form 'gcc ...' func_infer_tag () { $opt_debug if test -n "$available_tags" && test -z "$tagname"; then CC_quoted= for arg in $CC; do func_quote_for_eval "$arg" CC_quoted="$CC_quoted $func_quote_for_eval_result" done case $@ in # Blanks in the command may have been stripped by the calling shell, # but not from the CC environment variable when configure was run. " $CC "* | "$CC "* | " `$ECHO $CC` "* | "`$ECHO $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$ECHO $CC_quoted` "* | "`$ECHO $CC_quoted` "*) ;; # Blanks at the start of $base_compile will cause this to fail # if we don't check for them as well. *) for z in $available_tags; do if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then # Evaluate the configuration. eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" CC_quoted= for arg in $CC; do # Double-quote args containing other shell metacharacters. func_quote_for_eval "$arg" CC_quoted="$CC_quoted $func_quote_for_eval_result" done case "$@ " in " $CC "* | "$CC "* | " `$ECHO $CC` "* | "`$ECHO $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$ECHO $CC_quoted` "* | "`$ECHO $CC_quoted` "*) # The compiler in the base compile command matches # the one in the tagged configuration. # Assume this is the tagged configuration we want. tagname=$z break ;; esac fi done # If $tagname still isn't set, then no tagged configuration # was found and let the user know that the "--tag" command # line option must be used. if test -z "$tagname"; then func_echo "unable to infer tagged configuration" func_fatal_error "specify a tag with \`--tag'" # else # func_verbose "using $tagname tagged configuration" fi ;; esac fi } # func_write_libtool_object output_name pic_name nonpic_name # Create a libtool object file (analogous to a ".la" file), # but don't create it if we're doing a dry run. func_write_libtool_object () { write_libobj=${1} if test "$build_libtool_libs" = yes; then write_lobj=\'${2}\' else write_lobj=none fi if test "$build_old_libs" = yes; then write_oldobj=\'${3}\' else write_oldobj=none fi $opt_dry_run || { cat >${write_libobj}T <?"'"'"' &()|`$[]' \ && func_warning "libobj name \`$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" objname="$func_basename_result" xdir="$func_dirname_result" lobj=${xdir}$objdir/$objname test -z "$base_compile" && \ func_fatal_help "you must specify a compilation command" # Delete any leftover library objects. if test "$build_old_libs" = yes; then removelist="$obj $lobj $libobj ${libobj}T" else removelist="$lobj $libobj ${libobj}T" fi # On Cygwin there's no "real" PIC flag so we must build both object types case $host_os in cygwin* | mingw* | pw32* | os2* | cegcc*) pic_mode=default ;; esac if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then # non-PIC code in shared libraries is not supported pic_mode=default fi # Calculate the filename of the output object if compiler does # not support -o with -c if test "$compiler_c_o" = no; then output_obj=`$ECHO "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext} lockfile="$output_obj.lock" else output_obj= need_locks=no lockfile= fi # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file if test "$need_locks" = yes; then until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done elif test "$need_locks" = warn; then if test -f "$lockfile"; then $ECHO "\ *** ERROR, $lockfile exists and contains: `cat $lockfile 2>/dev/null` This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support \`-c' and \`-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi removelist="$removelist $output_obj" $ECHO "$srcfile" > "$lockfile" fi $opt_dry_run || $RM $removelist removelist="$removelist $lockfile" trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 if test -n "$fix_srcfile_path"; then eval srcfile=\"$fix_srcfile_path\" fi func_quote_for_eval "$srcfile" qsrcfile=$func_quote_for_eval_result # Only build a PIC object if we are building libtool libraries. if test "$build_libtool_libs" = yes; then # Without this assignment, base_compile gets emptied. fbsd_hideous_sh_bug=$base_compile if test "$pic_mode" != no; then command="$base_compile $qsrcfile $pic_flag" else # Don't build PIC code command="$base_compile $qsrcfile" fi func_mkdir_p "$xdir$objdir" if test -z "$output_obj"; then # Place PIC objects in $objdir command="$command -o $lobj" fi func_show_eval_locale "$command" \ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' if test "$need_locks" = warn && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support \`-c' and \`-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed, then go on to compile the next one if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then func_show_eval '$MV "$output_obj" "$lobj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi # Allow error messages only from the first compilation. if test "$suppress_opt" = yes; then suppress_output=' >/dev/null 2>&1' fi fi # Only build a position-dependent object if we build old libraries. if test "$build_old_libs" = yes; then if test "$pic_mode" != yes; then # Don't build PIC code command="$base_compile $qsrcfile$pie_flag" else command="$base_compile $qsrcfile $pic_flag" fi if test "$compiler_c_o" = yes; then command="$command -o $obj" fi # Suppress compiler output if we already did a PIC compilation. command="$command$suppress_output" func_show_eval_locale "$command" \ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' if test "$need_locks" = warn && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support \`-c' and \`-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then func_show_eval '$MV "$output_obj" "$obj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi fi $opt_dry_run || { func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" # Unlock the critical section if it was locked if test "$need_locks" != no; then removelist=$lockfile $RM "$lockfile" fi } exit $EXIT_SUCCESS } $opt_help || { test "$mode" = compile && func_mode_compile ${1+"$@"} } func_mode_help () { # We need to display help for each of the modes. case $mode in "") # Generic help is extracted from the usage comments # at the start of this file. func_help ;; clean) $ECHO \ "Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... Remove files from the build directory. RM is the name of the program to use to delete files associated with each FILE (typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed to RM. If FILE is a libtool library, object or program, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; compile) $ECHO \ "Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE Compile a source file into a libtool library object. This mode accepts the following additional options: -o OUTPUT-FILE set the output file name to OUTPUT-FILE -no-suppress do not suppress compiler output for multiple passes -prefer-pic try to building PIC objects only -prefer-non-pic try to building non-PIC objects only -shared do not build a \`.o' file suitable for static linking -static only build a \`.o' file suitable for static linking COMPILE-COMMAND is a command to be used in creating a \`standard' object file from the given SOURCEFILE. The output file name is determined by removing the directory component from SOURCEFILE, then substituting the C source code suffix \`.c' with the library object suffix, \`.lo'." ;; execute) $ECHO \ "Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... Automatically set library path, then run a program. This mode accepts the following additional options: -dlopen FILE add the directory containing FILE to the library path This mode sets the library path environment variable according to \`-dlopen' flags. If any of the ARGS are libtool executable wrappers, then they are translated into their corresponding uninstalled binary, and any of their required library directories are added to the library path. Then, COMMAND is executed, with ARGS as arguments." ;; finish) $ECHO \ "Usage: $progname [OPTION]... --mode=finish [LIBDIR]... Complete the installation of libtool libraries. Each LIBDIR is a directory that contains libtool libraries. The commands that this mode executes may require superuser privileges. Use the \`--dry-run' option if you just want to see what would be executed." ;; install) $ECHO \ "Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... Install executables or libraries. INSTALL-COMMAND is the installation command. The first component should be either the \`install' or \`cp' program. The following components of INSTALL-COMMAND are treated specially: -inst-prefix PREFIX-DIR Use PREFIX-DIR as a staging area for installation The rest of the components are interpreted as arguments to that command (only BSD-compatible install options are recognized)." ;; link) $ECHO \ "Usage: $progname [OPTION]... --mode=link LINK-COMMAND... Link object files or libraries together to form another library, or to create an executable program. LINK-COMMAND is a command using the C compiler that you would use to create a program from several object files. The following components of LINK-COMMAND are treated specially: -all-static do not do any dynamic linking at all -avoid-version do not add a version suffix if possible -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) -export-symbols SYMFILE try to export only the symbols listed in SYMFILE -export-symbols-regex REGEX try to export only the symbols matching REGEX -LLIBDIR search LIBDIR for required installed libraries -lNAME OUTPUT-FILE requires the installed library libNAME -module build a library that can dlopened -no-fast-install disable the fast-install mode -no-install link a not-installable executable -no-undefined declare that a library does not refer to external symbols -o OUTPUT-FILE create OUTPUT-FILE from the specified objects -objectlist FILE Use a list of object files found in FILE to specify objects -precious-files-regex REGEX don't remove output files matching REGEX -release RELEASE specify package release information -rpath LIBDIR the created library will eventually be installed in LIBDIR -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries -shared only do dynamic linking of libtool libraries -shrext SUFFIX override the standard shared library file extension -static do not do any dynamic linking of uninstalled libtool libraries -static-libtool-libs do not do any dynamic linking of libtool libraries -version-info CURRENT[:REVISION[:AGE]] specify library version info [each variable defaults to 0] -weak LIBNAME declare that the target provides the LIBNAME interface All other options (arguments beginning with \`-') are ignored. Every other argument is treated as a filename. Files ending in \`.la' are treated as uninstalled libtool libraries, other files are standard or library object files. If the OUTPUT-FILE ends in \`.la', then a libtool library is created, only library objects (\`.lo' files) may be specified, and \`-rpath' is required, except when creating a convenience library. If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created using \`ar' and \`ranlib', or on Windows using \`lib'. If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file is created, otherwise an executable program is created." ;; uninstall) $ECHO \ "Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... Remove libraries from an installation directory. RM is the name of the program to use to delete files associated with each FILE (typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed to RM. If FILE is a libtool library, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; *) func_fatal_help "invalid operation mode \`$mode'" ;; esac $ECHO $ECHO "Try \`$progname --help' for more information about other modes." exit $? } # Now that we've collected a possible --mode arg, show help if necessary $opt_help && func_mode_help # func_mode_execute arg... func_mode_execute () { $opt_debug # The first argument is the command name. cmd="$nonopt" test -z "$cmd" && \ func_fatal_help "you must specify a COMMAND" # Handle -dlopen flags immediately. for file in $execute_dlfiles; do test -f "$file" \ || func_fatal_help "\`$file' is not a file" dir= case $file in *.la) # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "\`$lib' is not a valid libtool archive" # Read the libtool library. dlname= library_names= func_source "$file" # Skip this library if it cannot be dlopened. if test -z "$dlname"; then # Warn if it was a shared library. test -n "$library_names" && \ func_warning "\`$file' was not linked with \`-export-dynamic'" continue fi func_dirname "$file" "" "." dir="$func_dirname_result" if test -f "$dir/$objdir/$dlname"; then dir="$dir/$objdir" else if test ! -f "$dir/$dlname"; then func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" fi fi ;; *.lo) # Just add the directory containing the .lo file. func_dirname "$file" "" "." dir="$func_dirname_result" ;; *) func_warning "\`-dlopen' is ignored for non-libtool libraries and objects" continue ;; esac # Get the absolute pathname. absdir=`cd "$dir" && pwd` test -n "$absdir" && dir="$absdir" # Now add the directory to shlibpath_var. if eval "test -z \"\$$shlibpath_var\""; then eval "$shlibpath_var=\"\$dir\"" else eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" fi done # This variable tells wrapper scripts just to set shlibpath_var # rather than running their programs. libtool_execute_magic="$magic" # Check if any of the arguments is a wrapper script. args= for file do case $file in -*) ;; *) # Do a test to see if this is really a libtool program. if func_ltwrapper_script_p "$file"; then func_source "$file" # Transform arg to wrapped name. file="$progdir/$program" elif func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" func_source "$func_ltwrapper_scriptname_result" # Transform arg to wrapped name. file="$progdir/$program" fi ;; esac # Quote arguments (to preserve shell metacharacters). func_quote_for_eval "$file" args="$args $func_quote_for_eval_result" done if test "X$opt_dry_run" = Xfalse; then if test -n "$shlibpath_var"; then # Export the shlibpath_var. eval "export $shlibpath_var" fi # Restore saved environment variables for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${save_$lt_var+set}\" = set; then $lt_var=\$save_$lt_var; export $lt_var else $lt_unset $lt_var fi" done # Now prepare to actually exec the command. exec_cmd="\$cmd$args" else # Display what would be done. if test -n "$shlibpath_var"; then eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" $ECHO "export $shlibpath_var" fi $ECHO "$cmd$args" exit $EXIT_SUCCESS fi } test "$mode" = execute && func_mode_execute ${1+"$@"} # func_mode_finish arg... func_mode_finish () { $opt_debug libdirs="$nonopt" admincmds= if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then for dir do libdirs="$libdirs $dir" done for libdir in $libdirs; do if test -n "$finish_cmds"; then # Do each command in the finish commands. func_execute_cmds "$finish_cmds" 'admincmds="$admincmds '"$cmd"'"' fi if test -n "$finish_eval"; then # Do the single finish_eval. eval cmds=\"$finish_eval\" $opt_dry_run || eval "$cmds" || admincmds="$admincmds $cmds" fi done fi # Exit here if they wanted silent mode. $opt_silent && exit $EXIT_SUCCESS $ECHO "X----------------------------------------------------------------------" | $Xsed $ECHO "Libraries have been installed in:" for libdir in $libdirs; do $ECHO " $libdir" done $ECHO $ECHO "If you ever happen to want to link against installed libraries" $ECHO "in a given directory, LIBDIR, you must either use libtool, and" $ECHO "specify the full pathname of the library, or use the \`-LLIBDIR'" $ECHO "flag during linking and do at least one of the following:" if test -n "$shlibpath_var"; then $ECHO " - add LIBDIR to the \`$shlibpath_var' environment variable" $ECHO " during execution" fi if test -n "$runpath_var"; then $ECHO " - add LIBDIR to the \`$runpath_var' environment variable" $ECHO " during linking" fi if test -n "$hardcode_libdir_flag_spec"; then libdir=LIBDIR eval flag=\"$hardcode_libdir_flag_spec\" $ECHO " - use the \`$flag' linker flag" fi if test -n "$admincmds"; then $ECHO " - have your system administrator run these commands:$admincmds" fi if test -f /etc/ld.so.conf; then $ECHO " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" fi $ECHO $ECHO "See any operating system documentation about shared libraries for" case $host in solaris2.[6789]|solaris2.1[0-9]) $ECHO "more information, such as the ld(1), crle(1) and ld.so(8) manual" $ECHO "pages." ;; *) $ECHO "more information, such as the ld(1) and ld.so(8) manual pages." ;; esac $ECHO "X----------------------------------------------------------------------" | $Xsed exit $EXIT_SUCCESS } test "$mode" = finish && func_mode_finish ${1+"$@"} # func_mode_install arg... func_mode_install () { $opt_debug # There may be an optional sh(1) argument at the beginning of # install_prog (especially on Windows NT). if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || # Allow the use of GNU shtool's install command. $ECHO "X$nonopt" | $GREP shtool >/dev/null; then # Aesthetically quote it. func_quote_for_eval "$nonopt" install_prog="$func_quote_for_eval_result " arg=$1 shift else install_prog= arg=$nonopt fi # The real first argument should be the name of the installation program. # Aesthetically quote it. func_quote_for_eval "$arg" install_prog="$install_prog$func_quote_for_eval_result" # We need to accept at least all the BSD install flags. dest= files= opts= prev= install_type= isdir=no stripme= for arg do if test -n "$dest"; then files="$files $dest" dest=$arg continue fi case $arg in -d) isdir=yes ;; -f) case " $install_prog " in *[\\\ /]cp\ *) ;; *) prev=$arg ;; esac ;; -g | -m | -o) prev=$arg ;; -s) stripme=" -s" continue ;; -*) ;; *) # If the previous option needed an argument, then skip it. if test -n "$prev"; then prev= else dest=$arg continue fi ;; esac # Aesthetically quote the argument. func_quote_for_eval "$arg" install_prog="$install_prog $func_quote_for_eval_result" done test -z "$install_prog" && \ func_fatal_help "you must specify an install program" test -n "$prev" && \ func_fatal_help "the \`$prev' option requires an argument" if test -z "$files"; then if test -z "$dest"; then func_fatal_help "no file or destination specified" else func_fatal_help "you must specify a destination" fi fi # Strip any trailing slash from the destination. func_stripname '' '/' "$dest" dest=$func_stripname_result # Check to see that the destination is a directory. test -d "$dest" && isdir=yes if test "$isdir" = yes; then destdir="$dest" destname= else func_dirname_and_basename "$dest" "" "." destdir="$func_dirname_result" destname="$func_basename_result" # Not a directory, so check to see that there is only one file specified. set dummy $files; shift test "$#" -gt 1 && \ func_fatal_help "\`$dest' is not a directory" fi case $destdir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) for file in $files; do case $file in *.lo) ;; *) func_fatal_help "\`$destdir' must be an absolute directory name" ;; esac done ;; esac # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic="$magic" staticlibs= future_libdirs= current_libdirs= for file in $files; do # Do each installation. case $file in *.$libext) # Do the static libraries later. staticlibs="$staticlibs $file" ;; *.la) # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "\`$file' is not a valid libtool archive" library_names= old_library= relink_command= func_source "$file" # Add the libdir to current_libdirs if it is the destination. if test "X$destdir" = "X$libdir"; then case "$current_libdirs " in *" $libdir "*) ;; *) current_libdirs="$current_libdirs $libdir" ;; esac else # Note the libdir as a future libdir. case "$future_libdirs " in *" $libdir "*) ;; *) future_libdirs="$future_libdirs $libdir" ;; esac fi func_dirname "$file" "/" "" dir="$func_dirname_result" dir="$dir$objdir" if test -n "$relink_command"; then # Determine the prefix the user has applied to our future dir. inst_prefix_dir=`$ECHO "X$destdir" | $Xsed -e "s%$libdir\$%%"` # Don't allow the user to place us outside of our expected # location b/c this prevents finding dependent libraries that # are installed to the same prefix. # At present, this check doesn't affect windows .dll's that # are installed into $libdir/../bin (currently, that works fine) # but it's something to keep an eye on. test "$inst_prefix_dir" = "$destdir" && \ func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir" if test -n "$inst_prefix_dir"; then # Stick the inst_prefix_dir data into the link command. relink_command=`$ECHO "X$relink_command" | $Xsed -e "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` else relink_command=`$ECHO "X$relink_command" | $Xsed -e "s%@inst_prefix_dir@%%"` fi func_warning "relinking \`$file'" func_show_eval "$relink_command" \ 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"' fi # See the names of the shared library. set dummy $library_names; shift if test -n "$1"; then realname="$1" shift srcname="$realname" test -n "$relink_command" && srcname="$realname"T # Install the shared library and build the symlinks. func_show_eval "$install_prog $dir/$srcname $destdir/$realname" \ 'exit $?' tstripme="$stripme" case $host_os in cygwin* | mingw* | pw32* | cegcc*) case $realname in *.dll.a) tstripme="" ;; esac ;; esac if test -n "$tstripme" && test -n "$striplib"; then func_show_eval "$striplib $destdir/$realname" 'exit $?' fi if test "$#" -gt 0; then # Delete the old symlinks, and create new ones. # Try `ln -sf' first, because the `ln' binary might depend on # the symlink we replace! Solaris /bin/ln does not understand -f, # so we also need to try rm && ln -s. for linkname do test "$linkname" != "$realname" \ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" done fi # Do each command in the postinstall commands. lib="$destdir/$realname" func_execute_cmds "$postinstall_cmds" 'exit $?' fi # Install the pseudo-library for information purposes. func_basename "$file" name="$func_basename_result" instname="$dir/$name"i func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' # Maybe install the static library, too. test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library" ;; *.lo) # Install (i.e. copy) a libtool object. # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile="$destdir/$destname" else func_basename "$file" destfile="$func_basename_result" destfile="$destdir/$destfile" fi # Deduce the name of the destination old-style object file. case $destfile in *.lo) func_lo2o "$destfile" staticdest=$func_lo2o_result ;; *.$objext) staticdest="$destfile" destfile= ;; *) func_fatal_help "cannot copy a libtool object to \`$destfile'" ;; esac # Install the libtool object if requested. test -n "$destfile" && \ func_show_eval "$install_prog $file $destfile" 'exit $?' # Install the old object if enabled. if test "$build_old_libs" = yes; then # Deduce the name of the old-style object file. func_lo2o "$file" staticobj=$func_lo2o_result func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' fi exit $EXIT_SUCCESS ;; *) # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile="$destdir/$destname" else func_basename "$file" destfile="$func_basename_result" destfile="$destdir/$destfile" fi # If the file is missing, and there is a .exe on the end, strip it # because it is most likely a libtool script we actually want to # install stripped_ext="" case $file in *.exe) if test ! -f "$file"; then func_stripname '' '.exe' "$file" file=$func_stripname_result stripped_ext=".exe" fi ;; esac # Do a test to see if this is really a libtool program. case $host in *cygwin* | *mingw*) if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" wrapper=$func_ltwrapper_scriptname_result else func_stripname '' '.exe' "$file" wrapper=$func_stripname_result fi ;; *) wrapper=$file ;; esac if func_ltwrapper_script_p "$wrapper"; then notinst_deplibs= relink_command= func_source "$wrapper" # Check the variables that should have been set. test -z "$generated_by_libtool_version" && \ func_fatal_error "invalid libtool wrapper script \`$wrapper'" finalize=yes for lib in $notinst_deplibs; do # Check to see that each library is installed. libdir= if test -f "$lib"; then func_source "$lib" fi libfile="$libdir/"`$ECHO "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test if test -n "$libdir" && test ! -f "$libfile"; then func_warning "\`$lib' has not been installed in \`$libdir'" finalize=no fi done relink_command= func_source "$wrapper" outputname= if test "$fast_install" = no && test -n "$relink_command"; then $opt_dry_run || { if test "$finalize" = yes; then tmpdir=`func_mktempdir` func_basename "$file$stripped_ext" file="$func_basename_result" outputname="$tmpdir/$file" # Replace the output file specification. relink_command=`$ECHO "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'` $opt_silent || { func_quote_for_expand "$relink_command" eval "func_echo $func_quote_for_expand_result" } if eval "$relink_command"; then : else func_error "error: relink \`$file' with the above command before installing it" $opt_dry_run || ${RM}r "$tmpdir" continue fi file="$outputname" else func_warning "cannot relink \`$file'" fi } else # Install the binary that we compiled earlier. file=`$ECHO "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"` fi fi # remove .exe since cygwin /usr/bin/install will append another # one anyway case $install_prog,$host in */usr/bin/install*,*cygwin*) case $file:$destfile in *.exe:*.exe) # this is ok ;; *.exe:*) destfile=$destfile.exe ;; *:*.exe) func_stripname '' '.exe' "$destfile" destfile=$func_stripname_result ;; esac ;; esac func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' $opt_dry_run || if test -n "$outputname"; then ${RM}r "$tmpdir" fi ;; esac done for file in $staticlibs; do func_basename "$file" name="$func_basename_result" # Set up the ranlib parameters. oldlib="$destdir/$name" func_show_eval "$install_prog \$file \$oldlib" 'exit $?' if test -n "$stripme" && test -n "$old_striplib"; then func_show_eval "$old_striplib $oldlib" 'exit $?' fi # Do each command in the postinstall commands. func_execute_cmds "$old_postinstall_cmds" 'exit $?' done test -n "$future_libdirs" && \ func_warning "remember to run \`$progname --finish$future_libdirs'" if test -n "$current_libdirs"; then # Maybe just do a dry run. $opt_dry_run && current_libdirs=" -n$current_libdirs" exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' else exit $EXIT_SUCCESS fi } test "$mode" = install && func_mode_install ${1+"$@"} # func_generate_dlsyms outputname originator pic_p # Extract symbols from dlprefiles and create ${outputname}S.o with # a dlpreopen symbol table. func_generate_dlsyms () { $opt_debug my_outputname="$1" my_originator="$2" my_pic_p="${3-no}" my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'` my_dlsyms= if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then if test -n "$NM" && test -n "$global_symbol_pipe"; then my_dlsyms="${my_outputname}S.c" else func_error "not configured to extract global symbols from dlpreopened files" fi fi if test -n "$my_dlsyms"; then case $my_dlsyms in "") ;; *.c) # Discover the nlist of each of the dlfiles. nlist="$output_objdir/${my_outputname}.nm" func_show_eval "$RM $nlist ${nlist}S ${nlist}T" # Parse the name list into a source file. func_verbose "creating $output_objdir/$my_dlsyms" $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ /* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */ /* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */ #ifdef __cplusplus extern \"C\" { #endif /* External symbol declarations for the compiler. */\ " if test "$dlself" = yes; then func_verbose "generating symbol list for \`$output'" $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" # Add our own program objects to the symbol list. progfiles=`$ECHO "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` for progfile in $progfiles; do func_verbose "extracting global C symbols from \`$progfile'" $opt_dry_run || eval "$NM $progfile | $global_symbol_pipe >> '$nlist'" done if test -n "$exclude_expsyms"; then $opt_dry_run || { eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi if test -n "$export_symbols_regex"; then $opt_dry_run || { eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi # Prepare the list of exported symbols if test -z "$export_symbols"; then export_symbols="$output_objdir/$outputname.exp" $opt_dry_run || { $RM $export_symbols eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' ;; esac } else $opt_dry_run || { eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' case $host in *cygwin | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' ;; esac } fi fi for dlprefile in $dlprefiles; do func_verbose "extracting global C symbols from \`$dlprefile'" func_basename "$dlprefile" name="$func_basename_result" $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' eval "$NM $dlprefile 2>/dev/null | $global_symbol_pipe >> '$nlist'" } done $opt_dry_run || { # Make sure we have at least an empty file. test -f "$nlist" || : > "$nlist" if test -n "$exclude_expsyms"; then $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T $MV "$nlist"T "$nlist" fi # Try sorting and uniquifying the output. if $GREP -v "^: " < "$nlist" | if sort -k 3 /dev/null 2>&1; then sort -k 3 else sort +2 fi | uniq > "$nlist"S; then : else $GREP -v "^: " < "$nlist" > "$nlist"S fi if test -f "$nlist"S; then eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' else $ECHO '/* NONE */' >> "$output_objdir/$my_dlsyms" fi $ECHO >> "$output_objdir/$my_dlsyms" "\ /* The mapping between symbol names and symbols. */ typedef struct { const char *name; void *address; } lt_dlsymlist; " case $host in *cygwin* | *mingw* | *cegcc* ) $ECHO >> "$output_objdir/$my_dlsyms" "\ /* DATA imports from DLLs on WIN32 con't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */" lt_dlsym_const= ;; *osf5*) echo >> "$output_objdir/$my_dlsyms" "\ /* This system does not cope well with relocations in const data */" lt_dlsym_const= ;; *) lt_dlsym_const=const ;; esac $ECHO >> "$output_objdir/$my_dlsyms" "\ extern $lt_dlsym_const lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[]; $lt_dlsym_const lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[] = {\ { \"$my_originator\", (void *) 0 }," case $need_lib_prefix in no) eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; *) eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; esac $ECHO >> "$output_objdir/$my_dlsyms" "\ {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt_${my_prefix}_LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif\ " } # !$opt_dry_run pic_flag_for_symtable= case "$compile_command " in *" -static "*) ;; *) case $host in # compiling the symbol table file with pic_flag works around # a FreeBSD bug that causes programs to crash when -lm is # linked before any other PIC object. But we must not use # pic_flag when linking with -static. The problem exists in # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; *-*-hpux*) pic_flag_for_symtable=" $pic_flag" ;; *) if test "X$my_pic_p" != Xno; then pic_flag_for_symtable=" $pic_flag" fi ;; esac ;; esac symtab_cflags= for arg in $LTCFLAGS; do case $arg in -pie | -fpie | -fPIE) ;; *) symtab_cflags="$symtab_cflags $arg" ;; esac done # Now compile the dynamic symbol file. func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' # Clean up the generated files. func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"' # Transform the symbol file into the correct name. symfileobj="$output_objdir/${my_outputname}S.$objext" case $host in *cygwin* | *mingw* | *cegcc* ) if test -f "$output_objdir/$my_outputname.def"; then compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` else compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` fi ;; *) compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` ;; esac ;; *) func_fatal_error "unknown suffix for \`$my_dlsyms'" ;; esac else # We keep going just in case the user didn't refer to # lt_preloaded_symbols. The linker will fail if global_symbol_pipe # really was required. # Nullify the symbol file. compile_command=`$ECHO "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"` finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"` fi } # func_win32_libid arg # return the library type of file 'arg' # # Need a lot of goo to handle *both* DLLs and import libs # Has to be a shell function in order to 'eat' the argument # that is supplied when $file_magic_command is called. func_win32_libid () { $opt_debug win32_libid_type="unknown" win32_fileres=`file -L $1 2>/dev/null` case $win32_fileres in *ar\ archive\ import\ library*) # definitely import win32_libid_type="x86 archive import" ;; *ar\ archive*) # could be an import, or static if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | $EGREP 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then win32_nmres=`eval $NM -f posix -A $1 | $SED -n -e ' 1,100{ / I /{ s,.*,import, p q } }'` case $win32_nmres in import*) win32_libid_type="x86 archive import";; *) win32_libid_type="x86 archive static";; esac fi ;; *DLL*) win32_libid_type="x86 DLL" ;; *executable*) # but shell scripts are "executable" too... case $win32_fileres in *MS\ Windows\ PE\ Intel*) win32_libid_type="x86 DLL" ;; esac ;; esac $ECHO "$win32_libid_type" } # func_extract_an_archive dir oldlib func_extract_an_archive () { $opt_debug f_ex_an_ar_dir="$1"; shift f_ex_an_ar_oldlib="$1" func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" 'exit $?' if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then : else func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" fi } # func_extract_archives gentop oldlib ... func_extract_archives () { $opt_debug my_gentop="$1"; shift my_oldlibs=${1+"$@"} my_oldobjs="" my_xlib="" my_xabs="" my_xdir="" for my_xlib in $my_oldlibs; do # Extract the objects. case $my_xlib in [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; *) my_xabs=`pwd`"/$my_xlib" ;; esac func_basename "$my_xlib" my_xlib="$func_basename_result" my_xlib_u=$my_xlib while :; do case " $extracted_archives " in *" $my_xlib_u "*) func_arith $extracted_serial + 1 extracted_serial=$func_arith_result my_xlib_u=lt$extracted_serial-$my_xlib ;; *) break ;; esac done extracted_archives="$extracted_archives $my_xlib_u" my_xdir="$my_gentop/$my_xlib_u" func_mkdir_p "$my_xdir" case $host in *-darwin*) func_verbose "Extracting $my_xabs" # Do not bother doing anything if just a dry run $opt_dry_run || { darwin_orig_dir=`pwd` cd $my_xdir || exit $? darwin_archive=$my_xabs darwin_curdir=`pwd` darwin_base_archive=`basename "$darwin_archive"` darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` if test -n "$darwin_arches"; then darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` darwin_arch= func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" for darwin_arch in $darwin_arches ; do func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}" $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" func_extract_an_archive "`pwd`" "${darwin_base_archive}" cd "$darwin_curdir" $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" done # $darwin_arches ## Okay now we've a bunch of thin objects, gotta fatten them up :) darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u` darwin_file= darwin_files= for darwin_file in $darwin_filelist; do darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP` $LIPO -create -output "$darwin_file" $darwin_files done # $darwin_filelist $RM -rf unfat-$$ cd "$darwin_orig_dir" else cd $darwin_orig_dir func_extract_an_archive "$my_xdir" "$my_xabs" fi # $darwin_arches } # !$opt_dry_run ;; *) func_extract_an_archive "$my_xdir" "$my_xabs" ;; esac my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP` done func_extract_archives_result="$my_oldobjs" } # func_emit_wrapper_part1 [arg=no] # # Emit the first part of a libtool wrapper script on stdout. # For more information, see the description associated with # func_emit_wrapper(), below. func_emit_wrapper_part1 () { func_emit_wrapper_part1_arg1=no if test -n "$1" ; then func_emit_wrapper_part1_arg1=$1 fi $ECHO "\ #! $SHELL # $output - temporary wrapper script for $objdir/$outputname # Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION # # The $output program cannot be directly executed until all the libtool # libraries that it depends on are installed. # # This wrapper script should never be moved out of the build directory. # If it is, it will not operate correctly. # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. Xsed='${SED} -e 1s/^X//' sed_quote_subst='$sed_quote_subst' # Be Bourne compatible if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH relink_command=\"$relink_command\" # This environment variable determines our operation mode. if test \"\$libtool_install_magic\" = \"$magic\"; then # install mode needs the following variables: generated_by_libtool_version='$macro_version' notinst_deplibs='$notinst_deplibs' else # When we are sourced in execute mode, \$file and \$ECHO are already set. if test \"\$libtool_execute_magic\" != \"$magic\"; then ECHO=\"$qecho\" file=\"\$0\" # Make sure echo works. if test \"X\$1\" = X--no-reexec; then # Discard the --no-reexec flag, and continue. shift elif test \"X\`{ \$ECHO '\t'; } 2>/dev/null\`\" = 'X\t'; then # Yippee, \$ECHO works! : else # Restart under the correct shell, and then maybe \$ECHO will work. exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"} fi fi\ " $ECHO "\ # Find the directory that this script lives in. thisdir=\`\$ECHO \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\` test \"x\$thisdir\" = \"x\$file\" && thisdir=. # Follow symbolic links until we get to the real thisdir. file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\` while test -n \"\$file\"; do destdir=\`\$ECHO \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\` # If there was a directory component, then change thisdir. if test \"x\$destdir\" != \"x\$file\"; then case \"\$destdir\" in [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; *) thisdir=\"\$thisdir/\$destdir\" ;; esac fi file=\`\$ECHO \"X\$file\" | \$Xsed -e 's%^.*/%%'\` file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\` done " } # end: func_emit_wrapper_part1 # func_emit_wrapper_part2 [arg=no] # # Emit the second part of a libtool wrapper script on stdout. # For more information, see the description associated with # func_emit_wrapper(), below. func_emit_wrapper_part2 () { func_emit_wrapper_part2_arg1=no if test -n "$1" ; then func_emit_wrapper_part2_arg1=$1 fi $ECHO "\ # Usually 'no', except on cygwin/mingw when embedded into # the cwrapper. WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_part2_arg1 if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then # special case for '.' if test \"\$thisdir\" = \".\"; then thisdir=\`pwd\` fi # remove .libs from thisdir case \"\$thisdir\" in *[\\\\/]$objdir ) thisdir=\`\$ECHO \"X\$thisdir\" | \$Xsed -e 's%[\\\\/][^\\\\/]*$%%'\` ;; $objdir ) thisdir=. ;; esac fi # Try to get the absolute directory name. absdir=\`cd \"\$thisdir\" && pwd\` test -n \"\$absdir\" && thisdir=\"\$absdir\" " if test "$fast_install" = yes; then $ECHO "\ program=lt-'$outputname'$exeext progdir=\"\$thisdir/$objdir\" if test ! -f \"\$progdir/\$program\" || { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ test \"X\$file\" != \"X\$progdir/\$program\"; }; then file=\"\$\$-\$program\" if test ! -d \"\$progdir\"; then $MKDIR \"\$progdir\" else $RM \"\$progdir/\$file\" fi" $ECHO "\ # relink executable if necessary if test -n \"\$relink_command\"; then if relink_command_output=\`eval \$relink_command 2>&1\`; then : else $ECHO \"\$relink_command_output\" >&2 $RM \"\$progdir/\$file\" exit 1 fi fi $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || { $RM \"\$progdir/\$program\"; $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } $RM \"\$progdir/\$file\" fi" else $ECHO "\ program='$outputname' progdir=\"\$thisdir/$objdir\" " fi $ECHO "\ if test -f \"\$progdir/\$program\"; then" # Export our shlibpath_var if we have one. if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then $ECHO "\ # Add our own library path to $shlibpath_var $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" # Some systems cannot cope with colon-terminated $shlibpath_var # The second colon is a workaround for a bug in BeOS R4 sed $shlibpath_var=\`\$ECHO \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\` export $shlibpath_var " fi # fixup the dll searchpath if we need to. if test -n "$dllsearchpath"; then $ECHO "\ # Add the dll search path components to the executable PATH PATH=$dllsearchpath:\$PATH " fi $ECHO "\ if test \"\$libtool_execute_magic\" != \"$magic\"; then # Run the actual program with our arguments. " case $host in # Backslashes separate directories on plain windows *-*-mingw | *-*-os2* | *-cegcc*) $ECHO "\ exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} " ;; *) $ECHO "\ exec \"\$progdir/\$program\" \${1+\"\$@\"} " ;; esac $ECHO "\ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 exit 1 fi else # The program doesn't exist. \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 $ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 exit 1 fi fi\ " } # end: func_emit_wrapper_part2 # func_emit_wrapper [arg=no] # # Emit a libtool wrapper script on stdout. # Don't directly open a file because we may want to # incorporate the script contents within a cygwin/mingw # wrapper executable. Must ONLY be called from within # func_mode_link because it depends on a number of variables # set therein. # # ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR # variable will take. If 'yes', then the emitted script # will assume that the directory in which it is stored is # the $objdir directory. This is a cygwin/mingw-specific # behavior. func_emit_wrapper () { func_emit_wrapper_arg1=no if test -n "$1" ; then func_emit_wrapper_arg1=$1 fi # split this up so that func_emit_cwrapperexe_src # can call each part independently. func_emit_wrapper_part1 "${func_emit_wrapper_arg1}" func_emit_wrapper_part2 "${func_emit_wrapper_arg1}" } # func_to_host_path arg # # Convert paths to host format when used with build tools. # Intended for use with "native" mingw (where libtool itself # is running under the msys shell), or in the following cross- # build environments: # $build $host # mingw (msys) mingw [e.g. native] # cygwin mingw # *nix + wine mingw # where wine is equipped with the `winepath' executable. # In the native mingw case, the (msys) shell automatically # converts paths for any non-msys applications it launches, # but that facility isn't available from inside the cwrapper. # Similar accommodations are necessary for $host mingw and # $build cygwin. Calling this function does no harm for other # $host/$build combinations not listed above. # # ARG is the path (on $build) that should be converted to # the proper representation for $host. The result is stored # in $func_to_host_path_result. func_to_host_path () { func_to_host_path_result="$1" if test -n "$1" ; then case $host in *mingw* ) lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' case $build in *mingw* ) # actually, msys # awkward: cmd appends spaces to result lt_sed_strip_trailing_spaces="s/[ ]*\$//" func_to_host_path_tmp1=`( cmd //c echo "$1" |\ $SED -e "$lt_sed_strip_trailing_spaces" ) 2>/dev/null || echo ""` func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\ $SED -e "$lt_sed_naive_backslashify"` ;; *cygwin* ) func_to_host_path_tmp1=`cygpath -w "$1"` func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\ $SED -e "$lt_sed_naive_backslashify"` ;; * ) # Unfortunately, winepath does not exit with a non-zero # error code, so we are forced to check the contents of # stdout. On the other hand, if the command is not # found, the shell will set an exit code of 127 and print # *an error message* to stdout. So we must check for both # error code of zero AND non-empty stdout, which explains # the odd construction: func_to_host_path_tmp1=`winepath -w "$1" 2>/dev/null` if test "$?" -eq 0 && test -n "${func_to_host_path_tmp1}"; then func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\ $SED -e "$lt_sed_naive_backslashify"` else # Allow warning below. func_to_host_path_result="" fi ;; esac if test -z "$func_to_host_path_result" ; then func_error "Could not determine host path corresponding to" func_error " '$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback: func_to_host_path_result="$1" fi ;; esac fi } # end: func_to_host_path # func_to_host_pathlist arg # # Convert pathlists to host format when used with build tools. # See func_to_host_path(), above. This function supports the # following $build/$host combinations (but does no harm for # combinations not listed here): # $build $host # mingw (msys) mingw [e.g. native] # cygwin mingw # *nix + wine mingw # # Path separators are also converted from $build format to # $host format. If ARG begins or ends with a path separator # character, it is preserved (but converted to $host format) # on output. # # ARG is a pathlist (on $build) that should be converted to # the proper representation on $host. The result is stored # in $func_to_host_pathlist_result. func_to_host_pathlist () { func_to_host_pathlist_result="$1" if test -n "$1" ; then case $host in *mingw* ) lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' # Remove leading and trailing path separator characters from # ARG. msys behavior is inconsistent here, cygpath turns them # into '.;' and ';.', and winepath ignores them completely. func_to_host_pathlist_tmp2="$1" # Once set for this call, this variable should not be # reassigned. It is used in tha fallback case. func_to_host_pathlist_tmp1=`echo "$func_to_host_pathlist_tmp2" |\ $SED -e 's|^:*||' -e 's|:*$||'` case $build in *mingw* ) # Actually, msys. # Awkward: cmd appends spaces to result. lt_sed_strip_trailing_spaces="s/[ ]*\$//" func_to_host_pathlist_tmp2=`( cmd //c echo "$func_to_host_pathlist_tmp1" |\ $SED -e "$lt_sed_strip_trailing_spaces" ) 2>/dev/null || echo ""` func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp2" |\ $SED -e "$lt_sed_naive_backslashify"` ;; *cygwin* ) func_to_host_pathlist_tmp2=`cygpath -w -p "$func_to_host_pathlist_tmp1"` func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp2" |\ $SED -e "$lt_sed_naive_backslashify"` ;; * ) # unfortunately, winepath doesn't convert pathlists func_to_host_pathlist_result="" func_to_host_pathlist_oldIFS=$IFS IFS=: for func_to_host_pathlist_f in $func_to_host_pathlist_tmp1 ; do IFS=$func_to_host_pathlist_oldIFS if test -n "$func_to_host_pathlist_f" ; then func_to_host_path "$func_to_host_pathlist_f" if test -n "$func_to_host_path_result" ; then if test -z "$func_to_host_pathlist_result" ; then func_to_host_pathlist_result="$func_to_host_path_result" else func_to_host_pathlist_result="$func_to_host_pathlist_result;$func_to_host_path_result" fi fi fi IFS=: done IFS=$func_to_host_pathlist_oldIFS ;; esac if test -z "$func_to_host_pathlist_result" ; then func_error "Could not determine the host path(s) corresponding to" func_error " '$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback. This may break if $1 contains DOS-style drive # specifications. The fix is not to complicate the expression # below, but for the user to provide a working wine installation # with winepath so that path translation in the cross-to-mingw # case works properly. lt_replace_pathsep_nix_to_dos="s|:|;|g" func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp1" |\ $SED -e "$lt_replace_pathsep_nix_to_dos"` fi # Now, add the leading and trailing path separators back case "$1" in :* ) func_to_host_pathlist_result=";$func_to_host_pathlist_result" ;; esac case "$1" in *: ) func_to_host_pathlist_result="$func_to_host_pathlist_result;" ;; esac ;; esac fi } # end: func_to_host_pathlist # func_emit_cwrapperexe_src # emit the source code for a wrapper executable on stdout # Must ONLY be called from within func_mode_link because # it depends on a number of variable set therein. func_emit_cwrapperexe_src () { cat < #include #ifdef _MSC_VER # include # include # include # define setmode _setmode #else # include # include # ifdef __CYGWIN__ # include # define HAVE_SETENV # ifdef __STRICT_ANSI__ char *realpath (const char *, char *); int putenv (char *); int setenv (const char *, const char *, int); # endif # endif #endif #include #include #include #include #include #include #include #include #if defined(PATH_MAX) # define LT_PATHMAX PATH_MAX #elif defined(MAXPATHLEN) # define LT_PATHMAX MAXPATHLEN #else # define LT_PATHMAX 1024 #endif #ifndef S_IXOTH # define S_IXOTH 0 #endif #ifndef S_IXGRP # define S_IXGRP 0 #endif #ifdef _MSC_VER # define S_IXUSR _S_IEXEC # define stat _stat # ifndef _INTPTR_T_DEFINED # define intptr_t int # endif #endif #ifndef DIR_SEPARATOR # define DIR_SEPARATOR '/' # define PATH_SEPARATOR ':' #endif #if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ defined (__OS2__) # define HAVE_DOS_BASED_FILE_SYSTEM # define FOPEN_WB "wb" # ifndef DIR_SEPARATOR_2 # define DIR_SEPARATOR_2 '\\' # endif # ifndef PATH_SEPARATOR_2 # define PATH_SEPARATOR_2 ';' # endif #endif #ifndef DIR_SEPARATOR_2 # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) #else /* DIR_SEPARATOR_2 */ # define IS_DIR_SEPARATOR(ch) \ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) #endif /* DIR_SEPARATOR_2 */ #ifndef PATH_SEPARATOR_2 # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) #else /* PATH_SEPARATOR_2 */ # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) #endif /* PATH_SEPARATOR_2 */ #ifdef __CYGWIN__ # define FOPEN_WB "wb" #endif #ifndef FOPEN_WB # define FOPEN_WB "w" #endif #ifndef _O_BINARY # define _O_BINARY 0 #endif #define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) #define XFREE(stale) do { \ if (stale) { free ((void *) stale); stale = 0; } \ } while (0) #undef LTWRAPPER_DEBUGPRINTF #if defined DEBUGWRAPPER # define LTWRAPPER_DEBUGPRINTF(args) ltwrapper_debugprintf args static void ltwrapper_debugprintf (const char *fmt, ...) { va_list args; va_start (args, fmt); (void) vfprintf (stderr, fmt, args); va_end (args); } #else # define LTWRAPPER_DEBUGPRINTF(args) #endif const char *program_name = NULL; void *xmalloc (size_t num); char *xstrdup (const char *string); const char *base_name (const char *name); char *find_executable (const char *wrapper); char *chase_symlinks (const char *pathspec); int make_executable (const char *path); int check_executable (const char *path); char *strendzap (char *str, const char *pat); void lt_fatal (const char *message, ...); void lt_setenv (const char *name, const char *value); char *lt_extend_str (const char *orig_value, const char *add, int to_end); void lt_opt_process_env_set (const char *arg); void lt_opt_process_env_prepend (const char *arg); void lt_opt_process_env_append (const char *arg); int lt_split_name_value (const char *arg, char** name, char** value); void lt_update_exe_path (const char *name, const char *value); void lt_update_lib_path (const char *name, const char *value); static const char *script_text_part1 = EOF func_emit_wrapper_part1 yes | $SED -e 's/\([\\"]\)/\\\1/g' \ -e 's/^/ "/' -e 's/$/\\n"/' echo ";" cat <"))); for (i = 0; i < newargc; i++) { LTWRAPPER_DEBUGPRINTF (("(main) newargz[%d] : %s\n", i, (newargz[i] ? newargz[i] : ""))); } EOF case $host_os in mingw*) cat <<"EOF" /* execv doesn't actually work on mingw as expected on unix */ rval = _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz); if (rval == -1) { /* failed to start process */ LTWRAPPER_DEBUGPRINTF (("(main) failed to launch target \"%s\": errno = %d\n", lt_argv_zero, errno)); return 127; } return rval; EOF ;; *) cat <<"EOF" execv (lt_argv_zero, newargz); return rval; /* =127, but avoids unused variable warning */ EOF ;; esac cat <<"EOF" } void * xmalloc (size_t num) { void *p = (void *) malloc (num); if (!p) lt_fatal ("Memory exhausted"); return p; } char * xstrdup (const char *string) { return string ? strcpy ((char *) xmalloc (strlen (string) + 1), string) : NULL; } const char * base_name (const char *name) { const char *base; #if defined (HAVE_DOS_BASED_FILE_SYSTEM) /* Skip over the disk name in MSDOS pathnames. */ if (isalpha ((unsigned char) name[0]) && name[1] == ':') name += 2; #endif for (base = name; *name; name++) if (IS_DIR_SEPARATOR (*name)) base = name + 1; return base; } int check_executable (const char *path) { struct stat st; LTWRAPPER_DEBUGPRINTF (("(check_executable) : %s\n", path ? (*path ? path : "EMPTY!") : "NULL!")); if ((!path) || (!*path)) return 0; if ((stat (path, &st) >= 0) && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return 1; else return 0; } int make_executable (const char *path) { int rval = 0; struct stat st; LTWRAPPER_DEBUGPRINTF (("(make_executable) : %s\n", path ? (*path ? path : "EMPTY!") : "NULL!")); if ((!path) || (!*path)) return 0; if (stat (path, &st) >= 0) { rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); } return rval; } /* Searches for the full path of the wrapper. Returns newly allocated full path name if found, NULL otherwise Does not chase symlinks, even on platforms that support them. */ char * find_executable (const char *wrapper) { int has_slash = 0; const char *p; const char *p_next; /* static buffer for getcwd */ char tmp[LT_PATHMAX + 1]; int tmp_len; char *concat_name; LTWRAPPER_DEBUGPRINTF (("(find_executable) : %s\n", wrapper ? (*wrapper ? wrapper : "EMPTY!") : "NULL!")); if ((wrapper == NULL) || (*wrapper == '\0')) return NULL; /* Absolute path? */ #if defined (HAVE_DOS_BASED_FILE_SYSTEM) if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } else { #endif if (IS_DIR_SEPARATOR (wrapper[0])) { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } #if defined (HAVE_DOS_BASED_FILE_SYSTEM) } #endif for (p = wrapper; *p; p++) if (*p == '/') { has_slash = 1; break; } if (!has_slash) { /* no slashes; search PATH */ const char *path = getenv ("PATH"); if (path != NULL) { for (p = path; *p; p = p_next) { const char *q; size_t p_len; for (q = p; *q; q++) if (IS_PATH_SEPARATOR (*q)) break; p_len = q - p; p_next = (*q == '\0' ? q : q + 1); if (p_len == 0) { /* empty path: current directory */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal ("getcwd failed"); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); } else { concat_name = XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, p, p_len); concat_name[p_len] = '/'; strcpy (concat_name + p_len + 1, wrapper); } if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } } /* not found in PATH; assume curdir */ } /* Relative path | not found in path: prepend cwd */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal ("getcwd failed"); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); return NULL; } char * chase_symlinks (const char *pathspec) { #ifndef S_ISLNK return xstrdup (pathspec); #else char buf[LT_PATHMAX]; struct stat s; char *tmp_pathspec = xstrdup (pathspec); char *p; int has_symlinks = 0; while (strlen (tmp_pathspec) && !has_symlinks) { LTWRAPPER_DEBUGPRINTF (("checking path component for symlinks: %s\n", tmp_pathspec)); if (lstat (tmp_pathspec, &s) == 0) { if (S_ISLNK (s.st_mode) != 0) { has_symlinks = 1; break; } /* search backwards for last DIR_SEPARATOR */ p = tmp_pathspec + strlen (tmp_pathspec) - 1; while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) p--; if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) { /* no more DIR_SEPARATORS left */ break; } *p = '\0'; } else { char *errstr = strerror (errno); lt_fatal ("Error accessing file %s (%s)", tmp_pathspec, errstr); } } XFREE (tmp_pathspec); if (!has_symlinks) { return xstrdup (pathspec); } tmp_pathspec = realpath (pathspec, buf); if (tmp_pathspec == 0) { lt_fatal ("Could not follow symlinks for %s", pathspec); } return xstrdup (tmp_pathspec); #endif } char * strendzap (char *str, const char *pat) { size_t len, patlen; assert (str != NULL); assert (pat != NULL); len = strlen (str); patlen = strlen (pat); if (patlen <= len) { str += len - patlen; if (strcmp (str, pat) == 0) *str = '\0'; } return str; } static void lt_error_core (int exit_status, const char *mode, const char *message, va_list ap) { fprintf (stderr, "%s: %s: ", program_name, mode); vfprintf (stderr, message, ap); fprintf (stderr, ".\n"); if (exit_status >= 0) exit (exit_status); } void lt_fatal (const char *message, ...) { va_list ap; va_start (ap, message); lt_error_core (EXIT_FAILURE, "FATAL", message, ap); va_end (ap); } void lt_setenv (const char *name, const char *value) { LTWRAPPER_DEBUGPRINTF (("(lt_setenv) setting '%s' to '%s'\n", (name ? name : ""), (value ? value : ""))); { #ifdef HAVE_SETENV /* always make a copy, for consistency with !HAVE_SETENV */ char *str = xstrdup (value); setenv (name, str, 1); #else int len = strlen (name) + 1 + strlen (value) + 1; char *str = XMALLOC (char, len); sprintf (str, "%s=%s", name, value); if (putenv (str) != EXIT_SUCCESS) { XFREE (str); } #endif } } char * lt_extend_str (const char *orig_value, const char *add, int to_end) { char *new_value; if (orig_value && *orig_value) { int orig_value_len = strlen (orig_value); int add_len = strlen (add); new_value = XMALLOC (char, add_len + orig_value_len + 1); if (to_end) { strcpy (new_value, orig_value); strcpy (new_value + orig_value_len, add); } else { strcpy (new_value, add); strcpy (new_value + add_len, orig_value); } } else { new_value = xstrdup (add); } return new_value; } int lt_split_name_value (const char *arg, char** name, char** value) { const char *p; int len; if (!arg || !*arg) return 1; p = strchr (arg, (int)'='); if (!p) return 1; *value = xstrdup (++p); len = strlen (arg) - strlen (*value); *name = XMALLOC (char, len); strncpy (*name, arg, len-1); (*name)[len - 1] = '\0'; return 0; } void lt_opt_process_env_set (const char *arg) { char *name = NULL; char *value = NULL; if (lt_split_name_value (arg, &name, &value) != 0) { XFREE (name); XFREE (value); lt_fatal ("bad argument for %s: '%s'", env_set_opt, arg); } lt_setenv (name, value); XFREE (name); XFREE (value); } void lt_opt_process_env_prepend (const char *arg) { char *name = NULL; char *value = NULL; char *new_value = NULL; if (lt_split_name_value (arg, &name, &value) != 0) { XFREE (name); XFREE (value); lt_fatal ("bad argument for %s: '%s'", env_prepend_opt, arg); } new_value = lt_extend_str (getenv (name), value, 0); lt_setenv (name, new_value); XFREE (new_value); XFREE (name); XFREE (value); } void lt_opt_process_env_append (const char *arg) { char *name = NULL; char *value = NULL; char *new_value = NULL; if (lt_split_name_value (arg, &name, &value) != 0) { XFREE (name); XFREE (value); lt_fatal ("bad argument for %s: '%s'", env_append_opt, arg); } new_value = lt_extend_str (getenv (name), value, 1); lt_setenv (name, new_value); XFREE (new_value); XFREE (name); XFREE (value); } void lt_update_exe_path (const char *name, const char *value) { LTWRAPPER_DEBUGPRINTF (("(lt_update_exe_path) modifying '%s' by prepending '%s'\n", (name ? name : ""), (value ? value : ""))); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); /* some systems can't cope with a ':'-terminated path #' */ int len = strlen (new_value); while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1])) { new_value[len-1] = '\0'; } lt_setenv (name, new_value); XFREE (new_value); } } void lt_update_lib_path (const char *name, const char *value) { LTWRAPPER_DEBUGPRINTF (("(lt_update_lib_path) modifying '%s' by prepending '%s'\n", (name ? name : ""), (value ? value : ""))); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); lt_setenv (name, new_value); XFREE (new_value); } } EOF } # end: func_emit_cwrapperexe_src # func_mode_link arg... func_mode_link () { $opt_debug case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) # It is impossible to link a dll without this setting, and # we shouldn't force the makefile maintainer to figure out # which system we are compiling for in order to pass an extra # flag for every libtool invocation. # allow_undefined=no # FIXME: Unfortunately, there are problems with the above when trying # to make a dll which has undefined symbols, in which case not # even a static library is built. For now, we need to specify # -no-undefined on the libtool link line when we can be certain # that all symbols are satisfied, otherwise we get a static library. allow_undefined=yes ;; *) allow_undefined=yes ;; esac libtool_args=$nonopt base_compile="$nonopt $@" compile_command=$nonopt finalize_command=$nonopt compile_rpath= finalize_rpath= compile_shlibpath= finalize_shlibpath= convenience= old_convenience= deplibs= old_deplibs= compiler_flags= linker_flags= dllsearchpath= lib_search_path=`pwd` inst_prefix_dir= new_inherited_linker_flags= avoid_version=no dlfiles= dlprefiles= dlself=no export_dynamic=no export_symbols= export_symbols_regex= generated= libobjs= ltlibs= module=no no_install=no objs= non_pic_objects= precious_files_regex= prefer_static_libs=no preload=no prev= prevarg= release= rpath= xrpath= perm_rpath= temp_rpath= thread_safe=no vinfo= vinfo_number=no weak_libs= single_module="${wl}-single_module" func_infer_tag $base_compile # We need to know -static, to get the right output filenames. for arg do case $arg in -shared) test "$build_libtool_libs" != yes && \ func_fatal_configuration "can not build a shared library" build_old_libs=no break ;; -all-static | -static | -static-libtool-libs) case $arg in -all-static) if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then func_warning "complete static linking is impossible in this configuration" fi if test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; -static) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=built ;; -static-libtool-libs) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; esac build_libtool_libs=no build_old_libs=yes break ;; esac done # See if our shared archives depend on static archives. test -n "$old_archive_from_new_cmds" && build_old_libs=yes # Go through the arguments, transforming them on the way. while test "$#" -gt 0; do arg="$1" shift func_quote_for_eval "$arg" qarg=$func_quote_for_eval_unquoted_result func_append libtool_args " $func_quote_for_eval_result" # If the previous option needs an argument, assign it. if test -n "$prev"; then case $prev in output) func_append compile_command " @OUTPUT@" func_append finalize_command " @OUTPUT@" ;; esac case $prev in dlfiles|dlprefiles) if test "$preload" = no; then # Add the symbol object into the linking commands. func_append compile_command " @SYMFILE@" func_append finalize_command " @SYMFILE@" preload=yes fi case $arg in *.la | *.lo) ;; # We handle these cases below. force) if test "$dlself" = no; then dlself=needless export_dynamic=yes fi prev= continue ;; self) if test "$prev" = dlprefiles; then dlself=yes elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then dlself=yes else dlself=needless export_dynamic=yes fi prev= continue ;; *) if test "$prev" = dlfiles; then dlfiles="$dlfiles $arg" else dlprefiles="$dlprefiles $arg" fi prev= continue ;; esac ;; expsyms) export_symbols="$arg" test -f "$arg" \ || func_fatal_error "symbol file \`$arg' does not exist" prev= continue ;; expsyms_regex) export_symbols_regex="$arg" prev= continue ;; framework) case $host in *-*-darwin*) case "$deplibs " in *" $qarg.ltframework "*) ;; *) deplibs="$deplibs $qarg.ltframework" # this is fixed later ;; esac ;; esac prev= continue ;; inst_prefix) inst_prefix_dir="$arg" prev= continue ;; objectlist) if test -f "$arg"; then save_arg=$arg moreargs= for fil in `cat "$save_arg"` do # moreargs="$moreargs $fil" arg=$fil # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test "$pic_object" = none && test "$non_pic_object" = none; then func_fatal_error "cannot find name of object for \`$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" if test "$pic_object" != none; then # Prepend the subdirectory the object is found in. pic_object="$xdir$pic_object" if test "$prev" = dlfiles; then if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then dlfiles="$dlfiles $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test "$prev" = dlprefiles; then # Preload the old-style object. dlprefiles="$dlprefiles $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg="$pic_object" fi # Non-PIC object. if test "$non_pic_object" != none; then # Prepend the subdirectory the object is found in. non_pic_object="$xdir$non_pic_object" # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test "$pic_object" = none ; then arg="$non_pic_object" fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object="$pic_object" func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "\`$arg' is not a valid libtool object" fi fi done else func_fatal_error "link input file \`$arg' does not exist" fi arg=$save_arg prev= continue ;; precious_regex) precious_files_regex="$arg" prev= continue ;; release) release="-$arg" prev= continue ;; rpath | xrpath) # We need an absolute path. case $arg in [\\/]* | [A-Za-z]:[\\/]*) ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac if test "$prev" = rpath; then case "$rpath " in *" $arg "*) ;; *) rpath="$rpath $arg" ;; esac else case "$xrpath " in *" $arg "*) ;; *) xrpath="$xrpath $arg" ;; esac fi prev= continue ;; shrext) shrext_cmds="$arg" prev= continue ;; weak) weak_libs="$weak_libs $arg" prev= continue ;; xcclinker) linker_flags="$linker_flags $qarg" compiler_flags="$compiler_flags $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xcompiler) compiler_flags="$compiler_flags $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xlinker) linker_flags="$linker_flags $qarg" compiler_flags="$compiler_flags $wl$qarg" prev= func_append compile_command " $wl$qarg" func_append finalize_command " $wl$qarg" continue ;; *) eval "$prev=\"\$arg\"" prev= continue ;; esac fi # test -n "$prev" prevarg="$arg" case $arg in -all-static) if test -n "$link_static_flag"; then # See comment for -static flag below, for more details. func_append compile_command " $link_static_flag" func_append finalize_command " $link_static_flag" fi continue ;; -allow-undefined) # FIXME: remove this flag sometime in the future. func_fatal_error "\`-allow-undefined' must not be used because it is the default" ;; -avoid-version) avoid_version=yes continue ;; -dlopen) prev=dlfiles continue ;; -dlpreopen) prev=dlprefiles continue ;; -export-dynamic) export_dynamic=yes continue ;; -export-symbols | -export-symbols-regex) if test -n "$export_symbols" || test -n "$export_symbols_regex"; then func_fatal_error "more than one -exported-symbols argument is not allowed" fi if test "X$arg" = "X-export-symbols"; then prev=expsyms else prev=expsyms_regex fi continue ;; -framework) prev=framework continue ;; -inst-prefix-dir) prev=inst_prefix continue ;; # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* # so, if we see these flags be careful not to treat them like -L -L[A-Z][A-Z]*:*) case $with_gcc/$host in no/*-*-irix* | /*-*-irix*) func_append compile_command " $arg" func_append finalize_command " $arg" ;; esac continue ;; -L*) func_stripname '-L' '' "$arg" dir=$func_stripname_result if test -z "$dir"; then if test "$#" -gt 0; then func_fatal_error "require no space between \`-L' and \`$1'" else func_fatal_error "need path for \`-L' option" fi fi # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) absdir=`cd "$dir" && pwd` test -z "$absdir" && \ func_fatal_error "cannot determine absolute directory name of \`$dir'" dir="$absdir" ;; esac case "$deplibs " in *" -L$dir "*) ;; *) deplibs="$deplibs -L$dir" lib_search_path="$lib_search_path $dir" ;; esac case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "X$dir" | $Xsed -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$dir:"*) ;; ::) dllsearchpath=$dir;; *) dllsearchpath="$dllsearchpath:$dir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) dllsearchpath="$dllsearchpath:$testbindir";; esac ;; esac continue ;; -l*) if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc*) # These systems don't actually have a C or math library (as such) continue ;; *-*-os2*) # These systems don't actually have a C library (as such) test "X$arg" = "X-lc" && continue ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. test "X$arg" = "X-lc" && continue ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C and math libraries are in the System framework deplibs="$deplibs System.ltframework" continue ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype test "X$arg" = "X-lc" && continue ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work test "X$arg" = "X-lc" && continue ;; esac elif test "X$arg" = "X-lc_r"; then case $host in *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc_r directly, use -pthread flag. continue ;; esac fi deplibs="$deplibs $arg" continue ;; -module) module=yes continue ;; # Tru64 UNIX uses -model [arg] to determine the layout of C++ # classes, name mangling, and exception handling. # Darwin uses the -arch flag to determine output architecture. -model|-arch|-isysroot) compiler_flags="$compiler_flags $arg" func_append compile_command " $arg" func_append finalize_command " $arg" prev=xcompiler continue ;; -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads) compiler_flags="$compiler_flags $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case "$new_inherited_linker_flags " in *" $arg "*) ;; * ) new_inherited_linker_flags="$new_inherited_linker_flags $arg" ;; esac continue ;; -multi_module) single_module="${wl}-multi_module" continue ;; -no-fast-install) fast_install=no continue ;; -no-install) case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) # The PATH hackery in wrapper scripts is required on Windows # and Darwin in order for the loader to find any dlls it needs. func_warning "\`-no-install' is ignored for $host" func_warning "assuming \`-no-fast-install' instead" fast_install=no ;; *) no_install=yes ;; esac continue ;; -no-undefined) allow_undefined=no continue ;; -objectlist) prev=objectlist continue ;; -o) prev=output ;; -precious-files-regex) prev=precious_regex continue ;; -release) prev=release continue ;; -rpath) prev=rpath continue ;; -R) prev=xrpath continue ;; -R*) func_stripname '-R' '' "$arg" dir=$func_stripname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac case "$xrpath " in *" $dir "*) ;; *) xrpath="$xrpath $dir" ;; esac continue ;; -shared) # The effects of -shared are defined in a previous loop. continue ;; -shrext) prev=shrext continue ;; -static | -static-libtool-libs) # The effects of -static are defined in a previous loop. # We used to do the same as -all-static on platforms that # didn't have a PIC flag, but the assumption that the effects # would be equivalent was wrong. It would break on at least # Digital Unix and AIX. continue ;; -thread-safe) thread_safe=yes continue ;; -version-info) prev=vinfo continue ;; -version-number) prev=vinfo vinfo_number=yes continue ;; -weak) prev=weak continue ;; -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result arg= save_ifs="$IFS"; IFS=',' for flag in $args; do IFS="$save_ifs" func_quote_for_eval "$flag" arg="$arg $wl$func_quote_for_eval_result" compiler_flags="$compiler_flags $func_quote_for_eval_result" done IFS="$save_ifs" func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Wl,*) func_stripname '-Wl,' '' "$arg" args=$func_stripname_result arg= save_ifs="$IFS"; IFS=',' for flag in $args; do IFS="$save_ifs" func_quote_for_eval "$flag" arg="$arg $wl$func_quote_for_eval_result" compiler_flags="$compiler_flags $wl$func_quote_for_eval_result" linker_flags="$linker_flags $func_quote_for_eval_result" done IFS="$save_ifs" func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Xcompiler) prev=xcompiler continue ;; -Xlinker) prev=xlinker continue ;; -XCClinker) prev=xcclinker continue ;; # -msg_* for osf cc -msg_*) func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" ;; # -64, -mips[0-9] enable 64-bit mode on the SGI compiler # -r[0-9][0-9]* specifies the processor on the SGI compiler # -xarch=*, -xtarget=* enable 64-bit mode on the Sun compiler # +DA*, +DD* enable 64-bit mode on the HP compiler # -q* pass through compiler args for the IBM compiler # -m*, -t[45]*, -txscale* pass through architecture-specific # compiler args for GCC # -F/path gives path to uninstalled frameworks, gcc on darwin # -p, -pg, --coverage, -fprofile-* pass through profiling flag for GCC # @file GCC response files -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*) func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" func_append compile_command " $arg" func_append finalize_command " $arg" compiler_flags="$compiler_flags $arg" continue ;; # Some other compiler flag. -* | +*) func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" ;; *.$objext) # A standard object. objs="$objs $arg" ;; *.lo) # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test "$pic_object" = none && test "$non_pic_object" = none; then func_fatal_error "cannot find name of object for \`$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" if test "$pic_object" != none; then # Prepend the subdirectory the object is found in. pic_object="$xdir$pic_object" if test "$prev" = dlfiles; then if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then dlfiles="$dlfiles $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test "$prev" = dlprefiles; then # Preload the old-style object. dlprefiles="$dlprefiles $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg="$pic_object" fi # Non-PIC object. if test "$non_pic_object" != none; then # Prepend the subdirectory the object is found in. non_pic_object="$xdir$non_pic_object" # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test "$pic_object" = none ; then arg="$non_pic_object" fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object="$pic_object" func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "\`$arg' is not a valid libtool object" fi fi ;; *.$libext) # An archive. deplibs="$deplibs $arg" old_deplibs="$old_deplibs $arg" continue ;; *.la) # A libtool-controlled library. if test "$prev" = dlfiles; then # This library was specified with -dlopen. dlfiles="$dlfiles $arg" prev= elif test "$prev" = dlprefiles; then # The library was specified with -dlpreopen. dlprefiles="$dlprefiles $arg" prev= else deplibs="$deplibs $arg" fi continue ;; # Some other compiler argument. *) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" ;; esac # arg # Now actually substitute the argument into the commands. if test -n "$arg"; then func_append compile_command " $arg" func_append finalize_command " $arg" fi done # argument parsing loop test -n "$prev" && \ func_fatal_help "the \`$prevarg' option requires an argument" if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then eval arg=\"$export_dynamic_flag_spec\" func_append compile_command " $arg" func_append finalize_command " $arg" fi oldlibs= # calculate the name of the file, without its directory func_basename "$output" outputname="$func_basename_result" libobjs_save="$libobjs" if test -n "$shlibpath_var"; then # get the directories listed in $shlibpath_var eval shlib_search_path=\`\$ECHO \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\` else shlib_search_path= fi eval sys_lib_search_path=\"$sys_lib_search_path_spec\" eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" func_dirname "$output" "/" "" output_objdir="$func_dirname_result$objdir" # Create the object directory. func_mkdir_p "$output_objdir" # Determine the type of output case $output in "") func_fatal_help "you must specify an output file" ;; *.$libext) linkmode=oldlib ;; *.lo | *.$objext) linkmode=obj ;; *.la) linkmode=lib ;; *) linkmode=prog ;; # Anything else should be a program. esac specialdeplibs= libs= # Find all interdependent deplibs by searching for libraries # that are linked more than once (e.g. -la -lb -la) for deplib in $deplibs; do if $opt_duplicate_deps ; then case "$libs " in *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; esac fi libs="$libs $deplib" done if test "$linkmode" = lib; then libs="$predeps $libs $compiler_lib_search_path $postdeps" # Compute libraries that are listed more than once in $predeps # $postdeps and mark them as special (i.e., whose duplicates are # not to be eliminated). pre_post_deps= if $opt_duplicate_compiler_generated_deps; then for pre_post_dep in $predeps $postdeps; do case "$pre_post_deps " in *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;; esac pre_post_deps="$pre_post_deps $pre_post_dep" done fi pre_post_deps= fi deplibs= newdependency_libs= newlib_search_path= need_relink=no # whether we're linking any uninstalled libtool libraries notinst_deplibs= # not-installed libtool libraries notinst_path= # paths that contain not-installed libtool libraries case $linkmode in lib) passes="conv dlpreopen link" for file in $dlfiles $dlprefiles; do case $file in *.la) ;; *) func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file" ;; esac done ;; prog) compile_deplibs= finalize_deplibs= alldeplibs=no newdlfiles= newdlprefiles= passes="conv scan dlopen dlpreopen link" ;; *) passes="conv" ;; esac for pass in $passes; do # The preopen pass in lib mode reverses $deplibs; put it back here # so that -L comes before libs that need it for instance... if test "$linkmode,$pass" = "lib,link"; then ## FIXME: Find the place where the list is rebuilt in the wrong ## order, and fix it there properly tmp_deplibs= for deplib in $deplibs; do tmp_deplibs="$deplib $tmp_deplibs" done deplibs="$tmp_deplibs" fi if test "$linkmode,$pass" = "lib,link" || test "$linkmode,$pass" = "prog,scan"; then libs="$deplibs" deplibs= fi if test "$linkmode" = prog; then case $pass in dlopen) libs="$dlfiles" ;; dlpreopen) libs="$dlprefiles" ;; link) libs="$deplibs %DEPLIBS%" test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" ;; esac fi if test "$linkmode,$pass" = "lib,dlpreopen"; then # Collect and forward deplibs of preopened libtool libs for lib in $dlprefiles; do # Ignore non-libtool-libs dependency_libs= case $lib in *.la) func_source "$lib" ;; esac # Collect preopened libtool deplibs, except any this library # has declared as weak libs for deplib in $dependency_libs; do deplib_base=`$ECHO "X$deplib" | $Xsed -e "$basename"` case " $weak_libs " in *" $deplib_base "*) ;; *) deplibs="$deplibs $deplib" ;; esac done done libs="$dlprefiles" fi if test "$pass" = dlopen; then # Collect dlpreopened libraries save_deplibs="$deplibs" deplibs= fi for deplib in $libs; do lib= found=no case $deplib in -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads) if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else compiler_flags="$compiler_flags $deplib" if test "$linkmode" = lib ; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) new_inherited_linker_flags="$new_inherited_linker_flags $deplib" ;; esac fi fi continue ;; -l*) if test "$linkmode" != lib && test "$linkmode" != prog; then func_warning "\`-l' is ignored for archives/objects" continue fi func_stripname '-l' '' "$deplib" name=$func_stripname_result if test "$linkmode" = lib; then searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" else searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" fi for searchdir in $searchdirs; do for search_ext in .la $std_shrext .so .a; do # Search the libtool library lib="$searchdir/lib${name}${search_ext}" if test -f "$lib"; then if test "$search_ext" = ".la"; then found=yes else found=no fi break 2 fi done done if test "$found" != yes; then # deplib doesn't seem to be a libtool library if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" fi continue else # deplib is a libtool library # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, # We need to do some special things here, and not later. if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then case " $predeps $postdeps " in *" $deplib "*) if func_lalib_p "$lib"; then library_names= old_library= func_source "$lib" for l in $old_library $library_names; do ll="$l" done if test "X$ll" = "X$old_library" ; then # only static version available found=no func_dirname "$lib" "" "." ladir="$func_dirname_result" lib=$ladir/$old_library if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" fi continue fi fi ;; *) ;; esac fi fi ;; # -l *.ltframework) if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" if test "$linkmode" = lib ; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) new_inherited_linker_flags="$new_inherited_linker_flags $deplib" ;; esac fi fi continue ;; -L*) case $linkmode in lib) deplibs="$deplib $deplibs" test "$pass" = conv && continue newdependency_libs="$deplib $newdependency_libs" func_stripname '-L' '' "$deplib" newlib_search_path="$newlib_search_path $func_stripname_result" ;; prog) if test "$pass" = conv; then deplibs="$deplib $deplibs" continue fi if test "$pass" = scan; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi func_stripname '-L' '' "$deplib" newlib_search_path="$newlib_search_path $func_stripname_result" ;; *) func_warning "\`-L' is ignored for archives/objects" ;; esac # linkmode continue ;; # -L -R*) if test "$pass" = link; then func_stripname '-R' '' "$deplib" dir=$func_stripname_result # Make sure the xrpath contains only unique directories. case "$xrpath " in *" $dir "*) ;; *) xrpath="$xrpath $dir" ;; esac fi deplibs="$deplib $deplibs" continue ;; *.la) lib="$deplib" ;; *.$libext) if test "$pass" = conv; then deplibs="$deplib $deplibs" continue fi case $linkmode in lib) # Linking convenience modules into shared libraries is allowed, # but linking other static libraries is non-portable. case " $dlpreconveniencelibs " in *" $deplib "*) ;; *) valid_a_lib=no case $deplibs_check_method in match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` if eval "\$ECHO \"X$deplib\"" 2>/dev/null | $Xsed -e 10q \ | $EGREP "$match_pattern_regex" > /dev/null; then valid_a_lib=yes fi ;; pass_all) valid_a_lib=yes ;; esac if test "$valid_a_lib" != yes; then $ECHO $ECHO "*** Warning: Trying to link with static lib archive $deplib." $ECHO "*** I have the capability to make that library automatically link in when" $ECHO "*** you link to this library. But I can only do this if you have a" $ECHO "*** shared version of the library, which you do not appear to have" $ECHO "*** because the file extensions .$libext of this argument makes me believe" $ECHO "*** that it is just a static archive that I should not use here." else $ECHO $ECHO "*** Warning: Linking the shared library $output against the" $ECHO "*** static library $deplib is not portable!" deplibs="$deplib $deplibs" fi ;; esac continue ;; prog) if test "$pass" != link; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi continue ;; esac # linkmode ;; # *.$libext *.lo | *.$objext) if test "$pass" = conv; then deplibs="$deplib $deplibs" elif test "$linkmode" = prog; then if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then # If there is no dlopen support or we're linking statically, # we need to preload. newdlprefiles="$newdlprefiles $deplib" compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else newdlfiles="$newdlfiles $deplib" fi fi continue ;; %DEPLIBS%) alldeplibs=yes continue ;; esac # case $deplib if test "$found" = yes || test -f "$lib"; then : else func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'" fi # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$lib" \ || func_fatal_error "\`$lib' is not a valid libtool archive" func_dirname "$lib" "" "." ladir="$func_dirname_result" dlname= dlopen= dlpreopen= libdir= library_names= old_library= inherited_linker_flags= # If the library was installed with an old release of libtool, # it will not redefine variables installed, or shouldnotlink installed=yes shouldnotlink=no avoidtemprpath= # Read the .la file func_source "$lib" # Convert "-framework foo" to "foo.ltframework" if test -n "$inherited_linker_flags"; then tmp_inherited_linker_flags=`$ECHO "X$inherited_linker_flags" | $Xsed -e 's/-framework \([^ $]*\)/\1.ltframework/g'` for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do case " $new_inherited_linker_flags " in *" $tmp_inherited_linker_flag "*) ;; *) new_inherited_linker_flags="$new_inherited_linker_flags $tmp_inherited_linker_flag";; esac done fi dependency_libs=`$ECHO "X $dependency_libs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` if test "$linkmode,$pass" = "lib,link" || test "$linkmode,$pass" = "prog,scan" || { test "$linkmode" != prog && test "$linkmode" != lib; }; then test -n "$dlopen" && dlfiles="$dlfiles $dlopen" test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen" fi if test "$pass" = conv; then # Only check for convenience libraries deplibs="$lib $deplibs" if test -z "$libdir"; then if test -z "$old_library"; then func_fatal_error "cannot find name of link library for \`$lib'" fi # It is a libtool convenience library, so add in its objects. convenience="$convenience $ladir/$objdir/$old_library" old_convenience="$old_convenience $ladir/$objdir/$old_library" tmp_libs= for deplib in $dependency_libs; do deplibs="$deplib $deplibs" if $opt_duplicate_deps ; then case "$tmp_libs " in *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; esac fi tmp_libs="$tmp_libs $deplib" done elif test "$linkmode" != prog && test "$linkmode" != lib; then func_fatal_error "\`$lib' is not a convenience library" fi continue fi # $pass = conv # Get the name of the library we link against. linklib= for l in $old_library $library_names; do linklib="$l" done if test -z "$linklib"; then func_fatal_error "cannot find name of link library for \`$lib'" fi # This library was specified with -dlopen. if test "$pass" = dlopen; then if test -z "$libdir"; then func_fatal_error "cannot -dlopen a convenience library: \`$lib'" fi if test -z "$dlname" || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then # If there is no dlname, no dlopen support or we're linking # statically, we need to preload. We also need to preload any # dependent libraries so libltdl's deplib preloader doesn't # bomb out in the load deplibs phase. dlprefiles="$dlprefiles $lib $dependency_libs" else newdlfiles="$newdlfiles $lib" fi continue fi # $pass = dlopen # We need an absolute path. case $ladir in [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; *) abs_ladir=`cd "$ladir" && pwd` if test -z "$abs_ladir"; then func_warning "cannot determine absolute directory name of \`$ladir'" func_warning "passing it literally to the linker, although it might fail" abs_ladir="$ladir" fi ;; esac func_basename "$lib" laname="$func_basename_result" # Find the relevant object directory and library name. if test "X$installed" = Xyes; then if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then func_warning "library \`$lib' was moved." dir="$ladir" absdir="$abs_ladir" libdir="$abs_ladir" else dir="$libdir" absdir="$libdir" fi test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes else if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then dir="$ladir" absdir="$abs_ladir" # Remove this search path later notinst_path="$notinst_path $abs_ladir" else dir="$ladir/$objdir" absdir="$abs_ladir/$objdir" # Remove this search path later notinst_path="$notinst_path $abs_ladir" fi fi # $installed = yes func_stripname 'lib' '.la' "$laname" name=$func_stripname_result # This library was specified with -dlpreopen. if test "$pass" = dlpreopen; then if test -z "$libdir" && test "$linkmode" = prog; then func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'" fi # Prefer using a static library (so that no silly _DYNAMIC symbols # are required to link). if test -n "$old_library"; then newdlprefiles="$newdlprefiles $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ dlpreconveniencelibs="$dlpreconveniencelibs $dir/$old_library" # Otherwise, use the dlname, so that lt_dlopen finds it. elif test -n "$dlname"; then newdlprefiles="$newdlprefiles $dir/$dlname" else newdlprefiles="$newdlprefiles $dir/$linklib" fi fi # $pass = dlpreopen if test -z "$libdir"; then # Link the convenience library if test "$linkmode" = lib; then deplibs="$dir/$old_library $deplibs" elif test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$dir/$old_library $compile_deplibs" finalize_deplibs="$dir/$old_library $finalize_deplibs" else deplibs="$lib $deplibs" # used for prog,scan pass fi continue fi if test "$linkmode" = prog && test "$pass" != link; then newlib_search_path="$newlib_search_path $ladir" deplibs="$lib $deplibs" linkalldeplibs=no if test "$link_all_deplibs" != no || test -z "$library_names" || test "$build_libtool_libs" = no; then linkalldeplibs=yes fi tmp_libs= for deplib in $dependency_libs; do case $deplib in -L*) func_stripname '-L' '' "$deplib" newlib_search_path="$newlib_search_path $func_stripname_result" ;; esac # Need to link against all dependency_libs? if test "$linkalldeplibs" = yes; then deplibs="$deplib $deplibs" else # Need to hardcode shared library paths # or/and link against static libraries newdependency_libs="$deplib $newdependency_libs" fi if $opt_duplicate_deps ; then case "$tmp_libs " in *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; esac fi tmp_libs="$tmp_libs $deplib" done # for deplib continue fi # $linkmode = prog... if test "$linkmode,$pass" = "prog,link"; then if test -n "$library_names" && { { test "$prefer_static_libs" = no || test "$prefer_static_libs,$installed" = "built,yes"; } || test -z "$old_library"; }; then # We need to hardcode the library path if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then # Make sure the rpath contains only unique directories. case "$temp_rpath:" in *"$absdir:"*) ;; *) temp_rpath="$temp_rpath$absdir:" ;; esac fi # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) compile_rpath="$compile_rpath $absdir" esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) finalize_rpath="$finalize_rpath $libdir" esac ;; esac fi # $linkmode,$pass = prog,link... if test "$alldeplibs" = yes && { test "$deplibs_check_method" = pass_all || { test "$build_libtool_libs" = yes && test -n "$library_names"; }; }; then # We only need to search for static libraries continue fi fi link_static=no # Whether the deplib will be linked statically use_static_libs=$prefer_static_libs if test "$use_static_libs" = built && test "$installed" = yes; then use_static_libs=no fi if test -n "$library_names" && { test "$use_static_libs" = no || test -z "$old_library"; }; then case $host in *cygwin* | *mingw* | *cegcc*) # No point in relinking DLLs because paths are not encoded notinst_deplibs="$notinst_deplibs $lib" need_relink=no ;; *) if test "$installed" = no; then notinst_deplibs="$notinst_deplibs $lib" need_relink=yes fi ;; esac # This is a shared library # Warn about portability, can't link against -module's on some # systems (darwin). Don't bleat about dlopened modules though! dlopenmodule="" for dlpremoduletest in $dlprefiles; do if test "X$dlpremoduletest" = "X$lib"; then dlopenmodule="$dlpremoduletest" break fi done if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then $ECHO if test "$linkmode" = prog; then $ECHO "*** Warning: Linking the executable $output against the loadable module" else $ECHO "*** Warning: Linking the shared library $output against the loadable module" fi $ECHO "*** $linklib is not portable!" fi if test "$linkmode" = lib && test "$hardcode_into_libs" = yes; then # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) compile_rpath="$compile_rpath $absdir" esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) finalize_rpath="$finalize_rpath $libdir" esac ;; esac fi if test -n "$old_archive_from_expsyms_cmds"; then # figure out the soname set dummy $library_names shift realname="$1" shift libname=`eval "\\$ECHO \"$libname_spec\""` # use dlname if we got it. it's perfectly good, no? if test -n "$dlname"; then soname="$dlname" elif test -n "$soname_spec"; then # bleh windows case $host in *cygwin* | mingw* | *cegcc*) func_arith $current - $age major=$func_arith_result versuffix="-$major" ;; esac eval soname=\"$soname_spec\" else soname="$realname" fi # Make a new name for the extract_expsyms_cmds to use soroot="$soname" func_basename "$soroot" soname="$func_basename_result" func_stripname 'lib' '.dll' "$soname" newlib=libimp-$func_stripname_result.a # If the library has no export list, then create one now if test -f "$output_objdir/$soname-def"; then : else func_verbose "extracting exported symbol list from \`$soname'" func_execute_cmds "$extract_expsyms_cmds" 'exit $?' fi # Create $newlib if test -f "$output_objdir/$newlib"; then :; else func_verbose "generating import library for \`$soname'" func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' fi # make sure the library variables are pointing to the new library dir=$output_objdir linklib=$newlib fi # test -n "$old_archive_from_expsyms_cmds" if test "$linkmode" = prog || test "$mode" != relink; then add_shlibpath= add_dir= add= lib_linked=yes case $hardcode_action in immediate | unsupported) if test "$hardcode_direct" = no; then add="$dir/$linklib" case $host in *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; *-*-sysv4*uw2*) add_dir="-L$dir" ;; *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ *-*-unixware7*) add_dir="-L$dir" ;; *-*-darwin* ) # if the lib is a (non-dlopened) module then we can not # link against it, someone is ignoring the earlier warnings if /usr/bin/file -L $add 2> /dev/null | $GREP ": [^:]* bundle" >/dev/null ; then if test "X$dlopenmodule" != "X$lib"; then $ECHO "*** Warning: lib $linklib is a module, not a shared library" if test -z "$old_library" ; then $ECHO $ECHO "*** And there doesn't seem to be a static archive available" $ECHO "*** The link will probably fail, sorry" else add="$dir/$old_library" fi elif test -n "$old_library"; then add="$dir/$old_library" fi fi esac elif test "$hardcode_minus_L" = no; then case $host in *-*-sunos*) add_shlibpath="$dir" ;; esac add_dir="-L$dir" add="-l$name" elif test "$hardcode_shlibpath_var" = no; then add_shlibpath="$dir" add="-l$name" else lib_linked=no fi ;; relink) if test "$hardcode_direct" = yes && test "$hardcode_direct_absolute" = no; then add="$dir/$linklib" elif test "$hardcode_minus_L" = yes; then add_dir="-L$dir" # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) add_dir="$add_dir -L$inst_prefix_dir$libdir" ;; esac fi add="-l$name" elif test "$hardcode_shlibpath_var" = yes; then add_shlibpath="$dir" add="-l$name" else lib_linked=no fi ;; *) lib_linked=no ;; esac if test "$lib_linked" != yes; then func_fatal_configuration "unsupported hardcode properties" fi if test -n "$add_shlibpath"; then case :$compile_shlibpath: in *":$add_shlibpath:"*) ;; *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;; esac fi if test "$linkmode" = prog; then test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" test -n "$add" && compile_deplibs="$add $compile_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" if test "$hardcode_direct" != yes && test "$hardcode_minus_L" != yes && test "$hardcode_shlibpath_var" = yes; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; esac fi fi fi if test "$linkmode" = prog || test "$mode" = relink; then add_shlibpath= add_dir= add= # Finalize command for both is simple: just hardcode it. if test "$hardcode_direct" = yes && test "$hardcode_direct_absolute" = no; then add="$libdir/$linklib" elif test "$hardcode_minus_L" = yes; then add_dir="-L$libdir" add="-l$name" elif test "$hardcode_shlibpath_var" = yes; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; esac add="-l$name" elif test "$hardcode_automatic" = yes; then if test -n "$inst_prefix_dir" && test -f "$inst_prefix_dir$libdir/$linklib" ; then add="$inst_prefix_dir$libdir/$linklib" else add="$libdir/$linklib" fi else # We cannot seem to hardcode it, guess we'll fake it. add_dir="-L$libdir" # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) add_dir="$add_dir -L$inst_prefix_dir$libdir" ;; esac fi add="-l$name" fi if test "$linkmode" = prog; then test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" test -n "$add" && finalize_deplibs="$add $finalize_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" fi fi elif test "$linkmode" = prog; then # Here we assume that one of hardcode_direct or hardcode_minus_L # is not unsupported. This is valid on all known static and # shared platforms. if test "$hardcode_direct" != unsupported; then test -n "$old_library" && linklib="$old_library" compile_deplibs="$dir/$linklib $compile_deplibs" finalize_deplibs="$dir/$linklib $finalize_deplibs" else compile_deplibs="-l$name -L$dir $compile_deplibs" finalize_deplibs="-l$name -L$dir $finalize_deplibs" fi elif test "$build_libtool_libs" = yes; then # Not a shared library if test "$deplibs_check_method" != pass_all; then # We're trying link a shared library against a static one # but the system doesn't support it. # Just print a warning and add the library to dependency_libs so # that the program can be linked against the static library. $ECHO $ECHO "*** Warning: This system can not link to static lib archive $lib." $ECHO "*** I have the capability to make that library automatically link in when" $ECHO "*** you link to this library. But I can only do this if you have a" $ECHO "*** shared version of the library, which you do not appear to have." if test "$module" = yes; then $ECHO "*** But as you try to build a module library, libtool will still create " $ECHO "*** a static module, that should work as long as the dlopening application" $ECHO "*** is linked with the -dlopen flag to resolve symbols at runtime." if test -z "$global_symbol_pipe"; then $ECHO $ECHO "*** However, this would only work if libtool was able to extract symbol" $ECHO "*** lists from a program, using \`nm' or equivalent, but libtool could" $ECHO "*** not find such a program. So, this module is probably useless." $ECHO "*** \`nm' from GNU binutils and a full rebuild may help." fi if test "$build_old_libs" = no; then build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi else deplibs="$dir/$old_library $deplibs" link_static=yes fi fi # link shared/static library? if test "$linkmode" = lib; then if test -n "$dependency_libs" && { test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes || test "$link_static" = yes; }; then # Extract -R from dependency_libs temp_deplibs= for libdir in $dependency_libs; do case $libdir in -R*) func_stripname '-R' '' "$libdir" temp_xrpath=$func_stripname_result case " $xrpath " in *" $temp_xrpath "*) ;; *) xrpath="$xrpath $temp_xrpath";; esac;; *) temp_deplibs="$temp_deplibs $libdir";; esac done dependency_libs="$temp_deplibs" fi newlib_search_path="$newlib_search_path $absdir" # Link against this library test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" # ... and its dependency_libs tmp_libs= for deplib in $dependency_libs; do newdependency_libs="$deplib $newdependency_libs" if $opt_duplicate_deps ; then case "$tmp_libs " in *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; esac fi tmp_libs="$tmp_libs $deplib" done if test "$link_all_deplibs" != no; then # Add the search paths of all dependency libraries for deplib in $dependency_libs; do path= case $deplib in -L*) path="$deplib" ;; *.la) func_dirname "$deplib" "" "." dir="$func_dirname_result" # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; *) absdir=`cd "$dir" && pwd` if test -z "$absdir"; then func_warning "cannot determine absolute directory name of \`$dir'" absdir="$dir" fi ;; esac if $GREP "^installed=no" $deplib > /dev/null; then case $host in *-*-darwin*) depdepl= eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` if test -n "$deplibrary_names" ; then for tmp in $deplibrary_names ; do depdepl=$tmp done if test -f "$absdir/$objdir/$depdepl" ; then depdepl="$absdir/$objdir/$depdepl" darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` if test -z "$darwin_install_name"; then darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` fi compiler_flags="$compiler_flags ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}" linker_flags="$linker_flags -dylib_file ${darwin_install_name}:${depdepl}" path= fi fi ;; *) path="-L$absdir/$objdir" ;; esac else eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` test -z "$libdir" && \ func_fatal_error "\`$deplib' is not a valid libtool archive" test "$absdir" != "$libdir" && \ func_warning "\`$deplib' seems to be moved" path="-L$absdir" fi ;; esac case " $deplibs " in *" $path "*) ;; *) deplibs="$path $deplibs" ;; esac done fi # link_all_deplibs != no fi # linkmode = lib done # for deplib in $libs if test "$pass" = link; then if test "$linkmode" = "prog"; then compile_deplibs="$new_inherited_linker_flags $compile_deplibs" finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" else compiler_flags="$compiler_flags "`$ECHO "X $new_inherited_linker_flags" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` fi fi dependency_libs="$newdependency_libs" if test "$pass" = dlpreopen; then # Link the dlpreopened libraries before other libraries for deplib in $save_deplibs; do deplibs="$deplib $deplibs" done fi if test "$pass" != dlopen; then if test "$pass" != conv; then # Make sure lib_search_path contains only unique directories. lib_search_path= for dir in $newlib_search_path; do case "$lib_search_path " in *" $dir "*) ;; *) lib_search_path="$lib_search_path $dir" ;; esac done newlib_search_path= fi if test "$linkmode,$pass" != "prog,link"; then vars="deplibs" else vars="compile_deplibs finalize_deplibs" fi for var in $vars dependency_libs; do # Add libraries to $var in reverse order eval tmp_libs=\"\$$var\" new_libs= for deplib in $tmp_libs; do # FIXME: Pedantically, this is the right thing to do, so # that some nasty dependency loop isn't accidentally # broken: #new_libs="$deplib $new_libs" # Pragmatically, this seems to cause very few problems in # practice: case $deplib in -L*) new_libs="$deplib $new_libs" ;; -R*) ;; *) # And here is the reason: when a library appears more # than once as an explicit dependence of a library, or # is implicitly linked in more than once by the # compiler, it is considered special, and multiple # occurrences thereof are not removed. Compare this # with having the same library being listed as a # dependency of multiple other libraries: in this case, # we know (pedantically, we assume) the library does not # need to be listed more than once, so we keep only the # last copy. This is not always right, but it is rare # enough that we require users that really mean to play # such unportable linking tricks to link the library # using -Wl,-lname, so that libtool does not consider it # for duplicate removal. case " $specialdeplibs " in *" $deplib "*) new_libs="$deplib $new_libs" ;; *) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$deplib $new_libs" ;; esac ;; esac ;; esac done tmp_libs= for deplib in $new_libs; do case $deplib in -L*) case " $tmp_libs " in *" $deplib "*) ;; *) tmp_libs="$tmp_libs $deplib" ;; esac ;; *) tmp_libs="$tmp_libs $deplib" ;; esac done eval $var=\"$tmp_libs\" done # for var fi # Last step: remove runtime libs from dependency_libs # (they stay in deplibs) tmp_libs= for i in $dependency_libs ; do case " $predeps $postdeps $compiler_lib_search_path " in *" $i "*) i="" ;; esac if test -n "$i" ; then tmp_libs="$tmp_libs $i" fi done dependency_libs=$tmp_libs done # for pass if test "$linkmode" = prog; then dlfiles="$newdlfiles" fi if test "$linkmode" = prog || test "$linkmode" = lib; then dlprefiles="$newdlprefiles" fi case $linkmode in oldlib) if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then func_warning "\`-dlopen' is ignored for archives" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "\`-l' and \`-L' are ignored for archives" ;; esac test -n "$rpath" && \ func_warning "\`-rpath' is ignored for archives" test -n "$xrpath" && \ func_warning "\`-R' is ignored for archives" test -n "$vinfo" && \ func_warning "\`-version-info/-version-number' is ignored for archives" test -n "$release" && \ func_warning "\`-release' is ignored for archives" test -n "$export_symbols$export_symbols_regex" && \ func_warning "\`-export-symbols' is ignored for archives" # Now set the variables for building old libraries. build_libtool_libs=no oldlibs="$output" objs="$objs$old_deplibs" ;; lib) # Make sure we only generate libraries of the form `libNAME.la'. case $outputname in lib*) func_stripname 'lib' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" ;; *) test "$module" = no && \ func_fatal_help "libtool library \`$output' must begin with \`lib'" if test "$need_lib_prefix" != no; then # Add the "lib" prefix for modules if required func_stripname '' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" else func_stripname '' '.la' "$outputname" libname=$func_stripname_result fi ;; esac if test -n "$objs"; then if test "$deplibs_check_method" != pass_all; then func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs" else $ECHO $ECHO "*** Warning: Linking the shared library $output against the non-libtool" $ECHO "*** objects $objs is not portable!" libobjs="$libobjs $objs" fi fi test "$dlself" != no && \ func_warning "\`-dlopen self' is ignored for libtool libraries" set dummy $rpath shift test "$#" -gt 1 && \ func_warning "ignoring multiple \`-rpath's for a libtool library" install_libdir="$1" oldlibs= if test -z "$rpath"; then if test "$build_libtool_libs" = yes; then # Building a libtool convenience library. # Some compilers have problems with a `.al' extension so # convenience libraries should have the same extension an # archive normally would. oldlibs="$output_objdir/$libname.$libext $oldlibs" build_libtool_libs=convenience build_old_libs=yes fi test -n "$vinfo" && \ func_warning "\`-version-info/-version-number' is ignored for convenience libraries" test -n "$release" && \ func_warning "\`-release' is ignored for convenience libraries" else # Parse the version information argument. save_ifs="$IFS"; IFS=':' set dummy $vinfo 0 0 0 shift IFS="$save_ifs" test -n "$7" && \ func_fatal_help "too many parameters to \`-version-info'" # convert absolute version numbers to libtool ages # this retains compatibility with .la files and attempts # to make the code below a bit more comprehensible case $vinfo_number in yes) number_major="$1" number_minor="$2" number_revision="$3" # # There are really only two kinds -- those that # use the current revision as the major version # and those that subtract age and use age as # a minor version. But, then there is irix # which has an extra 1 added just for fun # case $version_type in darwin|linux|osf|windows|none) func_arith $number_major + $number_minor current=$func_arith_result age="$number_minor" revision="$number_revision" ;; freebsd-aout|freebsd-elf|sunos) current="$number_major" revision="$number_minor" age="0" ;; irix|nonstopux) func_arith $number_major + $number_minor current=$func_arith_result age="$number_minor" revision="$number_minor" lt_irix_increment=no ;; *) func_fatal_configuration "$modename: unknown library version type \`$version_type'" ;; esac ;; no) current="$1" revision="$2" age="$3" ;; esac # Check that each of the things are valid numbers. case $current in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "CURRENT \`$current' must be a nonnegative integer" func_fatal_error "\`$vinfo' is not valid version information" ;; esac case $revision in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "REVISION \`$revision' must be a nonnegative integer" func_fatal_error "\`$vinfo' is not valid version information" ;; esac case $age in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "AGE \`$age' must be a nonnegative integer" func_fatal_error "\`$vinfo' is not valid version information" ;; esac if test "$age" -gt "$current"; then func_error "AGE \`$age' is greater than the current interface number \`$current'" func_fatal_error "\`$vinfo' is not valid version information" fi # Calculate the version variables. major= versuffix= verstring= case $version_type in none) ;; darwin) # Like Linux, but with the current version available in # verstring for coding it into the library header func_arith $current - $age major=.$func_arith_result versuffix="$major.$age.$revision" # Darwin ld doesn't like 0 for these options... func_arith $current + 1 minor_current=$func_arith_result xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" ;; freebsd-aout) major=".$current" versuffix=".$current.$revision"; ;; freebsd-elf) major=".$current" versuffix=".$current" ;; irix | nonstopux) if test "X$lt_irix_increment" = "Xno"; then func_arith $current - $age else func_arith $current - $age + 1 fi major=$func_arith_result case $version_type in nonstopux) verstring_prefix=nonstopux ;; *) verstring_prefix=sgi ;; esac verstring="$verstring_prefix$major.$revision" # Add in all the interfaces that we are compatible with. loop=$revision while test "$loop" -ne 0; do func_arith $revision - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring="$verstring_prefix$major.$iface:$verstring" done # Before this point, $major must not contain `.'. major=.$major versuffix="$major.$revision" ;; linux) func_arith $current - $age major=.$func_arith_result versuffix="$major.$age.$revision" ;; osf) func_arith $current - $age major=.$func_arith_result versuffix=".$current.$age.$revision" verstring="$current.$age.$revision" # Add in all the interfaces that we are compatible with. loop=$age while test "$loop" -ne 0; do func_arith $current - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring="$verstring:${iface}.0" done # Make executables depend on our current version. verstring="$verstring:${current}.0" ;; qnx) major=".$current" versuffix=".$current" ;; sunos) major=".$current" versuffix=".$current.$revision" ;; windows) # Use '-' rather than '.', since we only want one # extension on DOS 8.3 filesystems. func_arith $current - $age major=$func_arith_result versuffix="-$major" ;; *) func_fatal_configuration "unknown library version type \`$version_type'" ;; esac # Clear the version info if we defaulted, and they specified a release. if test -z "$vinfo" && test -n "$release"; then major= case $version_type in darwin) # we can't check for "0.0" in archive_cmds due to quoting # problems, so we reset it completely verstring= ;; *) verstring="0.0" ;; esac if test "$need_version" = no; then versuffix= else versuffix=".0.0" fi fi # Remove version info from name if versioning should be avoided if test "$avoid_version" = yes && test "$need_version" = no; then major= versuffix= verstring="" fi # Check to see if the archive will have undefined symbols. if test "$allow_undefined" = yes; then if test "$allow_undefined_flag" = unsupported; then func_warning "undefined symbols not allowed in $host shared libraries" build_libtool_libs=no build_old_libs=yes fi else # Don't allow undefined symbols. allow_undefined_flag="$no_undefined_flag" fi fi func_generate_dlsyms "$libname" "$libname" "yes" libobjs="$libobjs $symfileobj" test "X$libobjs" = "X " && libobjs= if test "$mode" != relink; then # Remove our outputs, but don't remove object files since they # may have been created when compiling PIC objects. removelist= tempremovelist=`$ECHO "$output_objdir/*"` for p in $tempremovelist; do case $p in *.$objext | *.gcno) ;; $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) if test "X$precious_files_regex" != "X"; then if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 then continue fi fi removelist="$removelist $p" ;; *) ;; esac done test -n "$removelist" && \ func_show_eval "${RM}r \$removelist" fi # Now set the variables for building old libraries. if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then oldlibs="$oldlibs $output_objdir/$libname.$libext" # Transform .lo files to .o files. oldobjs="$objs "`$ECHO "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP` fi # Eliminate all temporary directories. #for path in $notinst_path; do # lib_search_path=`$ECHO "X$lib_search_path " | $Xsed -e "s% $path % %g"` # deplibs=`$ECHO "X$deplibs " | $Xsed -e "s% -L$path % %g"` # dependency_libs=`$ECHO "X$dependency_libs " | $Xsed -e "s% -L$path % %g"` #done if test -n "$xrpath"; then # If the user specified any rpath flags, then add them. temp_xrpath= for libdir in $xrpath; do temp_xrpath="$temp_xrpath -R$libdir" case "$finalize_rpath " in *" $libdir "*) ;; *) finalize_rpath="$finalize_rpath $libdir" ;; esac done if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then dependency_libs="$temp_xrpath $dependency_libs" fi fi # Make sure dlfiles contains only unique files that won't be dlpreopened old_dlfiles="$dlfiles" dlfiles= for lib in $old_dlfiles; do case " $dlprefiles $dlfiles " in *" $lib "*) ;; *) dlfiles="$dlfiles $lib" ;; esac done # Make sure dlprefiles contains only unique files old_dlprefiles="$dlprefiles" dlprefiles= for lib in $old_dlprefiles; do case "$dlprefiles " in *" $lib "*) ;; *) dlprefiles="$dlprefiles $lib" ;; esac done if test "$build_libtool_libs" = yes; then if test -n "$rpath"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc*) # these systems don't actually have a c library (as such)! ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C library is in the System framework deplibs="$deplibs System.ltframework" ;; *-*-netbsd*) # Don't link with libc until the a.out ld.so is fixed. ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work ;; *) # Add libc to deplibs on all other systems if necessary. if test "$build_libtool_need_lc" = "yes"; then deplibs="$deplibs -lc" fi ;; esac fi # Transform deplibs into only deplibs that can be linked in shared. name_save=$name libname_save=$libname release_save=$release versuffix_save=$versuffix major_save=$major # I'm not sure if I'm treating the release correctly. I think # release should show up in the -l (ie -lgmp5) so we don't want to # add it in twice. Is that correct? release="" versuffix="" major="" newdeplibs= droppeddeps=no case $deplibs_check_method in pass_all) # Don't check for shared/static. Everything works. # This might be a little naive. We might want to check # whether the library exists or not. But this is on # osf3 & osf4 and I'm not really sure... Just # implementing what was already the behavior. newdeplibs=$deplibs ;; test_compile) # This code stresses the "libraries are programs" paradigm to its # limits. Maybe even breaks it. We compile a program, linking it # against the deplibs as a proxy for the library. Then we can check # whether they linked in statically or dynamically with ldd. $opt_dry_run || $RM conftest.c cat > conftest.c </dev/null` for potent_lib in $potential_libs; do # Follow soft links. if ls -lLd "$potent_lib" 2>/dev/null | $GREP " -> " >/dev/null; then continue fi # The statement above tries to avoid entering an # endless loop below, in case of cyclic links. # We might still enter an endless loop, since a link # loop can be closed while we follow links, # but so what? potlib="$potent_lib" while test -h "$potlib" 2>/dev/null; do potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` case $potliblink in [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; *) potlib=`$ECHO "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";; esac done if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | $SED -e 10q | $EGREP "$file_magic_regex" > /dev/null; then newdeplibs="$newdeplibs $a_deplib" a_deplib="" break 2 fi done done fi if test -n "$a_deplib" ; then droppeddeps=yes $ECHO $ECHO "*** Warning: linker path does not have real file for library $a_deplib." $ECHO "*** I have the capability to make that library automatically link in when" $ECHO "*** you link to this library. But I can only do this if you have a" $ECHO "*** shared version of the library, which you do not appear to have" $ECHO "*** because I did check the linker path looking for a file starting" if test -z "$potlib" ; then $ECHO "*** with $libname but no candidates were found. (...for file magic test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a file magic. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. newdeplibs="$newdeplibs $a_deplib" ;; esac done # Gone through all deplibs. ;; match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` for a_deplib in $deplibs; do case $a_deplib in -l*) func_stripname -l '' "$a_deplib" name=$func_stripname_result if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then case " $predeps $postdeps " in *" $a_deplib "*) newdeplibs="$newdeplibs $a_deplib" a_deplib="" ;; esac fi if test -n "$a_deplib" ; then libname=`eval "\\$ECHO \"$libname_spec\""` for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do potential_libs=`ls $i/$libname[.-]* 2>/dev/null` for potent_lib in $potential_libs; do potlib="$potent_lib" # see symlink-check above in file_magic test if eval "\$ECHO \"X$potent_lib\"" 2>/dev/null | $Xsed -e 10q | \ $EGREP "$match_pattern_regex" > /dev/null; then newdeplibs="$newdeplibs $a_deplib" a_deplib="" break 2 fi done done fi if test -n "$a_deplib" ; then droppeddeps=yes $ECHO $ECHO "*** Warning: linker path does not have real file for library $a_deplib." $ECHO "*** I have the capability to make that library automatically link in when" $ECHO "*** you link to this library. But I can only do this if you have a" $ECHO "*** shared version of the library, which you do not appear to have" $ECHO "*** because I did check the linker path looking for a file starting" if test -z "$potlib" ; then $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a regex pattern. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. newdeplibs="$newdeplibs $a_deplib" ;; esac done # Gone through all deplibs. ;; none | unknown | *) newdeplibs="" tmp_deplibs=`$ECHO "X $deplibs" | $Xsed \ -e 's/ -lc$//' -e 's/ -[LR][^ ]*//g'` if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then for i in $predeps $postdeps ; do # can't use Xsed below, because $i might contain '/' tmp_deplibs=`$ECHO "X $tmp_deplibs" | $Xsed -e "s,$i,,"` done fi if $ECHO "X $tmp_deplibs" | $Xsed -e 's/[ ]//g' | $GREP . >/dev/null; then $ECHO if test "X$deplibs_check_method" = "Xnone"; then $ECHO "*** Warning: inter-library dependencies are not supported in this platform." else $ECHO "*** Warning: inter-library dependencies are not known to be supported." fi $ECHO "*** All declared inter-library dependencies are being dropped." droppeddeps=yes fi ;; esac versuffix=$versuffix_save major=$major_save release=$release_save libname=$libname_save name=$name_save case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library with the System framework newdeplibs=`$ECHO "X $newdeplibs" | $Xsed -e 's/ -lc / System.ltframework /'` ;; esac if test "$droppeddeps" = yes; then if test "$module" = yes; then $ECHO $ECHO "*** Warning: libtool could not satisfy all declared inter-library" $ECHO "*** dependencies of module $libname. Therefore, libtool will create" $ECHO "*** a static module, that should work as long as the dlopening" $ECHO "*** application is linked with the -dlopen flag." if test -z "$global_symbol_pipe"; then $ECHO $ECHO "*** However, this would only work if libtool was able to extract symbol" $ECHO "*** lists from a program, using \`nm' or equivalent, but libtool could" $ECHO "*** not find such a program. So, this module is probably useless." $ECHO "*** \`nm' from GNU binutils and a full rebuild may help." fi if test "$build_old_libs" = no; then oldlibs="$output_objdir/$libname.$libext" build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi else $ECHO "*** The inter-library dependencies that have been dropped here will be" $ECHO "*** automatically added whenever a program is linked with this library" $ECHO "*** or is declared to -dlopen it." if test "$allow_undefined" = no; then $ECHO $ECHO "*** Since this library must not contain undefined symbols," $ECHO "*** because either the platform does not support them or" $ECHO "*** it was explicitly requested with -no-undefined," $ECHO "*** libtool will only create a static version of it." if test "$build_old_libs" = no; then oldlibs="$output_objdir/$libname.$libext" build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi fi fi # Done checking deplibs! deplibs=$newdeplibs fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" case $host in *-*-darwin*) newdeplibs=`$ECHO "X $newdeplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` new_inherited_linker_flags=`$ECHO "X $new_inherited_linker_flags" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` deplibs=`$ECHO "X $deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $deplibs " in *" -L$path/$objdir "*) new_libs="$new_libs -L$path/$objdir" ;; esac ;; esac done for deplib in $deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$new_libs $deplib" ;; esac ;; *) new_libs="$new_libs $deplib" ;; esac done deplibs="$new_libs" # All the library-specific variables (install_libdir is set above). library_names= old_library= dlname= # Test again, we may have decided not to build it any more if test "$build_libtool_libs" = yes; then if test "$hardcode_into_libs" = yes; then # Hardcode the library paths hardcode_libdirs= dep_rpath= rpath="$finalize_rpath" test "$mode" != relink && rpath="$compile_rpath$rpath" for libdir in $rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs="$libdir" else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" dep_rpath="$dep_rpath $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) perm_rpath="$perm_rpath $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir="$hardcode_libdirs" if test -n "$hardcode_libdir_flag_spec_ld"; then eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\" else eval dep_rpath=\"$hardcode_libdir_flag_spec\" fi fi if test -n "$runpath_var" && test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do rpath="$rpath$dir:" done eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" fi test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" fi shlibpath="$finalize_shlibpath" test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath" if test -n "$shlibpath"; then eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" fi # Get the real and link names of the library. eval shared_ext=\"$shrext_cmds\" eval library_names=\"$library_names_spec\" set dummy $library_names shift realname="$1" shift if test -n "$soname_spec"; then eval soname=\"$soname_spec\" else soname="$realname" fi if test -z "$dlname"; then dlname=$soname fi lib="$output_objdir/$realname" linknames= for link do linknames="$linknames $link" done # Use standard objects if they are pic test -z "$pic_flag" && libobjs=`$ECHO "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` test "X$libobjs" = "X " && libobjs= delfiles= if test -n "$export_symbols" && test -n "$include_expsyms"; then $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" export_symbols="$output_objdir/$libname.uexp" delfiles="$delfiles $export_symbols" fi orig_export_symbols= case $host_os in cygwin* | mingw* | cegcc*) if test -n "$export_symbols" && test -z "$export_symbols_regex"; then # exporting using user supplied symfile if test "x`$SED 1q $export_symbols`" != xEXPORTS; then # and it's NOT already a .def file. Must figure out # which of the given symbols are data symbols and tag # them as such. So, trigger use of export_symbols_cmds. # export_symbols gets reassigned inside the "prepare # the list of exported symbols" if statement, so the # include_expsyms logic still works. orig_export_symbols="$export_symbols" export_symbols= always_export_symbols=yes fi fi ;; esac # Prepare the list of exported symbols if test -z "$export_symbols"; then if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then func_verbose "generating symbol list for \`$libname.la'" export_symbols="$output_objdir/$libname.exp" $opt_dry_run || $RM $export_symbols cmds=$export_symbols_cmds save_ifs="$IFS"; IFS='~' for cmd in $cmds; do IFS="$save_ifs" eval cmd=\"$cmd\" func_len " $cmd" len=$func_len_result if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then func_show_eval "$cmd" 'exit $?' skipped_export=false else # The command line is too long to execute in one step. func_verbose "using reloadable object file for export list..." skipped_export=: # Break out early, otherwise skipped_export may be # set to false by a later but shorter cmd. break fi done IFS="$save_ifs" if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi fi if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols="$export_symbols" test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" $opt_dry_run || eval '$ECHO "X$include_expsyms" | $Xsed | $SP2NL >> "$tmp_export_symbols"' fi if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter delfiles="$delfiles $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi tmp_deplibs= for test_deplib in $deplibs; do case " $convenience " in *" $test_deplib "*) ;; *) tmp_deplibs="$tmp_deplibs $test_deplib" ;; esac done deplibs="$tmp_deplibs" if test -n "$convenience"; then if test -n "$whole_archive_flag_spec" && test "$compiler_needs_object" = yes && test -z "$libobjs"; then # extract the archives, so we have objects to list. # TODO: could optimize this to just extract one archive. whole_archive_flag_spec= fi if test -n "$whole_archive_flag_spec"; then save_libobjs=$libobjs eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= else gentop="$output_objdir/${outputname}x" generated="$generated $gentop" func_extract_archives $gentop $convenience libobjs="$libobjs $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi fi if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then eval flag=\"$thread_safe_flag_spec\" linker_flags="$linker_flags $flag" fi # Make a backup of the uninstalled library when relinking if test "$mode" = relink; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? fi # Do each of the archive commands. if test "$module" = yes && test -n "$module_cmds" ; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then eval test_cmds=\"$module_expsym_cmds\" cmds=$module_expsym_cmds else eval test_cmds=\"$module_cmds\" cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then eval test_cmds=\"$archive_expsym_cmds\" cmds=$archive_expsym_cmds else eval test_cmds=\"$archive_cmds\" cmds=$archive_cmds fi fi if test "X$skipped_export" != "X:" && func_len " $test_cmds" && len=$func_len_result && test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then : else # The command line is too long to link in one step, link piecewise # or, if using GNU ld and skipped_export is not :, use a linker # script. # Save the value of $output and $libobjs because we want to # use them later. If we have whole_archive_flag_spec, we # want to use save_libobjs as it was before # whole_archive_flag_spec was expanded, because we can't # assume the linker understands whole_archive_flag_spec. # This may have to be revisited, in case too many # convenience libraries get linked in and end up exceeding # the spec. if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then save_libobjs=$libobjs fi save_output=$output output_la=`$ECHO "X$output" | $Xsed -e "$basename"` # Clear the reloadable object creation command queue and # initialize k to one. test_cmds= concat_cmds= objlist= last_robj= k=1 if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then output=${output_objdir}/${output_la}.lnkscript func_verbose "creating GNU ld script: $output" $ECHO 'INPUT (' > $output for obj in $save_libobjs do $ECHO "$obj" >> $output done $ECHO ')' >> $output delfiles="$delfiles $output" elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then output=${output_objdir}/${output_la}.lnk func_verbose "creating linker input file list: $output" : > $output set x $save_libobjs shift firstobj= if test "$compiler_needs_object" = yes; then firstobj="$1 " shift fi for obj do $ECHO "$obj" >> $output done delfiles="$delfiles $output" output=$firstobj\"$file_list_spec$output\" else if test -n "$save_libobjs"; then func_verbose "creating reloadable object files..." output=$output_objdir/$output_la-${k}.$objext eval test_cmds=\"$reload_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 # Loop over the list of objects to be linked. for obj in $save_libobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result if test "X$objlist" = X || test "$len" -lt "$max_cmd_len"; then func_append objlist " $obj" else # The command $test_cmds is almost too long, add a # command to the queue. if test "$k" -eq 1 ; then # The first file doesn't have a previous command to add. eval concat_cmds=\"$reload_cmds $objlist $last_robj\" else # All subsequent reloadable object files will link in # the last one created. eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj~\$RM $last_robj\" fi last_robj=$output_objdir/$output_la-${k}.$objext func_arith $k + 1 k=$func_arith_result output=$output_objdir/$output_la-${k}.$objext objlist=$obj func_len " $last_robj" func_arith $len0 + $func_len_result len=$func_arith_result fi done # Handle the remaining objects by creating one last # reloadable object file. All subsequent reloadable object # files will link in the last one created. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\" if test -n "$last_robj"; then eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\" fi delfiles="$delfiles $output" else output= fi if ${skipped_export-false}; then func_verbose "generating symbol list for \`$libname.la'" export_symbols="$output_objdir/$libname.exp" $opt_dry_run || $RM $export_symbols libobjs=$output # Append the command to create the export file. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi fi test -n "$save_libobjs" && func_verbose "creating a temporary reloadable object file: $output" # Loop through the commands generated above and execute them. save_ifs="$IFS"; IFS='~' for cmd in $concat_cmds; do IFS="$save_ifs" $opt_silent || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test "$mode" = relink; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS="$save_ifs" if test -n "$export_symbols_regex" && ${skipped_export-false}; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi if ${skipped_export-false}; then if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols="$export_symbols" test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" $opt_dry_run || eval '$ECHO "X$include_expsyms" | $Xsed | $SP2NL >> "$tmp_export_symbols"' fi if test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter delfiles="$delfiles $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi fi libobjs=$output # Restore the value of output. output=$save_output if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= fi # Expand the library linking commands again to reset the # value of $libobjs for piecewise linking. # Do each of the archive commands. if test "$module" = yes && test -n "$module_cmds" ; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then cmds=$module_expsym_cmds else cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then cmds=$archive_expsym_cmds else cmds=$archive_cmds fi fi fi if test -n "$delfiles"; then # Append the command to remove temporary files to $cmds. eval cmds=\"\$cmds~\$RM $delfiles\" fi # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop="$output_objdir/${outputname}x" generated="$generated $gentop" func_extract_archives $gentop $dlprefiles libobjs="$libobjs $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi save_ifs="$IFS"; IFS='~' for cmd in $cmds; do IFS="$save_ifs" eval cmd=\"$cmd\" $opt_silent || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test "$mode" = relink; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS="$save_ifs" # Restore the uninstalled library and exit if test "$mode" = relink; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? if test -n "$convenience"; then if test -z "$whole_archive_flag_spec"; then func_show_eval '${RM}r "$gentop"' fi fi exit $EXIT_SUCCESS fi # Create links to the real library. for linkname in $linknames; do if test "$realname" != "$linkname"; then func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' fi done # If -module or -export-dynamic was specified, set the dlname. if test "$module" = yes || test "$export_dynamic" = yes; then # On all known operating systems, these are identical. dlname="$soname" fi fi ;; obj) if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then func_warning "\`-dlopen' is ignored for objects" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "\`-l' and \`-L' are ignored for objects" ;; esac test -n "$rpath" && \ func_warning "\`-rpath' is ignored for objects" test -n "$xrpath" && \ func_warning "\`-R' is ignored for objects" test -n "$vinfo" && \ func_warning "\`-version-info' is ignored for objects" test -n "$release" && \ func_warning "\`-release' is ignored for objects" case $output in *.lo) test -n "$objs$old_deplibs" && \ func_fatal_error "cannot build library object \`$output' from non-libtool objects" libobj=$output func_lo2o "$libobj" obj=$func_lo2o_result ;; *) libobj= obj="$output" ;; esac # Delete the old objects. $opt_dry_run || $RM $obj $libobj # Objects from convenience libraries. This assumes # single-version convenience libraries. Whenever we create # different ones for PIC/non-PIC, this we'll have to duplicate # the extraction. reload_conv_objs= gentop= # reload_cmds runs $LD directly, so let us get rid of # -Wl from whole_archive_flag_spec and hope we can get by with # turning comma into space.. wl= if test -n "$convenience"; then if test -n "$whole_archive_flag_spec"; then eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" reload_conv_objs=$reload_objs\ `$ECHO "X$tmp_whole_archive_flags" | $Xsed -e 's|,| |g'` else gentop="$output_objdir/${obj}x" generated="$generated $gentop" func_extract_archives $gentop $convenience reload_conv_objs="$reload_objs $func_extract_archives_result" fi fi # Create the old-style object. reload_objs="$objs$old_deplibs "`$ECHO "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test output="$obj" func_execute_cmds "$reload_cmds" 'exit $?' # Exit if we aren't doing a library object file. if test -z "$libobj"; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS fi if test "$build_libtool_libs" != yes; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi # Create an invalid libtool object if no PIC, so that we don't # accidentally link it into a program. # $show "echo timestamp > $libobj" # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? exit $EXIT_SUCCESS fi if test -n "$pic_flag" || test "$pic_mode" != default; then # Only do commands if we really have different PIC objects. reload_objs="$libobjs $reload_conv_objs" output="$libobj" func_execute_cmds "$reload_cmds" 'exit $?' fi if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS ;; prog) case $host in *cygwin*) func_stripname '' '.exe' "$output" output=$func_stripname_result.exe;; esac test -n "$vinfo" && \ func_warning "\`-version-info' is ignored for programs" test -n "$release" && \ func_warning "\`-release' is ignored for programs" test "$preload" = yes \ && test "$dlopen_support" = unknown \ && test "$dlopen_self" = unknown \ && test "$dlopen_self_static" = unknown && \ func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support." case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library is the System framework compile_deplibs=`$ECHO "X $compile_deplibs" | $Xsed -e 's/ -lc / System.ltframework /'` finalize_deplibs=`$ECHO "X $finalize_deplibs" | $Xsed -e 's/ -lc / System.ltframework /'` ;; esac case $host in *-*-darwin*) # Don't allow lazy linking, it breaks C++ global constructors # But is supposedly fixed on 10.4 or later (yay!). if test "$tagname" = CXX ; then case ${MACOSX_DEPLOYMENT_TARGET-10.0} in 10.[0123]) compile_command="$compile_command ${wl}-bind_at_load" finalize_command="$finalize_command ${wl}-bind_at_load" ;; esac fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" compile_deplibs=`$ECHO "X $compile_deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` finalize_deplibs=`$ECHO "X $finalize_deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $compile_deplibs " in *" -L$path/$objdir "*) new_libs="$new_libs -L$path/$objdir" ;; esac ;; esac done for deplib in $compile_deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$new_libs $deplib" ;; esac ;; *) new_libs="$new_libs $deplib" ;; esac done compile_deplibs="$new_libs" compile_command="$compile_command $compile_deplibs" finalize_command="$finalize_command $finalize_deplibs" if test -n "$rpath$xrpath"; then # If the user specified any rpath flags, then add them. for libdir in $rpath $xrpath; do # This is the magic to use -rpath. case "$finalize_rpath " in *" $libdir "*) ;; *) finalize_rpath="$finalize_rpath $libdir" ;; esac done fi # Now hardcode the library paths rpath= hardcode_libdirs= for libdir in $compile_rpath $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs="$libdir" else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" rpath="$rpath $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) perm_rpath="$perm_rpath $libdir" ;; esac fi case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$libdir:"*) ;; ::) dllsearchpath=$libdir;; *) dllsearchpath="$dllsearchpath:$libdir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) dllsearchpath="$dllsearchpath:$testbindir";; esac ;; esac done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir="$hardcode_libdirs" eval rpath=\" $hardcode_libdir_flag_spec\" fi compile_rpath="$rpath" rpath= hardcode_libdirs= for libdir in $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs="$libdir" else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" rpath="$rpath $flag" fi elif test -n "$runpath_var"; then case "$finalize_perm_rpath " in *" $libdir "*) ;; *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir="$hardcode_libdirs" eval rpath=\" $hardcode_libdir_flag_spec\" fi finalize_rpath="$rpath" if test -n "$libobjs" && test "$build_old_libs" = yes; then # Transform all the library objects into standard objects. compile_command=`$ECHO "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` finalize_command=`$ECHO "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` fi func_generate_dlsyms "$outputname" "@PROGRAM@" "no" # template prelinking step if test -n "$prelink_cmds"; then func_execute_cmds "$prelink_cmds" 'exit $?' fi wrappers_required=yes case $host in *cygwin* | *mingw* ) if test "$build_libtool_libs" != yes; then wrappers_required=no fi ;; *cegcc) # Disable wrappers for cegcc, we are cross compiling anyway. wrappers_required=no ;; *) if test "$need_relink" = no || test "$build_libtool_libs" != yes; then wrappers_required=no fi ;; esac if test "$wrappers_required" = no; then # Replace the output file specification. compile_command=`$ECHO "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` link_command="$compile_command$compile_rpath" # We have no uninstalled library dependencies, so finalize right now. exit_status=0 func_show_eval "$link_command" 'exit_status=$?' # Delete the generated files. if test -f "$output_objdir/${outputname}S.${objext}"; then func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"' fi exit $exit_status fi if test -n "$compile_shlibpath$finalize_shlibpath"; then compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" fi if test -n "$finalize_shlibpath"; then finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" fi compile_var= finalize_var= if test -n "$runpath_var"; then if test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do rpath="$rpath$dir:" done compile_var="$runpath_var=\"$rpath\$$runpath_var\" " fi if test -n "$finalize_perm_rpath"; then # We should set the runpath_var. rpath= for dir in $finalize_perm_rpath; do rpath="$rpath$dir:" done finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " fi fi if test "$no_install" = yes; then # We don't need to create a wrapper script. link_command="$compile_var$compile_command$compile_rpath" # Replace the output file specification. link_command=`$ECHO "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` # Delete the old output file. $opt_dry_run || $RM $output # Link the executable and exit func_show_eval "$link_command" 'exit $?' exit $EXIT_SUCCESS fi if test "$hardcode_action" = relink; then # Fast installation is not supported link_command="$compile_var$compile_command$compile_rpath" relink_command="$finalize_var$finalize_command$finalize_rpath" func_warning "this platform does not like uninstalled shared libraries" func_warning "\`$output' will be relinked during installation" else if test "$fast_install" != no; then link_command="$finalize_var$compile_command$finalize_rpath" if test "$fast_install" = yes; then relink_command=`$ECHO "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'` else # fast_install is set to needless relink_command= fi else link_command="$compile_var$compile_command$compile_rpath" relink_command="$finalize_var$finalize_command$finalize_rpath" fi fi # Replace the output file specification. link_command=`$ECHO "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` # Delete the old output files. $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname func_show_eval "$link_command" 'exit $?' # Now create the wrapper script. func_verbose "creating $output" # Quote the relink command for shipping. if test -n "$relink_command"; then # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done relink_command="(cd `pwd`; $relink_command)" relink_command=`$ECHO "X$relink_command" | $Xsed -e "$sed_quote_subst"` fi # Quote $ECHO for shipping. if test "X$ECHO" = "X$SHELL $progpath --fallback-echo"; then case $progpath in [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $progpath --fallback-echo";; *) qecho="$SHELL `pwd`/$progpath --fallback-echo";; esac qecho=`$ECHO "X$qecho" | $Xsed -e "$sed_quote_subst"` else qecho=`$ECHO "X$ECHO" | $Xsed -e "$sed_quote_subst"` fi # Only actually do things if not in dry run mode. $opt_dry_run || { # win32 will think the script is a binary if it has # a .exe suffix, so we strip it off here. case $output in *.exe) func_stripname '' '.exe' "$output" output=$func_stripname_result ;; esac # test for cygwin because mv fails w/o .exe extensions case $host in *cygwin*) exeext=.exe func_stripname '' '.exe' "$outputname" outputname=$func_stripname_result ;; *) exeext= ;; esac case $host in *cygwin* | *mingw* ) func_dirname_and_basename "$output" "" "." output_name=$func_basename_result output_path=$func_dirname_result cwrappersource="$output_path/$objdir/lt-$output_name.c" cwrapper="$output_path/$output_name.exe" $RM $cwrappersource $cwrapper trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 func_emit_cwrapperexe_src > $cwrappersource # The wrapper executable is built using the $host compiler, # because it contains $host paths and files. If cross- # compiling, it, like the target executable, must be # executed on the $host or under an emulation environment. $opt_dry_run || { $LTCC $LTCFLAGS -o $cwrapper $cwrappersource $STRIP $cwrapper } # Now, create the wrapper script for func_source use: func_ltwrapper_scriptname $cwrapper $RM $func_ltwrapper_scriptname_result trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 $opt_dry_run || { # note: this script will not be executed, so do not chmod. if test "x$build" = "x$host" ; then $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result else func_emit_wrapper no > $func_ltwrapper_scriptname_result fi } ;; * ) $RM $output trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 func_emit_wrapper no > $output chmod +x $output ;; esac } exit $EXIT_SUCCESS ;; esac # See if we need to build an old-fashioned archive. for oldlib in $oldlibs; do if test "$build_libtool_libs" = convenience; then oldobjs="$libobjs_save $symfileobj" addlibs="$convenience" build_libtool_libs=no else if test "$build_libtool_libs" = module; then oldobjs="$libobjs_save" build_libtool_libs=no else oldobjs="$old_deplibs $non_pic_objects" if test "$preload" = yes && test -f "$symfileobj"; then oldobjs="$oldobjs $symfileobj" fi fi addlibs="$old_convenience" fi if test -n "$addlibs"; then gentop="$output_objdir/${outputname}x" generated="$generated $gentop" func_extract_archives $gentop $addlibs oldobjs="$oldobjs $func_extract_archives_result" fi # Do each command in the archive commands. if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then cmds=$old_archive_from_new_cmds else # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop="$output_objdir/${outputname}x" generated="$generated $gentop" func_extract_archives $gentop $dlprefiles oldobjs="$oldobjs $func_extract_archives_result" fi # POSIX demands no paths to be encoded in archives. We have # to avoid creating archives with duplicate basenames if we # might have to extract them afterwards, e.g., when creating a # static archive out of a convenience library, or when linking # the entirety of a libtool archive into another (currently # not supported by libtool). if (for obj in $oldobjs do func_basename "$obj" $ECHO "$func_basename_result" done | sort | sort -uc >/dev/null 2>&1); then : else $ECHO "copying selected object files to avoid basename conflicts..." gentop="$output_objdir/${outputname}x" generated="$generated $gentop" func_mkdir_p "$gentop" save_oldobjs=$oldobjs oldobjs= counter=1 for obj in $save_oldobjs do func_basename "$obj" objbase="$func_basename_result" case " $oldobjs " in " ") oldobjs=$obj ;; *[\ /]"$objbase "*) while :; do # Make sure we don't pick an alternate name that also # overlaps. newobj=lt$counter-$objbase func_arith $counter + 1 counter=$func_arith_result case " $oldobjs " in *[\ /]"$newobj "*) ;; *) if test ! -f "$gentop/$newobj"; then break; fi ;; esac done func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" oldobjs="$oldobjs $gentop/$newobj" ;; *) oldobjs="$oldobjs $obj" ;; esac done fi eval cmds=\"$old_archive_cmds\" func_len " $cmds" len=$func_len_result if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then cmds=$old_archive_cmds else # the command line is too long to link in one step, link in parts func_verbose "using piecewise archive linking..." save_RANLIB=$RANLIB RANLIB=: objlist= concat_cmds= save_oldobjs=$oldobjs oldobjs= # Is there a better way of finding the last object in the list? for obj in $save_oldobjs do last_oldobj=$obj done eval test_cmds=\"$old_archive_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 for obj in $save_oldobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result func_append objlist " $obj" if test "$len" -lt "$max_cmd_len"; then : else # the above command should be used before it gets too long oldobjs=$objlist if test "$obj" = "$last_oldobj" ; then RANLIB=$save_RANLIB fi test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" objlist= len=$len0 fi done RANLIB=$save_RANLIB oldobjs=$objlist if test "X$oldobjs" = "X" ; then eval cmds=\"\$concat_cmds\" else eval cmds=\"\$concat_cmds~\$old_archive_cmds\" fi fi fi func_execute_cmds "$cmds" 'exit $?' done test -n "$generated" && \ func_show_eval "${RM}r$generated" # Now create the libtool archive. case $output in *.la) old_library= test "$build_old_libs" = yes && old_library="$libname.$libext" func_verbose "creating $output" # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done # Quote the link command for shipping. relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" relink_command=`$ECHO "X$relink_command" | $Xsed -e "$sed_quote_subst"` if test "$hardcode_automatic" = yes ; then relink_command= fi # Only create the output if not a dry run. $opt_dry_run || { for installed in no yes; do if test "$installed" = yes; then if test -z "$install_libdir"; then break fi output="$output_objdir/$outputname"i # Replace all uninstalled libtool libraries with the installed ones newdependency_libs= for deplib in $dependency_libs; do case $deplib in *.la) func_basename "$deplib" name="$func_basename_result" eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` test -z "$libdir" && \ func_fatal_error "\`$deplib' is not a valid libtool archive" newdependency_libs="$newdependency_libs $libdir/$name" ;; *) newdependency_libs="$newdependency_libs $deplib" ;; esac done dependency_libs="$newdependency_libs" newdlfiles= for lib in $dlfiles; do case $lib in *.la) func_basename "$lib" name="$func_basename_result" eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "\`$lib' is not a valid libtool archive" newdlfiles="$newdlfiles $libdir/$name" ;; *) newdlfiles="$newdlfiles $lib" ;; esac done dlfiles="$newdlfiles" newdlprefiles= for lib in $dlprefiles; do case $lib in *.la) # Only pass preopened files to the pseudo-archive (for # eventual linking with the app. that links it) if we # didn't already link the preopened objects directly into # the library: func_basename "$lib" name="$func_basename_result" eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "\`$lib' is not a valid libtool archive" newdlprefiles="$newdlprefiles $libdir/$name" ;; esac done dlprefiles="$newdlprefiles" else newdlfiles= for lib in $dlfiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; *) abs=`pwd`"/$lib" ;; esac newdlfiles="$newdlfiles $abs" done dlfiles="$newdlfiles" newdlprefiles= for lib in $dlprefiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; *) abs=`pwd`"/$lib" ;; esac newdlprefiles="$newdlprefiles $abs" done dlprefiles="$newdlprefiles" fi $RM $output # place dlname in correct position for cygwin tdlname=$dlname case $host,$output,$installed,$module,$dlname in *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;; esac $ECHO > $output "\ # $outputname - a libtool library file # Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION # # Please DO NOT delete this file! # It is necessary for linking the library. # The name that we can dlopen(3). dlname='$tdlname' # Names of this library. library_names='$library_names' # The name of the static archive. old_library='$old_library' # Linker flags that can not go in dependency_libs. inherited_linker_flags='$new_inherited_linker_flags' # Libraries that this one depends upon. dependency_libs='$dependency_libs' # Names of additional weak libraries provided by this library weak_library_names='$weak_libs' # Version information for $libname. current=$current age=$age revision=$revision # Is this an already installed library? installed=$installed # Should we warn about portability when linking against -modules? shouldnotlink=$module # Files to dlopen/dlpreopen dlopen='$dlfiles' dlpreopen='$dlprefiles' # Directory that this library needs to be installed in: libdir='$install_libdir'" if test "$installed" = no && test "$need_relink" = yes; then $ECHO >> $output "\ relink_command=\"$relink_command\"" fi done } # Do a symbolic link so that the libtool archive can be found in # LD_LIBRARY_PATH before the program is installed. func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' ;; esac exit $EXIT_SUCCESS } { test "$mode" = link || test "$mode" = relink; } && func_mode_link ${1+"$@"} # func_mode_uninstall arg... func_mode_uninstall () { $opt_debug RM="$nonopt" files= rmforce= exit_status=0 # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic="$magic" for arg do case $arg in -f) RM="$RM $arg"; rmforce=yes ;; -*) RM="$RM $arg" ;; *) files="$files $arg" ;; esac done test -z "$RM" && \ func_fatal_help "you must specify an RM program" rmdirs= origobjdir="$objdir" for file in $files; do func_dirname "$file" "" "." dir="$func_dirname_result" if test "X$dir" = X.; then objdir="$origobjdir" else objdir="$dir/$origobjdir" fi func_basename "$file" name="$func_basename_result" test "$mode" = uninstall && objdir="$dir" # Remember objdir for removal later, being careful to avoid duplicates if test "$mode" = clean; then case " $rmdirs " in *" $objdir "*) ;; *) rmdirs="$rmdirs $objdir" ;; esac fi # Don't error if the file doesn't exist and rm -f was used. if { test -L "$file"; } >/dev/null 2>&1 || { test -h "$file"; } >/dev/null 2>&1 || test -f "$file"; then : elif test -d "$file"; then exit_status=1 continue elif test "$rmforce" = yes; then continue fi rmfiles="$file" case $name in *.la) # Possibly a libtool archive, so verify it. if func_lalib_p "$file"; then func_source $dir/$name # Delete the libtool libraries and symlinks. for n in $library_names; do rmfiles="$rmfiles $objdir/$n" done test -n "$old_library" && rmfiles="$rmfiles $objdir/$old_library" case "$mode" in clean) case " $library_names " in # " " in the beginning catches empty $dlname *" $dlname "*) ;; *) rmfiles="$rmfiles $objdir/$dlname" ;; esac test -n "$libdir" && rmfiles="$rmfiles $objdir/$name $objdir/${name}i" ;; uninstall) if test -n "$library_names"; then # Do each command in the postuninstall commands. func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' fi if test -n "$old_library"; then # Do each command in the old_postuninstall commands. func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' fi # FIXME: should reinstall the best remaining shared library. ;; esac fi ;; *.lo) # Possibly a libtool object, so verify it. if func_lalib_p "$file"; then # Read the .lo file func_source $dir/$name # Add PIC object to the list of files to remove. if test -n "$pic_object" && test "$pic_object" != none; then rmfiles="$rmfiles $dir/$pic_object" fi # Add non-PIC object to the list of files to remove. if test -n "$non_pic_object" && test "$non_pic_object" != none; then rmfiles="$rmfiles $dir/$non_pic_object" fi fi ;; *) if test "$mode" = clean ; then noexename=$name case $file in *.exe) func_stripname '' '.exe' "$file" file=$func_stripname_result func_stripname '' '.exe' "$name" noexename=$func_stripname_result # $file with .exe has already been added to rmfiles, # add $file without .exe rmfiles="$rmfiles $file" ;; esac # Do a test to see if this is a libtool program. if func_ltwrapper_p "$file"; then if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" relink_command= func_source $func_ltwrapper_scriptname_result rmfiles="$rmfiles $func_ltwrapper_scriptname_result" else relink_command= func_source $dir/$noexename fi # note $name still contains .exe if it was in $file originally # as does the version of $file that was added into $rmfiles rmfiles="$rmfiles $objdir/$name $objdir/${name}S.${objext}" if test "$fast_install" = yes && test -n "$relink_command"; then rmfiles="$rmfiles $objdir/lt-$name" fi if test "X$noexename" != "X$name" ; then rmfiles="$rmfiles $objdir/lt-${noexename}.c" fi fi fi ;; esac func_show_eval "$RM $rmfiles" 'exit_status=1' done objdir="$origobjdir" # Try to remove the ${objdir}s in the directories where we deleted files for dir in $rmdirs; do if test -d "$dir"; then func_show_eval "rmdir $dir >/dev/null 2>&1" fi done exit $exit_status } { test "$mode" = uninstall || test "$mode" = clean; } && func_mode_uninstall ${1+"$@"} test -z "$mode" && { help="$generic_help" func_fatal_help "you must specify a MODE" } test -z "$exec_cmd" && \ func_fatal_help "invalid operation mode \`$mode'" if test -n "$exec_cmd"; then eval exec "$exec_cmd" exit $EXIT_FAILURE fi exit $exit_status # The TAGs below are defined such that we never get into a situation # in which we disable both kinds of libraries. Given conflicting # choices, we go for a static library, that is the most portable, # since we can't tell whether shared libraries were disabled because # the user asked for that or because the platform doesn't support # them. This is particularly important on AIX, because we don't # support having both static and shared libraries enabled at the same # time on that platform, so we default to a shared-only configuration. # If a disable-shared tag is given, we'll fallback to a static-only # configuration. But we'll never go from static-only to shared-only. # ### BEGIN LIBTOOL TAG CONFIG: disable-shared build_libtool_libs=no build_old_libs=yes # ### END LIBTOOL TAG CONFIG: disable-shared # ### BEGIN LIBTOOL TAG CONFIG: disable-static build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` # ### END LIBTOOL TAG CONFIG: disable-static # Local Variables: # mode:shell-script # sh-indentation:2 # End: # vi:sw=2 opensc-0.13.0/MacOSX/0000755000015201777760000000000012057406120011173 500000000000000opensc-0.13.0/MacOSX/10.5/0000755000015201777760000000000012057406120011556 500000000000000opensc-0.13.0/MacOSX/10.5/resources/0000755000015201777760000000000012057406062013575 500000000000000opensc-0.13.0/MacOSX/10.5/resources/ReadMe.html0000644000015201777760000000260112057406062015537 00000000000000

OpenSC, version 0.13.0

for Mac OS X 10.5 (Leopard), universal (32-bit i386 and ppc)

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.

Documentation:

The OpenSC Wiki is available at: http://www.opensc-project.org/opensc and should be consulted for further documentation and support.

opensc-0.13.0/MacOSX/10.5/resources/InstallationCheck.strings0000644000015201777760000000050212057406034020523 00000000000000"17" = "Mac OS X version 10.5.8 or later is required for this software."; "18" = "Mac OS X version 10.5 (starting from 10.5.8) is required for this software."; opensc-0.13.0/MacOSX/10.5/resources/background.jpg0000644000015201777760000003150512057406034016341 00000000000000JFIF,,       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.13.0/MacOSX/10.5/resources/ReadMe.html.in0000644000015201777760000000261312057406034016146 00000000000000

OpenSC, version @PACKAGE_VERSION@

for Mac OS X 10.5 (Leopard), universal (32-bit i386 and ppc)

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.

Documentation:

The OpenSC Wiki is available at: http://www.opensc-project.org/opensc and should be consulted for further documentation and support.

opensc-0.13.0/MacOSX/10.5/resources/License.html0000644000015201777760000006154212057406034015774 00000000000000 EST Install OpenSC

GNU LESSER GENERAL PUBLIC LICENSE

Version 2.1, February 1999

Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 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.

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.

opensc-0.13.0/MacOSX/10.5/scripts/0000755000015201777760000000000012057406034013251 500000000000000opensc-0.13.0/MacOSX/10.5/scripts/postflight0000755000015201777760000000135312057406034015304 00000000000000#!/bin/bash if !([ -e "/usr/lib/opensc-pkcs11.so" ]) then ln -s /Library/OpenSC/lib/opensc-pkcs11.so /usr/lib/opensc-pkcs11.so fi if [ -e "/Library/OpenSC/etc/opensc.conf.md5" ] then read cs_fromfile file < "/Library/OpenSC/etc/opensc.conf.md5" cs_calculated=$( md5 -q "/Library/OpenSC/etc/opensc.conf") if [ "$cs_fromfile" = "$cs_calculated" ] then mv /Library/OpenSC/etc/opensc.conf.orig /Library/OpenSC/etc/opensc.conf md5 -r /Library/OpenSC/etc/opensc.conf > /Library/OpenSC/etc/opensc.conf.md5 fi else mv /Library/OpenSC/etc/opensc.conf.orig /Library/OpenSC/etc/opensc.conf md5 -r /Library/OpenSC/etc/opensc.conf > /Library/OpenSC/etc/opensc.conf.md5 fi for f in /Library/OpenSC/bin/* do ln -sf $f /usr/local/bin done exit 0 opensc-0.13.0/MacOSX/10.5/scripts/InstallationCheck0000755000015201777760000000425312057406034016522 00000000000000#!/usr/bin/perl my $SYSTEM_VERS = "/System/Library/CoreServices/SystemVersion.plist"; my $EXIT_VALUE = 0; if ( $ENV{OS_INSTALL} == 1) { exit (0); } DO_CHECKS: { # 10.5.8 or higher system must be active if(CheckVersion("$SYSTEM_VERS", "10.5.8", "ProductVersion", "<")) { $EXIT_VALUE = ((1 << 6) | ( 1 << 5 ) | 17 ); last; } # 10.5 system must be active if(CheckVersion("$SYSTEM_VERS", "10.6", "ProductVersion", ">")) { $EXIT_VALUE = ((1 << 6) | ( 1 << 5 ) | 18 ); last; } } exit($EXIT_VALUE); ### sub CheckVersion { my $path = $_[0]; my $version = $_[1]; my $keyName = $_[2]; my $operator = $_[3]; if (! -e $path) { return 0; } if (!$operator) { $operator = "=="; } my $oldSeperator = $/; $/ = \0; open( PLIST, "$path") || do { return 0; }; $plistData = ; $plistData =~ /(.*?)<\/dict>/gis; @items = split(//, $plistData); shift @items; foreach $item (@items) { $item =~ /(.*?)<\/key>.*?(.*?)<\/string>/gis; $versiondata{ $1 } = $2; } close(PLIST); $/ = $oldSeperator; @theVersionArray = split(/\./, $versiondata{$keyName}); for ($i = 0; $i < 3; $i++) { if(!$theVersionArray[$i]) { $theVersionArray[$i] = '0'; } } @versionArray = split(/\./, $version); my $actualVersion; for ($i = 0; $i < 3; $i++) { if (($theVersionArray[$i] != $versionArray[$i]) or ($i == 2)) { $actualVersion = $theVersionArray[$i]; $version = $versionArray[$i]; last; } } my $expression = '$actualVersion ' . $operator . ' $version'; if( eval ($expression) ) { return 1; } else { return 0; } } sub CheckIOReg { $RESULT = 0; open(IOREGOUT, "/usr/sbin/ioreg |"); foreach $LINE () { $BUF .= $LINE; } close(IOREGOUT); foreach $ITEM (@_) { if($BUF =~ /$ITEM/g) { $RESULT = 1; last; } } return($RESULT); } opensc-0.13.0/MacOSX/build-package.in0000755000015201777760000001131012057406034014136 00000000000000#!/bin/bash set -ex OSX_RELEASE=${1:-10.6} INTEL_ONLY=${INTEL_ONLY:-no} test -x ./configure || ./bootstrap BUILDPATH=${PWD} case ${OSX_RELEASE} in "10.5") if test ${INTEL_ONLY} = "yes"; then export CFLAGS="-isysroot /Developer/SDKs/MacOSX10.5.sdk -arch i386 -mmacosx-version-min=10.5 -g" else export CFLAGS="-isysroot /Developer/SDKs/MacOSX10.5.sdk -arch i386 -arch ppc7400 -mmacosx-version-min=10.5 -g" fi ;; "10.6") export CFLAGS="-isysroot /Developer/SDKs/MacOSX10.6.sdk -arch i386 -arch x86_64 -mmacosx-version-min=10.6 -g" ;; *) echo "OSX ${OSX_RELEASE} is not supported!" exit 1 ;; esac export SED=/usr/bin/sed PREFIX=/Library/OpenSC export PKG_CONFIG_PATH=/usr/lib/pkgconfig # In case of OSX 10.5, link against static libltdl to work around # missing libltdl.3.dylib in 10.5 PPC version test ${OSX_RELEASE} = "10.5" && export LTLIB_LIBS="/Developer/SDKs/MacOSX10.5.sdk/usr/lib/libltdl.a" ./configure --prefix=$PREFIX \ --sysconfdir=$PREFIX/etc \ --disable-dependency-tracking \ --enable-shared \ --disable-static \ --enable-strict \ --disable-assert # check if make install is required case "${OSX_RELEASE}" in "10.5") if test ${INTEL_ONLY} = "yes"; then required_arch="i386"; else required_arch="ppc7400"; fi ;; "10.6") required_arch="x86_64" ;; esac if !(test -e src/libopensc/.libs/libopensc.dylib && (file src/libopensc/.libs/libopensc.dylib | grep $required_arch)); then make clean fi # compile make -j 2 # copy files rm -rf target make install DESTDIR=${BUILDPATH}/target # remove garbage rm -f target/Library/OpenSC/lib/*.la # generate .bundle (required by Adobe Acrobat) ./MacOSX/libtool-bundle target/Library/OpenSC/lib/opensc-pkcs11.so target/Library/OpenSC/lib if test ${OSX_RELEASE} = "10.6"; then # Build libp11+engine_pkcs11. Attention! Uses modified branches from github! test -d libp11 || git clone http://github.com/martinpaljak/libp11.git -b martin (cd libp11 test -x confiure || ./bootstrap ./configure --enable-static --disable-shared --disable-dependency-tracking --prefix=${BUILDPATH}/build && make && make install cd ..) test -d engine_pkcs11 || git clone http://github.com/martinpaljak/engine_pkcs11.git -b martin (cd engine_pkcs11 git checkout origin/martin test -x configure || ./bootstrap PKG_CONFIG_PATH=${BUILDPATH}/build/lib/pkgconfig:${PKG_CONFIG_PATH} ./configure --disable-dependency-tracking --prefix=/Library/OpenSC && make make install DESTDIR=${BUILDPATH}/target) fi if ! test -e OpenSC.tokend; then git clone http://github.com/martinpaljak/OpenSC.tokend.git fi # refresh remote branches, in case the script has changed the active branch and existing buildslave checkout is used. git --git-dir OpenSC.tokend/.git --work-tree OpenSC.tokend fetch --all case "${OSX_RELEASE}" in "10.5") git --git-dir OpenSC.tokend/.git --work-tree OpenSC.tokend checkout --force origin/10.5-0.12.2; rm -rf OpenSC.tokend/build if test ${INTEL_ONLY} = "yes"; then sed -e 's/ ppc7400//g' OpenSC.tokend/Tokend.xcodeproj/project.pbxproj > project.tmp mv project.tmp OpenSC.tokend/Tokend.xcodeproj/project.pbxproj fi ;; "10.6") git --git-dir OpenSC.tokend/.git --work-tree OpenSC.tokend checkout --force origin/10.6-0.12.2; rm -rf OpenSC.tokend/build ;; esac if ! test -e build-${OSX_RELEASE}.tar.gz; then case ${OSX_RELEASE} in "10.5") curl http://martinpaljak.net/download/build-10.5.tar.gz -o build-${OSX_RELEASE}.tar.gz ;; "10.6") curl http://martinpaljak.net/download/build-10.6.tar.gz -o build-${OSX_RELEASE}.tar.gz ;; esac fi # Unpack the binary building components if ! test -e OpenSC.tokend/build; then tar -C OpenSC.tokend -xzvf build-${OSX_RELEASE}.tar.gz fi # Create the symlink to OpenSC sources test -L OpenSC.tokend/build/opensc-src || ln -sf ${BUILDPATH}/src OpenSC.tokend/build/opensc-src # build and copy OpenSC.tokend xcodebuild -configuration Deployment -project OpenSC.tokend/Tokend.xcodeproj mkdir -p target/System/Library/Security/tokend mv OpenSC.tokend/build/OpenSC.tokend target/System/Library/Security/tokend # The "UnInstaller" mkdir -p target/usr/local/bin cp MacOSX/opensc-uninstall target/usr/local/bin # Build installer package /Developer/Applications/Utilities/PackageMaker.app/Contents/MacOS/PackageMaker \ -r target \ -o OpenSC-@PACKAGE_VERSION@-${OSX_RELEASE}.pkg \ -t "OpenSC @PACKAGE_VERSION@ for Mac OS X ${OSX_RELEASE}" \ -i org.opensc-project.mac \ -n @PACKAGE_VERSION@ \ -g 10.4 \ -b \ -v \ --no-relocate \ -e MacOSX/${OSX_RELEASE}/resources \ -s MacOSX/${OSX_RELEASE}/scripts # Create .dmg rm -f OpenSC-@PACKAGE_VERSION@-${OSX_RELEASE}.dmg TIMESTAMP=$(date +%Y.%m.%d) hdiutil create -srcfolder OpenSC-@PACKAGE_VERSION@-${OSX_RELEASE}.pkg -volname "OpenSC @PACKAGE_VERSION@ for Mac OS X ${OSX_RELEASE} (${TIMESTAMP})" OpenSC-@PACKAGE_VERSION@-${OSX_RELEASE}.dmg opensc-0.13.0/MacOSX/10.6/0000755000015201777760000000000012057406120011557 500000000000000opensc-0.13.0/MacOSX/10.6/resources/0000755000015201777760000000000012057406062013576 500000000000000opensc-0.13.0/MacOSX/10.6/resources/ReadMe.html0000644000015201777760000000262412057406062015545 00000000000000

OpenSC, version 0.13.0

for Mac OS X 10.6 (Snow Leopard) and 10.7 (Lion), universal (32-bit and 64-bit)

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.

Documentation:

The OpenSC Wiki is available at: http://www.opensc-project.org/opensc and should be consulted for further documentation and support.

opensc-0.13.0/MacOSX/10.6/resources/InstallationCheck.strings0000644000015201777760000000065212057406034020532 00000000000000"17" = "Mac OS X version 10.6.8 or later is required for this software. Please install the latest security updates!"; "18" = "Mac OS X version 10.6 (starting from 10.6.8) or 10.7 is required for this software."; opensc-0.13.0/MacOSX/10.6/resources/background.jpg0000644000015201777760000003150512057406034016342 00000000000000JFIF,,       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.13.0/MacOSX/10.6/resources/ReadMe.html.in0000644000015201777760000000263612057406034016154 00000000000000

OpenSC, version @PACKAGE_VERSION@

for Mac OS X 10.6 (Snow Leopard) and 10.7 (Lion), universal (32-bit and 64-bit)

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.

Documentation:

The OpenSC Wiki is available at: http://www.opensc-project.org/opensc and should be consulted for further documentation and support.

opensc-0.13.0/MacOSX/10.6/resources/License.html0000644000015201777760000006154212057406034015775 00000000000000 EST Install OpenSC

GNU LESSER GENERAL PUBLIC LICENSE

Version 2.1, February 1999

Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 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.

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.

opensc-0.13.0/MacOSX/10.6/scripts/0000755000015201777760000000000012057406034013252 500000000000000opensc-0.13.0/MacOSX/10.6/scripts/postflight0000755000015201777760000000135212057406034015304 00000000000000#!/bin/bash if !([ -e "/usr/lib/opensc-pkcs11.so" ]) then ln -s /Library/OpenSC/lib/opensc-pkcs11.so /usr/lib/opensc-pkcs11.so fi if [ -e "/Library/OpenSC/etc/opensc.conf.md5" ] then read cs_fromfile file < "/Library/OpenSC/etc/opensc.conf.md5" cs_calculated=$( md5 -q "/Library/OpenSC/etc/opensc.conf") if [ "$cs_fromfile" = "$cs_calculated" ] then mv /Library/OpenSC/etc/opensc.conf.orig /Library/OpenSC/etc/opensc.conf md5 -r /Library/OpenSC/etc/opensc.conf > /Library/OpenSC/etc/opensc.conf.md5 fi else mv /Library/OpenSC/etc/opensc.conf.orig /Library/OpenSC/etc/opensc.conf md5 -r /Library/OpenSC/etc/opensc.conf > /Library/OpenSC/etc/opensc.conf.md5 fi for f in /Library/OpenSC/bin/* do ln -sf $f /usr/local/bin done exit 0 opensc-0.13.0/MacOSX/10.6/scripts/InstallationCheck0000755000015201777760000000426312057406034016524 00000000000000#!/usr/bin/perl my $SYSTEM_VERS = "/System/Library/CoreServices/SystemVersion.plist"; my $EXIT_VALUE = 0; if ( $ENV{OS_INSTALL} == 1) { exit (0); } DO_CHECKS: { # 10.6.8 or higher system must be active if(CheckVersion("$SYSTEM_VERS", "10.6.8", "ProductVersion", "<")) { $EXIT_VALUE = ((1 << 6) | ( 1 << 5 ) | 17 ); last; } # 10.6 or 10.7 system must be active if(CheckVersion("$SYSTEM_VERS", "10.8", "ProductVersion", ">")) { $EXIT_VALUE = ((1 << 6) | ( 1 << 5 ) | 18 ); last; } } exit($EXIT_VALUE); ### sub CheckVersion { my $path = $_[0]; my $version = $_[1]; my $keyName = $_[2]; my $operator = $_[3]; if (! -e $path) { return 0; } if (!$operator) { $operator = "=="; } my $oldSeperator = $/; $/ = \0; open( PLIST, "$path") || do { return 0; }; $plistData = ; $plistData =~ /(.*?)<\/dict>/gis; @items = split(//, $plistData); shift @items; foreach $item (@items) { $item =~ /(.*?)<\/key>.*?(.*?)<\/string>/gis; $versiondata{ $1 } = $2; } close(PLIST); $/ = $oldSeperator; @theVersionArray = split(/\./, $versiondata{$keyName}); for ($i = 0; $i < 3; $i++) { if(!$theVersionArray[$i]) { $theVersionArray[$i] = '0'; } } @versionArray = split(/\./, $version); my $actualVersion; for ($i = 0; $i < 3; $i++) { if (($theVersionArray[$i] != $versionArray[$i]) or ($i == 2)) { $actualVersion = $theVersionArray[$i]; $version = $versionArray[$i]; last; } } my $expression = '$actualVersion ' . $operator . ' $version'; if( eval ($expression) ) { return 1; } else { return 0; } } sub CheckIOReg { $RESULT = 0; open(IOREGOUT, "/usr/sbin/ioreg |"); foreach $LINE () { $BUF .= $LINE; } close(IOREGOUT); foreach $ITEM (@_) { if($BUF =~ /$ITEM/g) { $RESULT = 1; last; } } return($RESULT); } opensc-0.13.0/MacOSX/Makefile.am0000644000015201777760000000111112057406034013145 00000000000000MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = build build-package.in libtool-bundle opensc-uninstall \ 10.5/resources \ 10.5/resources/background.jpg \ 10.5/resources/InstallationCheck.strings \ 10.5/resources/License.html \ 10.5/resources/ReadMe.html.in \ 10.5/scripts \ 10.5/scripts/InstallationCheck \ 10.5/scripts/postflight \ 10.6/resources \ 10.6/resources/background.jpg \ 10.6/resources/InstallationCheck.strings \ 10.6/resources/License.html \ 10.6/resources/ReadMe.html.in \ 10.6/scripts \ 10.6/scripts/InstallationCheck \ 10.6/scripts/postflight opensc-0.13.0/MacOSX/libtool-bundle0000755000015201777760000000453212057406034013764 00000000000000#!/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.13.0/MacOSX/opensc-uninstall0000755000015201777760000000205112057406034014341 00000000000000#!/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 rm -rf /Library/OpenSC rm -rf /System/Library/Security/tokend/OpenSC.tokend for file in /usr/lib/opensc-pkcs11.so /usr/local/bin/cardos-tool /usr/local/bin/cryptoflex-tool /usr/local/bin/eidenv /usr/local/bin/netkey-tool /usr/local/bin/opensc-explorer /usr/local/bin/opensc-tool /usr/local/bin/piv-tool /usr/local/bin/pkcs11-tool /usr/local/bin/pkcs15-crypt /usr/local/bin/pkcs15-init /usr/local/bin/pkcs15-tool /usr/local/bin/rutoken-tool /usr/local/bin/westcos-tool; do test -L $file && rm -f $file done rm -f /usr/local/bin/opensc-uninstall # delete receipts on 10.6 for file in /var/db/receipts/org.opensc-project.mac.bom /var/db/receipts/org.opensc-project.mac.plist; do test -f $file && rm -f $file done # delete receipts on 10.5 test -d /Library/Receipts/OpenSC-10.5.pkg && rm -rf /Library/Receipts/OpenSC-10.5.pkg echo "OpenSC has been removed from your system. See you again!" opensc-0.13.0/MacOSX/build0000755000015201777760000000031112057406034012137 00000000000000#!/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.13.0/MacOSX/Makefile.in0000644000015201777760000002753012057406055013176 00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program 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. @SET_MAKE@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = MacOSX DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ $(srcdir)/build-package.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = build-package CONFIG_CLEAN_VPATH_FILES = AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ SOURCES = DIST_SOURCES = DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUG_FILE = @DEBUG_FILE@ DEFAULT_PCSC_PROVIDER = @DEFAULT_PCSC_PROVIDER@ DEFAULT_SM_MODULE = @DEFAULT_SM_MODULE@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GIT = @GIT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBRARY_BITNESS = @LIBRARY_BITNESS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCT_CFLAGS = @OPENCT_CFLAGS@ OPENCT_LIBS = @OPENCT_LIBS@ OPENSC_LT_AGE = @OPENSC_LT_AGE@ OPENSC_LT_CURRENT = @OPENSC_LT_CURRENT@ OPENSC_LT_OLDEST = @OPENSC_LT_OLDEST@ OPENSC_LT_REVISION = @OPENSC_LT_REVISION@ OPENSC_VERSION_FIX = @OPENSC_VERSION_FIX@ OPENSC_VERSION_MAJOR = @OPENSC_VERSION_MAJOR@ OPENSC_VERSION_MINOR = @OPENSC_VERSION_MINOR@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OPTIONAL_OPENCT_CFLAGS = @OPTIONAL_OPENCT_CFLAGS@ OPTIONAL_OPENCT_LIBS = @OPTIONAL_OPENCT_LIBS@ OPTIONAL_OPENSSL_CFLAGS = @OPTIONAL_OPENSSL_CFLAGS@ OPTIONAL_OPENSSL_LIBS = @OPTIONAL_OPENSSL_LIBS@ OPTIONAL_PCSC_CFLAGS = @OPTIONAL_PCSC_CFLAGS@ OPTIONAL_READLINE_CFLAGS = @OPTIONAL_READLINE_CFLAGS@ OPTIONAL_READLINE_LIBS = @OPTIONAL_READLINE_LIBS@ OPTIONAL_ZLIB_CFLAGS = @OPTIONAL_ZLIB_CFLAGS@ OPTIONAL_ZLIB_LIBS = @OPTIONAL_ZLIB_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCSC_CFLAGS = @PCSC_CFLAGS@ PCSC_LIBS = @PCSC_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RC = @RC@ READLINE_CFLAGS = @READLINE_CFLAGS@ READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ WIN_LIBPREFIX = @WIN_LIBPREFIX@ XSLTPROC = @XSLTPROC@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ git = @git@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkcs11dir = @pkcs11dir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ xslstylesheetsdir = @xslstylesheetsdir@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = build build-package.in libtool-bundle opensc-uninstall \ 10.5/resources \ 10.5/resources/background.jpg \ 10.5/resources/InstallationCheck.strings \ 10.5/resources/License.html \ 10.5/resources/ReadMe.html.in \ 10.5/scripts \ 10.5/scripts/InstallationCheck \ 10.5/scripts/postflight \ 10.6/resources \ 10.6/resources/background.jpg \ 10.6/resources/InstallationCheck.strings \ 10.6/resources/License.html \ 10.6/resources/ReadMe.html.in \ 10.6/scripts \ 10.6/scripts/InstallationCheck \ 10.6/scripts/postflight all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign MacOSX/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign MacOSX/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): build-package: $(top_builddir)/config.status $(srcdir)/build-package.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags: TAGS TAGS: ctags: CTAGS CTAGS: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ distclean distclean-generic distclean-libtool distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: opensc-0.13.0/Makefile.mak0000644000015201777760000000022012057406034012226 00000000000000 SUBDIRS = etc win32 src all:: all depend install clean:: @for %i in ( $(SUBDIRS) ) do @cmd /c "cd %i && $(MAKE) /nologo /f Makefile.mak $@" opensc-0.13.0/svnignore0000644000015201777760000000122212057406034011757 00000000000000Makefile Makefile.in core archive acinclude.m4 aclocal.m4 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* .deps .libs .#*# .*.bak .*.orig .*.rej .*~ #*# *.bak *.d *.def *.dll *.exe *.la *.lib *.lo *.orig *.pdb *.rej *.u *.rc *.pc *~ *.gz *.bz2 *.[0-9] *.html *.gif *.css *.out *.tmp ChangeLog opensc.conf xsl-stylesheets opensc-config test-conf pkcs15-tool pkcs15-crypt pkcs15-init piv-tool eidenv opensc-explorer opensc-tool rutoken-tool cardos-info cryptoflex-tool netkey-tool pkcs11-tool pintest p15dump prngtest base64 lottery opensc-0.13.0/README0000644000015201777760000000023612057406034010706 00000000000000OpenSC documentation wiki is available online at http://www.opensc-project.org/opensc/ Please take a look at the documentation before trying to use OpenSC. opensc-0.13.0/src/0000755000015201777760000000000012057406120010670 500000000000000opensc-0.13.0/src/sm/0000755000015201777760000000000012057406120011307 500000000000000opensc-0.13.0/src/sm/sm-global-platform.c0000644000015201777760000003744512057406034015113 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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" #if 0 #include "libopensc/hash-strings.h" #endif #include "sm-module.h" static const struct sc_asn1_entry c_asn1_authentic_card_response[4] = { { "number", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "status", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "data", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 2 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_card_response[2] = { { "cardResponse", SC_ASN1_STRUCT, SC_ASN1_CTX | 1 | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; int sm_gp_decode_card_answer(struct sc_context *ctx, struct sc_remote_data *rdata, unsigned char *out, size_t out_len) { #if 0 struct sc_asn1_entry asn1_authentic_card_response[4], asn1_card_response[2]; struct sc_hash *hash = NULL; unsigned char *hex = NULL; size_t hex_len; int rv, offs; unsigned char card_data[SC_MAX_APDU_BUFFER_SIZE]; size_t card_data_len = sizeof(card_data), len_left = 0; LOG_FUNC_CALLED(ctx); if (!out || !out_len) LOG_FUNC_RETURN(ctx, 0); if (strstr(str_data, "DATA=")) { rv = sc_hash_parse(ctx, str_data, strlen(str_data), &hash); LOG_TEST_RET(ctx, rv, "SM GP decode card answer: parse input data error"); str_data = sc_hash_get(hash, "DATA"); } if (!strlen(str_data)) LOG_FUNC_RETURN(ctx, 0); hex_len = strlen(str_data) / 2; hex = calloc(1, hex_len); if (!hex) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "SM GP decode card answer: hex allocate error"); sc_log(ctx, "SM GP decode card answer: hex length %i", hex_len); rv = sc_hex_to_bin(str_data, hex, &hex_len); LOG_TEST_RET(ctx, rv, "SM GP decode card answer: data 'HEX to BIN' conversion error"); sc_log(ctx, "SM GP decode card answer: hex length %i", hex_len); if (hash) sc_hash_free(hash); for (offs = 0, len_left = hex_len; len_left; ) { int num, status; sc_copy_asn1_entry(c_asn1_authentic_card_response, asn1_authentic_card_response); sc_copy_asn1_entry(c_asn1_card_response, asn1_card_response); sc_format_asn1_entry(asn1_authentic_card_response + 0, &num, NULL, 0); sc_format_asn1_entry(asn1_authentic_card_response + 1, &status, NULL, 0); card_data_len = sizeof(card_data); sc_format_asn1_entry(asn1_authentic_card_response + 2, &card_data, &card_data_len, 0); sc_format_asn1_entry(asn1_card_response + 0, asn1_authentic_card_response, NULL, 0); rv = sc_asn1_decode(ctx, asn1_card_response, hex + hex_len - len_left, len_left, NULL, &len_left); if (rv) { sc_log(ctx, "SM GP decode card answer: ASN.1 parse error: %s", sc_strerror(rv)); return rv; } if (status != 0x9000) continue; if (asn1_authentic_card_response[2].flags & SC_ASN1_PRESENT) { sc_log(ctx, "SM GP decode card answer: card_data_len %i", card_data_len); if (out_len < offs + card_data_len) LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "SM GP decode card answer: buffer too small"); memcpy(out + offs, card_data, card_data_len); offs += card_data_len; } sc_log(ctx, "SM GP decode card answer: offs:%i,left:%i", offs, len_left); } free(hex); LOG_FUNC_RETURN(ctx, offs); #else LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); #endif } 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_log(ctx, "SM GP initialize: serial:%s", sc_dump_hex(sn.value, sn.len)); sc_log(ctx, "SM GP initialize: current_df_path %s", sc_print_path(&sm_info->current_path_df)); sc_log(ctx, "SM GP initialize: KMC length %i", gp_keyset->kmc_len); 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; 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(key, deriv, 16, &out, &out_len)) { if (ctx) sc_log(ctx, "SM GP get session key: des_ecb3 encryption error"); free(out); return NULL; } else if (out==NULL || out_len!=16) { if (ctx) sc_log(ctx, "SM GP get session key: des_ecb3 encryption error: out(%p,len:%i)", out, out_len); if (out) free(out); return NULL; } return out; } int sm_gp_get_cryptogram(unsigned char *session_key, unsigned char *left, unsigned char *right, unsigned char *out, int out_len) { unsigned char block[24]; DES_cblock kk,k2; DES_key_schedule ks,ks2; DES_cblock cksum={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 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); memcpy(&kk, session_key, 8); memcpy(&k2, session_key + 8, 8); DES_set_key_unchecked(&kk,&ks); DES_set_key_unchecked(&k2,&ks2); DES_cbc_cksum_3des(block,&cksum, sizeof(block),&ks,&ks2,&cksum); memcpy(out, cksum, 8); return 0; } int sm_gp_get_mac(unsigned char *key, DES_cblock *icv, unsigned char *in, int in_len, DES_cblock *out) { int len; unsigned char *block; DES_cblock kk, k2; DES_key_schedule ks,ks2; block = malloc(in_len + 8); if (!block) return SC_ERROR_MEMORY_FAILURE; 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); memcpy(&kk, key, 8); memcpy(&k2, key + 8, 8); DES_set_key_unchecked(&kk,&ks); DES_set_key_unchecked(&k2,&ks2); DES_cbc_cksum_3des(block, out, len ,&ks, &ks2, icv); 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_log(ctx, "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_log(ctx, "SM GP init session: session ENC: %s", sc_dump_hex(gp_session->session_enc, 16)); sc_log(ctx, "SM GP init session: session MAC: %s", sc_dump_hex(gp_session->session_mac, 16)); sc_log(ctx, "SM GP init session: session KEK: %s", sc_dump_hex(gp_session->session_kek, 16)); memset(cksum, 0, sizeof(cksum)); rv = sm_gp_get_cryptogram(gp_session->session_enc, gp_session->host_challenge, gp_session->card_challenge, cksum, sizeof(cksum)); LOG_TEST_RET(ctx, rv, "SM GP init session: cannot get cryptogram"); sc_log(ctx, "SM GP init session: cryptogram: %s", sc_dump_hex(cksum, 8)); if (memcmp(cksum, adata, adata_len)) LOG_FUNC_RETURN(ctx, SC_ERROR_SM_AUTHENTICATION_FAILED); sc_log(ctx, "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; DES_cblock mac; int rv, idx = 0, 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(gp_session->session_enc, gp_session->card_challenge, gp_session->host_challenge, host_cryptogram, sizeof(host_cryptogram)); LOG_TEST_RET(ctx, rv, "SM GP authentication: get host cryptogram error"); sc_log(ctx, "SM GP authentication: host_cryptogram:%s", sc_dump_hex(host_cryptogram, 8)); 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(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, len; if (!out || !out_len) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "SM GP encrypt command data error"); sc_log(ctx, "SM GP encrypt command data(len:%i,%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; 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_log(ctx, "SM GP securize APDU(cse:%X,cla:%X,ins:%X,data(len:%i,%p),lc:%i,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) LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "SM GP securize APDU: not enough place for encrypted data"); sc_log(ctx, "SM GP securize APDU: encrypted length %i", 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(gp_session->session_mac, &gp_session->mac_icv, buff, 5 + apdu->datalen, &mac); LOG_TEST_RET(ctx, rv, "SM GP securize APDU: get MAC error"); 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); } memcpy(sm_info->session.gp.mac_icv, mac, 8); LOG_FUNC_RETURN(ctx, rv); } opensc-0.13.0/src/sm/sm-module.h0000644000015201777760000001030612057406034013306 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _SM_MODULE_H #define _SM_MODULE_H #ifdef __cplusplus extern "C" { #endif #include #include #include "libopensc/sm.h" DES_LONG DES_cbc_cksum_3des(const unsigned char *in, DES_cblock *output, long length, DES_key_schedule *schedule, DES_key_schedule *schedule2, const_DES_cblock *ivec); DES_LONG DES_cbc_cksum_3des_emv96(const unsigned char *in, DES_cblock *output, long length, DES_key_schedule *schedule, DES_key_schedule *schedule2, const_DES_cblock *ivec); int sm_encrypt_des_ecb3(unsigned char *key, unsigned char *data, int data_len, unsigned 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); /* Global Platform definitions */ int sm_gp_get_mac(unsigned char *key, DES_cblock *icv, unsigned char *in, int in_len, DES_cblock *out); int sm_gp_get_cryptogram(unsigned char *session_key, unsigned char *left, unsigned char *right, 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.13.0/src/sm/Makefile.mak0000644000015201777760000000121312057406034013437 00000000000000TOPDIR = ..\.. TARGET = smm-local.dll OBJECTS = smm-local.obj sm-common.obj sm-global-platform.obj sm-cwa14890.obj sm-card-iasecc.obj sm-card-authentic.obj all: $(TARGET) !INCLUDE $(TOPDIR)\win32\Make.rules.mak $(TARGET): $(OBJECTS) ..\libopensc\opensc.lib echo LIBRARY $* > $*.def echo EXPORTS >> $*.def type $*.exports >> $*.def link /dll $(LINKFLAGS) /def:$*.def /out:$(TARGET) $(OBJECTS) ..\libopensc\opensc_a.lib $(ZLIB_LIB) $(OPENSSL_LIB) ..\common\libscdl.lib ws2_32.lib gdi32.lib advapi32.lib Crypt32.lib User32.lib if EXIST $(TARGET).manifest mt -manifest $(TARGET).manifest -outputresource:$(TARGET);2 .c.obj: cl $(COPTS) /c $< opensc-0.13.0/src/sm/sm-card-authentic.c0000644000015201777760000002405112057406034014711 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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" #if 0 #include "libopensc/hash-strings.h" #endif #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_log(ctx, "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_log(ctx, "key_buf:%s", sc_dump_hex(key_buff, 16)); rv = sm_encrypt_des_ecb3(master_key, key_buff, sizeof(key_buff), &tmp, &tmp_len); LOG_TEST_RET(ctx, rv, "GP init session: cannot derivate key"); 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) { char dump_buf[2048]; sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, gp_session->card_challenge, sizeof(gp_session->card_challenge), dump_buf, sizeof(dump_buf)); sc_log(ctx, "Card challenge: %s", dump_buf); sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, gp_session->host_challenge, sizeof(gp_session->host_challenge), dump_buf, sizeof(dump_buf)); sc_log(ctx, "Host challenge: %s", dump_buf); sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, gp_keyset->enc, sizeof(gp_keyset->enc), dump_buf, sizeof(dump_buf)); sc_log(ctx, "ENC: %s", dump_buf); sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, gp_keyset->mac, sizeof(gp_keyset->mac), dump_buf, sizeof(dump_buf)); sc_log(ctx, "MAC: %s", dump_buf); sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, gp_keyset->kek, sizeof(gp_keyset->kek), dump_buf, sizeof(dump_buf)); sc_log(ctx, "KEK: %s", dump_buf); } 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_log(ctx, "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); } #if 0 static int sm_authentic_get_apdu_read_binary(struct sc_context *ctx, struct sm_info *sm_info, char *init_data, struct sc_remote_apdu **rapdus) { struct sm_info_read_binary *rb = &sm_info->cmd_params.read_binary; size_t offs = rb->offset, size = rb->size; int rv = SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(ctx); sc_log(ctx, "SM get 'READ BINARY' APDUs: offset:%i,size:%i", offs, size); while (size) { int sz = size > SM_MAX_DATA_SIZE ? SM_MAX_DATA_SIZE : size; struct sc_remote_apdu *rapdu = NULL; rv = sc_remote_apdu_allocate(rapdus, &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.resplen = sz; rapdu->apdu.le = sz; rv = sm_gp_securize_apdu(ctx, sm_info, init_data, &rapdu->apdu); LOG_TEST_RET(ctx, rv, "SM get 'READ BINARY' APDUs: securize error"); offs += sz; size -= sz; } LOG_FUNC_RETURN(ctx, rv); } static int sm_authentic_get_apdu_update_binary(struct sc_context *ctx, struct sm_info *sm_info, char *init_data, struct sc_remote_apdu **rapdus) { struct sm_info_update_binary *ub = &sm_info->cmd_params.update_binary; size_t offs = ub->offset, size = ub->size, data_offs = 0; int rv = SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(ctx); sc_log(ctx, "SM get 'UPDATE BINARY' APDUs: offset:%i,size:%i", offs, size); while (size) { int sz = size > SM_MAX_DATA_SIZE ? SM_MAX_DATA_SIZE : size; struct sc_remote_apdu *rapdu = NULL; rv = sc_remote_apdu_allocate(rapdus, &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, ub->data + data_offs, sz); rapdu->apdu.datalen = sz; rapdu->apdu.lc = sz; rv = sm_gp_securize_apdu(ctx, sm_info, init_data, &rapdu->apdu); LOG_TEST_RET(ctx, rv, "SM get 'READ BINARY' APDUs: securize error"); offs += sz; data_offs += sz; size -= sz; } LOG_FUNC_RETURN(ctx, rv); } static int sm_authentic_get_apdu_create_file(struct sc_context *ctx, struct sm_info *sm_info, char *init_data, struct sc_remote_apdu **rapdus) { struct sm_info_create_file *cf = &sm_info->cmd_params.create_file; struct sc_remote_apdu *rapdu = NULL; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "SM get 'CREATE FILE' APDU: FCP(%i) %p", cf->fcp_len, cf->fcp); rv = sc_remote_apdu_allocate(rapdus, &rapdu); LOG_TEST_RET(ctx, rv, "SM get 'CREATE FILE' APDU: 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, cf->fcp, cf->fcp_len); rapdu->apdu.datalen = cf->fcp_len; rapdu->apdu.lc = cf->fcp_len; rv = sm_gp_securize_apdu(ctx, sm_info, init_data, &rapdu->apdu); LOG_TEST_RET(ctx, rv, "SM get 'CREATE FILE' APDU: securize error"); LOG_FUNC_RETURN(ctx, rv); } #endif static int sm_authentic_get_apdu_release(struct sc_context *ctx, struct sm_info *sm_info, char *init_data, struct sc_remote_data *rdata) { struct sc_remote_apdu *rapdu = NULL; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "SM get 'SM RELEASE' APDU"); if (!rdata || !rdata->alloc) LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "SM get 'SM RELEASE' APDU: invalid remote data"); rv = rdata->alloc(rdata, &rapdu); LOG_TEST_RET(ctx, rv, "SM get 'SM RELEASE' APDU: cannot allocate remote apdu"); rapdu->apdu.cse = SC_APDU_CASE_1; rapdu->apdu.cla = 0x00; rapdu->apdu.ins = 0x20; rapdu->apdu.p1 = 0x00; rapdu->apdu.p2 = 0xC0; rv = sm_gp_securize_apdu(ctx, sm_info, init_data, &rapdu->apdu); LOG_TEST_RET(ctx, rv, "SM get 'SM RELEASE' APDUs: 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_log(ctx, "SM get APDUs: rdata:%p, init_len:%i", rdata, init_len); sc_log(ctx, "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) { #if 0 case SM_CMD_FILE_READ: rv = sm_authentic_get_apdu_read_binary(ctx, sm_info, init_data, &rapdus); LOG_TEST_RET(ctx, rv, "SM get APDUs: add 'READ BINARY' failed"); break; case SM_CMD_FILE_UPDATE: rv = sm_authentic_get_apdu_update_binary(ctx, sm_info, init_data, &rapdus); LOG_TEST_RET(ctx, rv, "SM get APDUs: add 'UPDATE BINARY' failed"); break; case SM_CMD_FILE_CREATE: rv = sm_authentic_get_apdu_create_file(ctx, sm_info, init_data, &rapdus); LOG_TEST_RET(ctx, rv, "SM get APDUs: add 'FILE CREATE' failed"); break; case SM_CMD_PIN_VERIFY: rv = sm_authentic_get_apdu_verify_pin(ctx, sm_info, init_data, &rapdus); LOG_TEST_RET(ctx, rv, "SM get APDUs: add 'VERIFY PIN' failed"); break; case SM_CMD_PIN_RESET: break; case SM_CMD_PIN_CREATE: break; case SM_CMD_RSA_GENERATE: break; case SM_CMD_RSA_UPDATE: break; #endif 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"); } #if 0 if (release_sm) { rv = sm_authentic_get_apdu_release(ctx, sm_info, init_data, init_len, out, out_num); LOG_TEST_RET(ctx, rv, "SM get APDUs: add 'release' failed"); sm_gp_close_session(ctx, &sm_info->session.gp); } #endif LOG_FUNC_RETURN(ctx, rv); } opensc-0.13.0/src/sm/sm-common.c0000644000015201777760000002112512057406034013305 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "libopensc/opensc.h" #include "libopensc/asn1.h" #include "libopensc/log.h" #include "sm-module.h" /* * From crypto/des/des_locl.h of OpenSSL . */ #define c2l(c,l) (l =((DES_LONG)(*((c)++))) , \ l|=((DES_LONG)(*((c)++)))<< 8L, \ l|=((DES_LONG)(*((c)++)))<<16L, \ l|=((DES_LONG)(*((c)++)))<<24L) #define c2ln(c,l1,l2,n) { \ c+=n; \ l1=l2=0; \ switch (n) { \ case 8: l2 =((DES_LONG)(*(--(c))))<<24L; \ case 7: l2|=((DES_LONG)(*(--(c))))<<16L; \ case 6: l2|=((DES_LONG)(*(--(c))))<< 8L; \ case 5: l2|=((DES_LONG)(*(--(c)))); \ case 4: l1 =((DES_LONG)(*(--(c))))<<24L; \ case 3: l1|=((DES_LONG)(*(--(c))))<<16L; \ case 2: l1|=((DES_LONG)(*(--(c))))<< 8L; \ case 1: l1|=((DES_LONG)(*(--(c)))); \ } \ } #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(DES_cblock *input, DES_cblock *output, long length, DES_key_schedule *ks1, DES_key_schedule *ks2, DES_cblock *iv, int enc) { int off=((int)length-1)/8; long l8=((length+7)/8)*8; 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 (length >= sizeof(DES_cblock)) memcpy(icv_out,output[off],sizeof(DES_cblock)); } else { if (length >= sizeof(DES_cblock)) memcpy(icv_out,input[off],sizeof(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(DES_cblock)); } DES_LONG DES_cbc_cksum_3des_emv96(const unsigned char *in, DES_cblock *output, long length, DES_key_schedule *schedule, DES_key_schedule *schedule2, const_DES_cblock *ivec) { register DES_LONG tout0,tout1,tin0,tin1; register long l=length; DES_LONG tin[2]; unsigned char *out = &(*output)[0]; const unsigned char *iv = &(*ivec)[0]; 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((DES_LONG *)tin,schedule, 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((DES_LONG *)tin,schedule,schedule2,schedule); tout0=tin[0]; tout1=tin[1]; if (out != NULL) { l2c(tout0,out); l2c(tout1,out); } tout0=tin0=tin1=tin[0]=tin[1]=0; /* Transform the data in tout1 so that it will match the return value that the MIT Kerberos mit_des_cbc_cksum API returns. */ tout1 = ((tout1 >> 24L) & 0x000000FF) | ((tout1 >> 8L) & 0x0000FF00) | ((tout1 << 8L) & 0x00FF0000) | ((tout1 << 24L) & 0xFF000000); return(tout1); } DES_LONG DES_cbc_cksum_3des(const unsigned char *in, DES_cblock *output, long length, DES_key_schedule *schedule, DES_key_schedule *schedule2, const_DES_cblock *ivec) { register DES_LONG tout0,tout1,tin0,tin1; register long l=length; DES_LONG tin[2]; unsigned char *out = &(*output)[0]; const unsigned char *iv = &(*ivec)[0]; 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((DES_LONG *)tin,schedule,schedule2,schedule); /* 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); } tout0=tin0=tin1=tin[0]=tin[1]=0; /* Transform the data in tout1 so that it will match the return value that the MIT Kerberos mit_des_cbc_cksum API returns. */ tout1 = ((tout1 >> 24L) & 0x000000FF) | ((tout1 >> 8L) & 0x0000FF00) | ((tout1 << 8L) & 0x00FF0000) | ((tout1 << 24L) & 0xFF000000); return(tout1); } int sm_encrypt_des_ecb3(unsigned char *key, unsigned char *data, int data_len, unsigned char **out, int *out_len) { int ii; DES_cblock kk,k2; DES_key_schedule ks,ks2; if (!out || !out_len) return -1; *out_len = data_len + 7; *out_len -= *out_len % 8; *out = malloc(*out_len); if (!(*out)) return -1; 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 * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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" #if 0 #include "libopensc/hash-strings.h" #endif #include "sm-module.h" static const struct sc_asn1_entry c_asn1_card_response[2] = { { "cardResponse", SC_ASN1_STRUCT, SC_ASN1_CTX | 1 | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_iasecc_response[4] = { { "number", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "status", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "data", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 2 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; int sm_cwa_get_mac(struct sc_context *ctx, unsigned char *key, DES_cblock *icv, unsigned char *in, int in_len, DES_cblock *out, int force_padding) { DES_cblock kk, k2; DES_key_schedule ks,ks2; unsigned char padding[8] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; unsigned char *buf; LOG_FUNC_CALLED(ctx); sc_log(ctx, "sm_cwa_get_mac() data length %i", in_len); buf = malloc(in_len + 8); if (!buf) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); sc_log(ctx, "sm_cwa_get_mac() in_data(%i) %s", in_len, sc_dump_hex(in, in_len)); 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_log(ctx, "sm_cwa_get_mac() data to MAC(%i) %s", in_len, sc_dump_hex(buf, in_len)); sc_log(ctx, "sm_cwa_get_mac() ICV %s", sc_dump_hex((unsigned char *)icv, 8)); memcpy(&kk, key, 8); memcpy(&k2, key + 8, 8); DES_set_key_unchecked(&kk,&ks); DES_set_key_unchecked(&k2,&ks2); DES_cbc_cksum_3des_emv96(buf, out, in_len ,&ks, &ks2, icv); 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_log(ctx, "IFD.RND %s", sc_dump_hex(session_data->ifd.rnd, 8)); sc_log(ctx, "IFD.SN %s", sc_dump_hex(session_data->ifd.sn, 8)); 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_log(ctx, "IFD.RND %s", sc_dump_hex(session_data->ifd.rnd, 8)); sc_log(ctx, "IFD.SN %s", sc_dump_hex(session_data->ifd.sn, 8)); sc_log(ctx, "IFD.K %s", sc_dump_hex(session_data->ifd.k, 32)); sc_log(ctx, "ICC.RND %s", sc_dump_hex(session_data->icc.rnd, 8)); sc_log(ctx, "ICC.SN %s", sc_dump_hex(session_data->icc.sn, 8)); 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) { DES_cblock icv = {0, 0, 0, 0, 0, 0, 0, 0}; 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_log(ctx, "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_log(ctx, "sm_ecc_decode_auth_data() decrypted(%i) %s", decrypted_len, sc_dump_hex(decrypted, decrypted_len)); if (memcmp(decrypted, session_data->icc.rnd, 8)) LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); if (memcmp(decrypted + 8, session_data->icc.sn, 8)) LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); if (memcmp(decrypted + 16, session_data->ifd.rnd, 8)) LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); if (memcmp(decrypted + 24, session_data->ifd.sn, 8)) 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_log(ctx, "K_IFD %s", sc_dump_hex(session_data->ifd.k, 32)); sc_log(ctx, "K_ICC %s", sc_dump_hex(session_data->icc.k, 32)); if (mechanism == IASECC_ALGORITHM_SYMMETRIC_SHA1) { xored[35] = 0x01; sc_log(ctx, "XOR for SkEnc %s", sc_dump_hex(xored, 36)); SHA1(xored, 36, buff); memcpy(&session_data->session_enc[0], buff, sizeof(session_data->session_enc)); xored[35] = 0x02; sc_log(ctx, "XOR for SkMac %s", sc_dump_hex(xored, 36)); 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); } void sm_cwa_incr_ssc(struct sm_cwa_session *session_data) { int ii; if (!session_data) return; for (ii=7; ii>=0; ii--) { session_data->ssc[ii] += 1; if (session_data->ssc[ii]) break; } } 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; size_t encrypted_len; DES_cblock icv = {0, 0, 0, 0, 0, 0, 0, 0}, cblock; int rv, offs; LOG_FUNC_CALLED(ctx); sc_log(ctx, "SM IAS/ECC initialize: serial %s", sc_dump_hex(sm_info->serialnr.value, sm_info->serialnr.len)); sc_log(ctx, "SM IAS/ECC initialize: card challenge %s", sc_dump_hex(cwa_session->card_challenge, 8)); sc_log(ctx, "SM IAS/ECC initialize: current_df_path %s", sc_print_path(&sm_info->current_path_df)); sc_log(ctx, "SM IAS/ECC initialize: CRT_AT reference 0x%X", cwa_session->params.crt_at.refs[0]); 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_log(ctx, "S(%i) %s", offs, sc_dump_hex(buf, offs)); rv = sm_encrypt_des_cbc3(ctx, cwa_keyset->enc, buf, offs, &encrypted, &encrypted_len, 1); LOG_TEST_RET(ctx, rv, "_encrypt_des_cbc3() failed"); sc_log(ctx, "ENCed(%i) %s", encrypted_len, sc_dump_hex(encrypted, encrypted_len)); memcpy(buf, encrypted, encrypted_len); offs = encrypted_len; rv = sm_cwa_get_mac(ctx, cwa_keyset->mac, &icv, buf, offs, &cblock, 1); LOG_TEST_RET(ctx, rv, "sm_ecc_get_mac() failed"); sc_log(ctx, "MACed(%i) %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)); free(encrypted); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } 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]; DES_cblock cblock, icv; unsigned char *encrypted = NULL, edfb_data[0x200], mac_data[0x200]; size_t encrypted_len, edfb_len = 0, mac_len = 0; int rv, offs; LOG_FUNC_CALLED(ctx); sc_log(ctx, "securize APDU (cla:%X,ins:%X,p1:%X,p2:%X,data(%i):%p)", apdu->cla, apdu->ins, apdu->p1, apdu->p2, apdu->datalen, apdu->data); sm_cwa_incr_ssc(session_data); rv = sm_encrypt_des_cbc3(ctx, session_data->session_enc, apdu->data, apdu->datalen, &encrypted, &encrypted_len, 0); LOG_TEST_RET(ctx, rv, "securize APDU: DES CBC3 encryption failed"); sc_log(ctx, "encrypted data (len:%i, %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_log(ctx, "securize APDU: EDFB(len:%i,%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 = offs; sc_log(ctx, "securize APDU: MAC data(len:%i,%s)", mac_len, sc_dump_hex(mac_data, mac_len)); memset(icv, 0, sizeof(icv)); rv = sm_cwa_get_mac(ctx, session_data->session_mac, &icv, mac_data, mac_len, &cblock, 0); LOG_TEST_RET(ctx, rv, "securize APDU: MAC calculation error"); sc_log(ctx, "securize APDU: MAC:%s", sc_dump_hex(cblock, sizeof(cblock))); 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_log(ctx, "securize APDU: SM data(len:%i,%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_cwa_incr_ssc(session_data); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } opensc-0.13.0/src/sm/Makefile.in0000644000015201777760000004726112057406056013316 00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program 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. @SET_MAKE@ # Process this file with automake to create Makefile.in VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/sm DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__installdirs = "$(DESTDIR)$(libdir)" LTLIBRARIES = $(lib_LTLIBRARIES) am__DEPENDENCIES_1 = libsmm_local_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ ../libopensc/libopensc.la am_libsmm_local_la_OBJECTS = smm-local.lo sm-common.lo \ sm-global-platform.lo sm-cwa14890.lo sm-card-authentic.lo \ sm-card-iasecc.lo libsmm_local_la_OBJECTS = $(am_libsmm_local_la_OBJECTS) AM_V_lt = $(am__v_lt_$(V)) am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) am__v_lt_0 = --silent libsmm_local_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libsmm_local_la_LDFLAGS) $(LDFLAGS) \ -o $@ DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_$(V)) am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) am__v_CC_0 = @echo " CC " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_$(V)) am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) am__v_CCLD_0 = @echo " CCLD " $@; AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; SOURCES = $(libsmm_local_la_SOURCES) DIST_SOURCES = $(libsmm_local_la_SOURCES) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUG_FILE = @DEBUG_FILE@ DEFAULT_PCSC_PROVIDER = @DEFAULT_PCSC_PROVIDER@ DEFAULT_SM_MODULE = @DEFAULT_SM_MODULE@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GIT = @GIT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBRARY_BITNESS = @LIBRARY_BITNESS@ LIBS = $(top_builddir)/src/libopensc/libopensc.la $(top_builddir)/src/common/libcompat.la LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCT_CFLAGS = @OPENCT_CFLAGS@ OPENCT_LIBS = @OPENCT_LIBS@ OPENSC_LT_AGE = @OPENSC_LT_AGE@ OPENSC_LT_CURRENT = @OPENSC_LT_CURRENT@ OPENSC_LT_OLDEST = @OPENSC_LT_OLDEST@ OPENSC_LT_REVISION = @OPENSC_LT_REVISION@ OPENSC_VERSION_FIX = @OPENSC_VERSION_FIX@ OPENSC_VERSION_MAJOR = @OPENSC_VERSION_MAJOR@ OPENSC_VERSION_MINOR = @OPENSC_VERSION_MINOR@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OPTIONAL_OPENCT_CFLAGS = @OPTIONAL_OPENCT_CFLAGS@ OPTIONAL_OPENCT_LIBS = @OPTIONAL_OPENCT_LIBS@ OPTIONAL_OPENSSL_CFLAGS = @OPTIONAL_OPENSSL_CFLAGS@ OPTIONAL_OPENSSL_LIBS = @OPTIONAL_OPENSSL_LIBS@ OPTIONAL_PCSC_CFLAGS = @OPTIONAL_PCSC_CFLAGS@ OPTIONAL_READLINE_CFLAGS = @OPTIONAL_READLINE_CFLAGS@ OPTIONAL_READLINE_LIBS = @OPTIONAL_READLINE_LIBS@ OPTIONAL_ZLIB_CFLAGS = @OPTIONAL_ZLIB_CFLAGS@ OPTIONAL_ZLIB_LIBS = @OPTIONAL_ZLIB_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCSC_CFLAGS = @PCSC_CFLAGS@ PCSC_LIBS = @PCSC_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RC = @RC@ READLINE_CFLAGS = @READLINE_CFLAGS@ READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ WIN_LIBPREFIX = @WIN_LIBPREFIX@ XSLTPROC = @XSLTPROC@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ git = @git@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkcs11dir = @pkcs11dir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ xslstylesheetsdir = @xslstylesheetsdir@ MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = Makefile.mak AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_READLINE_CFLAGS) AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/common -I$(top_builddir)/src/include lib_LTLIBRARIES = libsmm-local.la libsmm_local_la_SOURCES = smm-local.c sm-module.h sm-common.c sm-global-platform.c sm-cwa14890.c \ sm-card-authentic.c sm-card-iasecc.c \ smm-local.exports libsmm_local_la_LIBADD = $(OPTIONAL_OPENSSL_LIBS) ../libopensc/libopensc.la libsmm_local_la_LDFLAGS = -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@ all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/sm/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/sm/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ test "$$dir" != "$$p" || dir=.; \ echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done libsmm-local.la: $(libsmm_local_la_OBJECTS) $(libsmm_local_la_DEPENDENCIES) $(AM_V_CCLD)$(libsmm_local_la_LINK) -rpath $(libdir) $(libsmm_local_la_OBJECTS) $(libsmm_local_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm-card-authentic.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm-card-iasecc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm-common.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm-cwa14890.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm-global-platform.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smm-local.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: CTAGS CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) installdirs: for dir in "$(DESTDIR)$(libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-libLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-libLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ clean-libLTLIBRARIES clean-libtool ctags distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am \ install-libLTLIBRARIES install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags uninstall uninstall-am uninstall-libLTLIBRARIES # noinst_HEADERS = sm.h # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: opensc-0.13.0/src/sm/smm-local.c0000644000015201777760000002377412057406034013300 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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_log(ctx, "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_log(ctx, "SM get KMC: hex to bin failed for '%s'; error %i", kmc, rv); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } sc_log(ctx, "SM type:%X, KMC(%i) %s", sm_info->sm_type, hex_len, sc_dump_hex(hex, hex_len)); if (hex_len != 16 && hex_len != 48 ) return SC_ERROR_INVALID_DATA; memcpy(gp_keyset->kmc, hex, hex_len); gp_keyset->kmc_len = hex_len; 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_log(ctx, "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_log(ctx, "No %s value in OpenSC config", name); return SC_ERROR_SM_KEYSET_NOT_FOUND; } sc_log(ctx, "keyset::enc(%i) %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_log(ctx, "SM get %s: hex to bin failed for '%s'; error %i", name, value, rv); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } sc_log(ctx, "ENC(%i) %s", hex_len, sc_dump_hex(hex, hex_len)); if (hex_len != 16) return SC_ERROR_INVALID_DATA; memcpy(cwa_keyset->enc, hex, hex_len); } sc_log(ctx, "%s %s", name, sc_dump_hex(cwa_keyset->enc, 16)); /* 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_log(ctx, "No %s value in OpenSC config", name); return SC_ERROR_SM_KEYSET_NOT_FOUND; } sc_log(ctx, "keyset::mac(%i) %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_log(ctx, "SM get '%s': hex to bin failed for '%s'; error %i", name, value, rv); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } sc_log(ctx, "MAC(%i) %s", hex_len, sc_dump_hex(hex, hex_len)); if (hex_len != 16) return SC_ERROR_INVALID_DATA; memcpy(cwa_keyset->mac, hex, hex_len); } sc_log(ctx, "%s %s", name, sc_dump_hex(cwa_keyset->mac, 16)); 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_log(ctx, "SM get 'ifd_serial': hex to bin failed for '%s'; error %i", value, rv); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } if (hex_len != sizeof(cwa_session->ifd.sn)) { sc_log(ctx, "SM get 'ifd_serial': invalid IFD serial length: %i", hex_len); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } memcpy(cwa_session->ifd.sn, hex, hex_len); rv = RAND_bytes(cwa_session->ifd.rnd, 8); if (!rv) { sc_log(ctx, "Generate random error: %i", rv); return SC_ERROR_SM_RAND_FAILED; } rv = RAND_bytes(cwa_session->ifd.k, 32); if (!rv) { sc_log(ctx, "Generate random error: %i", rv); return SC_ERROR_SM_RAND_FAILED; } sc_log(ctx, "IFD.Serial: %s", sc_dump_hex(cwa_session->ifd.sn, sizeof(cwa_session->ifd.sn))); sc_log(ctx, "IFD.Rnd: %s", sc_dump_hex(cwa_session->ifd.rnd, sizeof(cwa_session->ifd.rnd))); sc_log(ctx, "IFD.K: %s", sc_dump_hex(cwa_session->ifd.k, sizeof(cwa_session->ifd.k))); 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_log(ctx, "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_log(ctx, "SM get APDUs: out:%p", out); sc_log(ctx, "SM get APDUs: serial %s", sc_dump_hex(sm_info->serialnr.value, sm_info->serialnr.len)); 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; LOG_FUNC_CALLED(ctx); sc_log(ctx, "SM finalize: out buffer(%i) %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) { sc_log(ctx, "Module init data '%s'", data); return SC_SUCCESS; } /** * Module CleanUp * * Module specific cleanup */ int module_cleanup(struct sc_context *ctx) { sc_log(ctx, "Module cleanup: TODO"); return SC_SUCCESS; } int test(struct sc_context *ctx, struct sm_info *info, char *out, size_t *out_len) { sc_log(ctx, "Test"); return SC_SUCCESS; } opensc-0.13.0/src/sm/sm-card-iasecc.c0000644000015201777760000006100712057406034014156 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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" 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_log(ctx, "SM get 'READ BINARY' APDUs: offset:%i,size:%i", cmd_data->offs, cmd_data->count); offs = cmd_data->offs; while (cmd_data->count > data_offs) { int sz = (cmd_data->count - data_offs) > SM_MAX_DATA_SIZE ? SM_MAX_DATA_SIZE : (cmd_data->count - data_offs); 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_log(ctx, "SM get 'UPDATE BINARY' APDUs: offset:%i,size:%i", cmd_data->offs, cmd_data->count); offs = cmd_data->offs; while (data_offs < cmd_data->count) { int sz = (cmd_data->count - data_offs) > SM_MAX_DATA_SIZE ? SM_MAX_DATA_SIZE : (cmd_data->count - data_offs); 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); sc_log(ctx, "SM get 'CREATE FILE' APDU: FCP(%i) %s", cmd_data->size, sc_dump_hex(cmd_data->data,cmd_data->size)); 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); 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)sm_info->cmd_data; struct sc_remote_apdu *rapdu = NULL; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "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); sc_log(ctx, "SM get 'VERIFY PIN' APDU: ", pin_data->pin_reference); if (!pin_data) 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 '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: invelid 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); sc_log(ctx, "SM get 'RESET PIN' APDU; reference %i", pin_data->pin_reference); if (!pin_data) 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 '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_log(ctx, "SM get 'SDO UPDATE' APDU, SDO(class:0x%X,ref:%i)", update->sdo_class, update->sdo_ref); for (ii=0; update->fields[ii].tag && ii < IASECC_SDO_TAGS_UPDATE_MAX; ii++) { unsigned char *encoded = NULL; size_t encoded_len, offs; encoded_len = iasecc_sdo_encode_update_field(ctx, update->sdo_class, update->sdo_ref, &update->fields[ii], &encoded); LOG_TEST_RET(ctx, encoded_len, "SM get 'SDO UPDATE' APDU: encode component error"); sc_log(ctx, "SM IAS/ECC get APDUs: encoded component '%s'", sc_dump_hex(encoded, encoded_len)); for (offs = 0; offs < encoded_len; ) { int len = (encoded_len - offs) > SM_MAX_DATA_SIZE ? SM_MAX_DATA_SIZE : (encoded_len - offs); 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_log(ctx, "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_log(ctx, "SM get 'UPDATE RSA' APDU: SDO(class:%X,ref:%X)", cmd_data->update_prv.sdo_class, cmd_data->update_prv.sdo_ref); } if (cmd_data->update_pub.sdo_class) { to_update[ii++] = &cmd_data->update_pub; sc_log(ctx, "SM get 'UPDATE RSA' APDU: SDO(class:%X,ref:%X)", cmd_data->update_pub.sdo_class, cmd_data->update_pub.sdo_ref); } for (jj=0;jj<2 && to_update[jj];jj++) { for (ii=0; to_update[jj]->fields[ii].tag && ii < IASECC_SDO_TAGS_UPDATE_MAX; ii++) { unsigned char *encoded = NULL; size_t encoded_len, offs; sc_log(ctx, "SM IAS/ECC get APDUs: component(num %i:%i) class:%X, ref:%X", jj, ii, to_update[jj]->sdo_class, to_update[jj]->sdo_ref); encoded_len = iasecc_sdo_encode_update_field(ctx, to_update[jj]->sdo_class, to_update[jj]->sdo_ref, &to_update[jj]->fields[ii], &encoded); LOG_TEST_RET(ctx, encoded_len, "SM get 'UPDATE RSA' APDU: cannot encode key component"); sc_log(ctx, "SM IAS/ECC get APDUs: component encoded %s", sc_dump_hex(encoded, encoded_len)); for (offs = 0; offs < encoded_len; ) { int len = (encoded_len - offs) > SM_MAX_DATA_SIZE ? SM_MAX_DATA_SIZE : (encoded_len - offs); 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); } #if 0 static int sm_iasecc_get_apdu_pso_dst(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_apdu **rapdus) { struct sm_info_iasecc_pso_dst *ipd = &sm_info->cmd_params.iasecc_pso_dst; struct sc_remote_apdu *rapdu = NULL; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "SM get 'PSO DST' APDU"); if (ipd->pso_data_len > SM_MAX_DATA_SIZE) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); rv = sc_remote_apdu_allocate(rapdus, &rapdu); LOG_TEST_RET(ctx, rv, "SM get 'PSO HASH' APDU: cannot allocate remote apdu"); rapdu->apdu.cse = SC_APDU_CASE_3_SHORT; rapdu->apdu.cla = 0x00; rapdu->apdu.ins = 0x2A; rapdu->apdu.p1 = 0x90; rapdu->apdu.p2 = 0xA0; memcpy((unsigned char *)rapdu->apdu.data, ipd->pso_data, ipd->pso_data_len); rapdu->apdu.datalen = ipd->pso_data_len; rapdu->apdu.lc = ipd->pso_data_len; rv = sm_iasecc_securize_apdu(ctx, sm_info, rapdu); LOG_TEST_RET(ctx, rv, "SM get 'PSO HASH' APDU: securize error"); rapdu->get_response = 1; rv = sc_remote_apdu_allocate(rapdus, &rapdu); LOG_TEST_RET(ctx, rv, "SM get 'PSO DST' APDU: cannot allocate remote apdu"); rapdu->apdu.cse = SC_APDU_CASE_2_SHORT; rapdu->apdu.cla = 0x00; rapdu->apdu.ins = 0x2A; rapdu->apdu.p1 = 0x9E; rapdu->apdu.p2 = 0x9A; rapdu->apdu.le = ipd->key_size; rv = sm_iasecc_securize_apdu(ctx, sm_info, rapdu); LOG_TEST_RET(ctx, rv, "SM get 'PSO DST' APDU: securize error"); rapdu->get_response = 1; LOG_FUNC_RETURN(ctx, rv); } #endif #if 0 static int sm_iasecc_get_apdu_raw_apdu(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_apdu **rapdus) { struct sc_apdu *apdu = sm_info->cmd_params.raw_apdu.apdu; size_t data_offs, data_len = apdu->datalen; int rv = SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(ctx); sc_log(ctx, "SM get 'RAW APDU' APDU"); data_offs = 0; data_len = apdu->datalen; for (; data_len; ) { int sz = data_len > SM_MAX_DATA_SIZE ? SM_MAX_DATA_SIZE : data_len; struct sc_remote_apdu *rapdu = NULL; rv = sc_remote_apdu_allocate(rapdus, &rapdu); LOG_TEST_RET(ctx, rv, "SM get 'RAW APDU' APDUs: cannot allocate remote apdu"); rapdu->apdu.cse = apdu->cse; rapdu->apdu.cla = apdu->cla | ((data_offs + sz) < data_len ? 0x10 : 0x00); rapdu->apdu.ins = apdu->ins; rapdu->apdu.p1 = apdu->p1; rapdu->apdu.p2 = apdu->p2; memcpy((unsigned char *)rapdu->apdu.data, apdu->data + data_offs, sz); rapdu->apdu.datalen = sz; rapdu->apdu.lc = sz; rv = sm_iasecc_securize_apdu(ctx, sm_info, rapdu); LOG_TEST_RET(ctx, rv, "SM get 'UPDATE BINARY' APDUs: securize error"); rapdu->get_response = 1; data_offs += sz; data_len -= sz; } LOG_FUNC_RETURN(ctx, rv); } #endif 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_log(ctx, "SM IAS/ECC get APDUs: init_len:%i", init_len); sc_log(ctx, "SM IAS/ECC get APDUs: rdata:%p", rdata); sc_log(ctx, "SM IAS/ECC get APDUs: serial %s", sc_dump_hex(sm_info->serialnr.value, sm_info->serialnr.len)); 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_log(ctx, "SKENC %s", sc_dump_hex(cwa_session->session_enc, sizeof(cwa_session->session_enc))); sc_log(ctx, "SKMAC %s", sc_dump_hex(cwa_session->session_mac, sizeof(cwa_session->session_mac))); sc_log(ctx, "SSC %s", sc_dump_hex(cwa_session->ssc, sizeof(cwa_session->ssc))); 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; #if 0 case SM_CMD_PSO_DST: rv = sm_iasecc_get_apdu_pso_dst(ctx, sm_info, &rapdus); LOG_TEST_RET(ctx, rv, "SM IAS/ECC get APDUs: 'PSO DST' failed"); break; case SM_CMD_APDU_RAW: rv = sm_iasecc_get_apdu_raw_apdu(ctx, sm_info, &rapdus); LOG_TEST_RET(ctx, rv, "SM IAS/ECC get APDUs: 'RAW APDU' failed"); break; #endif 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_log(ctx, "IAS/ECC decode answer() rdata length %i, out length %i", 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_log(ctx, "IAS/ECC decode response(%i) %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_log(ctx, "IAS/ECC decode response() SW:%02X%02X, MAC:%s", status[0], status[1], sc_dump_hex(ticket, ticket_len)); if (status[0] != 0x90 || status[1] != 0x00) continue; if (asn1_iasecc_sm_data_object[0].flags & SC_ASN1_PRESENT) { sc_log(ctx, "IAS/ECC decode answer() object present"); 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_log(ctx, "IAS/ECC decrypted data(%i) %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): unsufficient output buffer size"); memcpy(out + offs, decrypted, decrypted_len); offs += decrypted_len; sc_log(ctx, "IAS/ECC decode card answer(s): out_len/offs %i/%i", out_len, offs); } free(decrypted); } } LOG_FUNC_RETURN(ctx, offs); } opensc-0.13.0/src/common/0000755000015201777760000000000012057406120012160 500000000000000opensc-0.13.0/src/common/ChangeLog.compat_getopt0000644000015201777760000000106612057406034016525 000000000000002002-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.13.0/src/common/README.compat_strlcpy0000644000015201777760000000053012057406034016024 00000000000000strncpy() is unsafe since it does not always add a final NUL-byte. OpenBSD developped 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.13.0/src/common/compat_strlcpy.c0000644000015201777760000000324512057406034015317 00000000000000/* $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. */ #include "config.h" #ifndef HAVE_STRLCPY /* empty file if strlcpy is available */ #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.13.0/src/common/compat_getpass.h0000644000015201777760000000017512057406034015271 00000000000000#ifndef __COMPAT_GETPASS_H #define __COMPAT_GETPASS_H #ifndef HAVE_GETPASS char *getpass (const char *prompt); #endif #endif opensc-0.13.0/src/common/libscdl.c0000644000015201777760000000307612057406034013672 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #if HAVE_CONFIG_H #include #endif #include "libscdl.h" #ifdef WIN32 #include void *sc_dlopen(const char *filename) { return (void *)LoadLibrary(filename); } void *sc_dlsym(void *handle, const char *symbol) { return GetProcAddress((HANDLE)handle, symbol); } const char *sc_dlerror() { return "LoadLibrary/GetProcAddress failed"; } int sc_dlclose(void *handle) { return FreeLibrary((HANDLE)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.13.0/src/common/compat_getopt_main.c0000644000015201777760000002427412057406034016132 00000000000000/* * 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(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 (! 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.13.0/src/common/Makefile.mak0000644000015201777760000000070412057406034014314 00000000000000TOPDIR = ..\.. COMMON_OBJECTS = compat_getpass.obj compat_getopt.obj compat_strlcpy.obj compat_strlcat.obj simclist.obj all: common.lib libpkcs11.lib libscdl.lib common.lib: $(COMMON_OBJECTS) lib $(LIBFLAGS) /out:common.lib $(COMMON_OBJECTS) libpkcs11.lib: libpkcs11.obj libscdl.obj lib $(LIBFLAGS) /out:libpkcs11.lib libpkcs11.obj libscdl.lib: libscdl.obj lib $(LIBFLAGS) /out:libscdl.lib libscdl.obj !INCLUDE $(TOPDIR)\win32\Make.rules.mak opensc-0.13.0/src/common/compat_strlcpy.30000644000015201777760000001045312057406034015236 00000000000000.\" $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.13.0/src/common/compat_getopt.h0000644000015201777760000000560412057406034015127 00000000000000/* * 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 #if defined(HAVE_GETOPT_H) && defined(HAVE_GETOPT_LONG) && defined(HAVE_GETOPT_LONG_ONLY) #include #else /* Prevent mingw32 from including an incompatible getopt implementation */ #define __GETOPT_H__ #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 /* HAVE_GETOPT_H && HAVE_GETOPT_LONG && HAVE_GETOPT_LONG_ONLY */ #endif /* MY_GETOPT_H_INCLUDED */ opensc-0.13.0/src/common/README.compat_getopt0000644000015201777760000001255012057406034015633 00000000000000my_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 noticable bugs in several versions of the GNU implementations, and because the GNU versions aren't always available on some systems (*BSD, for example.) Other systems don't include any sort of standard argument parser (Win32 with Microsoft tools, for example, has no getopt.) These should do all the expected Unix- and GNU-style argument parsing, including permution, bunching, long options with single or 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 permution, a '-' prefix to the short option specification returns 1 for non-options, ':' after a short option indicates a required argument, and '::' after a short option specification indicates an optional argument (which must appear in the same word.) If you'd like to recieve ':' instead of '?' for missing option arguments, prefix the 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.13.0/src/common/compat_getopt.c0000644000015201777760000002146612057406034015126 00000000000000/* * 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. */ #include "config.h" #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 permution 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 = *shortopts; 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.13.0/src/common/compat_getopt.txt0000644000015201777760000002263012057406034015515 00000000000000 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.13.0/src/common/compat_dummy.c0000644000015201777760000000016212057406034014745 00000000000000/* Required so ld will not complain regarding empty lib */ void compat_dummy (void); void compat_dummy (void) { } opensc-0.13.0/src/common/compat_strlcat.c0000644000015201777760000000462012057406034015271 00000000000000/* $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. */ #include "config.h" #ifndef HAVE_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.13.0/src/common/simclist.c0000644000015201777760000013327512057406034014112 00000000000000/* * 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 #include /* for gettimeofday() */ #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 #define NDEBUG #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" /* minumum 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 *restrict l, struct list_entry_s *tmp, unsigned int pos); /* set default values for initialized lists */ static int list_attributes_setdefaults(list_t *restrict l); #ifndef NDEBUG /* check whether the list internal REPresentation is valid -- Costs O(n) */ static int list_repOk(const list_t *restrict l); /* check whether the list attribute set is valid -- Costs O(1) */ static int list_attrOk(const list_t *restrict l); #endif /* do not inline, this is recursive */ static void list_sort_quicksort(list_t *restrict l, int versus, unsigned int first, struct list_entry_s *fel, unsigned int last, struct list_entry_s *lel); static inline void list_sort_selectionsort(list_t *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 *restrict l, int versus); static inline struct list_entry_s *list_findpos(const list_t *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;*/ \ 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 randno 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 inline void seed_random() { if (random_seed == 0) random_seed = (unsigned)getpid() ^ (unsigned)time(NULL); } static 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 *restrict l) { if (l == NULL) return -1; 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)); 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->spareels = (struct list_entry_s **)malloc(SIMCLIST_MAX_SPARE_ELEMS * sizeof(struct list_entry_s *)); l->spareelsnum = 0; #ifdef SIMCLIST_WITH_THREADS l->threadcount = 0; #endif list_attributes_setdefaults(l); assert(list_repOk(l)); assert(list_attrOk(l)); return 0; } void list_destroy(list_t *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 *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 *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 *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 *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 *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 *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 *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 *restrict l, const void *data) { return list_insert_at(l, data, l->numels); } int list_prepend(list_t *restrict l, const void *data) { return list_insert_at(l, data, 0); } void *list_fetch(list_t *restrict l) { return list_extract_at(l, 0); } void *list_get_at(const list_t *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 *restrict l) { return list_get_minmax(l, +1); } void *list_get_min(const list_t *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 *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 inline struct list_entry_s *list_findpos(const list_t *restrict l, int posstart) { struct list_entry_s *ptr; float x; int i; /* accept 1 slot overflow for fetching head and tail sentinels */ if (posstart < -1 || posstart > (int)l->numels) return NULL; x = (float)(posstart+1) / l->numels; 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 *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); 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 *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); memcpy(lent->data, data, datalen); } else { lent->data = (void*)data; } /* actually append element */ prec = list_findpos(l, pos-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 *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 *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 *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 */ 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 *restrict l) { struct list_entry_s *s; if (l->iter_active) return -1; 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 *restrict l) { return l->numels; } int list_empty(const list_t *restrict l) { return (l->numels == 0); } int list_locate(const list_t *restrict l, const void *data) { struct list_entry_s *el; int pos = 0; 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 *restrict l, const void *indicator) { const struct list_entry_s *iter; if (l->attrs.seeker == 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 *restrict l, const void *data) { return (list_locate(l, data) >= 0); } int list_concat(const list_t *l1, const list_t *l2, list_t *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; list_init(dest); 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)); 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)); 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; cnt < (unsigned int)err; cnt++) dest->mid = dest->mid->next; } else if (err/2 < 0) { /* correct pos LEFT (err/2)-1 moves */ err = -err/2; for (cnt = 0; cnt < (unsigned int)err; cnt++) dest->mid = dest->mid->prev; } assert(!(list_repOk(l1) && list_repOk(l2)) || list_repOk(dest)); return 0; } int list_sort(list_t *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; 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 *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 inline void list_sort_selectionsort(list_t *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 *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)); 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 *restrict l) { if (l->iter_active) return 0; l->iter_pos = 0; l->iter_active = 1; l->iter_curentry = l->head_sentinel->next; return 1; } void *list_iterator_next(list_t *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 *restrict l) { if (! l->iter_active) return 0; return (l->iter_pos < l->numels); } int list_iterator_stop(list_t *restrict l) { if (! l->iter_active) return 0; l->iter_pos = 0; l->iter_active = 0; return 1; } int list_hash(const list_t *restrict l, list_hash_t *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 *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 *restrict filename, list_dump_info_t *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 *restrict l, int fd, size_t *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 *restrict l, int fd, size_t *restrict len) { struct list_dump_header_s header; unsigned long cnt; void *buf; 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); 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); 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); 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); 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 *restrict l, const char *restrict filename, size_t *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 *restrict l, const char *restrict filename, size_t *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 *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->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 *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 *restrict l) { int ok; ok = (l->attrs.copy_data == 0 || l->attrs.meter != NULL); return ok; } #endif opensc-0.13.0/src/common/libscdl.h0000644000015201777760000000206612057406034013675 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.13.0/src/common/compat_strlcat.h0000644000015201777760000000025012057406034015271 00000000000000/** * @file * @brief prototypes of strlcpy()/strlcat() imported from OpenBSD */ #ifndef HAVE_STRLCAT size_t strlcat(char *dst, const char *src, size_t siz); #endif opensc-0.13.0/src/common/compat_getpass.c0000644000015201777760000000073712057406034015270 00000000000000#include "config.h" #ifndef HAVE_GETPASS /* empty file if getpass is available */ #include #include "compat_getpass.h" #ifdef _WIN32 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; } #else #error Need getpass implementation #endif #endif /* HAVE_GETPASS */ opensc-0.13.0/src/common/compat_strlcpy.h0000644000015201777760000000056212057406034015323 00000000000000/* * MUSCLE SmartCard Development ( http://www.linuxnet.com ) * * Copyright (C) 2004 * Ludovic Rousseau * * $Id: strlcpycat.h 1421 2005-04-12 12:09:21Z rousseau $ */ /** * @file * @brief prototypes of strlcpy()/strlcat() imported from OpenBSD */ #ifndef HAVE_STRLCPY size_t strlcpy(char *dst, const char *src, size_t siz); #endif opensc-0.13.0/src/common/Makefile.am0000644000015201777760000000132212057406034014136 00000000000000MAINTAINERCLEANFILES = $(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 AM_CPPFLAGS = -I$(top_srcdir)/src libcompat_la_SOURCES = \ compat_dummy.c \ compat_strlcat.h compat_strlcat.c \ compat_strlcpy.h compat_strlcpy.c \ compat_getpass.h compat_getpass.c \ compat_getopt.h compat_getopt.c \ simclist.c simclist.h compat_getopt_main_LDADD = libcompat.la libpkcs11_la_SOURCES = libpkcs11.c libpkcs11.h libscdl_la_SOURCES = libscdl.c libscdl.h opensc-0.13.0/src/common/simclist.h0000644000015201777760000007504112057406034014113 00000000000000/* * 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 /* Be friend of both C90 and C99 compilers */ #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* "inline" and "restrict" are keywords */ #else # define inline /* inline */ # define 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 responsability 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 responsability of the function to handle possible NULL values in any * argument. */ typedef int (*element_seeker)(const void *el, const void *indicator); /** * an element lenght meter. * * An element meter is a function that: * -# receives the reference to an element el * -# returns its size in bytes * * It is responsability 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 responsability 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 responsability 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 *restrict el, uint32_t *restrict serializ_len); /** * 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 *restrict data, uint32_t *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 *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 *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 *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 *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 responsability 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 *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 *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 *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 *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 *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 *restrict l, const void *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 *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 *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 *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 *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 *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 *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 occurence 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 *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 *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 *restrict l, unsigned int posstart, unsigned int posend); /** * clear all the elements off of the list. * * The element datums 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 *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 *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 *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 *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 *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 *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 threated 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 *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 *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 *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 *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 *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 *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 *restrict l, list_hash_t *restrict hash); #ifdef SIMCLIST_DUMPRESTORE /** * get meta informations 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 *restrict info); /** * get meta informations 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 *restrict filename, list_dump_info_t *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 *restrict l, int fd, size_t *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 *restrict l, const char *restrict filename, size_t *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 *restrict l, int fd, size_t *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 *restrict l, const char *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.13.0/src/common/libpkcs11.c0000644000015201777760000000337412057406034014050 00000000000000/* * Convenience pkcs11 library that can be linked into an application, * and will bind to a specific pkcs11 module. * * Copyright (C) 2002 Olaf Kirch */ #include "config.h" #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); mod = calloc(1, sizeof(*mod)); mod->_magic = MAGIC; if (mspec == NULL) return NULL; mod->handle = sc_dlopen(mspec); if (mod->handle == NULL) { fprintf(stderr, "sc_dlopen failed: %s\n", sc_dlerror()); goto failed; } /* 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); failed: C_UnloadModule((void *) 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 (sc_dlclose(mod->handle) < 0) return CKR_FUNCTION_FAILED; memset(mod, 0, sizeof(*mod)); free(mod); return CKR_OK; } opensc-0.13.0/src/common/Makefile.in0000644000015201777760000004561712057406055014171 00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program 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. @SET_MAKE@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = compat_getopt_main$(EXEEXT) subdir = src/common DIST_COMMON = $(dist_noinst_DATA) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libcompat_la_LIBADD = am_libcompat_la_OBJECTS = compat_dummy.lo compat_strlcat.lo \ compat_strlcpy.lo compat_getpass.lo compat_getopt.lo \ simclist.lo libcompat_la_OBJECTS = $(am_libcompat_la_OBJECTS) AM_V_lt = $(am__v_lt_$(V)) am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) am__v_lt_0 = --silent libpkcs11_la_LIBADD = am_libpkcs11_la_OBJECTS = libpkcs11.lo libpkcs11_la_OBJECTS = $(am_libpkcs11_la_OBJECTS) libscdl_la_LIBADD = am_libscdl_la_OBJECTS = libscdl.lo libscdl_la_OBJECTS = $(am_libscdl_la_OBJECTS) PROGRAMS = $(noinst_PROGRAMS) compat_getopt_main_SOURCES = compat_getopt_main.c compat_getopt_main_OBJECTS = compat_getopt_main.$(OBJEXT) compat_getopt_main_DEPENDENCIES = libcompat.la DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_$(V)) am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) am__v_CC_0 = @echo " CC " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_$(V)) am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) am__v_CCLD_0 = @echo " CCLD " $@; AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; SOURCES = $(libcompat_la_SOURCES) $(libpkcs11_la_SOURCES) \ $(libscdl_la_SOURCES) compat_getopt_main.c DIST_SOURCES = $(libcompat_la_SOURCES) $(libpkcs11_la_SOURCES) \ $(libscdl_la_SOURCES) compat_getopt_main.c DATA = $(dist_noinst_DATA) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUG_FILE = @DEBUG_FILE@ DEFAULT_PCSC_PROVIDER = @DEFAULT_PCSC_PROVIDER@ DEFAULT_SM_MODULE = @DEFAULT_SM_MODULE@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GIT = @GIT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBRARY_BITNESS = @LIBRARY_BITNESS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCT_CFLAGS = @OPENCT_CFLAGS@ OPENCT_LIBS = @OPENCT_LIBS@ OPENSC_LT_AGE = @OPENSC_LT_AGE@ OPENSC_LT_CURRENT = @OPENSC_LT_CURRENT@ OPENSC_LT_OLDEST = @OPENSC_LT_OLDEST@ OPENSC_LT_REVISION = @OPENSC_LT_REVISION@ OPENSC_VERSION_FIX = @OPENSC_VERSION_FIX@ OPENSC_VERSION_MAJOR = @OPENSC_VERSION_MAJOR@ OPENSC_VERSION_MINOR = @OPENSC_VERSION_MINOR@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OPTIONAL_OPENCT_CFLAGS = @OPTIONAL_OPENCT_CFLAGS@ OPTIONAL_OPENCT_LIBS = @OPTIONAL_OPENCT_LIBS@ OPTIONAL_OPENSSL_CFLAGS = @OPTIONAL_OPENSSL_CFLAGS@ OPTIONAL_OPENSSL_LIBS = @OPTIONAL_OPENSSL_LIBS@ OPTIONAL_PCSC_CFLAGS = @OPTIONAL_PCSC_CFLAGS@ OPTIONAL_READLINE_CFLAGS = @OPTIONAL_READLINE_CFLAGS@ OPTIONAL_READLINE_LIBS = @OPTIONAL_READLINE_LIBS@ OPTIONAL_ZLIB_CFLAGS = @OPTIONAL_ZLIB_CFLAGS@ OPTIONAL_ZLIB_LIBS = @OPTIONAL_ZLIB_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCSC_CFLAGS = @PCSC_CFLAGS@ PCSC_LIBS = @PCSC_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RC = @RC@ READLINE_CFLAGS = @READLINE_CFLAGS@ READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ WIN_LIBPREFIX = @WIN_LIBPREFIX@ XSLTPROC = @XSLTPROC@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ git = @git@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkcs11dir = @pkcs11dir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ xslstylesheetsdir = @xslstylesheetsdir@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak noinst_LTLIBRARIES = libcompat.la libpkcs11.la libscdl.la 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 AM_CPPFLAGS = -I$(top_srcdir)/src libcompat_la_SOURCES = \ compat_dummy.c \ compat_strlcat.h compat_strlcat.c \ compat_strlcpy.h compat_strlcpy.c \ compat_getpass.h compat_getpass.c \ compat_getopt.h compat_getopt.c \ simclist.c simclist.h compat_getopt_main_LDADD = libcompat.la libpkcs11_la_SOURCES = libpkcs11.c libpkcs11.h libscdl_la_SOURCES = libscdl.c libscdl.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/common/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/common/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ test "$$dir" != "$$p" || dir=.; \ echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done libcompat.la: $(libcompat_la_OBJECTS) $(libcompat_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libcompat_la_OBJECTS) $(libcompat_la_LIBADD) $(LIBS) libpkcs11.la: $(libpkcs11_la_OBJECTS) $(libpkcs11_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libpkcs11_la_OBJECTS) $(libpkcs11_la_LIBADD) $(LIBS) libscdl.la: $(libscdl_la_OBJECTS) $(libscdl_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libscdl_la_OBJECTS) $(libscdl_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list compat_getopt_main$(EXEEXT): $(compat_getopt_main_OBJECTS) $(compat_getopt_main_DEPENDENCIES) @rm -f compat_getopt_main$(EXEEXT) $(AM_V_CCLD)$(LINK) $(compat_getopt_main_OBJECTS) $(compat_getopt_main_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compat_dummy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compat_getopt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compat_getopt_main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compat_getpass.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compat_strlcat.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compat_strlcpy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpkcs11.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libscdl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/simclist.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: CTAGS CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(DATA) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ ctags distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags uninstall uninstall-am # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: opensc-0.13.0/src/common/libpkcs11.h0000644000015201777760000000173612057406034014055 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ void *C_LoadModule(const char *name, CK_FUNCTION_LIST_PTR_PTR); CK_RV C_UnloadModule(void *module); opensc-0.13.0/src/common/LICENSE.compat_getopt0000644000015201777760000000212612057406034015756 00000000000000compat_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.13.0/src/Makefile.mak0000644000015201777760000000061412057406034013024 00000000000000TOPDIR = .. !INCLUDE $(TOPDIR)\win32\Make.rules.mak SUBDIRS = common scconf pkcs15init libopensc pkcs11 tools tests !IF "$(MINIDRIVER_DEF)" == "/DENABLE_MINIDRIVER" SUBDIRS = $(SUBDIRS) minidriver !ENDIF !IF "$(SM_DEF)" == "/DENABLE_SM" SUBDIRS = $(SUBDIRS) sm !ENDIF all:: all depend install clean:: @for %i in ( $(SUBDIRS) ) do \ @cmd /c "cd %i && $(MAKE) /nologo /f Makefile.mak $@" opensc-0.13.0/src/pkcs15init/0000755000015201777760000000000012057406120012662 500000000000000opensc-0.13.0/src/pkcs15init/pkcs15-gpk.c0000644000015201777760000007506612057406034014655 00000000000000/* * GPK specific operation for PKCS15 initialization * * Copyright (C) 2002 Olaf Kirch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/cards.h" #include "libopensc/log.h" #include "pkcs15-init.h" #include "profile.h" /* this could be removed once we include libopensc/internal.h */ #ifndef _WIN32 #define msleep(t) usleep((t) * 1000) #else #include #define msleep(t) Sleep(t) #define sleep(t) Sleep((t) * 1000) #endif #define PK_INIT_IMMEDIATELY #define GPK_MAX_PINS 8 #define GPK_PIN_SCOPE 8 #define GPK_FTYPE_SECRET_CODE 0x21 #define GPK_FTYPE_PUBLIC_KEY 0x2C /* * Key components (for storing private keys) */ struct pkcomp { unsigned char tag; u8 * data; unsigned int size; }; struct pkpart { struct pkcomp components[7]; unsigned int count; unsigned int size; }; struct pkdata { unsigned int algo; unsigned int usage; struct pkpart _public, _private; unsigned int bits, bytes; }; /* * Local functions */ static int gpk_pkfile_create(sc_profile_t *, sc_pkcs15_card_t *, sc_file_t *); static int gpk_encode_rsa_key(sc_profile_t *, sc_card_t *, struct sc_pkcs15_prkey_rsa *, struct pkdata *, struct sc_pkcs15_prkey_info *); static int gpk_encode_dsa_key(sc_profile_t *, sc_card_t *, struct sc_pkcs15_prkey_dsa *, struct pkdata *, struct sc_pkcs15_prkey_info *); static int gpk_store_pk(struct sc_profile *, sc_pkcs15_card_t *, sc_file_t *, struct pkdata *); static int gpk_init_pinfile(sc_profile_t *, sc_pkcs15_card_t *, sc_file_t *); static int gpk_pkfile_init_public(sc_profile_t *, sc_pkcs15_card_t *, sc_file_t *, unsigned int, unsigned int, unsigned int); static int gpk_pkfile_init_private(sc_card_t *, sc_file_t *, unsigned int); static int gpk_read_rsa_key(sc_card_t *, struct sc_pkcs15_pubkey_rsa *); /* * Erase the card */ static int gpk_erase_card(struct sc_profile *pro, sc_pkcs15_card_t *p15card) { int locked; if (sc_card_ctl(p15card->card, SC_CARDCTL_GPK_IS_LOCKED, &locked) == 0 && locked) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "This card is already personalized, unable to " "create PKCS#15 structure."); return SC_ERROR_NOT_SUPPORTED; } return sc_card_ctl(p15card->card, SC_CARDCTL_ERASE_CARD, NULL); } /* * Create a new DF * This will usually be the application DF */ static int gpk_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { struct sc_file *pinfile; int r, locked; SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); if (sc_card_ctl(p15card->card, SC_CARDCTL_GPK_IS_LOCKED, &locked) == 0 && locked) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "This card is already personalized, unable to " "create PKCS#15 structure."); return SC_ERROR_NOT_SUPPORTED; } /* Create the DF. */ r = sc_pkcs15init_create_file(profile, p15card, df); if (r < 0) return r; /* See if there's a file called "pinfile" that resides within * this DF. If so, create it */ if (sc_profile_get_file(profile, "pinfile", &pinfile) >= 0) { /* Build the pin file's path from the DF path + its * file ID */ pinfile->path = df->path; sc_append_file_id(&pinfile->path, pinfile->id); r = gpk_init_pinfile(profile, p15card, pinfile); sc_file_free(pinfile); if (r < 0) return r; /* TODO: What for it was used ? for (i = 0; i < GPK_MAX_PINS; i++) * sc_keycache_put_pin(&df->path, GPK_PIN_SCOPE|i, (const u8 *) " "); */ } SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r); } /* * Select a PIN reference */ static int gpk_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_auth_info_t *auth_info) { int preferred, current; SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; if ((current = auth_info->attrs.pin.reference) < 0) current = 0; if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) { preferred = GPK_PIN_SCOPE | 0; } else { preferred = current | GPK_PIN_SCOPE; if (preferred & 1) preferred++; if (preferred < (GPK_PIN_SCOPE | 2)) preferred = GPK_PIN_SCOPE | 2; if (preferred > 15) return SC_ERROR_TOO_MANY_OBJECTS; } if (current > preferred) return SC_ERROR_TOO_MANY_OBJECTS; auth_info->attrs.pin.reference = preferred; SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, 0); } /* * Store a PIN */ static int gpk_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df, sc_pkcs15_object_t *pin_obj, const u8 *pin, size_t pin_len, const u8 *puk, size_t puk_len) { sc_pkcs15_auth_info_t *auth_info = (sc_pkcs15_auth_info_t *) pin_obj->data; struct sc_pkcs15_pin_attributes *pin_attrs = &auth_info->attrs.pin; u8 nulpin[8]; int r; SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) { /* SO PIN reference must be 0 */ if (pin_attrs->reference != (GPK_PIN_SCOPE | 0)) return SC_ERROR_INVALID_ARGUMENTS; } else { /* PIN references must be even numbers * (the odd numbered PIN entries contain the * PUKs). * Returning SC_ERROR_INVALID_PIN_REFERENCE will * tell the caller to pick a different value. */ if ((pin_attrs->reference & 1) || !(pin_attrs->reference & GPK_PIN_SCOPE)) return SC_ERROR_INVALID_PIN_REFERENCE; if (pin_attrs->reference >= (GPK_PIN_SCOPE + GPK_MAX_PINS)) return SC_ERROR_TOO_MANY_OBJECTS; } /* No PUK given, but the PIN file specifies an unblock * PIN for every PIN. * Use the same value for the PUK for now. * Alternatively, we could leave the unblock PIN at the default * value, but deliberately block it. */ if (puk == NULL || puk_len == 0) { puk = pin; puk_len = pin_len; } r = sc_select_file(p15card->card, &df->path, NULL); sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "select df path: %i", r); if (r < 0) return r; /* Current PIN is 00:00:00:00:00:00:00:00 */ memset(nulpin, 0, sizeof(nulpin)); r = sc_change_reference_data(p15card->card, SC_AC_CHV, pin_attrs->reference, nulpin, sizeof(nulpin), pin, pin_len, NULL); sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "change CHV %i", r); if (r < 0) return r; /* Current PUK is 00:00:00:00:00:00:00:00 */ r = sc_change_reference_data(p15card->card, SC_AC_CHV, pin_attrs->reference + 1, nulpin, sizeof(nulpin), puk, puk_len, NULL); sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "change CHV+1 %i", r); if (r < 0) return r; SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r); } /* * Lock a file operation */ static int gpk_lock(sc_card_t *card, sc_file_t *file, unsigned int op) { struct sc_cardctl_gpk_lock args; args.file = file; args.operation = op; return sc_card_ctl(card, SC_CARDCTL_GPK_LOCK, &args); } /* * Lock the pin file */ static int gpk_lock_pinfile(struct sc_profile *profile, sc_pkcs15_card_t *p15card, sc_file_t *pinfile) { struct sc_path path; struct sc_file *parent = NULL; int r; SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); /* Select the parent DF */ path = pinfile->path; if (path.len >= 2) path.len -= 2; if (path.len == 0) sc_format_path("3F00", &path); if ((r = sc_select_file(p15card->card, &path, &parent)) < 0) return r; /* Present PINs etc as necessary */ r = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_LOCK); if (r >= 0) r = gpk_lock(p15card->card, pinfile, SC_AC_OP_WRITE); sc_file_free(parent); SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r); } /* * Initialize pin file */ static int gpk_init_pinfile(struct sc_profile *profile, sc_pkcs15_card_t *p15card, sc_file_t *file) { const sc_acl_entry_t *acl; unsigned char buffer[GPK_MAX_PINS * 8], *blk; struct sc_file *pinfile; unsigned int so_attempts[2], user_attempts[2]; unsigned int npins, i, j, cks; int r; SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); /* Set defaults */ so_attempts[0] = sc_profile_get_pin_retries(profile, SC_PKCS15INIT_SO_PIN); so_attempts[1] = sc_profile_get_pin_retries(profile, SC_PKCS15INIT_SO_PUK); user_attempts[0] = sc_profile_get_pin_retries(profile, SC_PKCS15INIT_USER_PIN); user_attempts[1] = sc_profile_get_pin_retries(profile, SC_PKCS15INIT_USER_PUK); sc_file_dup(&pinfile, file); if (pinfile == NULL) return SC_ERROR_OUT_OF_MEMORY; /* Create the PIN file. */ acl = sc_file_get_acl_entry(pinfile, SC_AC_OP_WRITE); if (acl->method != SC_AC_NEVER) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "PIN file most be protected by WRITE=NEVER"); sc_file_free(pinfile); return SC_ERROR_INVALID_ARGUMENTS; } sc_file_add_acl_entry(pinfile, SC_AC_OP_WRITE, SC_AC_NONE, 0); if (pinfile->size == 0) pinfile->size = GPK_MAX_PINS * 8; sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Now create file"); /* Now create the file */ if ((r = sc_pkcs15init_create_file(profile, p15card, pinfile)) < 0 || (r = sc_select_file(p15card->card, &pinfile->path, NULL)) < 0) { goto out; } /* Set up the PIN file contents. * We assume the file will contain pairs of PINs/PUKs */ npins = pinfile->size / 8; memset(buffer, 0, sizeof(buffer)); for (i = 0, blk = buffer; i < npins; blk += 8, i += 1) { /* Determine the number of PIN/PUK presentation * attempts. If the profile defines a SO PIN, * it will be stored in the first PIN/PUK pair. */ blk[0] = user_attempts[i & 1]; if (i < 2 && so_attempts[0]) blk[0] = so_attempts[i & 1]; if ((i & 1) == 0) { /* This is a PIN. If there's room in the file, * the next will be a PUK so take note of the * unlock code */ if (i + 1 < npins) blk[2] = GPK_PIN_SCOPE | (i + 1); } /* Compute the CKS */ for (j = 0, cks = 0; j < 8; j++) cks ^= blk[j]; blk[3] = ~cks; } r = sc_write_binary(p15card->card, 0, buffer, npins * 8, 0); if (r >= 0) r = gpk_lock_pinfile(profile, p15card, pinfile); out: sc_file_free(pinfile); SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r); } /* * Create a key file */ static int gpk_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj) { sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; struct sc_file *keyfile = NULL; size_t bytes, mod_len, exp_len, prv_len, pub_len; int r, algo; /* The caller is supposed to have chosen a key file path for us */ if (key_info->path.len == 0 || key_info->modulus_length == 0) return SC_ERROR_INVALID_ARGUMENTS; /* Get the file we're supposed to create */ r = sc_profile_get_file_by_path(profile, &key_info->path, &keyfile); if (r < 0) return r; /* Compute the file size. * We assume private keys are stored as CRT elements. * - 512, 768 bit keys: all CRT elements fit into one record * - >= 1024: each CRT element into a record of its own * * We also assume the public exponent is 32bit max * * Rules * - private key records must have a length divisible by 8 */ mod_len = key_info->modulus_length / 8; exp_len = 4; bytes = mod_len / 2; pub_len = 8 + ((3 + mod_len + 3 + exp_len + 3) & ~3UL); if (5 * bytes < 256) { prv_len = 8 + ((3 + 5 * bytes + 7) & ~7UL); } else { prv_len = 8 + 5 * ((3 + bytes + 7) & ~7UL); } keyfile->size = pub_len + prv_len; switch (obj->type) { case SC_PKCS15_TYPE_PRKEY_RSA: algo = SC_ALGORITHM_RSA; break; case SC_PKCS15_TYPE_PRKEY_DSA: algo = SC_ALGORITHM_DSA; break; default: sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported public key algorithm"); return SC_ERROR_NOT_SUPPORTED; } /* Fix up PIN references in file ACL and create the PK file */ if ((r = sc_pkcs15init_fixup_file(profile, p15card, keyfile)) < 0 || (r = gpk_pkfile_create(profile, p15card, keyfile)) < 0) goto done; #ifdef PK_INIT_IMMEDIATELY /* Initialize the public key header */ r = gpk_pkfile_init_public(profile, p15card, keyfile, algo, key_info->modulus_length, key_info->usage); if (r < 0) goto done; /* Create the private key portion */ r = gpk_pkfile_init_private(p15card->card, keyfile, prv_len); #endif done: if (keyfile) sc_file_free(keyfile); return r; } /* * Store a private key */ static int gpk_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, struct sc_pkcs15_prkey *key) { sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; struct sc_file *keyfile = NULL; struct pkdata data; int r; /* The caller is supposed to have chosen a key file path for us */ if (key_info->path.len == 0 || key_info->modulus_length == 0) return SC_ERROR_INVALID_ARGUMENTS; /* Get the file we're supposed to create */ r = sc_select_file(p15card->card, &key_info->path, &keyfile); if (r < 0) return r; switch (key->algorithm) { case SC_ALGORITHM_RSA: r = gpk_encode_rsa_key(profile, p15card->card, &key->u.rsa, &data, key_info); break; case SC_ALGORITHM_DSA: r = gpk_encode_dsa_key(profile, p15card->card, &key->u.dsa, &data, key_info); break; default: return SC_ERROR_NOT_SUPPORTED; } if (r >= 0) r = gpk_store_pk(profile, p15card, keyfile, &data); if (keyfile) sc_file_free(keyfile); return r; } /* * On-board key generation. */ static int gpk_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) { struct sc_cardctl_gpk_genkey args; sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; unsigned int keybits; sc_file_t *keyfile; int r, n; sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "path=%s, %d bits\n", sc_print_path(&key_info->path), key_info->modulus_length); if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "GPK supports generating only RSA keys."); return SC_ERROR_NOT_SUPPORTED; } /* The caller is supposed to have chosen a key file path for us */ if (key_info->path.len == 0 || key_info->modulus_length == 0) return SC_ERROR_INVALID_ARGUMENTS; keybits = key_info->modulus_length; if ((r = sc_select_file(p15card->card, &key_info->path, &keyfile)) < 0) return r; #ifndef PK_INIT_IMMEDIATELY r = gpk_pkfile_init_public(profile, p15card, keyfile, SC_ALGORITHM_RSA, keybits, key_info->usage); if (r < 0) { sc_file_free(keyfile); return r; } if ((r = gpk_pkfile_init_private(p15card->card, keyfile, 5 * ((3 + keybits / 16 + 7) & ~7UL))) < 0) { sc_file_free(keyfile); return r; } #endif sc_file_free(keyfile); memset(&args, 0, sizeof(args)); /*args.exponent = 0x10001;*/ n = key_info->path.len; args.fid = (key_info->path.value[n-2] << 8) | key_info->path.value[n-1]; args.privlen = keybits; r = sc_card_ctl(p15card->card, SC_CARDCTL_GPK_GENERATE_KEY, &args); if (r < 0) return r; /* This is fairly weird. The GENERATE RSA KEY command returns * immediately, but obviously it needs more time to complete. * This is why we sleep here. */ sleep(20); pubkey->algorithm = SC_ALGORITHM_RSA; return gpk_read_rsa_key(p15card->card, &pubkey->u.rsa); } /* * GPK public/private key file handling is hideous. * 600 lines of coke sweat and tears... */ /* * Create the PK file * XXX: Handle the UPDATE ACL = NEVER case just like for EFsc files */ static int gpk_pkfile_create(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *file) { struct sc_file *found = NULL; int r; r = sc_select_file(p15card->card, &file->path, &found); if (r == SC_ERROR_FILE_NOT_FOUND) { r = sc_pkcs15init_create_file(profile, p15card, file); if (r >= 0) r = sc_select_file(p15card->card, &file->path, &found); } else { /* XXX: make sure the file has correct type and size? */ } if (r >= 0) r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); if (found) sc_file_free(found); return r; } static int gpk_pkfile_keybits(unsigned int bits, unsigned char *p) { switch (bits) { case 512: *p = 0x00; return 0; case 768: *p = 0x10; return 0; case 1024: *p = 0x11; return 0; } return SC_ERROR_NOT_SUPPORTED; } static int gpk_pkfile_keyalgo(unsigned int algo, unsigned char *p) { switch (algo) { case SC_ALGORITHM_RSA: *p = 0x00; return 0; case SC_ALGORITHM_DSA: *p = 0x01; return 0; } return SC_ERROR_NOT_SUPPORTED; } /* * Set up the public key record for a signature only public key */ static int gpk_pkfile_init_public(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *file, unsigned int algo, unsigned int bits, unsigned int usage) { struct sc_context *ctx = p15card->card->ctx; const sc_acl_entry_t *acl; sc_file_t *tmp = NULL; u8 sysrec[7], buffer[256]; unsigned int n, npins; int r, card_type; /* Find out what sort of GPK we're using */ if ((r = sc_card_ctl(p15card->card, SC_CARDCTL_GPK_VARIANT, &card_type)) < 0) return r; /* Set up the system record */ memset(sysrec, 0, sizeof(sysrec)); /* Mapping keyUsage to sysrec[2]: * 0x00 sign & unwrap * 0x10 sign only * 0x20 unwrap only * 0x30 CA key * * We start with a value of 0x30. * If the key allows decryption, clear the sign only bit. * Likewise, if it allows signing, clear the unwrap only bit. */ sysrec[2] = 0x30; if (usage & (SC_PKCS15_PRKEY_USAGE_DECRYPT|SC_PKCS15_PRKEY_USAGE_UNWRAP)) sysrec[2] &= ~0x10; if (usage & (SC_PKCS15_PRKEY_USAGE_SIGN|SC_PKCS15_PRKEY_USAGE_NONREPUDIATION)) sysrec[2] &= ~0x20; if (sysrec[2] == 0x30) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Key usage should specify at least one of sign or decipher"); return SC_ERROR_INVALID_ARGUMENTS; } /* Set the key size and algorithm */ if ((r = gpk_pkfile_keybits(bits, &sysrec[1])) < 0 || (r = gpk_pkfile_keyalgo(algo, &sysrec[5])) < 0) return r; /* Set PIN protection if requested. * As the crypto ACLs are stored inside the file, * we have to get them from the profile here. */ r = sc_profile_get_file_by_path(profile, &file->path, &tmp); if (r < 0) return r; /* Fix up PIN references in file ACL */ if ((r = sc_pkcs15init_fixup_file(profile, p15card, tmp)) < 0) goto out; acl = sc_file_get_acl_entry(tmp, SC_AC_OP_CRYPTO); for (npins = 0; acl; acl = acl->next) { if (acl->method == SC_AC_NONE || acl->method == SC_AC_NEVER) continue; if (acl->method != SC_AC_CHV) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Authentication method not " "supported for private key files.\n"); r = SC_ERROR_NOT_SUPPORTED; goto out; } if (++npins >= 2) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Too many pins for PrKEY file!\n"); r = SC_ERROR_NOT_SUPPORTED; goto out; } sysrec[2] += 0x40; sysrec[3] >>= 4; sysrec[3] |= acl->key_ref << 4; } /* compute checksum - yet another slightly different * checksum algorithm courtesy of Gemplus */ if (card_type >= SC_CARD_TYPE_GPK_GPK8000) { /* This is according to the gpk reference manual */ sysrec[6] = 0xA5; } else { /* And this is what you have to use for the GPK4000 */ sysrec[6] = 0xFF; } for (n = 0; n < 6; n++) sysrec[6] ^= sysrec[n]; r = sc_read_record(p15card->card, 1, buffer, sizeof(buffer), SC_RECORD_BY_REC_NR); if (r >= 0) { if (r != 7 || buffer[0] != 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "first record of public key file is not Lsys0"); return SC_ERROR_OBJECT_NOT_VALID; } r = sc_update_record(p15card->card, 1, sysrec, sizeof(sysrec), SC_RECORD_BY_REC_NR); } else { r = sc_append_record(p15card->card, sysrec, sizeof(sysrec), 0); } out: if (tmp) sc_file_free(tmp); return r; } static int gpk_pkfile_update_public(struct sc_profile *profile, sc_pkcs15_card_t *p15card, struct pkpart *part) { struct sc_context *ctx = p15card->card->ctx; struct pkcomp *pe; unsigned char buffer[256]; unsigned int m, n, tag; int r = 0, found; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Updating public key elements\n"); /* If we've been given a key with public parts, write them now */ for (n = 2; n < 256; n++) { r = sc_read_record(p15card->card, n, buffer, sizeof(buffer), SC_RECORD_BY_REC_NR); if (r < 0) { r = 0; break; } /* Check for bad record */ if (r < 2) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "key file format error: " "record %u too small (%u bytes)\n", n, r); return SC_ERROR_OBJECT_NOT_VALID; } tag = buffer[0]; for (m = 0, found = 0; m < part->count; m++) { pe = part->components + m; if (pe->tag == tag) { r = sc_update_record(p15card->card, n, pe->data, pe->size, SC_RECORD_BY_REC_NR); if (r < 0) return r; pe->tag = 0; /* mark as stored */ found++; break; } } if (!found) sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "GPK unknown PK tag %u\n", tag); } /* Write all remaining elements */ for (m = 0; r >= 0 && m < part->count; m++) { pe = part->components + m; if (pe->tag != 0) r = sc_append_record(p15card->card, pe->data, pe->size, 0); } return r; } static int gpk_pkfile_init_private(sc_card_t *card, sc_file_t *file, unsigned int privlen) { struct sc_cardctl_gpk_pkinit args; args.file = file; args.privlen = privlen; return sc_card_ctl(card, SC_CARDCTL_GPK_PKINIT, &args); } static int gpk_pkfile_load_private(sc_card_t *card, sc_file_t *file, u8 *data, unsigned int len, unsigned int datalen) { struct sc_cardctl_gpk_pkload args; args.file = file; args.data = data; args.len = len; args.datalen = datalen; return sc_card_ctl(card, SC_CARDCTL_GPK_PKLOAD, &args); } static int gpk_pkfile_update_private(struct sc_profile *profile, sc_pkcs15_card_t *p15card, sc_file_t *file, struct pkpart *part) { unsigned int m, size, nb, cks; struct pkcomp *pe; u8 data[256]; int r = 0; sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Updating private key elements\n"); for (m = 0; m < part->count; m++) { pe = part->components + m; if (pe->size + 8 > sizeof(data)) return SC_ERROR_BUFFER_TOO_SMALL; memcpy(data, pe->data, pe->size); size = pe->size; /* We must set a secure messaging key before each * Load Private Key command. Any key will do... * The GPK _is_ weird. */ r = sc_pkcs15init_verify_secret(profile, p15card, NULL, SC_AC_PRO, 1); if (r < 0) break; /* Pad out data to a multiple of 8 and checksum. * The GPK manual is a bit unclear about whether you * checksum first and then pad, or vice versa. * The following code does seem to work though: */ for (nb = 0, cks = 0xff; nb < size; nb++) cks ^= data[nb]; data[nb++] = cks; while (nb & 7) data[nb++] = 0; r = gpk_pkfile_load_private(p15card->card, file, data, size-1, nb); if (r < 0) break; } return r; } /* Sum up the size of the public key elements * Each element is type + tag + bignum */ static void gpk_compute_publen(struct pkpart *part) { unsigned int n, publen = 8; /* length of sysrec0 */ for (n = 0; n < part->count; n++) publen += 2 + part->components[n].size; part->size = (publen + 3) & ~3UL; } /* Sum up the size of the private key elements * Each element is type + tag + bignum + checksum, padded to a multiple * of eight */ static void gpk_compute_privlen(struct pkpart *part) { unsigned int n, privlen = 8; for (n = 0; n < part->count; n++) privlen += (3 + part->components[n].size + 7) & ~7UL; part->size = privlen; } /* * Convert BIGNUM to GPK representation, optionally zero padding to size. * Note that the bignum's we're given are big-endian, while the GPK * wants them little-endian. */ static void gpk_bn2bin(unsigned char *dest, sc_pkcs15_bignum_t *bn, unsigned int size) { u8 *src; unsigned int n; assert(bn->len <= size); memset(dest, 0, size); for (n = bn->len, src = bn->data; n--; src++) dest[n] = *src; } /* * Add a BIGNUM component, optionally padding out the number to size bytes */ static void gpk_add_bignum(struct pkpart *part, unsigned int tag, sc_pkcs15_bignum_t *bn, size_t size) { struct pkcomp *comp; if (size == 0) size = bn->len; comp = &part->components[part->count++]; memset(comp, 0, sizeof(*comp)); comp->tag = tag; comp->size = size + 1; comp->data = malloc(size + 1); /* Add the tag */ comp->data[0] = tag; /* Add the BIGNUM */ gpk_bn2bin(comp->data + 1, bn, size); /* printf("TAG 0x%02x, len=%u\n", tag, comp->size); */ } static int gpk_encode_rsa_key(sc_profile_t *profile, sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa, struct pkdata *p, sc_pkcs15_prkey_info_t *info) { if (!rsa->modulus.len || !rsa->exponent.len) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "incomplete RSA public key"); return SC_ERROR_INVALID_ARGUMENTS; } /* Make sure the exponent is 0x10001 because that's * the only exponent supported by GPK4000 and GPK8000 */ if (rsa->exponent.len != 3 || memcmp(rsa->exponent.data, "\001\000\001", 3)) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unsupported RSA exponent"); return SC_ERROR_INVALID_ARGUMENTS; } memset(p, 0, sizeof(*p)); p->algo = SC_ALGORITHM_RSA; p->usage = info->usage; p->bytes = rsa->modulus.len; p->bits = p->bytes << 3; /* Set up the list of public elements */ gpk_add_bignum(&p->_public, 0x01, &rsa->modulus, 0); gpk_add_bignum(&p->_public, 0x07, &rsa->exponent, 0); /* Set up the list of private elements */ if (!rsa->p.len || !rsa->q.len || !rsa->dmp1.len || !rsa->dmq1.len || !rsa->iqmp.len) { /* No or incomplete CRT information */ if (!rsa->d.len) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "incomplete RSA private key"); return SC_ERROR_INVALID_ARGUMENTS; } gpk_add_bignum(&p->_private, 0x04, &rsa->d, 0); } else if (5 * (p->bytes / 2) < 256) { /* All CRT elements are stored in one record */ struct pkcomp *comp; unsigned int K = p->bytes / 2; u8 *crtbuf; crtbuf = malloc(5 * K + 1); crtbuf[0] = 0x05; gpk_bn2bin(crtbuf + 1 + 0 * K, &rsa->p, K); gpk_bn2bin(crtbuf + 1 + 1 * K, &rsa->q, K); gpk_bn2bin(crtbuf + 1 + 2 * K, &rsa->iqmp, K); gpk_bn2bin(crtbuf + 1 + 3 * K, &rsa->dmp1, K); gpk_bn2bin(crtbuf + 1 + 4 * K, &rsa->dmq1, K); comp = &p->_private.components[p->_private.count++]; comp->tag = 0x05; comp->size = 5 * K + 1; comp->data = crtbuf; } else { /* CRT elements stored in individual records. * Make sure they're all fixed length even if they're * shorter */ gpk_add_bignum(&p->_private, 0x51, &rsa->p, p->bytes/2); gpk_add_bignum(&p->_private, 0x52, &rsa->q, p->bytes/2); gpk_add_bignum(&p->_private, 0x53, &rsa->iqmp, p->bytes/2); gpk_add_bignum(&p->_private, 0x54, &rsa->dmp1, p->bytes/2); gpk_add_bignum(&p->_private, 0x55, &rsa->dmq1, p->bytes/2); } return 0; } /* * Encode a DSA key. * Confusingly, the GPK manual says that the GPK8000 can handle * DSA with 512 as well as 1024 bits, but all byte sizes shown * in the tables are 512 bits only... */ static int gpk_encode_dsa_key(sc_profile_t *profile, sc_card_t *card, struct sc_pkcs15_prkey_dsa *dsa, struct pkdata *p, sc_pkcs15_prkey_info_t *info) { if (!dsa->p.len || !dsa->q.len || !dsa->g.len || !dsa->pub.len || !dsa->priv.len) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "incomplete DSA public key"); return SC_ERROR_INVALID_ARGUMENTS; } memset(p, 0, sizeof(*p)); p->algo = SC_ALGORITHM_RSA; p->usage = info->usage; p->bytes = dsa->q.len; p->bits = dsa->q.len << 3; /* Make sure the key is either 512 or 1024 bits */ if (p->bytes <= 64) { p->bits = 512; p->bytes = 64; } else if (p->bytes <= 128) { p->bits = 1024; p->bytes = 128; } else { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "incompatible DSA key size (%u bits)", p->bits); return SC_ERROR_INVALID_ARGUMENTS; } /* Set up the list of public elements */ gpk_add_bignum(&p->_public, 0x09, &dsa->p, 0); gpk_add_bignum(&p->_public, 0x0a, &dsa->q, 0); gpk_add_bignum(&p->_public, 0x0b, &dsa->g, 0); gpk_add_bignum(&p->_public, 0x0c, &dsa->pub, 0); /* Set up the list of private elements */ gpk_add_bignum(&p->_private, 0x0d, &dsa->priv, 0); return 0; } static int gpk_store_pk(struct sc_profile *profile, sc_pkcs15_card_t *p15card, sc_file_t *file, struct pkdata *p) { struct sc_context *ctx = p15card->card->ctx; size_t fsize; int r; /* Compute length of private/public key parts */ gpk_compute_publen(&p->_public); gpk_compute_privlen(&p->_private); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Storing pk: %u bits, pub %u bytes, priv %u bytes\n", p->bits, p->_public.size, p->_private.size); fsize = p->_public.size + p->_private.size; if (fsize > file->size) return SC_ERROR_FILE_TOO_SMALL; /* Put the system record */ #ifndef PK_INIT_IMMEDIATELY r = gpk_pkfile_init_public(profile, p15card, file, p->algo, p->bits, p->usage); if (r < 0) return r; #endif /* Put the public key elements */ r = gpk_pkfile_update_public(profile, p15card, &p->_public); if (r < 0) return r; /* Create the private key part */ #ifndef PK_INIT_IMMEDIATELY r = gpk_pkfile_init_private(p15card->card, file, p->_private.size); if (r < 0) return r; #endif /* Now store the private key elements */ r = gpk_pkfile_update_private(profile, p15card, file, &p->_private); return r; } static int gpk_read_rsa_key(sc_card_t *card, struct sc_pkcs15_pubkey_rsa *rsa) { int n, r; /* Read modulus and exponent */ for (n = 2; ; n++) { sc_pkcs15_bignum_t *bn; u8 buffer[256]; size_t m; r = sc_read_record(card, n, buffer, sizeof(buffer), SC_RECORD_BY_REC_NR); if (r < 1) break; if (buffer[0] == 0x01) bn = &rsa->modulus; else if (buffer[0] == 0x07) bn = &rsa->exponent; else continue; bn->len = r - 1; bn->data = malloc(bn->len); for (m = 0; m < bn->len; m++) bn->data[m] = buffer[bn->len - m]; } return 0; } static struct sc_pkcs15init_operations sc_pkcs15init_gpk_operations = { gpk_erase_card, NULL, /* init_card */ gpk_create_dir, NULL, /* create_domain */ gpk_select_pin_reference, gpk_create_pin, NULL, /* select_key_reference */ gpk_create_key, gpk_store_key, gpk_generate_key, NULL, NULL, /* encode private/public key */ NULL, /* finalize_card */ NULL, /* delete_object */ NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ NULL /* sanity_check */ }; struct sc_pkcs15init_operations *sc_pkcs15init_get_gpk_ops(void) { return &sc_pkcs15init_gpk_operations; } opensc-0.13.0/src/pkcs15init/pkcs15-rutoken.c0000644000015201777760000002307012057406034015547 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 pr_sec_attr = { 0x43, 1, 1, 0, 0, 0, 0, 1, 2, 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 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_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "Incorrect PIN\n"); break; } } sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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.13.0/src/pkcs15init/pkcs15-myeid.c0000644000015201777760000005327412057406034015200 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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" #undef KEEP_AC_NONE_FOR_INIT_APPLET #define MYEID_MAX_PINS 14 unsigned char MYEID_DEFAULT_PUBKEY[] = {0x01, 0x00, 0x01}; #define MYEID_DEFAULT_PUBKEY_LEN sizeof(MYEID_DEFAULT_PUBKEY) /* 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); 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); 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); 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); 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; 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); if (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 = p15card->card->ctx; struct sc_file *file = NULL; int r = 0, ii; static const char *create_dfs[] = { "PKCS15-PrKDF", "PKCS15-PuKDF", "PKCS15-CDF", "PKCS15-CDF-TRUSTED", "PKCS15-DODF", NULL }; static const int create_dfs_val[] = { SC_PKCS15_PRKDF, SC_PKCS15_PUKDF, SC_PKCS15_CDF, SC_PKCS15_CDF_TRUSTED, SC_PKCS15_DODF }; if (!profile || !p15card || !df) return SC_ERROR_INVALID_ARGUMENTS; 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]); if (sc_profile_get_file(profile, create_dfs[ii], &file)) { 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; int r; LOG_FUNC_CALLED(ctx); sc_log(ctx, "PIN('%s',ref:%i,flags:0x%X,pin_len:%d,puk_len:%d)\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; int r; LOG_FUNC_CALLED(card->ctx); if (type == SC_PKCS15_TYPE_PRKEY_RSA || type == SC_PKCS15_TYPE_PRKEY_EC) tag = "private-key"; else if (type == SC_PKCS15_TYPE_PUBKEY_RSA || type == SC_PKCS15_TYPE_PUBKEY_EC) 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; 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); } /* * Store a private key */ 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 *key_info = (struct sc_pkcs15_prkey_info *) object->data; struct sc_file *file = NULL; int keybits = key_info->modulus_length, r; LOG_FUNC_CALLED(card->ctx); /* 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: if (sc_card_find_ec_alg(p15card->card, keybits) == NULL) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Unsupported EC key size"); break; default: LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Unsupported key type"); break; } sc_log(ctx, "create MyEID private key ID:%s", sc_pkcs15_print_id(&key_info->id)); /* Get the private key file */ r = myeid_new_file(profile, card, object->type, key_info->key_reference, &file); LOG_TEST_RET(ctx, r, "Cannot get new MyEID private key file"); sc_log(ctx, "Key file size %d", keybits); file->size = keybits; if (object->type == SC_PKCS15_TYPE_PRKEY_RSA) file->ef_structure = SC_CARDCTL_MYEID_KEY_RSA; else if (object->type == SC_PKCS15_TYPE_PRKEY_EC) file->ef_structure = SC_CARDCTL_MYEID_KEY_EC; memcpy(&key_info->path.value, &file->path.value, file->path.len); key_info->key_reference = file->path.value[file->path.len - 1] & 0xFF; sc_log(ctx, "Path of MyEID private key file to create %s", sc_print_path(&file->path)); /* 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 private 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_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, keybits = key_info->modulus_length; LOG_FUNC_CALLED(ctx); 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: if (sc_card_find_ec_alg(p15card->card, keybits) == NULL) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Unsupported EC key size"); if(key_info->field_length != 0) keybits = key_info->field_length; else key_info->field_length = keybits; break; default: LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Store key failed: Unsupported key type"); break; } sc_log(ctx, "store MyEID 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 store MyEID key: select key file failed"); r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); LOG_TEST_RET(ctx, r, "No authorisation to store MyEID private key"); if (file) sc_file_free(file); /* Fill in data structure */ memset(&args, 0, sizeof (args)); args.op_type = OP_TYPE_STORE; if(object->type == SC_PKCS15_TYPE_PRKEY_RSA) { //args.key_len_bits = keybits; 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 = prkey->u.rsa.modulus.len; args.mod = prkey->u.rsa.modulus.data; } else { 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; } /* 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; size_t keybits = key_info->modulus_length; unsigned char raw_pubkey[256]; 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: if (sc_card_find_ec_alg(p15card->card, keybits) == NULL) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Unsupported EC key size"); if(key_info->field_length != 0) keybits = key_info->field_length; else key_info->field_length = keybits; break; default: LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Unsupported key type"); break; } sc_log(ctx, "Generate key with ID:%s and path:%s", 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); 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 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"); /* Keypair 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); 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) { pubkey->algorithm = SC_ALGORITHM_EC; r = sc_select_file(card, &file->path, 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"); pubkey->u.ec.ecpointQ.value = malloc(data_obj.DataLen - 2); pubkey->u.ec.ecpointQ.len = data_obj.DataLen - 2; pubkey->data.value = malloc(data_obj.DataLen); pubkey->data.len = data_obj.DataLen; pubkey->u.ec.params.field_length = keybits; /* Omit the first 2 bytes (0x86??) */ memcpy(pubkey->u.ec.ecpointQ.value, data_obj.Data + 2, data_obj.DataLen - 2); memcpy(pubkey->data.value, data_obj.Data, data_obj.DataLen); } } if (file) 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.13.0/src/pkcs15init/pkcs15-setcos.c0000644000015201777760000004717612057406034015375 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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; 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "MF doesn't exist, creating now"); /* Fix up the file's ACLs */ r = sc_pkcs15init_fixup_file(profile, p15card, mf); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "MF fixup failed"); mf->status = SC_FILE_STATUS_CREATION; r = sc_create_file(p15card->card, mf); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "MF creation failed"); } SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot select MF"); /* Create the global pin file if it doesn't exist yet */ r = sc_profile_get_file(profile, "pinfile", &pinfile); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot get 'pinfile' from profile"); r = sc_select_file(p15card->card, &pinfile->path, NULL); if (r == SC_ERROR_FILE_NOT_FOUND) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Global pin file doesn't exist, creating now"); /* Fix up the file's ACLs */ r = sc_pkcs15init_fixup_file(profile, p15card, pinfile); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Pinfile creation failed"); } sc_file_free(pinfile); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Select pinfile failed"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "SetCOS file ACL fixup failed"); r = sc_create_file(p15card->card, df); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "SetCOS create file failed"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; auth_info_prof.attrs.pin.reference = 1; /* Default SO PIN ref. */ auth_info_prof.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &auth_info_prof); /* For the SO pin, we take the first available pin reference = 1 */ if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) auth_info->attrs.pin.reference = auth_info_prof.attrs.pin.reference; /* sc_pkcs15init_create_pin() starts checking if -1 is an acceptable * pin reference, which isn't for the SetCOS cards. And since the * value 1 has been assigned to the SO pin, we'll jump to 2. */ else if (auth_info->attrs.pin.reference <= 0) auth_info->attrs.pin.reference = auth_info_prof.attrs.pin.reference + 1; return 0; } /* * 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; 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", &pinfile); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "No 'pinfile' template in profile"); r = sc_select_file(p15card->card, &pinfile->path, &pinfile); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot select 'pinfile'"); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "pinfile->status:%X", pinfile->status); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "create PIN with reference:%X, flags:%X, path:%s", 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' */ if (ignore_ac && (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)) { sc_file_t *mf = profile->mf_info->file; r = sc_card_ctl(p15card->card, SC_CARDCTL_SETCOS_ACTIVATE_FILE, NULL); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot set 'pinfile' into the activated state"); r = sc_select_file(p15card->card, &mf->path, NULL); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot select MF"); r = sc_card_ctl(p15card->card, SC_CARDCTL_SETCOS_ACTIVATE_FILE, NULL); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot set MF into the activated state"); } if(pinfile) sc_file_free(pinfile); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported file type"); return SC_ERROR_INVALID_ARGUMENTS; } /* Get template from profile */ snprintf(name, sizeof(name), "template-%s", tag); if (sc_profile_get_file(profile, name, &file) < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Profile doesn't define %s", name); return SC_ERROR_NOT_SUPPORTED; } /* Auto-increment FID for next object */ file->id += num; p = &file->path; *p = profile->df_info->file->path; 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; int keybits = key_info->modulus_length, r; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (object->type != SC_PKCS15_TYPE_PRKEY_RSA) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "Create key failed: RSA only supported"); /* Parameter check */ if ( (keybits < 512) || (keybits > 1024) || (keybits & 0x7)) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "Invalid key length"); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "create private key ID:%s\n", sc_pkcs15_print_id(&key_info->id)); /* Get the private key file */ r = setcos_new_file(profile, p15card->card, SC_PKCS15_TYPE_PRKEY_RSA, key_info->key_reference, &file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot get new private key file"); /* Take enough room for a 1024 bit key */ if (file->size < 512) file->size = 512; /* Replace the path of instantiated key template by the path from the object data. */ memcpy(&file->path, &key_info->path, sizeof(file->path)); file->id = file->path.value[file->path.len - 2] * 0x100 + file->path.value[file->path.len - 1]; key_info->key_reference = file->path.value[file->path.len - 1] & 0xFF; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Path of private key file to create %s\n", sc_print_path(&file->path)); r = sc_select_file(p15card->card, &file->path, NULL); if (!r) { r = sc_pkcs15init_delete_by_path(profile, p15card, &file->path); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to delete private key file"); } else if (r != SC_ERROR_FILE_NOT_FOUND) { SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Select private key file error"); } /* Now create the key file */ r = sc_pkcs15init_create_file(profile, p15card, file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot create private key file"); sc_file_free(file); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); } /* * 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, keybits = key_info->modulus_length; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (object->type != SC_PKCS15_TYPE_PRKEY_RSA) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "Store key failed: RSA only supported"); /* Parameter check */ if ( (keybits < 512) || (keybits > 1024) || (keybits & 0x7)) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "Invalid key length"); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "store key with ID:%s and path:%s\n", sc_pkcs15_print_id(&key_info->id), sc_print_path(&key_info->path)); r = sc_select_file(p15card->card, &key_info->path, &file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot store key: select key file failed"); r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "No authorisation to store private key"); /* 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Card control 'GENERATE_STORE_KEY' failed"); if (file) sc_file_free(file); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "Generate key failed: RSA only supported"); /* Parameter check */ if ( (keybits < 512) || (keybits > 1024) || (keybits & 0x7)) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "Invalid key length"); r = sc_select_file(p15card->card, &key_info->path, &file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot store key: select key file failed"); /* Authenticate */ r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "No authorisation to store private key"); /* 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Card control 'GENERATE_STORE_KEY' failed"); /* Keypair 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_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot get key modulus: 'SETCOS_GETDATA' failed"); keybits = ((raw_pubkey[0] * 256) + raw_pubkey[1]); /* modulus bit length */ if (keybits != key_info->modulus_length) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "key-size from card[%i] does not match[%i]\n", keybits, key_info->modulus_length); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PKCS15INIT, "Failed to generate key"); } memcpy (pubkey->u.rsa.modulus.data, &raw_pubkey[2], pubkey->u.rsa.modulus.len); } 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.13.0/src/pkcs15init/oberthur.profile0000644000015201777760000001055712057406034016032 00000000000000# # 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 specifique 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=CHV1; } 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=CHV1; } EF template-private-des { file-id = 4000; type = internal-ef; size = 24; # 192 bits # READ acl used insted of DECRYPT/ENCRYPT/CHECKSUM ACL = UPDATE=CHV1, READ=CHV1; } # Private data EF OberthurAWP-privdata-info { ACL = WRITE=CHV1, UPDATE=CHV1, READ=CHV1; } 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.13.0/src/pkcs15init/asepcos.profile0000644000015201777760000000504412057406034015630 00000000000000# # 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.13.0/src/pkcs15init/iasecc.profile0000644000015201777760000000553412057406034015426 00000000000000# # 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 specifique 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.13.0/src/pkcs15init/profile.c0000644000015201777760000016044012057406034014417 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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_PRKEY_DSA_ACCESS 0x12 #define DEF_PUBKEY_ACCESS 0x12 #define TEMPLATE_FILEID_MIN_DIFF 0x20 /* #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 }, { "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 }, { "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 }, { 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 void new_macro(sc_profile_t *, const char *, scconf_list *); static sc_macro_t * find_macro(sc_profile_t *, const char *); 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, but no DSA */ pro->rsa_access_flags = DEF_PRKEY_RSA_ACCESS; pro->dsa_access_flags = DEF_PRKEY_DSA_ACCESS; pro->pin_encoding = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; pro->pin_minlen = 4; pro->pin_maxlen = 8; 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]; DWORD temp_len; long rc; HKEY hKey; #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 rc = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\OpenSC Project\\OpenSC", 0, KEY_QUERY_VALUE, &hKey); if (rc == ERROR_SUCCESS) { temp_len = PATH_MAX; rc = RegQueryValueEx(hKey, "ProfileDir", NULL, NULL, (LPBYTE) temp_path, &temp_len); if ((rc == ERROR_SUCCESS) && (temp_len < PATH_MAX)) profile_dir = temp_path; RegCloseKey(hKey); } if (!profile_dir) { rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\OpenSC Project\\OpenSC", 0, KEY_QUERY_VALUE, &hKey); if (rc == ERROR_SUCCESS) { temp_len = PATH_MAX; rc = RegQueryValueEx(hKey, "ProfileDir", NULL, NULL, (LPBYTE) temp_path, &temp_len); if ((rc == ERROR_SUCCESS) && (temp_len < PATH_MAX)) profile_dir = temp_path; RegCloseKey(hKey); } } #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) LOG_FUNC_RETURN(ctx, SC_ERROR_FILE_NOT_FOUND); if (res == 0) 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); 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); } if (profile->p15_spec) sc_pkcs15_card_free(profile->p15_spec); memset(profile, 0, sizeof(*profile)); 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; *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->ident); 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); 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); LOG_TEST_RET(ctx, r, "Profile error: cannot add file"); } if (ret) *ret = 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) { 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 = fi_path.value[fi_path.len - 2] * 0x100 + fi_path.value[fi_path.len - 1]; if (fi->file->type == SC_FILE_TYPE_BSO) continue; for (ffi = templ->ef_list; ffi; ffi = ffi->next) { struct sc_path ffi_path = ffi->file->path; int dlt, ffi_id = ffi_path.value[ffi_path.len - 2] * 0x100 + ffi_path.value[ffi_path.len - 1]; if (ffi->file->type == SC_FILE_TYPE_BSO) continue; 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->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; 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); } else if (!strcasecmp(name+7, "TokenInfo")) { file = profile->p15_spec->file_tokeninfo; dont_free = 1; } else if (!strcasecmp(name+7, "ODF")) { file = profile->p15_spec->file_odf; dont_free = 1; } else if (!strcasecmp(name+7, "UnusedSpace")) { file = profile->p15_spec->file_unusedspace; dont_free = 1; } else if (!strcasecmp(name+7, "AppDF")) { file = init_file(SC_FILE_TYPE_DF); } else { if (map_str2int(cur, name+7, &df_type, pkcs15DfNames)) return NULL; file = init_file(SC_FILE_TYPE_WORKING_EF); profile->df[df_type] = file; } 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 (strncasecmp(name, "PKCS15-", 7) || !strcasecmp(name+7, "AppDF")) 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 (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 = cur->file->file; struct sc_path *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 = cur->file->file; struct sc_path temp, *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)) { parse_error(cur, "File path too long\n"); return 1; } *path = df->path; } 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 (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 (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 (get_uint(cur, argv[0], &reclength)) return 1; cur->file->file->record_length = reclength; return 0; } static int do_aid(struct state *cur, int argc, char **argv) { struct sc_file *file = cur->file->file; const char *name = argv[0]; unsigned int len; int res = 0; 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 = cur->file->file; const char *name = argv[0]; unsigned int len; int res = 0; #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) { 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 = cur->file->file; char oper[64], *what = NULL; memset(oper, 0, sizeof(oper)); while (argc--) { unsigned int op, method, id; 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; acl = sc_file_get_acl_entry(file, op); 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) { 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_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; 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; for (item = blk->items; item; item = item->next) { name = item->key; if (item->type != SCCONF_ITEM_TYPE_VALUE) continue; #ifdef DEBUG_PROFILE printf("Defining %s\n", name); #endif new_macro(cur->profile, name, item->value.list); } return 0; } static void new_macro(sc_profile_t *profile, const char *name, scconf_list *value) { sc_macro_t *mac; if ((mac = find_macro(profile, name)) == NULL) { mac = calloc(1, sizeof(*mac)); if (mac == NULL) return; mac->name = strdup(name); mac->next = profile->macro_list; profile->macro_list = mac; } mac->value = value; } 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 }, { 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}, { "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 build_argv(struct state *cur, const char *cmdname, scconf_list *list, char **argv, unsigned int max) { unsigned int argc; const char *str; sc_macro_t *mac; 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] != '$') { argv[argc++] = list->data; continue; } /* Expand macro reference */ if (!(mac = find_macro(cur->profile, str + 1))) { parse_error(cur, "%s: unknown macro \"%s\"", cmdname, str); return SC_ERROR_SYNTAX_ERROR; } #ifdef DEBUG_PROFILE { scconf_list *list; printf("Expanding macro %s:", mac->name); for (list = mac->value; list; list = list->next) printf(" %s", list->data); printf("\n"); } #endif r = build_argv(cur, cmdname, mac->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 (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; unsigned int len; len = path? path->len : 0; for (fi = pro->ef_list; fi; fi = fi->next) { sc_path_t *fpath = &fi->file->path; if (!strcasecmp(fi->ident, name) && fpath->len >= len && !memcmp(fpath->value, path->value, len)) 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->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((int) *value)) { *num = 0; return get_uint(cur, value, type); } 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; if (strstr(value, "0x") == value) *vp = strtoul(value + 2, &ep, 16); else if (strstr(value, "x") == value) *vp = strtoul(value + 1, &ep, 16); else *vp = strtoul(value, &ep, 0); if (*ep != '\0') { parse_error(cur, "invalid integer argument \"%s\"\n", value); return 1; } 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((int) *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[64]; char * unget; char * str; int argc; char ** argv; }; static void expr_eval(struct num_exp_ctx *, unsigned int *, unsigned 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; do { if ((s = ctx->str) == NULL || *s == '\0') { if (ctx->argc == 0) { if (eof_okay) return NULL; expr_fail(ctx); } ctx->str = s = *(ctx->argv++); ctx->argc--; } while (isspace(*s)) s++; } while (*s == '\0'); if (isdigit(*s)) { while (isdigit(*s)) expr_put(ctx, *s++); } else if (*s == '$') { expr_put(ctx, *s++); while (isalnum(*s) || *s == '-' || *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); } static void expr_term(struct num_exp_ctx *ctx, unsigned int *vp) { char *tok; tok = expr_get(ctx); if (*tok == '(') { expr_eval(ctx, vp, 1); expr_expect(ctx, ')'); } else if (isdigit(*tok)) { char *ep; *vp = strtoul(tok, &ep, 0); if (*ep) expr_fail(ctx); } 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) { unsigned int left, right, new_pri; char *tok, op; expr_term(ctx, &left); while (1) { tok = __expr_get(ctx, 1); if (tok == NULL) break; op = tok[0]; new_pri = 0; switch (op) { case '*': case '/': new_pri++; case '+': case '-': new_pri++; case '&': new_pri++; case '|': new_pri++; 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); switch (op) { case '*': left *= right; break; case '/': 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); 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.13.0/src/pkcs15init/pkcs15-authentic.c0000644000015201777760000006660212057406034016054 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 #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); LOG_TEST_RET(ctx, rv, "'UPDATE' authentication failed"); rv = sc_erase_binary(p15card->card, 0, file->size, 0); } LOG_TEST_RET(ctx, rv, "Binary erase error"); sc_file_free(file); 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:%i,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; 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) 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); LOG_TEST_RET(ctx, rv, "Cannot set key ACLs from file"); sc_file_free(file); 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)); if (out) *out = sdo; else free(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; if (sdo->file) 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:%i,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); LOG_TEST_RET(ctx, rv, "DF for the private objects not defined"); rv = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_CRYPTO); LOG_TEST_RET(ctx, rv, "SC_AC_OP_CRYPTO authentication failed for parent DF"); sc_file_free(parent); key_info->access_flags = SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE | SC_PKCS15_PRKEY_ACCESS_SENSITIVE; rv = authentic_sdo_allocate_prvkey(profile, card, key_info, &sdo); LOG_TEST_RET(ctx, rv, "IasEcc: init SDO private key failed"); 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_RET(ctx, rv, "SC_AC_OP_CRYPTO authentication failed for parent DF"); rv = sc_card_ctl(card, SC_CARDCTL_AUTHENTIC_SDO_DELETE, sdo); LOG_TEST_RET(ctx, rv, "SC_CARDCTL_AUTHENTIC_SDO_DELETE failed for private key"); rv = sc_card_ctl(card, SC_CARDCTL_AUTHENTIC_SDO_CREATE, sdo); } LOG_TEST_RET(ctx, rv, "SC_CARDCTL_AUTHENTIC_SDO_CREATE failed"); rv = authentic_pkcs15_fix_access(p15card, file_p_prvkey, object); LOG_TEST_RET(ctx, rv, "cannot fix access rules for private key"); rv = authentic_pkcs15_fix_usage(p15card, object); LOG_TEST_RET(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_RET(ctx, rv, "Failed to allocate PrvKey SDO as object content"); 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 long caps; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "generate key(bits:%i,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 likage 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, &pubkey->data.value, &pubkey->data.len); LOG_TEST_RET(ctx, rv, "encode public key failed"); /* Here fix the key's supported algorithms, if these ones will be implemented * (see src/libopensc/pkcs15-prkey.c). */ authentic_free_sdo_data(sdo); rv = sc_pkcs15_allocate_object_content(ctx, object, pubkey->data.value, pubkey->data.len); LOG_TEST_RET(ctx, rv, "Failed to allocate public key as object content"); 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:%i,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:%i)", 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_RET(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_RET(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_RET(ctx, rv, "SC_CARDCTL_AUTHENTIC_SDO_DELETE failed for private key"); 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,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)); LOG_TEST_RET(ctx, rv, "Get challenge error"); len = file->size > sizeof(buffer) ? sizeof(buffer) : file->size; rv = sc_update_binary(p15card->card, 0, buffer, len, 0); LOG_TEST_RET(ctx, rv, "Get challenge error"); sc_file_free(file); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static struct sc_pkcs15init_operations sc_pkcs15init_authentic_operations = { authentic_pkcs15_erase_card, NULL, /* init_card */ NULL, /* create_dir */ NULL, /* create_domain */ NULL, /* select_pin_reference */ NULL, /* 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.13.0/src/pkcs15init/ias_adele_admin1.profile0000644000015201777760000001166012057406034017333 00000000000000# # 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 insted 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.13.0/src/pkcs15init/pkcs15-oberthur.h0000644000015201777760000000464512057406034015726 00000000000000#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 #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 { unsigned 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.13.0/src/pkcs15init/pkcs15-oberthur.c0000644000015201777760000007235412057406034015723 00000000000000/* * Oberthur specific operation for PKCS #15 initialization * * Copyright (C) 2002 Juha Yrjl * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include "config.h" #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" #include "profile.h" #include "pkcs15-init.h" #include "pkcs15-oberthur.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 = p15card->card->ctx; struct sc_file *file = NULL; int rv; size_t sz; char *buffer = NULL; if (!p15card || !profile) return SC_ERROR_INVALID_ARGUMENTS; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "cosm_write_tokeninfo() label '%s'; flags 0x%X", label, flags); if (sc_profile_get_file(profile, COSM_TITLE"-token-info", &file)) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INCONSISTENT_PROFILE, "Cannot find "COSM_TITLE"-token-info"); if (file->size < 16) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INCONSISTENT_PROFILE, "Unsufficient size of the "COSM_TITLE"-token-info file"); buffer = calloc(1, file->size); if (!buffer) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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; free(buffer); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "id %04X", df->id); if (df->type==SC_FILE_TYPE_DF) { rv = sc_pkcs15init_authenticate(profile, p15card, df, SC_AC_OP_DELETE); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot authenticate SC_AC_OP_DELETE"); } /* Select the parent DF */ path = df->path; path.len -= 2; rv = sc_select_file(p15card->card, &path, &parent); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannnot select parent"); rv = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_DELETE); sc_file_free(parent); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannnot authenticate SC_AC_OP_DELETE"); 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); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "erase file dir %04X",dir->id); rv = cosm_delete_file(p15card, profile, dir); sc_file_free(dir); if (rv < 0 && rv != SC_ERROR_FILE_NOT_FOUND) goto done; } sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "erase file ddf %04X",df->id); rv = cosm_delete_file(p15card, profile, df); if (sc_profile_get_file(profile, "private-DF", &dir) >= 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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; SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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", #if 0 "PKCS15-AppDF", "PKCS15-ODF", "PKCS15-AODF", "PKCS15-PrKDF", "PKCS15-PuKDF", "PKCS15-CDF", "PKCS15-DODF", #endif NULL }; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); #if 0 rv = sc_pkcs15init_create_file(profile, p15card, df); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to create DIR DF"); #endif /* 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "Inconsistent profile: cannot find %s", create_dfs[ii]); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INCONSISTENT_PROFILE, "Profile do not contains Oberthur AWP file"); } rv = sc_pkcs15init_create_file(profile, p15card, file); sc_file_free(file); if (rv != SC_ERROR_FILE_ALREADY_EXISTS) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to create Oberthur AWP file"); } rv = cosm_write_tokeninfo(p15card, profile, NULL, COSM_TOKEN_FLAG_TOKEN_INITIALIZED | COSM_TOKEN_FLAG_PRN_GENERATION); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); } 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, profile_auth_puk; struct sc_cardctl_oberthur_createpin_info args; unsigned char *puk_buff = NULL; 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "pin lens %i/%i", 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "'CREATE_PIN' card specific command failed"); if (!(ainfo->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) && (profile_auth_puk.tries_left > 0)) { struct sc_file *file = NULL; if (sc_profile_get_file(profile, COSM_TITLE"-puk-file", &file)) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INCONSISTENT_PROFILE, "Cannot find PUKFILE"); rv = sc_pkcs15init_update_file(profile, p15card, file, oberthur_puk, sizeof(oberthur_puk)); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to update pukfile"); if (file) sc_file_free(file); } if (puk_buff) free(puk_buff); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "ref %i; flags 0x%X", ainfo->attrs.pin.reference, ainfo->attrs.pin.flags); if (ainfo->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) { if (ainfo->attrs.pin.reference != 4) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_PIN_REFERENCE, "cosm_update_pin() invalid SOPIN reference"); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Update SOPIN ignored"); rv = SC_SUCCESS; } else { rv = cosm_create_reference_data(profile, p15card, ainfo, pin, pin_len, puk, puk_len); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "cosm_update_pin() failed to change PIN"); rv = cosm_write_tokeninfo(p15card, profile, NULL, COSM_TOKEN_FLAG_TOKEN_INITIALIZED | COSM_TOKEN_FLAG_PRN_GENERATION | COSM_TOKEN_FLAG_LOGIN_REQUIRED | COSM_TOKEN_FLAG_USER_PIN_INITIALIZED); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "cosm_update_pin() failed to update tokeninfo"); } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "ref %i; flags %X", pin_attrs->reference, pin_attrs->flags); if (sc_profile_get_file(profile, COSM_TITLE "-AppDF", &pinfile) < 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Profile doesn't define \"%s\"", COSM_TITLE "-AppDF"); 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; } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "create '%s'; ref 0x%X; flags %X", pin_obj->label, pin_attrs->reference, pin_attrs->flags); if (sc_profile_get_file(profile, COSM_TITLE "-AppDF", &pin_file) < 0) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INCONSISTENT_PROFILE, "\""COSM_TITLE"-AppDF\" not defined"); 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) { SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "SOPIN unblocking is not supported"); } else { if (pin_attrs->reference != 4) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_PIN_REFERENCE, "Invalid SOPIN reference"); } } else { if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) { if (pin_attrs->reference != 0x84) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_PIN_REFERENCE, "Invalid User PUK reference"); } else { if (pin_attrs->reference != 0x81) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_PIN_REFERENCE, "Invalid User PIN reference"); } } if (pin && pin_len) { rv = cosm_update_pin(profile, p15card, auth_info, pin, pin_len, puk, puk_len); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Update PIN failed"); } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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_PUBKEY_DSA: desc = "DSA public key"; _template = "template-public-key"; 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File type %X not supported by card driver", type); return SC_ERROR_INVALID_ARGUMENTS; } type &= SC_PKCS15_TYPE_CLASS_MASK; } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "cosm_new_file() template %s; num %i",_template, num); if (sc_profile_get_file(profile, _template, &file) < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Profile doesn't define %s template '%s'", desc, _template); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); } file->id |= (num & 0xFF); file->path.value[file->path.len-1] |= (num & 0xFF); if (file->type == SC_FILE_TYPE_INTERNAL_EF) { file->ef_structure = structure; } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "cosm_new_file() file size %i; ef type %i/%i; id %04X",file->size, file->type, file->ef_structure, file->id); *out = file; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); } 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); file = sc_file_new(); if (!file) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to add ACL entry to the temporary public key file"); *pubkey_file = file; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, rv); } 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) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot generate key: failed to select private object DF"); rv = sc_pkcs15init_authenticate(profile, p15card, tmpf, SC_AC_OP_CRYPTO); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot generate key: 'CRYPTO' authentication failed"); rv = sc_pkcs15init_authenticate(profile, p15card, tmpf, SC_AC_OP_CREATE); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot generate key: 'CREATE' authentication failed"); sc_file_free(tmpf); rv = sc_select_file(p15card->card, &key_info->path, &prkf); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to generate key: cannot select private key file"); /* In the private key DF create the temporary public RSA file. */ rv = cosm_get_temporary_public_key_file(p15card->card, prkf, &tmpf); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Error while getting temporary public key file"); rv = sc_pkcs15init_create_file(profile, p15card, tmpf); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "cosm_generate_key() failed to create temporary public key EF"); 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_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "cosm_generate_key() cannot allocate pubkey"); rv = sc_card_ctl(p15card->card, SC_CARDCTL_OBERTHUR_GENERATE_KEY, &args); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "cosm_generate_key() CARDCTL_OBERTHUR_GENERATE_KEY failed"); /* extract public key */ pubkey->algorithm = SC_ALGORITHM_RSA; pubkey->u.rsa.modulus.len = key_info->modulus_length / 8; pubkey->u.rsa.modulus.data = malloc(key_info->modulus_length / 8); if (!pubkey->u.rsa.modulus.data) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "cosm_generate_key() cannot allocate modulus buf"); /* FIXME and if the exponent length is not 3? */ pubkey->u.rsa.exponent.len = 3; pubkey->u.rsa.exponent.data = malloc(3); if (!pubkey->u.rsa.exponent.data) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "cosm_generate_key() cannot allocate exponent buf"); 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "cosm_generate_key() now delete temporary public key"); rv = cosm_delete_file(p15card, profile, tmpf); sc_file_free(tmpf); sc_file_free(prkf); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "Create key failed: RSA only supported"); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "create private key ID:%s", sc_pkcs15_print_id(&key_info->id)); /* Here, the path of private key file should be defined. * Neverthelles, we need to instanciate private key to get the ACLs. */ rv = cosm_new_file(profile, p15card->card, SC_PKCS15_TYPE_PRKEY_RSA, key_info->key_reference, &file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot create key: failed to allocate new key object"); file->size = key_info->modulus_length; memcpy(&file->path, &key_info->path, sizeof(file->path)); file->id = file->path.value[file->path.len - 2] * 0x100 + file->path.value[file->path.len - 1]; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Path of private key file to create %s", sc_print_path(&file->path)); rv = sc_select_file(p15card->card, &file->path, NULL); if (rv == 0) { rv = cosm_delete_file(p15card, profile, file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to delete private key file"); } else if (rv != SC_ERROR_FILE_NOT_FOUND) { SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Select private key file error"); } rv = sc_pkcs15init_create_file(profile, p15card, file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to create private key file"); key_info->key_reference = file->path.value[file->path.len - 1]; sc_file_free(file); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "Store key failed: RSA only supported"); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "store key with ID:%s and path:%s", sc_pkcs15_print_id(&key_info->id), sc_print_path(&key_info->path)); rv = sc_select_file(p15card->card, &key_info->path, &file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot store key: select key file failed"); rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "No authorisation to store private key"); if (key_info->id.len > sizeof(update_info.id)) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot update private key"); if (file) sc_file_free(file); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); } 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); #ifdef ENABLE_OPENSSL switch(op) { case SC_AC_OP_ERASE: sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Update DF; erase object('%s',type:%X)", object->label, object->type); rv = awp_update_df_delete(p15card, profile, object); break; case SC_AC_OP_CREATE: sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Update DF; create object('%s',type:%X)", object->label, object->type); rv = awp_update_df_create(p15card, profile, object); break; } #endif 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, label_len; unsigned char *buf = NULL; SC_FUNC_CALLED(ctx, 1); if (sc_profile_get_file(profile, COSM_TITLE"-token-info", &file)) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INCONSISTENT_PROFILE, "cannot find "COSM_TITLE"-token-info"); buf = calloc(1, file->size); if (!buf) 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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); 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); } 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.13.0/src/pkcs15init/pkcs15-muscle.c0000644000015201777760000002226712057406034015357 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include "libopensc/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) return r; 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) return r; /* Create the application DF */ if ((r = sc_pkcs15init_create_file(profile, p15card, df)) < 0) return r; if ((r = sc_select_file(p15card->card, &df->path, NULL)) < 0) return r; 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) return r; auth_info->attrs.pin.flags &= ~SC_PKCS15_PIN_FLAG_LOCAL; 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "Muscle supports RSA keys only."); return SC_ERROR_NOT_SUPPORTED; } /* Verification stuff */ /* Used for verification AND for obtaining private key acls */ r = sc_profile_get_file_by_path(profile, &key_info->path, &prkf); if(!prkf) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_NOT_SUPPORTED); 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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; unsigned int keybits; int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Muscle supports only RSA keys (for now)."); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_NOT_SUPPORTED); } keybits = key_info->modulus_length & ~7UL; if (keybits > 2048) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unable to generate key, max size is %d", 2048); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_INVALID_ARGUMENTS); } /* Verification stuff */ /* Used for verification AND for obtaining private key acls */ r = sc_profile_get_file_by_path(profile, &key_info->path, &prkf); if(!prkf) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_NOT_SUPPORTED); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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; 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; } 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.13.0/src/pkcs15init/pkcs15-incrypto34.c0000644000015201777760000004462312057406034016105 00000000000000/* * Incrypto34 specific operation for PKCS15 initialization * * Copyright (C) 2005 ST Incard srl, Giuseppe Amato * Copyright (C) 2002 Olaf Kirch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" #include "pkcs15-init.h" #include "profile.h" #ifndef MIN # define MIN(a, b) (((a) < (b))? (a) : (b)) #endif struct tlv { unsigned char * base; unsigned char * end; unsigned char * current; unsigned char * next; }; #define RSAKEY_MAX_BITS 1024 #define RSAKEY_MAX_SIZE (RSAKEY_MAX_BITS/8) struct rsakey { struct bignum { size_t len; u8 data[RSAKEY_MAX_SIZE]; } n, d; }; /* * Local functions */ static int incrypto34_store_pin(sc_profile_t *profile, sc_card_t *card, sc_pkcs15_auth_info_t *auth_info, int puk_id, const u8 *pin, size_t pin_len); static int incrypto34_create_sec_env(sc_profile_t *, sc_card_t *, unsigned int, unsigned int); static int incrypto34_put_key(struct sc_profile *, struct sc_pkcs15_card *, int, sc_pkcs15_prkey_info_t *, struct sc_pkcs15_prkey_rsa *); static int incrypto34_key_algorithm(unsigned int, int *); static int incrypto34_extract_pubkey(sc_card_t *, int, u8, sc_pkcs15_bignum_t *); /* Object IDs for PIN objects. * SO PIN = 0x01, SO PUK = 0x02 * each user pin is 2*N+1, each corresponding PUK is 2*N+2 */ #define INCRYPTO34_PIN_ID_MIN 1 #define INCRYPTO34_PIN_ID_MAX 15 #define INCRYPTO34_KEY_ID_MIN 16 #define INCRYPTO34_KEY_ID_MAX 31 #define INCRYPTO34_AC_NEVER 0xFF #define INCRYPTO34_ALGO_RSA 0x08 #define INCRYPTO34_ALGO_RSA_PURE 0x0C #define INCRYPTO34_ALGO_RSA_SIG 0x88 #define INCRYPTO34_ALGO_RSA_PURE_SIG 0x8C #define INCRYPTO34_ALGO_RSA_SIG_SHA1 0xC8 #define INCRYPTO34_ALGO_RSA_PURE_SIG_SHA1 0xCC #define INCRYPTO34_SIGN_RSA INCRYPTO34_ALGO_RSA_SIG #define INCRYPTO34_DECIPHER_RSA INCRYPTO34_ALGO_RSA_PURE #define INCRYPTO34_ALGO_PIN 0x87 static void tlv_init(struct tlv *tlv, u8 *base, size_t size) { tlv->base = base; tlv->end = base + size; tlv->current = tlv->next = base; } static void tlv_next(struct tlv *tlv, u8 tag) { assert(tlv->next + 2 < tlv->end); tlv->current = tlv->next; *(tlv->next++) = tag; *(tlv->next++) = 0; } static void tlv_add(struct tlv *tlv, u8 val) { assert(tlv->next + 1 < tlv->end); *(tlv->next++) = val; tlv->current[1]++; } static size_t tlv_len(struct tlv *tlv) { return tlv->next - tlv->base; } /* * Try to delete pkcs15 structure * This is not quite the same as erasing the whole token, but * it's close enough to be useful. */ static int incrypto34_erase(struct sc_profile *profile, sc_pkcs15_card_t *p15card) { int r; struct sc_file *file; struct sc_path path; memset(&file, 0, sizeof(file)); sc_format_path("3F00", &path); if ((r = sc_select_file(p15card->card, &path, &file)) < 0) return r; if (sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_DELETE) < 0) return sc_pkcs15init_erase_card_recursively(p15card, profile); else return sc_card_ctl(p15card->card, SC_CARDCTL_INCRYPTO34_ERASE_FILES, NULL); } /* * Create the Application DF */ static int incrypto34_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { int r; struct sc_file *file; struct sc_path path; memset(&file, 0, sizeof(file)); sc_format_path("3F00", &path); if ((r = sc_select_file(p15card->card, &path, &file)) < 0) return r; if ((r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_CREATE)) < 0) return r; /* Create the application DF */ if ((r = sc_pkcs15init_create_file(profile, p15card, df)) < 0) return r; if ((r = sc_select_file(p15card->card, &df->path, NULL)) < 0) return r; /* Create a security environment for this DF. */ if ((r = incrypto34_create_sec_env(profile, p15card->card, 0x01, 0x00)) < 0) return r; return 0; } /* * Caller passes in a suggested PIN reference. * See if it's good, and if it isn't, propose something better */ static int incrypto34_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_auth_info_t *auth_info) { int preferred, current; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; if ((current = auth_info->attrs.pin.reference) < 0) current = INCRYPTO34_PIN_ID_MIN; if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) { preferred = 1; } else { preferred = current; /* PINs are even numbered, PUKs are odd */ if (!(preferred & 1)) preferred++; if (preferred >= 126) return SC_ERROR_TOO_MANY_OBJECTS; } if (current > preferred || preferred > INCRYPTO34_PIN_ID_MAX) return SC_ERROR_TOO_MANY_OBJECTS; auth_info->attrs.pin.reference = preferred; return 0; } /* * Store a PIN */ static int incrypto34_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df, sc_pkcs15_object_t *pin_obj, const u8 *pin, size_t pin_len, const u8 *puk, size_t puk_len) { sc_pkcs15_auth_info_t *auth_info = (sc_pkcs15_auth_info_t *) pin_obj->data; unsigned int puk_id = INCRYPTO34_AC_NEVER; int r; if (!pin || !pin_len) return SC_ERROR_INVALID_ARGUMENTS; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; r = sc_select_file(p15card->card, &df->path, NULL); if (r < 0) return r; if (puk && puk_len) { struct sc_pkcs15_auth_info puk_ainfo; sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, &puk_ainfo); puk_ainfo.attrs.pin.reference = puk_id = auth_info->attrs.pin.reference + 1; r = incrypto34_store_pin(profile, p15card->card, &puk_ainfo, INCRYPTO34_AC_NEVER, puk, puk_len); } if (r >= 0) { r = incrypto34_store_pin(profile, p15card->card, auth_info, puk_id, pin, pin_len); } return r; } /* * Select a key reference */ static int incrypto34_select_key_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_prkey_info_t *key_info) { if (key_info->key_reference < INCRYPTO34_KEY_ID_MIN) key_info->key_reference = INCRYPTO34_KEY_ID_MIN; if (key_info->key_reference > INCRYPTO34_KEY_ID_MAX) return SC_ERROR_TOO_MANY_OBJECTS; return 0; } /* * Create a private key object. * This is a no-op. */ static int incrypto34_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj) { return 0; } /* * Store a private key object. */ static int incrypto34_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_prkey_t *key) { sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; sc_card_t *card = p15card->card; int algorithm, r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Incrypto34 supports RSA keys only."); return SC_ERROR_NOT_SUPPORTED; } if (incrypto34_key_algorithm(key_info->usage, &algorithm) < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Incrypto34 does not support keys " "that can both sign _and_ decrypt."); return SC_ERROR_NOT_SUPPORTED; } r = incrypto34_put_key(profile, p15card, algorithm, key_info, &key->u.rsa); return r; } /* * Key generation */ static int incrypto34_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) { sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; sc_card_t *card = p15card->card; struct sc_pkcs15_prkey_rsa key_obj; struct sc_cardctl_incrypto34_genkey_info args; struct sc_file *temp; u8 abignum[RSAKEY_MAX_SIZE]; unsigned int keybits; int algorithm, r, delete_it = 0; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Incrypto34 supports only RSA keys."); return SC_ERROR_NOT_SUPPORTED; } if (incrypto34_key_algorithm(key_info->usage, &algorithm) < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Incrypto34 does not support keys " "that can both sign _and_ decrypt."); return SC_ERROR_NOT_SUPPORTED; } keybits = key_info->modulus_length & ~7UL; if (keybits > RSAKEY_MAX_BITS) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unable to generate key, max size is %d", RSAKEY_MAX_BITS); return SC_ERROR_INVALID_ARGUMENTS; } if (sc_profile_get_file(profile, "tempfile", &temp) < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Profile doesn't define temporary file " "for key generation."); return SC_ERROR_NOT_SUPPORTED; } memset(pubkey, 0, sizeof(*pubkey)); if ((r = sc_pkcs15init_create_file(profile, p15card, temp)) < 0) goto out; delete_it = 1; /* Create a key object, initializing components to 0xff */ memset(&key_obj, 0, sizeof(key_obj)); memset(abignum, 0xFF, sizeof(abignum)); key_obj.modulus.data = abignum; key_obj.modulus.len = keybits >> 3; key_obj.d.data = abignum; key_obj.d.len = keybits >> 3; r = incrypto34_put_key(profile, p15card, algorithm, key_info, &key_obj); if (r < 0) goto out; memset(&args, 0, sizeof(args)); args.key_id = key_info->key_reference; args.key_bits = keybits; args.fid = temp->id; r = sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_GENERATE_KEY, &args); if (r < 0) goto out; /* extract public key from file and delete it */ if ((r = sc_select_file(card, &temp->path, NULL)) < 0) goto out; r = incrypto34_extract_pubkey(card, 1, 0x10, &pubkey->u.rsa.modulus); if (r < 0) goto out; r = incrypto34_extract_pubkey(card, 2, 0x11, &pubkey->u.rsa.exponent); if (r < 0) goto out; pubkey->algorithm = SC_ALGORITHM_RSA; out: if (delete_it) { sc_pkcs15init_rmdir(p15card, profile, temp); } sc_file_free(temp); if (r < 0) { if (pubkey->u.rsa.modulus.data) free (pubkey->u.rsa.modulus.data); if (pubkey->u.rsa.exponent.data) free (pubkey->u.rsa.exponent.data); } return r; } /* * Store a PIN or PUK */ static int incrypto34_store_pin(sc_profile_t *profile, sc_card_t *card, sc_pkcs15_auth_info_t *auth_info, int puk_id, const u8 *pin, size_t pin_len) { struct sc_cardctl_incrypto34_obj_info args; unsigned char buffer[256]; unsigned char pinpadded[16]; struct tlv tlv; unsigned int attempts, minlen, maxlen; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; /* We need to do padding because pkcs15-lib.c does it. * Would be nice to have a flag in the profile that says * "no padding required". */ maxlen = MIN(profile->pin_maxlen, sizeof(pinpadded)); if (pin_len > maxlen) pin_len = maxlen; memcpy(pinpadded, pin, pin_len); while (pin_len < maxlen) pinpadded[pin_len++] = profile->pin_pad_char; pin = pinpadded; attempts = auth_info->tries_left; minlen = auth_info->attrs.pin.min_length; tlv_init(&tlv, buffer, sizeof(buffer)); /* object address: class, id */ tlv_next(&tlv, 0x83); tlv_add(&tlv, 0x00); /* class byte: usage TEST, k=0 */ tlv_add(&tlv, auth_info->attrs.pin.reference); /* parameters */ tlv_next(&tlv, 0x85); tlv_add(&tlv, 0x02); /* options byte */ tlv_add(&tlv, attempts & 0xf); /* flags byte */ tlv_add(&tlv, INCRYPTO34_ALGO_PIN); /* algorithm = pin-test */ tlv_add(&tlv, attempts & 0xf); /* errcount = attempts */ /* usecount: not documented, but seems to work like this: * - value of 0xff means pin can be presented any number * of times * - anything less: max # of times before BS object is blocked. */ tlv_add(&tlv, 0xff); /* DEK: RFU */ tlv_add(&tlv, 0x00); /* ARA counter: the number of times the PIN can be used before the he must be verified again (0 or ff for unlimited usage) */ tlv_add(&tlv, 0x00); tlv_add(&tlv, minlen); /* minlen */ /* AC conditions */ tlv_next(&tlv, 0x86); tlv_add(&tlv, 0x00); /* use: always */ tlv_add(&tlv, auth_info->attrs.pin.reference); /* change: PIN */ tlv_add(&tlv, puk_id); /* unblock: PUK */ tlv_add(&tlv, 0xFF); /*RFU*/ tlv_add(&tlv, 0xFF); /*RFU*/ tlv_add(&tlv, 0xFF); /*RFU*/ tlv_add(&tlv, 0xFF); /*unused on pins*/ tlv_add(&tlv, 0xFF); /*RFU*/ tlv_add(&tlv, 0xFF); /*RFU*/ tlv_add(&tlv, 0xFF); /*RFU*/ /* data: PIN */ tlv_next(&tlv, 0x8f); while (pin_len--) tlv_add(&tlv, *pin++); args.data = buffer; args.len = tlv_len(&tlv); return sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_PUT_DATA_OCI, &args); } /* * Create an empty security environment */ static int incrypto34_create_sec_env(struct sc_profile *profile, struct sc_card *card, unsigned int se_id, unsigned int key_id) { struct sc_cardctl_incrypto34_obj_info args; struct tlv tlv; unsigned char buffer[64]; tlv_init(&tlv, buffer, sizeof(buffer)); tlv_next(&tlv, 0x83); tlv_add(&tlv, se_id); tlv_next(&tlv, 0x86); tlv_add(&tlv, 0); tlv_add(&tlv, 0); tlv_next(&tlv, 0x8f); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); args.data = buffer; args.len = tlv_len(&tlv); return sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_PUT_DATA_SECI, &args); } /* * Determine the key algorithm based on the intended usage * Note that Incrypto34 does not support keys that can be used * for signing _and_ decipherment */ #define USAGE_ANY_SIGN (SC_PKCS15_PRKEY_USAGE_SIGN|\ SC_PKCS15_PRKEY_USAGE_NONREPUDIATION) #define USAGE_ANY_DECIPHER (SC_PKCS15_PRKEY_USAGE_DECRYPT|\ SC_PKCS15_PRKEY_USAGE_UNWRAP) static int incrypto34_key_algorithm(unsigned int usage, int *algop) { int sign = 0, decipher = 0; if (usage & USAGE_ANY_SIGN) { *algop = INCRYPTO34_SIGN_RSA; sign = 1; } if (usage & USAGE_ANY_DECIPHER) { *algop = INCRYPTO34_DECIPHER_RSA; decipher = 1; } return (sign == decipher)? -1 : 0; } static int incrypto34_change_key_data(struct sc_card *card, unsigned int key_id, unsigned int num, const u8 *data, size_t len) { struct sc_cardctl_incrypto34_obj_info args; args.data = (u8 *)data; args.len = len; args.key_id = key_id; args.key_class = num; return sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_CHANGE_KEY_DATA, &args); } /* * Create a private key object */ #define INCRYPTO34_KEY_OPTIONS 0x02 #define INCRYPTO34_KEY_FLAGS 0x00 static int incrypto34_store_key_component(struct sc_card *card, int algorithm, unsigned int key_id, unsigned int pin_id, unsigned int num, const u8 *data, size_t len, int last) { int r; struct sc_cardctl_incrypto34_obj_info args; struct tlv tlv; unsigned char buffer[256]; unsigned int n; /* Initialize the TLV encoder */ tlv_init(&tlv, buffer, sizeof(buffer)); /* Object address */ tlv_next(&tlv, 0x83); tlv_add(&tlv, 0x20|num); /* PSO, n-th component */ tlv_add(&tlv, key_id); /* Object parameters */ tlv_next(&tlv, 0x85); tlv_add(&tlv, INCRYPTO34_KEY_OPTIONS|(last? 0x00 : 0x20)); tlv_add(&tlv, INCRYPTO34_KEY_FLAGS); tlv_add(&tlv, algorithm); tlv_add(&tlv, 0x0F); /* Error Counter*/ tlv_add(&tlv, 0xFF); /* use count */ tlv_add(&tlv, 0xFF); /* RFU */ tlv_add(&tlv, 0x00); /* RFU */ tlv_add(&tlv, 0x00); /* RFU */ /* AC bytes */ tlv_next(&tlv, 0x86); tlv_add(&tlv, pin_id); /* AC USE */ tlv_add(&tlv, pin_id); /* AC CHANGE */ tlv_add(&tlv, 0xFF); /* AC_UNBLOCK */ tlv_add(&tlv, 0xFF); /* RFU */ tlv_add(&tlv, 0xFF); /* RFU */ tlv_add(&tlv, 0xFF); /* RFU */ tlv_add(&tlv, 0); /* AC_GENKEY */ tlv_add(&tlv, 0xFF); /* RFU */ tlv_add(&tlv, 0xFF); /* RFU */ tlv_add(&tlv, 0xFF); /* RFU */ /* SM bytes */ tlv_next(&tlv, 0x8B); for (n = 0; n < 16; n++) tlv_add(&tlv, 0xFF); /* key component */ tlv_next(&tlv, 0x8f); tlv_add(&tlv, len+1); tlv_add(&tlv, 0); while (len--) tlv_add(&tlv, *data++); args.data = buffer; args.len = tlv_len(&tlv); r = sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_PUT_DATA_OCI, &args); return r; } static int incrypto34_put_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, int algorithm, sc_pkcs15_prkey_info_t *key_info, struct sc_pkcs15_prkey_rsa *key) { int r, key_id, pin_id; key_id = key_info->key_reference; pin_id = sc_pkcs15init_get_pin_reference(p15card, profile, SC_AC_SYMBOLIC, SC_PKCS15INIT_USER_PIN); if (pin_id < 0) pin_id = 0; r = incrypto34_store_key_component(p15card->card, algorithm, key_id, pin_id, 0, key->modulus.data, key->modulus.len, 0); if (r >= 0) { r = incrypto34_store_key_component(p15card->card, algorithm, key_id, pin_id, 1, key->d.data, key->d.len, 1); } if (SC_ERROR_FILE_ALREADY_EXISTS == r || r >=0) { r = incrypto34_change_key_data(p15card->card, 0x80|key_id, 0x20, key->modulus.data, key->modulus.len); if (r < 0) return r; r = incrypto34_change_key_data(p15card->card, 0x80|key_id, 0x21, key->d.data, key->d.len); } return r; } /* * Extract a key component from the public key file populated by * GENERATE KEY PAIR */ static int incrypto34_extract_pubkey(sc_card_t *card, int nr, u8 tag, sc_pkcs15_bignum_t *bn) { u8 buf[256]; int r, count; r = sc_read_record(card, nr, buf, sizeof(buf), SC_RECORD_BY_REC_NR); if (r < 0) return r; count = r - 4; if (count <= 0 || buf[0] != tag || buf[1] != count + 2 || buf[2] != count + 1 || buf[3] != 0) return SC_ERROR_INTERNAL; bn->len = count; bn->data = malloc(count); memcpy(bn->data, buf + 4, count); return 0; } static int incrypto34_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { return 0; } static struct sc_pkcs15init_operations sc_pkcs15init_incrypto34_operations; static struct sc_pkcs15init_operations sc_pkcs15init_incrypto34_operations = { incrypto34_erase, incrypto34_init_card, /* init_card */ incrypto34_create_dir, NULL, /* create_domain */ incrypto34_select_pin_reference, incrypto34_create_pin, incrypto34_select_key_reference, incrypto34_create_key, incrypto34_store_key, incrypto34_generate_key, NULL, NULL, /* encode private/public key */ NULL, /* finalize_card */ NULL, /* delete_object */ NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ NULL /* sanity_check */ }; struct sc_pkcs15init_operations * sc_pkcs15init_get_incrypto34_ops(void) { return &sc_pkcs15init_incrypto34_operations; } opensc-0.13.0/src/pkcs15init/Makefile.mak0000644000015201777760000000130712057406034015016 00000000000000TOPDIR = ..\.. TARGET = pkcs15init.lib OBJECTS = pkcs15-lib.obj profile.obj \ pkcs15-gpk.obj pkcs15-miocos.obj pkcs15-cflex.obj \ pkcs15-cardos.obj pkcs15-jcop.obj pkcs15-starcos.obj \ pkcs15-oberthur.obj pkcs15-oberthur-awp.obj \ pkcs15-setcos.obj pkcs15-incrypto34.obj \ pkcs15-muscle.obj pkcs15-asepcos.obj pkcs15-rutoken.obj \ pkcs15-entersafe.obj pkcs15-rtecp.obj pkcs15-westcos.obj \ pkcs15-myeid.obj pkcs15-authentic.obj pkcs15-iasecc.obj \ pkcs15-epass2003.obj pkcs15-openpgp.obj pkcs15-sc-hsm.obj all: $(TARGET) $(TARGET): $(OBJECTS) lib $(LIBFLAGS) /out:$(TARGET) $(OBJECTS) !INCLUDE $(TOPDIR)\win32\Make.rules.mak opensc-0.13.0/src/pkcs15init/pkcs15-miocos.c0000644000015201777760000002100112057406034015341 00000000000000/* * MioCOS specific operation for PKCS15 initialization * * Copyright (C) 2002 Juha Yrjölä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" #include "pkcs15-init.h" #include "profile.h" #define MIOCOS_PIN_ID_MIN 1 #define MIOCOS_PIN_ID_MAX 15 /* * Allocate a file */ static int miocos_new_file(struct sc_profile *profile, sc_card_t *card, unsigned int type, unsigned int num, sc_file_t **out) { struct sc_file *file; struct sc_path *p; char name[64]; const char *tag = NULL, *desc = NULL; while (1) { switch (type) { case SC_PKCS15_TYPE_PRKEY_RSA: desc = "RSA private key"; tag = "private-key"; break; case SC_PKCS15_TYPE_PUBKEY_RSA: desc = "RSA public key"; tag = "public-key"; break; case SC_PKCS15_TYPE_PRKEY: desc = "extractable private key"; tag = "extractable-key"; break; case SC_PKCS15_TYPE_CERT: desc = "certificate"; tag = "certificate"; break; case SC_PKCS15_TYPE_DATA_OBJECT: desc = "data object"; tag = "data"; break; } if (tag) break; /* If this is a specific type such as * SC_PKCS15_TYPE_CERT_FOOBAR, fall back to * the generic class (SC_PKCS15_TYPE_CERT) */ if (!(type & ~SC_PKCS15_TYPE_CLASS_MASK)) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File type not supported by card driver"); return SC_ERROR_INVALID_ARGUMENTS; } type &= SC_PKCS15_TYPE_CLASS_MASK; } snprintf(name, sizeof(name), "template-%s", tag); if (sc_profile_get_file(profile, name, &file) < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Profile doesn't define %s template (%s)", desc, name); return SC_ERROR_NOT_SUPPORTED; } /* Now construct file from template */ file->id += num; p = &file->path; *p = profile->df_info->file->path; p->value[p->len++] = file->id >> 8; p->value[p->len++] = file->id; *out = file; return 0; } static int miocos_update_private_key(struct sc_profile *profile, sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa) { int r; u8 buf[266]; memcpy(buf, "\x30\x82\x01\x06\x80\x81\x80", 7); memcpy(buf + 7, rsa->modulus.data, 128); memcpy(buf + 7 + 128, "\x82\x81\x80", 3); memcpy(buf + 10 + 128, rsa->d.data, 128); r = sc_update_binary(card, 0, buf, sizeof(buf), 0); return r; } /* * Initialize the Application DF */ static int miocos_create_dir(struct sc_profile *profile, sc_pkcs15_card_t *p15card, struct sc_file *df) { /* Create the application DF */ if (sc_pkcs15init_create_file(profile, p15card, profile->df_info->file)) return 1; return 0; } /* * Validate PIN reference */ static int miocos_select_pin_reference(struct sc_profile *profile, sc_pkcs15_card_t *p15card, struct sc_pkcs15_auth_info *auth_info) { if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; if (auth_info->attrs.pin.reference < MIOCOS_PIN_ID_MIN) auth_info->attrs.pin.reference = MIOCOS_PIN_ID_MIN; return SC_SUCCESS; } /* * Create new PIN */ static int miocos_create_pin(struct sc_profile *profile, sc_pkcs15_card_t *p15card, struct sc_file *df, struct sc_pkcs15_object *pin_obj, const u8 *pin, size_t pin_len, const u8 *puk, size_t puk_len) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_auth_info *auth_info = (struct sc_pkcs15_auth_info *)pin_obj->data; struct sc_pkcs15_pin_attributes *pin_attrs = &auth_info->attrs.pin; struct sc_pkcs15_auth_info tmpinfo; struct sc_cardctl_miocos_ac_info ac_info; int r; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); /* Ignore SOPIN */ if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) return SC_SUCCESS; auth_info->path = profile->df_info->file->path; r = sc_select_file(p15card->card, &auth_info->path, NULL); if (r) return r; memset(&ac_info, 0, sizeof(ac_info)); ac_info.ref = pin_attrs->reference; sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &tmpinfo); ac_info.max_tries = tmpinfo.tries_left; sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, &tmpinfo); ac_info.max_unblock_tries = tmpinfo.tries_left; if (pin_len > 8) pin_len = 8; memcpy(ac_info.key_value, pin, pin_len); if (puk_len > 8) puk_len = 8; strncpy((char *) ac_info.unblock_value, (const char *) puk, puk_len); r = sc_card_ctl(p15card->card, SC_CARDCTL_MIOCOS_CREATE_AC, &ac_info); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Miocos create AC failed"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); } /* * Create private key file */ static int miocos_create_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)object->data; struct sc_file *file; int r; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (object->type != SC_PKCS15_TYPE_PRKEY_RSA) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "MioCOS supports only 1024-bit RSA keys."); if (key_info->modulus_length != 1024) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "MioCOS supports only 1024-bit RSA keys."); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "create private key ID:%s\n", sc_pkcs15_print_id(&key_info->id)); r = miocos_new_file(profile, p15card->card, SC_PKCS15_TYPE_PRKEY_RSA, key_info->key_reference, &file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot create key: failed to allocate new key object"); memcpy(&file->path, &key_info->path, sizeof(file->path)); file->id = file->path.value[file->path.len - 2] * 0x100 + file->path.value[file->path.len - 1]; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Path of private key file to create %s\n", sc_print_path(&file->path)); r = sc_pkcs15init_create_file(profile, p15card, file); sc_file_free(file); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); } /* * Store a private key */ static int miocos_store_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, struct sc_pkcs15_prkey *key) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)object->data; struct sc_pkcs15_prkey_rsa *rsa; struct sc_file *file = NULL; int r; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (object->type != SC_PKCS15_TYPE_PRKEY_RSA || key->algorithm != SC_ALGORITHM_RSA) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "MioCOS supports only 1024-bit RSA keys."); rsa = &key->u.rsa; if (rsa->modulus.len != 128) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "MioCOS supports only 1024-bit RSA keys."); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "store key with ID:%s and path:%s\n", sc_pkcs15_print_id(&key_info->id), sc_print_path(&key_info->path)); r = sc_select_file(p15card->card, &key_info->path, &file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot store key: select key file failed"); r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "No authorisation to store private key"); r = miocos_update_private_key(profile, p15card->card, rsa); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); } static struct sc_pkcs15init_operations sc_pkcs15init_miocos_operations = { NULL, /* erase_card */ NULL, /* init_card */ miocos_create_dir, NULL, /* create_domain */ miocos_select_pin_reference, miocos_create_pin, NULL, /* select_key_reference */ miocos_create_key, miocos_store_key, NULL, /* generate_key */ NULL, NULL, /* encode private/public key */ NULL, /* finalize_card */ NULL, /* delete_object */ NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ NULL /* sanity_check */ }; struct sc_pkcs15init_operations *sc_pkcs15init_get_miocos_ops(void) { return &sc_pkcs15init_miocos_operations; } opensc-0.13.0/src/pkcs15init/README0000644000015201777760000000107412057406034013470 00000000000000 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.13.0/src/pkcs15init/pkcs15-iasecc.c0000644000015201777760000017221512057406034015315 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 #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, "Cannnot 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->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]); } 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 %i; 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; 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); 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); if (file) sc_file_free(file); 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: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "'IDA' not actually 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 instanciate private key file"); /* Convert PKCS15 ACLs to SE ACLs */ rv = iasecc_file_convert_acls(ctx, profile, 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) 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 { LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Unknown SCB method"); } } /* 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_MEMORY_FAILURE; *(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", 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_MEMORY_FAILURE, "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); 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) LOG_FUNC_RETURN(ctx, SC_ERROR_MEMORY_FAILURE); sdo->docp.non_repudiation.tag = IASECC_DOCP_TAG_NON_REPUDATION; sdo->docp.non_repudiation.size = 1; sdo->data.prv_key.compulsory.value = calloc(1, 1); if (!sdo->data.prv_key.compulsory.value) LOG_FUNC_RETURN(ctx, SC_ERROR_MEMORY_FAILURE); 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) LOG_FUNC_RETURN(ctx, SC_ERROR_MEMORY_FAILURE); 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_REPUDATION can be used only for the localy 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 { LOG_TEST_RET(ctx, rv, "IasEcc: error while getting private key SDO data"); } if (out) *out = 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_MEMORY_FAILURE; 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); 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) LOG_FUNC_RETURN(ctx, SC_ERROR_MEMORY_FAILURE); 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) LOG_FUNC_RETURN(ctx, SC_ERROR_MEMORY_FAILURE); 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) LOG_FUNC_RETURN(ctx, SC_ERROR_MEMORY_FAILURE); sdo->data.pub_key.compulsory.tag = IASECC_SDO_PUBKEY_TAG_COMPULSORY; sdo->data.pub_key.compulsory.size = 1; } else { LOG_TEST_RET(ctx, rv, "iasecc_sdo_allocate_pubkey() error while getting public key SDO data"); } if (out) *out = 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 = card->ctx; struct sc_file *file = sc_file_new(); unsigned ii; int rv; LOG_FUNC_CALLED(ctx); if (file == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); else if (!card || !sdo) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); 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); } static 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) { 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; LOG_TEST_RET(ctx, rv, "SDO PRIVATE KEY UPDATE authentication failed"); if (dummy_file) sc_file_free(dummy_file); 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; key_info->access_flags |= SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE; key_info->access_flags |= SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE; 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(%i):%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"); key_info->usage |= SC_PKCS15_PRKEY_USAGE_SIGN; if (sdo_prvkey->docp.non_repudiation.value && sdo_prvkey->docp.non_repudiation.value[0]) { key_info->usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; 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"); key_info->usage |= SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER; } 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"); key_info->usage |= SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP; } } 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_RET(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_RET(ctx, rv, "create key slot: cannot instantiate PUBKEY_RSA file"); rv = iasecc_file_convert_acls(ctx, profile, file_p_prvkey); LOG_TEST_RET(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_RET(ctx, rv, "create key slot: cannot convert ACLs of the public key file"); rv = sc_profile_get_parent(profile, "private-key", &parent); LOG_TEST_RET(ctx, rv, "create key slot: cannot get parent of private key file"); rv = iasecc_file_convert_acls(ctx, profile, parent); LOG_TEST_RET(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_RET(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_RET(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_RET(ctx, rv, "create key slot: cannot create public key: ctl failed"); 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:%i,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 %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_RET(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_RET(ctx, rv, "Cannot create key slot"); } rv = sc_pkcs15_allocate_object_content(ctx, object, (unsigned char *)sdo_prvkey, sizeof(struct iasecc_sdo)); LOG_TEST_RET(ctx, rv, "Failed to allocate PrvKey SDO as object content"); rv = iasecc_pkcs15_fix_private_key_attributes(profile, p15card, object, (struct iasecc_sdo *)object->content.value); LOG_TEST_RET(ctx, rv, "Failed to fix private key PKCS#15 attributes"); key_info->path.len = 0; iasecc_sdo_free(card, sdo_pubkey); 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 long caps; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "generate key(bits:%i,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 %u\n", keybits); return SC_ERROR_INVALID_ARGUMENTS; } /* TODO: Check if native IAS middleware accepts the meaningfull 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"); if (file) 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; 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_MEMORY_FAILURE); 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_MEMORY_FAILURE); 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, &pubkey->data.value, &pubkey->data.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, pubkey->data.value, pubkey->data.len); LOG_TEST_RET(ctx, rv, "Failed to allocate public key as object content"); iasecc_sdo_free(card, sdo_pubkey); 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:%i,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:%i,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"); if (file) 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_MEMORY_FAILURE; 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); 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; LOG_TEST_RET(ctx, rv, "iasecc_pkcs15_delete_sdo() UPDATE authentication failed for SDO"); if (dummy_file) sc_file_free(dummy_file); 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); LOG_TEST_RET(ctx, rv, "iasecc_pkcs15_delete_sdo() store empty private key failed"); } 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 = sc_file_new(); int rv, key_ref; LOG_FUNC_CALLED(ctx); switch(object->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PUBKEY: sc_log(ctx, "Ignore delete of SDO-PubKey(ref:%X) '%s', path %s", key_ref, object->label, sc_print_path(path)); key_ref = ((struct sc_pkcs15_pubkey_info *)object->data)->key_reference; 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->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; char guid[39]; 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"); rv = sc_pkcs15_get_guid(p15card, key_obj, 1, guid, sizeof(guid)); 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 = (unsigned char *)guid; data_args.der_encoded.len = strlen(guid); 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, strlen(guid)); 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; char guid[39]; int rv, ii, keys_num; LOG_FUNC_CALLED(ctx); rv = sc_pkcs15_get_guid(p15card, key_obj, 1, guid, sizeof(guid)); 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); rv = sc_pkcs15_read_data_object(p15card, (struct sc_pkcs15_data_info *)data_obj->data, &dod); LOG_TEST_RET(ctx, rv, "Cannot read from 'CSP/'Default Key Container'"); if (strlen(guid) != dod->data_len || memcmp(guid, dod->data, strlen(guid))) LOG_FUNC_RETURN(ctx, SC_SUCCESS); 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; char guid[40]; unsigned char data[SC_PKCS15_MAX_ID_SIZE + 6]; size_t offs; int rv; LOG_FUNC_CALLED(ctx); memset(guid, 0, sizeof(guid)); rv = sc_pkcs15_get_guid(p15card, key_obj, 1, guid, sizeof(guid) - 1); LOG_TEST_RET(ctx, rv, "Cannot get private key GUID"); sc_log(ctx, "New key GUID: '%s'", 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 = 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; char guid[39]; int rv; LOG_FUNC_CALLED(ctx); rv = sc_pkcs15_get_guid(p15card, key_obj, 1, guid, sizeof(guid)); LOG_TEST_RET(ctx, rv, "Cannot get private key GUID"); rv = sc_pkcs15_find_data_object_by_name(p15card, "CSP", 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; iasecc_pkcs15_add_access_rule(object, SC_PKCS15_ACCESS_RULE_MODE_READ, NULL); 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; 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"); rv = iasecc_pkcs15_fix_file_access(p15card, pfile, object); LOG_TEST_RET(ctx, rv, "encode file access rules failed"); if (pfile) 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++) { struct sc_path fpath; 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"); fpath = file->path; 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); } if (indx == MAX_DATA_OBJS) LOG_TEST_RET(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_RET(ctx, rv, "iasecc_store_data_object() cannot convert profile ACLs"); rv = sc_profile_get_parent(profile, "public-data", &parent); LOG_TEST_RET(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_RET(ctx, rv, "iasecc_store_data_object() cannot select parent"); rv = sc_select_file(card, &file->path, &cfile); if (!rv) { rv = sc_pkcs15init_authenticate(profile, p15card, cfile, SC_AC_OP_DELETE); LOG_TEST_RET(ctx, rv, "iasecc_store_data_object() DELETE authentication failed"); rv = iasecc_pkcs15_delete_file(p15card, profile, cfile); LOG_TEST_RET(ctx, rv, "s_pkcs15init_store_data_object() delete pkcs15 file error"); } else if (rv != SC_ERROR_FILE_NOT_FOUND) { LOG_TEST_RET(ctx, rv, "iasecc_store_data_object() select file error"); } rv = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_CREATE); LOG_TEST_RET(ctx, rv, "iasecc_store_data_object() parent CREATE authentication failed"); file->size = data->len; rv = sc_create_file(card, file); LOG_TEST_RET(ctx, rv, "iasecc_store_data_object() cannot create DATA file"); rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); LOG_TEST_RET(ctx, rv, "iasecc_store_data_object() data file UPDATE authentication failed"); rv = sc_update_binary(card, 0, data->value, data->len, 0); LOG_TEST_RET(ctx, rv, "iasecc_store_data_object() update DATA file failed"); if (path) *path = file->path; if (parent) sc_file_free(parent); if (file) sc_file_free(file); if (cfile) 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 int iasecc_emu_update_tokeninfo(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_tokeninfo *tinfo) { LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); } 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; } #endif /* ENABLE_OPENSSL */ opensc-0.13.0/src/pkcs15init/gpk.profile0000644000015201777760000000554712057406034014764 00000000000000# # PKCS15 r/w profile for GPK cards # cardinfo { max-pin-length = 8; pin-encoding = BCD; pin-pad-char = 0x00; } # Additional filesystem info. # This is added to the file system info specified in the # main profile. filesystem { DF MF { ACL = CREATE=PRO1; DF PKCS15-AppDF { # The PIN file. # The GPK supports just one PIN file per DF, and the file # can hold up to 8 pins (or 4 PIN/PUK pairs). # # Note1: many commands use the short file ID (i.e. the lower # 5 bits of the FID) so you must be careful when picking FIDs # for the public key and PIN files. EF pinfile { file-id = 0000; structure = 0x21; # GPK specific record-length = 8; size = 64; # room for 8 pins ACL = *=NEVER; } # This template defines files for keys, certificates etc. # # When instantiating the template, each file id will be # combined with the last octet of the object's pkcs15 id # to form a unique file ID. template key-domain { # Private key files. # GPK private key files will never let you read the private key # part, so it's okay to set READ=NONE. What's more, we may need # read access so we're able to check the key size/type. EF private-key { file-id = 3010; # This is the base FileID structure = 0x2C; # GPK specific ACL = *=NEVER, READ=NONE, CRYPTO=$PIN, UPDATE=$PIN, WRITE=$PIN; } # Extractable private keys are stored in transparent EFs. # Encryption of the content is performed by libopensc. EF extractable-key { file-id = 3100; structure = transparent; ACL = *=NEVER, READ=$PIN, UPDATE=$PIN, WRITE=$PIN; } # data objects are stored in transparent EFs. EF data { file-id = 3200; structure = transparent; ACL = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN; } # private data objects are stored in transparent EFs. EF privdata { file-id = 3220; structure = transparent; ACL = *=NEVER, READ=$PIN, UPDATE=$PIN, WRITE=$PIN; } EF public-key { file-id = 3300; structure = transparent; ACL = *=NONE; } # Certificate template EF certificate { file-id = 3400; structure = transparent; ACL = *=NONE; } } } } } opensc-0.13.0/src/pkcs15init/epass2003.profile0000644000015201777760000001026412057406034015613 00000000000000# # 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.13.0/src/pkcs15init/sc-hsm.profile0000644000015201777760000000223112057406034015360 00000000000000# # PKCS15 r/w profile for SmartCard-HSM cards # cardinfo { label = "SmartCard-HSM"; manufacturer = "CardContact"; max-pin-length = 16; min-pin-length = 6; pin-encoding = ascii-numeric; } # 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; } } 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; exclusive-aid = E8:2B:06:01:04:01:81:C3:1F:02: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; } } } } opensc-0.13.0/src/pkcs15init/pkcs15-rtecp.c0000644000015201777760000005054212057406034015201 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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); assert(file); sc_file_free(file); } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Get MF info failed"); assert(file); r = sc_create_file(card, file); assert(file); sc_file_free(file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Create MF failed"); r = sc_profile_get_file(profile, "DIR", &file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Get DIR file info failed"); assert(file); r = sc_create_file(card, file); assert(file); sc_file_free(file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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"); 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) SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot fixup the ACLs of PIN file"); acl = sc_file_get_acl_entry(file, SC_AC_OP_PIN_RESET); if (acl && acl->method == SC_AC_CHV && acl->key_ref == RTECP_SO_PIN_REF) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Allow reset of User PIN with SoPIN\n"); reset_by_sopin = 1; } sc_file_free(file); } } file = sc_file_new(); if (!file) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, 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/ for Miller- * RSA_PUBkey Rabin test Attempts Reserve */ const unsigned char prkey_prop[] = { 0x23, 0x1F, 0, 0xFF, 0, 0 }; const unsigned char pbkey_prop[] = { 0x33, 0x1F, 0, 0xFF, 0, 0 }; /* GOSTR3410_PRkey/ * GOSTR3410_PUBkey paramset Attempts Reserve */ unsigned char prgkey_prop[] = { 0x03, '?', 0, 0xFF, 0, 0 }; unsigned char pbgkey_prop[] = { 0x13, '?', 0, 0xFF, 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "Unsupported key size %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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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); assert(file); sc_file_free(file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Authenticate failed"); file = sc_file_new(); if (!file) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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) 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) r = sc_create_file(p15card->card, file); assert(file); sc_file_free(file); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s\n", "Store public key failed"); } end: assert(buf); free(buf); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; } } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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); } 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 */ NULL, /* 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.13.0/src/pkcs15init/pkcs15-westcos.c0000644000015201777760000001712512057406034015553 00000000000000/* * pkcs15-westcos.c: pkcs15 support for westcos card * * Copyright (C) 2009 francois.leblanc@cev-sa.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #ifdef ENABLE_OPENSSL #include #include #include #include #include #include #endif #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "pkcs15-init.h" #include "profile.h" static int westcos_pkcs15init_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { int r; struct sc_path path; sc_format_path("3F00", &path); r = sc_select_file(p15card->card, &path, NULL); if(r) return (r); return r; } static int westcos_pkcs15init_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { int r; /* Create the application DF */ r = sc_pkcs15init_create_file(profile, p15card, df); r = sc_select_file(p15card->card, &df->path, NULL); if(r) return r; return 0; } /* * Select the PIN reference */ static int westcos_pkcs15_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_auth_info_t *auth_info) { if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) { auth_info->attrs.pin.reference = 1; } else { auth_info->attrs.pin.reference = 0; } return 0; } /* * Create a new PIN inside a DF */ static int westcos_pkcs15_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df, sc_pkcs15_object_t *pin_obj, const u8 *pin, size_t pin_len, const u8 *puk, size_t puk_len) { int r; sc_file_t *pinfile = NULL; if(pin_len>9 || puk_len>9) return SC_ERROR_INVALID_ARGUMENTS; r = sc_profile_get_file(profile, "PINFILE", &pinfile); if(r < 0) return r; r = sc_create_file(p15card->card, pinfile); if(r) { if(r != SC_ERROR_FILE_ALREADY_EXISTS) return (r); r = sc_select_file(p15card->card, &pinfile->path, NULL); if(r) return (r); } if(pinfile) sc_file_free(pinfile); if(pin != NULL) { sc_changekey_t ck; struct sc_pin_cmd_pin pin_cmd; int ret; memset(&pin_cmd, 0, sizeof(pin_cmd)); memset(&ck, 0, sizeof(ck)); memcpy(ck.key_template, "\x1e\x00\x00\x10", 4); pin_cmd.encoding = SC_PIN_ENCODING_GLP; pin_cmd.len = pin_len; pin_cmd.data = pin; pin_cmd.max_length = 8; ret = sc_build_pin(ck.new_key.key_value, sizeof(ck.new_key.key_value), &pin_cmd, 1); if(ret < 0) return SC_ERROR_CARD_CMD_FAILED; ck.new_key.key_len = ret; r = sc_card_ctl(p15card->card, SC_CARDCTL_WESTCOS_CHANGE_KEY, &ck); if(r) return r; } if(puk != NULL) { sc_changekey_t ck; struct sc_pin_cmd_pin puk_cmd; int ret; memset(&puk_cmd, 0, sizeof(puk_cmd)); memset(&ck, 0, sizeof(ck)); memcpy(ck.key_template, "\x1e\x00\x00\x20", 4); puk_cmd.encoding = SC_PIN_ENCODING_GLP; puk_cmd.len = puk_len; puk_cmd.data = puk; puk_cmd.max_length = 8; ret = sc_build_pin(ck.new_key.key_value, sizeof(ck.new_key.key_value), &puk_cmd, 1); if(ret < 0) return SC_ERROR_CARD_CMD_FAILED; ck.new_key.key_len = ret; r = sc_card_ctl(p15card->card, SC_CARDCTL_WESTCOS_CHANGE_KEY, &ck); if(r) return r; } return 0; } /* * Create a new key file */ static int westcos_pkcs15init_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj) { if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { return SC_ERROR_NOT_SUPPORTED; } return 0; } /* * Store a private key */ static int westcos_pkcs15init_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_prkey_t *key) { return SC_ERROR_NOT_SUPPORTED; } /* * Generate key */ static int westcos_pkcs15init_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) { #ifndef ENABLE_OPENSSL return SC_ERROR_NOT_SUPPORTED; #else int r = SC_ERROR_UNKNOWN; long lg; u8 *p; sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; RSA *rsa = NULL; BIGNUM *bn = NULL; BIO *mem = NULL; sc_file_t *prkf = NULL; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { return SC_ERROR_NOT_SUPPORTED; } #if OPENSSL_VERSION_NUMBER>=0x00908000L rsa = RSA_new(); bn = BN_new(); mem = BIO_new(BIO_s_mem()); if(rsa == NULL || bn == NULL || mem == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto out; } if(!BN_set_word(bn, RSA_F4) || !RSA_generate_key_ex(rsa, key_info->modulus_length, bn, NULL)) #else mem = BIO_new(BIO_s_mem()); if(mem == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto out; } rsa = RSA_generate_key(key_info->modulus_length, RSA_F4, NULL, NULL); if (!rsa) #endif { r = SC_ERROR_UNKNOWN; goto out; } rsa->meth = RSA_PKCS1_SSLeay(); if(pubkey != NULL) { if(!i2d_RSAPublicKey_bio(mem, rsa)) { r = SC_ERROR_UNKNOWN; goto out; } lg = BIO_get_mem_data(mem, &p); pubkey->algorithm = SC_ALGORITHM_RSA; r = sc_pkcs15_decode_pubkey(p15card->card->ctx, pubkey, p, lg); } (void) BIO_reset(mem); if(!i2d_RSAPrivateKey_bio(mem, rsa)) { r = SC_ERROR_UNKNOWN; goto out; } lg = BIO_get_mem_data(mem, &p); /* Get the private key file */ r = sc_profile_get_file_by_path(profile, &key_info->path, &prkf); if (r < 0) { char pbuf[SC_MAX_PATH_STRING_SIZE]; r = sc_path_print(pbuf, sizeof(pbuf), &key_info->path); if (r != SC_SUCCESS) pbuf[0] = '\0'; goto out; } prkf->size = lg; r = sc_pkcs15init_create_file(profile, p15card, prkf); if(r) goto out; r = sc_pkcs15init_update_file(profile, p15card, prkf, p, lg); if(r) goto out; out: if(mem) BIO_free(mem); if(bn) BN_free(bn); if(rsa) RSA_free(rsa); if(prkf) sc_file_free(prkf); return r; #endif } static int westcos_pkcs15init_finalize_card(sc_card_t *card) { int r; /* be sure authentificate card */ r = sc_card_ctl(card, SC_CARDCTL_WESTCOS_AUT_KEY, NULL); if(r) return (r); return sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_USER); } static struct sc_pkcs15init_operations sc_pkcs15init_westcos_operations = { NULL, /* erase_card */ westcos_pkcs15init_init_card, /* init_card */ westcos_pkcs15init_create_dir, /* create_dir */ NULL, /* create_domain */ westcos_pkcs15_select_pin_reference, /* select_pin_reference */ westcos_pkcs15_create_pin, /* create_pin */ NULL, /* select_key_reference */ westcos_pkcs15init_create_key, /* create_key */ westcos_pkcs15init_store_key, /* store_key */ westcos_pkcs15init_generate_key, /* generate_key */ NULL, NULL, /* encode private/public key */ westcos_pkcs15init_finalize_card, /* finalize_card */ NULL, /* delete_object */ NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ NULL /* sanity_check */ }; struct sc_pkcs15init_operations* sc_pkcs15init_get_westcos_ops(void) { return &sc_pkcs15init_westcos_operations; } opensc-0.13.0/src/pkcs15init/ias_adele_admin2.profile0000644000015201777760000001162612057406034017336 00000000000000# # 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 inconporating 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 insted 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.13.0/src/pkcs15init/pkcs15-starcos.c0000644000015201777760000006606012057406034015544 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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; /* 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) { 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; 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) { 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; 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; 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 (int) 12 + (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 */ size_t mod_len = rsa->modulus.len & 0xff, exp_len = rsa->exponent.len & 0xff; if (!buf) return (int) 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); 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) 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) return r; endpos = starcos_ipf_get_lastpos(buf, len); free(buf); return sc_update_binary(card, 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; /* requiere 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 DSA 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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.13.0/src/pkcs15init/pkcs15-epass2003.c0000644000015201777760000005271612057406034015511 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 surpport SO-PIN and SO-PUK. You can unblock key with PUK. \n"); { /* MF */ struct sc_file *mf_file; struct sc_file *skey_file; ret = sc_profile_get_file(profile, "MF", &mf_file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Get MF info failed"); ret = sc_create_file(card, mf_file); sc_file_free(mf_file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Create MF failed"); ret = sc_profile_get_file(profile, "SKey-MF", &skey_file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Get SKey info failed"); ret = sc_create_file(card, skey_file); sc_file_free(skey_file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Create SKey failed"); } { /* EF(DIR) */ struct sc_file *dir_file; /* get dir profile */ ret = sc_profile_get_file(profile, "DIR", &dir_file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Get EF(DIR) info failed"); ret = sc_create_file(card, dir_file); sc_file_free(dir_file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Get PKCS15-AppDF info failed"); ret = sc_create_file(card, df_file); sc_file_free(df_file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Create PKCS15-AppDF failed"); ret = sc_profile_get_file(profile, "SKey-AppDF", &skey_file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Get SKey info failed"); ret = sc_create_file(card, skey_file); sc_file_free(skey_file); ret = sc_profile_get_file(profile, "MAXPIN", &ef_file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Get MAXPIN info failed"); ret = sc_create_file(card, ef_file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Create MAXPIN failed"); ret = sc_select_file(card, &(ef_file->path), &ef_file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Select MAXPIN failed"); ret = sc_profile_get_pin_id(profile, 2, &id); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Get User PIN id error!"); user_maxtries = (u8) sc_profile_get_pin_retries(profile, id); ret = sc_profile_get_pin_id(profile, 1, &id); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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 (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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); 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; const char *_template = NULL, *desc = NULL; unsigned int structure = 0xFFFFFFFF; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "type %X; num %i\n", type, num); while (1) { switch (type) { 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_PUBKEY_DSA: desc = "DSA public key"; _template = "public-key"; 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File type %X not supported by card driver", type); return SC_ERROR_INVALID_ARGUMENTS; } type &= SC_PKCS15_TYPE_CLASS_MASK; } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "template %s; num %i\n", _template, num); if (sc_profile_get_file(profile, _template, &file) < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Profile doesn't define %s template '%s'\n", desc, _template); return SC_ERROR_NOT_SUPPORTED; } 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "file size %i; ef type %i/%i; id %04X, path_len %i\n", file->size, file->type, file->ef_structure, file->id, file->path.len); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "file path: %s", sc_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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "index %i; id %s\n", idx, sc_pkcs15_print_id(&key_info->id)); if (key->algorithm != SC_ALGORITHM_RSA || key->algorithm != SC_ALGORITHM_RSA) SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "store key: only support RSA"); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "create key: failed to allocate new key object"); file->size = keybits; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "private key path: %s", sc_print_path(&(file->path))); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "private key_info path: %s", sc_print_path(&(key_info->path))); r = sc_delete_file(p15card->card, &file->path); /* create */ r = sc_pkcs15init_create_file(profile, p15card, file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "create key: failed to create key file"); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "index %i; keybits %i\n", idx, keybits); if (keybits < 1024 || keybits > 2048 || (keybits % 0x20)) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Unsupported key size %u\n", keybits); return SC_ERROR_INVALID_ARGUMENTS; } path = key_info->path; path.len -= 2; r = sc_select_file(card, &path, &tfile); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "generate key: no private object DF"); r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "store key: cannot update private key"); if (file) 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; size_t idx = key_info->key_reference; size_t keybits = key_info->modulus_length; struct sc_file *tfile = NULL, *prkf = 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) return SC_ERROR_NOT_SUPPORTED; /* allocate key object */ r = cosm_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, idx, &file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "create key: failed to allocate new key object"); file->size = keybits; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "private key path: %s", sc_print_path(&file->path)); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "private key_info path: %s", sc_print_path(&(key_info->path))); r = sc_delete_file(p15card->card, &file->path); /* create */ r = sc_pkcs15init_create_file(profile, p15card, file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "create key: failed to create key file"); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "index %i; keybits %i\n", idx, keybits); if (keybits < 1024 || keybits > 2048 || (keybits % 0x20)) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Unsupported key size %u\n", keybits); return SC_ERROR_INVALID_ARGUMENTS; } path = key_info->path; path.len -= 2; r = sc_select_file(card, &path, &tfile); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "generate key: no private object DF"); r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_CRYPTO); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "generate key: pkcs15init_authenticate(SC_AC_OP_CRYPTO) failed"); r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_CREATE); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "generate key: pkcs15init_authenticate(SC_AC_OP_CREATE) failed"); sc_file_free(tfile); if ((r = cosm_new_file(profile, card, SC_PKCS15_TYPE_PUBKEY_RSA, idx, &pukf)) < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "generate key: create temporary pukf failed\n"); goto failed; } pukf->size = keybits; pukf->id = pukf->path.value[pukf->path.len - 2] * 0x100 + pukf->path.value[pukf->path.len - 1]; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "public key size %i; 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_delete_by_path(profile, p15card, &pukf->path); if (r != SC_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "generate key: failed to delete existing key file\n"); goto failed; } } /* create */ r = sc_pkcs15init_create_file(profile, p15card, pukf); if (r != SC_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "generate key: pukf create file failed\n"); goto 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; r = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_GENERATE_KEY, &gendat); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "generate RSA key pair failed"); /* get the modulus */ if (pubkey) { 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 failed; } 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); failed: if (pukf) sc_file_free(pukf); if (prkf) sc_file_free(prkf); 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; struct sc_pkcs15_object *objs[32]; int rv, nn, ii, update_df = 0; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Check and if needed update PinFlags"); rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 32); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to get PINs"); nn = rv; sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &profile_auth); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to get PIN info"); 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OBJECT_NOT_FOUND, "Cannot find AODF"); rv = sc_pkcs15init_update_any_df(p15card, profile, df, 0); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Update AODF error"); } 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.13.0/src/pkcs15init/pkcs15.profile0000644000015201777760000000761012057406034015302 00000000000000# # 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 # mozilla: SHA1(modulus) for RSA, SHA1(pub) for DSA # 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 = 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.13.0/src/pkcs15init/rutoken_ecp.profile0000644000015201777760000001362712057406034016517 00000000000000# # 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 = 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 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 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.13.0/src/pkcs15init/starcos.profile0000644000015201777760000000524412057406034015653 00000000000000# # 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; df_acl = *=$SOPIN; } } option onepin { macros { so-pin-flags = initialized, needs-padding; isf_acl = WRITE=$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.13.0/src/pkcs15init/incrypto34.profile0000644000015201777760000000522512057406034016212 00000000000000# # PKCS15 r/w profile for Incard's Incrypto34 (aka Italian CIE/CNS) # cardinfo { max-pin-length = 8; pin-encoding = ascii-numeric; pin-pad-char = 0x00; } # Define reasonable limits for PINs and PUK # We set the reference for SO pin+puk here, because # those are hard-coded (if a PUK us assigned). PIN so-pin { reference = 0; } PIN so-puk { reference = 1; } PIN user-pin { attempts = 3; } PIN user-puk { attempts = 10; } # Additional filesystem info. # This is added to the file system info specified in the # main profile. filesystem { DF MF { DF PKCS15-AppDF { size = 2048; # Prevent unauthorized updates of basic security # objects via PUT DATA OCI. ACL = UPDATE=NEVER; # Bump the size of the EF(PrKDF) - with split # keys, we may need a little more room. EF PKCS15-PrKDF { size = 256; } # This template defines files for keys, certificates etc. # # When instantiating the template, each file id will be # combined with the last octet of the object's pkcs15 id # to form a unique file ID. template key-domain { BSO private-key { # here ACLs should be defined } EF public-key { file-id = 3003; structure = transparent; ACL = *=NEVER, READ=NONE, UPDATE=$PIN, ERASE=$PIN; } # Certificate template EF certificate { file-id = 3104; structure = transparent; ACL = *=NEVER, READ=NONE, UPDATE=$PIN, ERASE=$PIN; } # Extractable private keys are stored in transparent EFs. # Encryption of the content is performed by libopensc. EF extractable-key { file-id = 3201; structure = transparent; ACL = *=NEVER, READ=$PIN, UPDATE=$PIN, ERASE=$PIN; } # data objects are stored in transparent EFs. EF data { file-id = 3302; structure = transparent; ACL = *=NEVER, READ=NONE, UPDATE=$PIN, ERASE=$PIN; } # private data objects are stored in transparent EFs. EF privdata { file-id = 3402; structure = transparent; ACL = *=NEVER, READ=$PIN, UPDATE=$PIN, ERASE=$PIN; } } # This is needed when generating a key on-card. EF tempfile { file-id = 7EAD; structure = linear-variable-tlv; ACL = *=NONE; size = 512; } } } } opensc-0.13.0/src/pkcs15init/myeid.profile0000644000015201777760000001357612057406034015313 00000000000000# # 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; 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; 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; 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-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-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 = READ=NONE, UPDATE=$PIN, DELETE=$PIN, GENERATE=$PIN; } EF public-key { file-id = 5501; structure = transparent; acl = READ=NONE, 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 = 4501; structure = transparent; acl = READ=NONE, UPDATE=$PIN, DELETE=$PIN; } } } } } opensc-0.13.0/src/pkcs15init/pkcs15-lib.c0000644000015201777760000034042212057406034014631 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #include #include #ifdef HAVE_GETTIMEOFDAY #include #endif #ifdef HAVE_STRINGS_H #include #endif #include #ifdef ENABLE_OPENSSL #include #include #include #include #include #include #include #endif #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 "profile.h" #include "pkcs15-init.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 0x03 #define DEFAULT_PRKEY_FLAGS 0x03 #define DEFAULT_PUBKEY_FLAGS 0x02 #define DEFAULT_CERT_FLAGS 0x02 #define DEFAULT_DATA_FLAGS 0x02 #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 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, struct sc_pkcs15init_keygen_args *args, unsigned int bits, unsigned int *out_bits); static int check_key_compatibility(struct sc_pkcs15_card *, struct sc_pkcs15_prkey *, unsigned int, unsigned int, unsigned int); 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 prkey_pkcs15_algo(struct sc_pkcs15_card *, struct sc_pkcs15_prkey *); static int select_intrinsic_id(struct sc_pkcs15_card *, struct sc_profile *, int, struct sc_pkcs15_id *, void *); static int select_id(struct sc_pkcs15_card *, int, struct sc_pkcs15_id *); static int select_object_path(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15_object *, struct sc_path *); static int sc_pkcs15init_get_pin_path(struct sc_pkcs15_card *, struct sc_pkcs15_id *, struct sc_path *); static int sc_pkcs15init_qualify_pin(struct sc_card *, const char *, unsigned int, struct sc_pkcs15_auth_info *); 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 }, { "gpk", (void *) sc_pkcs15init_get_gpk_ops }, { "miocos", (void *) sc_pkcs15init_get_miocos_ops }, { "flex", (void *) sc_pkcs15init_get_cryptoflex_ops }, { "cyberflex", (void *) sc_pkcs15init_get_cyberflex_ops }, { "cardos", (void *) sc_pkcs15init_get_cardos_ops }, { "etoken", (void *) sc_pkcs15init_get_cardos_ops }, /* legacy */ { "jcop", (void *) sc_pkcs15init_get_jcop_ops }, { "starcos", (void *) sc_pkcs15init_get_starcos_ops }, { "oberthur", (void *) sc_pkcs15init_get_oberthur_ops }, { "openpgp", (void *) sc_pkcs15init_get_openpgp_ops }, { "setcos", (void *) sc_pkcs15init_get_setcos_ops }, { "incrypto34", (void *) sc_pkcs15init_get_incrypto34_ops }, { "muscle", (void*) sc_pkcs15init_get_muscle_ops }, { "asepcos", (void*) sc_pkcs15init_get_asepcos_ops }, { "entersafe",(void*) sc_pkcs15init_get_entersafe_ops }, { "epass2003",(void*) sc_pkcs15init_get_epass2003_ops }, { "rutoken_ecp", (void *) sc_pkcs15init_get_rtecp_ops }, { "westcos", (void *) sc_pkcs15init_get_westcos_ops }, { "myeid", (void *) sc_pkcs15init_get_myeid_ops }, { "sc-hsm", (void *) sc_pkcs15init_get_sc_hsm_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_empty_callback(void *ptr) { } /* * 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); 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"); blk = blocks[0]; free(blocks); if (blk == NULL) continue; blocks = scconf_find_blocks(ctx->conf, blk, "pkcs15init", name); 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); } } #if 0 r = sc_pkcs15init_read_info(card, profile); if (r < 0) { sc_profile_free(profile); LOG_TEST_RET(ctx, r, "Read info error"); } #endif /* 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))) strcpy(card_profile, driver); 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); } } if (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 = p15card->card->ctx; int rv; 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, 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); 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, "parent 'DELETE' authentication failed"); } } LOG_TEST_RET(ctx, rv, "'DELETE' authentication failed"); memset(&path, 0, sizeof(path)); path.type = SC_PATH_TYPE_FILE_ID; 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); 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; 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; 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_enum_apps(card); if (aid) { sc_log(ctx, "finalize profile for AID %s", sc_dump_hex(aid->value, aid->len)); app = sc_find_app(card, aid); if (!app) { sc_log(ctx, "Cannot find oncard application"); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); } } 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; 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."); /* If the profile requires an SO PIN, check min/max length */ if (args->so_pin_len) { const char *pin_label; sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &pin_ainfo); r = sc_pkcs15init_qualify_pin(card, "SO PIN", args->so_pin_len, &pin_ainfo); LOG_TEST_RET(ctx, r, "Failed to qualify SO PIN"); /* 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 qulify 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')", 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_RET(ctx, r, "Failed to add 'SOPIN' AUTH object"); } } /* Perform card-specific initialization */ if (profile->ops->init_card) { r = profile->ops->init_card(profile, p15card); if (r < 0 && pin_obj) { sc_pkcs15_remove_object(p15card, pin_obj); sc_pkcs15_free_object(pin_obj); } LOG_TEST_RET(ctx, r, "Card specific init failed"); } /* Create the application directory */ r = profile->ops->create_dir(profile, p15card, df); LOG_TEST_RET(ctx, r, "Create 'DIR' error"); /* Store SO PIN */ if (pin_obj) 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 0 if (r > 0 && profile->ops->finalize_dir) r = profile->ops->finalize_dir(profile, p15card); #endif if (pin_obj) /* Remove 'virtual' AUTH object . */ sc_pkcs15_remove_object(p15card, pin_obj); if (r < 0) sc_pkcs15_free_object(pin_obj); LOG_TEST_RET(ctx, r, "Card specific create application DF failed"); /* Store the PKCS15 information on the card * We cannot use sc_pkcs15_create() because it makes * all sorts of assumptions about DF and EF names, and * doesn't work if secure messaging is required for the * MF (which is the case with the GPK) */ app = (struct sc_app_info *)calloc(1, sizeof(*app)); if (app == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Failed to allocate application info"); 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) { if (p15card->tokeninfo->label) free(p15card->tokeninfo->label); p15card->tokeninfo->label = strdup(args->label); } app->label = strdup(p15card->tokeninfo->label); /* 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); /* FIXME: what to do if sc_pkcs15init_update_dir failed? */ } else { free(app); /* unused */ } sc_pkcs15init_write_info(p15card, profile, 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); 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); else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "In Old API store PUK object is not supported"); if (r >= 0) r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_AODF, pin_obj); else sc_pkcs15_free_object(pin_obj); profile->dirty = 1; 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); auth_info->auth_id = args->auth_id; /* Now store the PINs */ sc_log(ctx, "Store PIN(%s,authID:%s)", pin_obj->label, sc_pkcs15_print_id(&auth_info->auth_id)); r = sc_pkcs15init_create_pin(p15card, profile, pin_obj, args); if (r < 0) sc_pkcs15_free_object(pin_obj); LOG_TEST_RET(ctx, r, "Card specific create PIN failed."); r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_AODF, pin_obj); if (r < 0) sc_pkcs15_free_object(pin_obj); LOG_TEST_RET(ctx, r, "Failed to add PIN object"); if (args->puk_id.len) r = sc_pkcs15init_store_puk(p15card, profile, args); profile->dirty = 1; 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; struct sc_pkcs15_keyinfo_gostparams *keyinfo_gostparams; struct sc_pkcs15_object *object; const char *label; unsigned int usage; int r = 0, key_type; LOG_FUNC_CALLED(ctx); if (!res_obj || !keybits) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "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 = "Private Key"; /* 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 = prkey_pkcs15_algo(p15card, key); LOG_TEST_RET(ctx, key_type, "Unsupported key type"); object = sc_pkcs15init_new_object(key_type, label, &keyargs->auth_id, NULL); if (object == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "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; /* Path is selected below */ if (keyargs->access_flags & SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE) { key_info->access_flags &= ~SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE; key_info->native = 0; } /* Select a Key ID if the user didn't specify one, * otherwise make sure it's compatible with our intended use */ r = select_id(p15card, SC_PKCS15_TYPE_PRKEY, &keyargs->id); LOG_TEST_RET(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) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "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) { struct sc_pkcs15_ec_parameters *ecparams = &keyargs->params.ec; key_info->params.data = &keyargs->params.ec; key_info->params.free_params = sc_pkcs15init_empty_callback; key_info->field_length = ecparams->field_length; } r = select_object_path(p15card, profile, object, &key_info->path); LOG_TEST_RET(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_RET(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 */ LOG_TEST_RET(ctx, SC_ERROR_TOO_MANY_OBJECTS, "Failed to select key reference"); key_info->key_reference++; } } *res_obj = object; LOG_FUNC_RETURN(ctx, 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; struct sc_pkcs15_prkey_info *key_info; int r, caller_supplied_id = 0; LOG_FUNC_CALLED(ctx); /* check supported key size */ r = check_keygen_params_consistency(p15card->card, keygen_args, keybits, &keybits); LOG_TEST_RET(ctx, r, "Invalid key size"); if (check_key_compatibility(p15card, &keygen_args->prkey_args.key, keygen_args->prkey_args.x509_usage, keybits, SC_ALGORITHM_ONBOARD_KEY_GEN)) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Cannot generate key with the given parameters"); if (profile->ops->generate_key == NULL) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Key generation not supported"); if (keygen_args->prkey_args.id.len) { caller_supplied_id = 1; /* Make sure that private key's ID is the unique inside the PKCS#15 application */ r = sc_pkcs15_find_prkey_by_id(p15card, &keygen_args->prkey_args.id, NULL); if (!r) LOG_TEST_RET(ctx, SC_ERROR_NON_UNIQUE_ID, "Non unique ID of the private key object"); else if (r != SC_ERROR_OBJECT_NOT_FOUND) LOG_TEST_RET(ctx, r, "Find private key error"); } /* Set up the PrKDF object */ r = sc_pkcs15init_init_prkdf(p15card, profile, &keygen_args->prkey_args, &keygen_args->prkey_args.key, keybits, &object); LOG_TEST_RET(ctx, r, "Set up private key object error"); key_info = (struct sc_pkcs15_prkey_info *) object->data; if (keygen_args->prkey_args.guid) { object->guid = strdup(keygen_args->prkey_args.guid); if (!object->guid) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate guid"); sc_log(ctx, "new key GUID: '%s'", object->guid); } /* Set up the PuKDF info. The public key will be filled in * by the card driver's generate_key function called below. * Auth.ID of the public key object is left empty. */ memset(&pubkey_args, 0, sizeof(pubkey_args)); pubkey_args.id = keygen_args->prkey_args.id; pubkey_args.label = keygen_args->pubkey_label ? keygen_args->pubkey_label : object->label; pubkey_args.usage = keygen_args->prkey_args.usage; pubkey_args.x509_usage = keygen_args->prkey_args.x509_usage; pubkey_args.params.gost = keygen_args->prkey_args.params.gost; /* Generate the private key on card */ r = profile->ops->create_key(profile, p15card, object); LOG_TEST_RET(ctx, r, "Cannot generate key: create key failed"); r = profile->ops->generate_key(profile, p15card, object, &pubkey_args.key); LOG_TEST_RET(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 = select_intrinsic_id(p15card, profile, SC_PKCS15_TYPE_PUBKEY, &iid, &pubkey_args.key); LOG_TEST_RET(ctx, r, "Select intrinsic ID error"); if (iid.len) key_info->id = iid; } pubkey_args.id = key_info->id; r = sc_pkcs15_encode_pubkey(ctx, &pubkey_args.key, &object->content.value, &object->content.len); LOG_TEST_RET(ctx, r, "Failed to encode public key"); r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_PRKDF, object); LOG_TEST_RET(ctx, r, "Failed to add generated private key object"); if (!r && profile->ops->emu_store_data) { r = profile->ops->emu_store_data(p15card, profile, object, NULL, NULL); if (r == SC_ERROR_NOT_IMPLEMENTED) r = SC_SUCCESS; LOG_TEST_RET(ctx, r, "Card specific 'store data' failed"); } r = sc_pkcs15init_store_public_key(p15card, profile, &pubkey_args, NULL); LOG_TEST_RET(ctx, r, "Failed to store public key"); if (res_obj) *res_obj = object; sc_pkcs15_erase_pubkey(&pubkey_args.key); profile->dirty = 1; 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; struct sc_pkcs15_prkey_info *key_info; struct sc_pkcs15_prkey key; int keybits, idx, r = 0; LOG_FUNC_CALLED(ctx); /* 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 */ if (check_key_compatibility(p15card, &key, keyargs->x509_usage, keybits, 0)) { /* 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."); } /* Select a intrinsic Key ID if user didn't specify one */ r = 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"); key_info = (struct sc_pkcs15_prkey_info *) object->data; r = sc_pkcs15init_encode_prvkey_content(p15card, &key, object); LOG_TEST_RET(ctx, r, "Failed to encode public key"); /* Get the number of private keys already on this card */ idx = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PRKEY, NULL, 0); r = profile->ops->create_key(profile, p15card, object); LOG_TEST_RET(ctx, r, "Card specific 'create key' failed"); r = profile->ops->store_key(profile, p15card, object, &key); LOG_TEST_RET(ctx, r, "Card specific 'store key' failed"); sc_pkcs15_free_object_content(object); r = sc_pkcs15init_encode_prvkey_content(p15card, &key, object); LOG_TEST_RET(ctx, r, "Failed to encode public key"); /* Now update the PrKDF */ r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_PRKDF, object); LOG_TEST_RET(ctx, r, "Failed to add new private key PKCS#15 object"); if (keyargs->guid) { object->guid = strdup(keyargs->guid); if (!object->guid) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate guid"); sc_log(ctx, "new key GUID: '%s'", object->guid); } if (!r && profile->ops->emu_store_data) { r = profile->ops->emu_store_data(p15card, profile, object, NULL, NULL); if (r == SC_ERROR_NOT_IMPLEMENTED) r = SC_SUCCESS; LOG_TEST_RET(ctx, r, "Card specific 'store data' failed"); } if (r >= 0 && res_obj) *res_obj = object; profile->dirty = 1; 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; 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 keybits, type, usage; int r; LOG_FUNC_CALLED(ctx); if (!keyargs) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Store public key aborted"); /* Create a copy of the key first */ key = keyargs->key; switch (key.algorithm) { case SC_ALGORITHM_RSA: keybits = sc_pkcs15init_keybits(&key.u.rsa.modulus); type = SC_PKCS15_TYPE_PUBKEY_RSA; break; #ifdef SC_PKCS15_TYPE_PUBKEY_DSA case SC_ALGORITHM_DSA: keybits = sc_pkcs15init_keybits(&key.u.dsa.q); type = SC_PKCS15_TYPE_PUBKEY_DSA; break; #endif case SC_ALGORITHM_GOSTR3410: keybits = SC_PKCS15_GOSTR3410_KEYSIZE; type = SC_PKCS15_TYPE_PUBKEY_GOSTR3410; break; case SC_ALGORITHM_EC: keybits = key.u.ec.params.field_length; type = SC_PKCS15_TYPE_PUBKEY_EC; break; default: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "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) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "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) return SC_ERROR_OUT_OF_MEMORY; 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; /* Select a intrinsic Key ID if the user didn't specify one */ r = select_intrinsic_id(p15card, profile, SC_PKCS15_TYPE_PUBKEY, &keyargs->id, &key); LOG_TEST_RET(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_RET(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) LOG_TEST_RET(ctx, SC_ERROR_NON_UNIQUE_ID, "Non unique ID of the public key object"); else if (r != SC_ERROR_OBJECT_NOT_FOUND) LOG_TEST_RET(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_RET(ctx, r, "Encode public key error"); /* Now create key file and store key */ 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); if (r >= 0 && res_obj) *res_obj = object; profile->dirty = 1; 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; const char *label = NULL; int r; LOG_FUNC_CALLED(ctx); label = args->label; if (!label) label = "Certificate"; r = 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"); /* 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); 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_log(ctx, "Store cert(%s,ID:%s,der(%p,%i))", object->label, sc_pkcs15_print_id(&cert_info->id), args->der_encoded.value, args->der_encoded.len); if (profile->pkcs15.direct_certificates) sc_der_copy(&cert_info->value, &args->der_encoded); else 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); 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; r = sc_pkcs15init_store_data(p15card, profile, object, &args->der_encoded, &data_object_info->path); LOG_TEST_RET(ctx, r, "Store 'DATA' object error"); /* Now update the DDF */ r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_DODF, object); LOG_TEST_RET(ctx, r, "'DODF' update error"); if (r >= 0 && res_obj) *res_obj = object; profile->dirty = 1; 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)", 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) LOG_TEST_RET(ctx, r, "Cannot delete file"); r = sc_pkcs15init_update_file(profile, p15card, file, data->value, data->len); *path = file->path; if (file) 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 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, 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, struct sc_pkcs15init_keygen_args *params, unsigned int keybits, unsigned int *out_keybits) { struct sc_context *ctx = card->ctx; unsigned int alg = params->prkey_args.key.algorithm; int i, rv; if (alg == SC_ALGORITHM_EC) { struct sc_pkcs15_ec_parameters *ecparams = ¶ms->prkey_args.params.ec; 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 = ecparams->field_length; } if (out_keybits) *out_keybits = keybits; 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); } 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, struct sc_pkcs15_prkey *key, unsigned int x509_usage, unsigned int key_length, unsigned int flags) { struct sc_algorithm_info *info; unsigned int count; count = p15card->card->algorithm_count; for (info = p15card->card->algorithms; count--; info++) { /* don't check flags if none was specified */ if (info->algorithm != key->algorithm || info->key_length != key_length) continue; if (flags != 0 && ((info->flags & flags) != flags)) continue; if (key->algorithm == SC_ALGORITHM_RSA) { if (info->u._rsa.exponent != 0 && key->u.rsa.exponent.len != 0) { struct sc_pkcs15_bignum *e = &key->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 (key->algorithm == SC_ALGORITHM_EC) { } return SC_SUCCESS; } return 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; 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 #define GETBN(dst, src, mem) \ do { dst.len = BN_num_bytes(src); \ assert(dst.len <= sizeof(mem)); \ BN_bn2bin(src, dst.data = mem); \ } while (0) /* 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 */ if (!key->dmp1.len || !key->dmq1.len || !key->iqmp.len) { static u8 dmp1[256], dmq1[256], iqmp[256]; RSA *rsa; BIGNUM *aux; BN_CTX *bn_ctx; rsa = RSA_new(); rsa->n = BN_bin2bn(key->modulus.data, key->modulus.len, NULL); rsa->e = BN_bin2bn(key->exponent.data, key->exponent.len, NULL); rsa->d = BN_bin2bn(key->d.data, key->d.len, NULL); rsa->p = BN_bin2bn(key->p.data, key->p.len, NULL); rsa->q = BN_bin2bn(key->q.data, key->q.len, NULL); if (!rsa->dmp1) rsa->dmp1 = BN_new(); if (!rsa->dmq1) rsa->dmq1 = BN_new(); if (!rsa->iqmp) rsa->iqmp = BN_new(); aux = BN_new(); bn_ctx = BN_CTX_new(); BN_sub(aux, rsa->q, BN_value_one()); BN_mod(rsa->dmq1, rsa->d, aux, bn_ctx); BN_sub(aux, rsa->p, BN_value_one()); BN_mod(rsa->dmp1, rsa->d, aux, bn_ctx); BN_mod_inverse(rsa->iqmp, rsa->q, rsa->p, bn_ctx); BN_clear_free(aux); BN_CTX_free(bn_ctx); /* Not thread safe, but much better than a memory leak */ GETBN(key->dmp1, rsa->dmp1, dmp1); GETBN(key->dmq1, rsa->dmq1, dmq1); GETBN(key->iqmp, rsa->iqmp, iqmp); RSA_free(rsa); } #undef GETBN #endif return 0; } 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_DSA: 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 sc_pkcs15init_keybits(&key->u.rsa.modulus); case SC_ALGORITHM_DSA: return sc_pkcs15init_keybits(&key->u.dsa.q); case SC_ALGORITHM_GOSTR3410: if (sc_pkcs15init_keybits(&key->u.gostr3410.d) > SC_PKCS15_GOSTR3410_KEYSIZE) { sc_log(ctx, "Unsupported key (keybits %u)", sc_pkcs15init_keybits(&key->u.gostr3410.d)); return SC_ERROR_OBJECT_NOT_VALID; } return SC_PKCS15_GOSTR3410_KEYSIZE; case SC_ALGORITHM_EC: /* calculation returns one bit too small, add one bu default */ sc_log(ctx, "Private EC key length %u", sc_pkcs15init_keybits(&key->u.ec.privateD) + 1); return sc_pkcs15init_keybits(&key->u.ec.privateD) + 1; } sc_log(ctx, "Unsupported key algorithm."); return SC_ERROR_NOT_SUPPORTED; } static int prkey_pkcs15_algo(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 SC_PKCS15_TYPE_PRKEY_RSA; case SC_ALGORITHM_DSA: return SC_PKCS15_TYPE_PRKEY_DSA; case SC_ALGORITHM_GOSTR3410: return SC_PKCS15_TYPE_PRKEY_GOSTR3410; case SC_ALGORITHM_EC: return SC_PKCS15_TYPE_PRKEY_EC; } 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; } static int select_intrinsic_id(struct sc_pkcs15_card *p15card, struct sc_profile *profile, int type, struct sc_pkcs15_id *id, void *data) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_pubkey *pubkey = NULL; unsigned id_style = profile->id_style; int rv, allocated = 0; LOG_FUNC_CALLED(ctx); #ifndef ENABLE_OPENSSL LOG_FUNC_RETURN(ctx, SC_SUCCESS); #else /* ID already exists */ if (id->len) LOG_FUNC_RETURN(ctx, SC_SUCCESS); /* Native ID style is not an intrisic one */ if (profile->id_style == SC_PKCS15INIT_ID_STYLE_NATIVE) LOG_FUNC_RETURN(ctx, SC_SUCCESS); /* 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 inintialized. */ if (pubkey->algorithm == SC_ALGORITHM_RSA && !pubkey->u.rsa.modulus.len) goto done; else if (pubkey->algorithm == SC_ALGORITHM_DSA && !pubkey->u.dsa.pub.data) goto done; else if (pubkey->algorithm == SC_ALGORITHM_GOSTR3410 && !pubkey->u.gostr3410.xy.data) goto done; 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; if (id_style == SC_PKCS15INIT_ID_STYLE_MOZILLA) { if (pubkey->algorithm == SC_ALGORITHM_RSA) SHA1(pubkey->u.rsa.modulus.data, pubkey->u.rsa.modulus.len, id->value); else if (pubkey->algorithm == SC_ALGORITHM_DSA) SHA1(pubkey->u.dsa.pub.data, pubkey->u.dsa.pub.len, id->value); else if (pubkey->algorithm == SC_ALGORITHM_EC) { /* ID should be SHA1 of the X coordinate according to PKCS#15 v1.1 */ /* skip the 04 tag and get the X component */ SHA1(pubkey->u.ec.ecpointQ.value+1, (pubkey->u.ec.ecpointQ.len - 1) / 2, id->value); } else goto done; id->len = SHA_DIGEST_LENGTH; } else if (id_style == SC_PKCS15INIT_ID_STYLE_RFC2459) { unsigned char *id_data = NULL; size_t id_data_len = 0; rv = sc_pkcs15_encode_pubkey(ctx, pubkey, &id_data, &id_data_len); LOG_TEST_RET(ctx, rv, "Encoding public key error"); if (!id_data || !id_data_len) LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "Encoding public key error"); SHA1(id_data, id_data_len, id->value); id->len = SHA_DIGEST_LENGTH; free(id_data); } else { sc_log(ctx, "Unsupported ID style: %i", profile->id_style); LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Non supported ID style"); } done: if (allocated) sc_pkcs15_free_pubkey(pubkey); LOG_FUNC_RETURN(ctx, id->len); #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; 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_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_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=%d)", 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 char * get_generalized_time(struct sc_context *ctx) { #ifdef HAVE_GETTIMEOFDAY struct timeval tv; #endif struct tm *tm_time; time_t t; char* ret; size_t r; #ifdef HAVE_GETTIMEOFDAY gettimeofday(&tv, NULL); t = tv.tv_sec; #else t = time(NULL); #endif tm_time = gmtime(&t); if (tm_time == NULL) { sc_log(ctx, "error: gmtime failed"); return NULL; } ret = calloc(1, 16); if (ret == NULL) { sc_log(ctx, "error: calloc failed"); return NULL; } /* print time in generalized time format */ r = strftime(ret, 16, "%Y%m%d%H%M%SZ", tm_time); if (r == 0) { sc_log(ctx, "error: strftime failed"); free(ret); return NULL; } return ret; } static int sc_pkcs15init_update_tokeninfo(struct sc_pkcs15_card *p15card, struct sc_profile *profile) { struct sc_card *card = p15card->card; struct sc_pkcs15_tokeninfo tokeninfo; unsigned char *buf = NULL; size_t size; int r; LOG_FUNC_CALLED(p15card->card->ctx); /* set lastUpdate field */ if (p15card->tokeninfo->last_update.gtime != NULL) free(p15card->tokeninfo->last_update.gtime); p15card->tokeninfo->last_update.gtime = get_generalized_time(card->ctx); if (p15card->tokeninfo->last_update.gtime == NULL) return SC_ERROR_INTERNAL; tokeninfo = *(p15card->tokeninfo); if (profile->ops->emu_update_tokeninfo) return profile->ops->emu_update_tokeninfo(profile, p15card, &tokeninfo); r = sc_pkcs15_encode_tokeninfo(card->ctx, &tokeninfo, &buf, &size); if (r >= 0) r = sc_pkcs15init_update_file(profile, p15card, p15card->file_tokeninfo, buf, size); if (buf) free(buf); LOG_FUNC_RETURN(p15card->card->ctx, r); } 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); last_update->gtime = get_generalized_time(ctx); if (last_update->gtime == NULL) return SC_ERROR_INTERNAL; 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); 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; int update_odf = is_new, r = 0; LOG_FUNC_CALLED(ctx); sc_profile_get_file_by_path(profile, &df->path, &file); if (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 = bufsize; df->path.index = 0; update_odf = 1; } free(buf); } if (file) 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_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; } 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; int df_type, r = 0; struct sc_pkcs15_df *df; struct sc_pkcs15_id new_id = *((struct sc_pkcs15_id *) new_value); 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"); 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_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; default: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Only 'LABEL' or 'ID' 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); 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); } } return r < 0 ? r : 0; } 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); switch(obj->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PUBKEY: path = ((struct sc_pkcs15_pubkey_info *)obj->data)->path; break; case SC_PKCS15_TYPE_PRKEY: path = ((struct sc_pkcs15_prkey_info *)obj->data)->path; break; case SC_PKCS15_TYPE_CERT: path = ((struct sc_pkcs15_cert_info *)obj->data)->path; break; case SC_PKCS15_TYPE_DATA_OBJECT: path = ((struct sc_pkcs15_data_info *)obj->data)->path; break; default: return SC_ERROR_NOT_SUPPORTED; } 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(file->size - certlen, 1); if (tmp == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto done; } r = sc_update_binary(p15card->card, 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 = 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: if (file) 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); } 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; } 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 = sizeof(pinbuf); 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)", type, reference); } if (p15card) { if (path && path->len) { struct sc_path tmp_path = *path; int iter; r = SC_ERROR_OBJECT_NOT_FOUND; for (iter = tmp_path.len/2; iter >= 0 && r == SC_ERROR_OBJECT_NOT_FOUND; iter--, tmp_path.len -= 2) 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'", pin_obj->label); } } if (pin_obj) { sc_log(ctx, "PIN object '%s'; pin_obj->content.len:%i", pin_obj->label, pin_obj->content.len); if (pin_obj->content.value && pin_obj->content.len) { if (pin_obj->content.len > pinsize) 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) { r = callbacks.get_pin(profile, pin_id, &auth_info, label, pinbuf, &pinsize); sc_log(ctx, "'get_pin' callback returned %i; pinsize:%i", r, pinsize); } break; case SC_AC_SCB: case SC_AC_PRO: pinsize = 0; r = 0; break; default: 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) { r = sc_pkcs15_verify_pin(p15card, pin_obj, pinsize ? pinbuf : NULL, 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 orther 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); sc_log(ctx, "path '%s', op=%u", sc_print_path(&file->path), op); 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) { 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); } if (file_tmp) 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); LOG_TEST_RET(ctx, r, "Cannot create parent DF"); r = sc_select_file(p15card->card, &path, NULL); LOG_TEST_RET(ctx, r, "Cannot select parent DF"); } else if (r == SC_SUCCESS && !strcmp(p15card->card->name, "STARCOS SPK 2.3")) { /* 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); sc_log(ctx, "create file '%s'", sc_print_path(&file->path)); /* Select parent DF and verify PINs/key as necessary */ r = do_select_parent(profile, p15card, file, &parent); LOG_TEST_RET(ctx, r, "Cannot create file: select parent error"); r = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_CREATE); LOG_TEST_RET(ctx, r, "Cannot create file: 'CREATE' authentication failed"); /* Fix up the file's ACLs */ r = sc_pkcs15init_fixup_file(profile, p15card, file); LOG_TEST_RET(ctx, r, "Cannot create file: file fixup failed"); /* ensure we are in the correct lifecycle */ r = sc_pkcs15init_set_lifecycle(p15card->card, SC_CARDCTRL_LIFECYCLE_ADMIN); if (r != SC_ERROR_NOT_SUPPORTED) LOG_TEST_RET(ctx, r, "Cannot create file: failed to set lifecycle 'ADMIN'"); r = sc_create_file(p15card->card, file); LOG_TEST_RET(ctx, r, "Create file failed"); if (parent) 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, unsigned int 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); sc_log(ctx, "path:%s; datalen:%i", 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 %u, have %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 */ 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, 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, unsigned int pin_len, struct sc_pkcs15_auth_info *auth_info) { struct sc_context *ctx = card->ctx; struct sc_pkcs15_pin_attributes *pin_attrs; if (pin_len == 0 || auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_SUCCESS; pin_attrs = &auth_info->attrs.pin; if (pin_len < pin_attrs->min_length) { sc_log(ctx, "%s too short (min length %u)", pin_name, pin_attrs->min_length); return SC_ERROR_WRONG_LENGTH; } if (pin_len > pin_attrs->max_length) { sc_log(ctx, "%s too long (max length %u)", pin_name, pin_attrs->max_length); return SC_ERROR_WRONG_LENGTH; } return 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); 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; } if (r >= 0) r = sc_pkcs15init_parse_info(card, mem, len, 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; if ((p == NULL) || (len == 0)) return 0; end = p + (len - 1); while (p < end) { /* more bytes to look at */ int 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 */ goto error; switch (tag) { case OPENSC_INFO_TAG_PROFILE: r = set_info_string(&profile->name, p, n); if (r < 0) return r; break; case OPENSC_INFO_TAG_OPTION: if (nopts >= SC_PKCS15INIT_MAX_OPTIONS - 1) { sc_log(card->ctx, "Too many options in OpenSC Info file"); return SC_ERROR_PKCS15INIT; } r = set_info_string(&profile->options[nopts], p, n); if (r < 0) return r; profile->options[++nopts] = NULL; break; default: /* Unknown options ignored */ ; } p += n; } return 0; error: sc_log(card->ctx, "OpenSC info file corrupted"); return SC_ERROR_PKCS15INIT; } static int do_encode_string(unsigned char **memp, unsigned char *end, unsigned char tag, const char *s) { unsigned char *p = *memp; int 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, file->size); sc_file_free(file); return r; } opensc-0.13.0/src/pkcs15init/iasecc_admin_eid.profile0000644000015201777760000001164712057406034017421 00000000000000# # 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 specifique 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 insted 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.13.0/src/pkcs15init/pkcs15-jcop.c0000644000015201777760000002551512057406034015021 00000000000000/* * JCOP specific operation for PKCS15 initialization * * Copyright 2003 Chaskiel Grundman * Copyright (C) 2002 Olaf Kirch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" #include "pkcs15-init.h" #include "profile.h" #define JCOP_MAX_PINS 3 /* * Erase the card */ static int jcop_erase_card(struct sc_profile *pro, sc_pkcs15_card_t *p15card) { /* later */ return SC_ERROR_NOT_SUPPORTED; } #if 0 /* * Create a new DF * This will usually be the application DF * for JCOP, it must be the application DF. no other DF's may exist. */ static int jcop_init_app(sc_profile_t *profile, sc_card_t *card, struct sc_pkcs15_pin_info *pin_info, const u8 *pin, size_t pin_len, const u8 *puk, size_t puk_len) { return SC_ERROR_NOT_SUPPORTED; } #else static int jcop_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *file) { return SC_ERROR_NOT_SUPPORTED; }; #endif /* * Select a PIN reference */ static int jcop_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_auth_info_t *auth_info) { int preferred, current; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; if ((current = auth_info->attrs.pin.reference) < 0) current = 0; if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) { preferred = 3; } else { preferred = current; if (preferred < 1) preferred=1; if (preferred > 2) return SC_ERROR_TOO_MANY_OBJECTS; } if (current > preferred) return SC_ERROR_TOO_MANY_OBJECTS; auth_info->attrs.pin.reference = preferred; return 0; } /* * Store a PIN */ static int jcop_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df, sc_pkcs15_object_t *pin_obj, const unsigned char *pin, size_t pin_len, const unsigned char *puk, size_t puk_len) { sc_pkcs15_auth_info_t *auth_info = (sc_pkcs15_auth_info_t *) pin_obj->data; struct sc_pkcs15_pin_attributes *pin_attrs = &auth_info->attrs.pin; unsigned char nulpin[16]; unsigned char padpin[16]; int r; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) { /* SO PIN reference must be 0 */ if (pin_attrs->reference != 3) return SC_ERROR_INVALID_ARGUMENTS; } else { if (pin_attrs->reference >= 3) return SC_ERROR_TOO_MANY_OBJECTS; } if (puk != NULL && puk_len > 0) { return SC_ERROR_NOT_SUPPORTED; } r = sc_select_file(p15card->card, &df->path, NULL); if (r < 0) return r; /* Current PIN is 00:00:00:00:00:00:00:00... */ memset(nulpin, 0, sizeof(nulpin)); memset(padpin, 0, sizeof(padpin)); memcpy(padpin, pin, pin_len); r = sc_change_reference_data(p15card->card, SC_AC_CHV, pin_attrs->reference, nulpin, sizeof(nulpin), padpin, sizeof(padpin), NULL); if (r < 0) return r; pin_attrs->flags &= ~SC_PKCS15_PIN_FLAG_LOCAL; return r; } /* * Create a new key file */ static int jcop_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj) { sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; sc_file_t *keyfile = NULL; size_t bytes, mod_len, prv_len; int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "JCOP supports only RSA keys."); return SC_ERROR_NOT_SUPPORTED; } /* The caller is supposed to have chosen a key file path for us */ if (key_info->path.len == 0 || key_info->modulus_length == 0) return SC_ERROR_INVALID_ARGUMENTS; /* Get the file we're supposed to create */ r = sc_profile_get_file_by_path(profile, &key_info->path, &keyfile); if (r < 0) return r; mod_len = key_info->modulus_length / 8; bytes = mod_len / 2; prv_len = 2 + 5 * bytes; keyfile->size = prv_len; /* Fix up PIN references in file ACL */ r = sc_pkcs15init_fixup_file(profile, p15card, keyfile); if (r >= 0) r = sc_pkcs15init_create_file(profile, p15card, keyfile); if (keyfile) sc_file_free(keyfile); return r; } static void jcop_bn2bin(unsigned char *dest, sc_pkcs15_bignum_t *bn, unsigned int size) { u8 *src; unsigned int n; assert(bn->len <= size); memset(dest, 0, size); for (n = size-bn->len, src = bn->data; n < size; n++,src++) dest[n] = *src; } /* * Store a private key * Private key file formats: (transparent file) * Non-CRT: * byte 0 0x05 * byte 1 Modulus length (in byte/4) * byte 2 Modulus (n) * byte 2+x private exponent (d) * * CRT: * byte 0 0x06 * byte 1 component length (in byte/2; component length is half * of modulus length * byte 2 Prime (p) * byte 2+x Prime (q) * byte 2+2*x Exponent 1 (d mod (p-1)) * byte 2+3*x Exponent 2 (d mod (q-1)) * byte 2+4*x Coefficient ((p ^ -1) mod q * * We use the CRT format, since that's what key generation does. * * Numbers are stored big endian. */ static int jcop_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_prkey_t *key) { sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; sc_file_t *keyfile; unsigned char keybuf[1024]; size_t size,base; int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "JCOP supports only RSA keys."); return SC_ERROR_NOT_SUPPORTED; } r = sc_profile_get_file_by_path(profile, &key_info->path, &keyfile); if (r < 0) return r; base=key_info->modulus_length / 16; size=2+5*base; keybuf[0]=6; keybuf[1]=base/4; jcop_bn2bin(&keybuf[2 + 0 * base], &key->u.rsa.p, base); jcop_bn2bin(&keybuf[2 + 1 * base], &key->u.rsa.q, base); jcop_bn2bin(&keybuf[2 + 2 * base], &key->u.rsa.dmp1, base); jcop_bn2bin(&keybuf[2 + 3 * base], &key->u.rsa.dmq1, base); jcop_bn2bin(&keybuf[2 + 4 * base], &key->u.rsa.iqmp, base); r = sc_pkcs15init_update_file(profile, p15card, keyfile, keybuf, size); sc_file_free(keyfile); return r; } /* * Generate a keypair */ static int jcop_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) { sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; struct sc_cardctl_jcop_genkey args; sc_file_t *temppubfile=NULL, *keyfile=NULL; unsigned char *keybuf=NULL; size_t mod_len, exp_len, pub_len, keybits; int r,delete_ok=0; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "JCOP supports only RSA keys."); return SC_ERROR_NOT_SUPPORTED; } r=sc_profile_get_file(profile, "temp-pubkey", &temppubfile); if (r < 0) goto out; r = sc_select_file(p15card->card, &key_info->path, &keyfile); if (r < 0) goto out; mod_len = key_info->modulus_length / 8; exp_len = 4; pub_len = 2 + mod_len + exp_len; temppubfile->size = pub_len; r = sc_pkcs15init_fixup_file(profile, p15card, temppubfile); if (r < 0) goto out; r = sc_pkcs15init_create_file(profile, p15card, temppubfile); if (r < 0) goto out; delete_ok=1; r = sc_pkcs15init_authenticate(profile, p15card, temppubfile, SC_AC_OP_UPDATE); if (r < 0) goto out; r = sc_pkcs15init_authenticate(profile, p15card, keyfile, SC_AC_OP_UPDATE); if (r < 0) goto out; keybits = key_info->modulus_length; /* generate key */ /* keysize is _not_ passed to the card at any point. it appears to infer it from the file size */ memset(&args, 0, sizeof(args)); args.exponent = 0x10001; sc_append_file_id(&args.pub_file_ref, temppubfile->id); sc_append_file_id(&args.pri_file_ref, keyfile->id); keybuf = malloc(keybits / 8); if (!keybuf) { r=SC_ERROR_OUT_OF_MEMORY; goto out; } args.pubkey = keybuf; args.pubkey_len = keybits / 8; r = sc_card_ctl(p15card->card, SC_CARDCTL_JCOP_GENERATE_KEY, (void *)&args); if (r < 0) goto out; /* extract public key */ pubkey->algorithm = SC_ALGORITHM_RSA; pubkey->u.rsa.modulus.len = keybits / 8; pubkey->u.rsa.modulus.data = keybuf; pubkey->u.rsa.exponent.len = 3; pubkey->u.rsa.exponent.data = malloc(3); if (!pubkey->u.rsa.exponent.data) { pubkey->u.rsa.modulus.data = NULL; r=SC_ERROR_OUT_OF_MEMORY; goto out; } memcpy(pubkey->u.rsa.exponent.data, "\x01\x00\x01", 3); out: if (r < 0 && keybuf) free(keybuf); if (delete_ok) sc_pkcs15init_rmdir(p15card, profile, temppubfile); if (keyfile) sc_file_free(keyfile); if (temppubfile) sc_file_free(temppubfile); return r; } static struct sc_pkcs15init_operations sc_pkcs15init_jcop_operations = { jcop_erase_card, NULL, /* init_card */ jcop_create_dir, NULL, /* create_domain */ jcop_select_pin_reference, jcop_create_pin, NULL, /* select_key_reference */ jcop_create_key, jcop_store_key, jcop_generate_key, NULL, NULL, /* encode private/public key */ NULL, /* finalize_card */ NULL, /* delete_object */ NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ NULL /* sanity_check */ }; struct sc_pkcs15init_operations *sc_pkcs15init_get_jcop_ops(void) { return &sc_pkcs15init_jcop_operations; } opensc-0.13.0/src/pkcs15init/openpgp.profile0000644000015201777760000000354212057406034015644 00000000000000# # 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.13.0/src/pkcs15init/iasecc_generic_pki.profile0000644000015201777760000001163012057406034017757 00000000000000# # 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 specifique 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 insted 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.13.0/src/pkcs15init/pkcs15-cflex.c0000644000015201777760000006202412057406034015163 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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; 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); /* 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. */ if (userpinfile) 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); 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) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported key size %u\n", key_info->modulus_length); r = SC_ERROR_INVALID_ARGUMENTS; goto out; } if (prkf->size < size) prkf->size = size; if (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: if (prkf) sc_file_free(prkf); if (pukf) 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; unsigned int keybits; unsigned char raw_pubkey[256]; sc_file_t *prkf = NULL, *pukf = NULL; int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Cryptoflex supports only RSA keys."); 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: if (pukf) sc_file_free(pukf); if (prkf) 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, *pukf; unsigned char keybuf[1024]; size_t size; int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Cryptoflex supports only RSA keys."); 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, 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, 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); /* 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); 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) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to create PIN file"); r = sc_update_binary(p15card->card, 0, buffer, 23, 0); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to update PIN file"); if (r < 0 || file_ret == NULL) sc_file_free(file); else *file_ret = file; /* 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; SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_NORMAL); *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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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.13.0/src/pkcs15init/authentic.profile0000644000015201777760000000555512057406034016166 00000000000000# # 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.13.0/src/pkcs15init/Makefile.am0000644000015201777760000000257212057406034014650 00000000000000include $(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 dist_pkgdata_DATA = \ cyberflex.profile \ flex.profile \ gpk.profile \ miocos.profile \ cardos.profile \ incrypto34.profile \ jcop.profile \ oberthur.profile \ starcos.profile \ setcos.profile \ pkcs15.profile \ muscle.profile \ rutoken.profile \ asepcos.profile \ entersafe.profile \ epass2003.profile \ rutoken_ecp.profile \ westcos.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 AM_CPPFLAGS = -DSC_PKCS15_PROFILE_DIRECTORY=\"$(pkgdatadir)\" \ -I$(top_srcdir)/src AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) libpkcs15init_la_SOURCES = \ pkcs15-lib.c profile.c \ pkcs15-westcos.c \ pkcs15-gpk.c pkcs15-miocos.c pkcs15-cflex.c \ pkcs15-cardos.c pkcs15-jcop.c pkcs15-starcos.c \ pkcs15-setcos.c pkcs15-incrypto34.c pkcs15-muscle.c \ pkcs15-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 opensc-0.13.0/src/pkcs15init/pkcs15-init.h0000644000015201777760000003371212057406034015034 00000000000000/* * Function prototypes for pkcs15-init * * Copyright (C) 2002 Olaf Kirch */ #ifndef PKCS15_INIT_H #define PKCS15_INIT_H #ifdef __cplusplus extern "C" { #endif #include "libopensc/pkcs15.h" #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 { struct sc_pkcs15_id id; struct sc_pkcs15_id auth_id; const char * label; const char * guid; unsigned long usage; unsigned long x509_usage; unsigned int flags; unsigned int access_flags; union { struct sc_pkcs15init_keyarg_gost_params gost; struct sc_pkcs15_ec_parameters ec; } 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 long usage; unsigned long x509_usage; union { struct sc_pkcs15init_keyarg_gost_params gost; struct sc_pkcs15_ec_parameters ec; } 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 long usage; unsigned int flags; unsigned int access_flags; unsigned long value_len; /* User requested length */ struct sc_pkcs15_der data_value; /* Wrong name: is not DER encoded */ }; struct sc_pkcs15init_certargs { struct sc_pkcs15_id id; const char * label; 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 extern struct sc_pkcs15_object *sc_pkcs15init_new_object(int, const char *, struct sc_pkcs15_id *, void *); 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_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_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 correcsponding 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 *, unsigned int); 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); /* 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 *); /* Helper function for CardOS */ extern int sc_pkcs15init_requires_restrictive_usage( struct sc_pkcs15_card *, struct sc_pkcs15init_prkeyargs *, unsigned int); 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 struct sc_pkcs15init_operations *sc_pkcs15init_get_gpk_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_miocos_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_cryptoflex_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_cyberflex_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_cardos_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_jcop_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_starcos_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_oberthur_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_setcos_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_incrypto34_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_muscle_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_asepcos_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_rutoken_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_entersafe_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_epass2003_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_rtecp_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_westcos_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_myeid_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_authentic_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_iasecc_ops(void); 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); #ifdef __cplusplus } #endif #endif /* PKCS15_INIT_H */ opensc-0.13.0/src/pkcs15init/setcos.profile0000644000015201777760000000731512057406034015476 00000000000000# # General purpose PKCS15 profile for SetCOS4.4 cards # cardinfo { max-pin-length = 8; pin-encoding = ascii-numeric; pin-pad-char = 0x00; } # Addtional 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; } } # Addtional 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.13.0/src/pkcs15init/pkcs15-oberthur-awp.c0000644000015201777760000016427512057406034016514 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * best view with tabstop=4 * */ #include #include #include #include "config.h" #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" #include "profile.h" #include "pkcs15-init.h" #include "pkcs15-oberthur.h" #include "libopensc/asn1.h" #ifdef ENABLE_OPENSSL 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "type 0x%X; num %i; info %p; obj %p", type, num, info_out, obj_out); 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "info template %s",name); if (sc_profile_get_file(profile, name, &ifile) < 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "profile does not defines template '%s'", name); return SC_ERROR_INCONSISTENT_PROFILE; } } if (otag) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "obj template %s",otag); if (sc_profile_get_file(profile, otag, &ofile) < 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "info_file(id:%04X,size:%i,rlen:%i)", ifile->id, ifile->size, ifile->record_length); *info_out = ifile; } else { sc_file_free(ifile); } } if (ofile) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "obj file %04X; size %i; ", ofile->id, ofile->size); if (obj_out) *obj_out = ofile; else sc_file_free(ofile); } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); 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 (!(pp = realloc(*blob, *blob_size + lv->len))) return SC_ERROR_OUT_OF_MEMORY; memcpy(pp + *blob_size, lv->value, lv->len); *blob_size += lv->len; break; default: sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Invalid tlv type %i",type); return SC_ERROR_INCORRECT_PARAMETERS; } *blob = pp; SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); } static int awp_new_container_entry(struct sc_pkcs15_card *p15card, unsigned char *buff, int len) { struct sc_context *ctx = p15card->card->ctx; int mm, rv = 0; unsigned ii, marks[5] = {4,6,8,10,0}; unsigned char rand_buf[0x10]; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); if (len<0x34) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INCORRECT_PARAMETERS, "Invalid container update size"); rv = sc_get_challenge(p15card->card, rand_buf, sizeof(rand_buf)); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot get challenge"); *(buff + 12) = 0x26; *(buff + 13) = '{'; for (ii=0, mm = 0; iicard->ctx; int rv; unsigned char *buff = NULL; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "container file(file-id:%X,rlen:%i,rcount:%i)", list_file->id, list_file->record_length, list_file->record_count); buff = malloc(list_file->record_length); if (!buff) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); memset(buff, 0, list_file->record_length); rv = awp_new_container_entry(p15card, buff, list_file->record_length); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot create container"); *(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); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "rv:%i", rv); 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "return after failure"); rv = 0; SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; unsigned char *list = NULL; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "create container(%X:%X:%X)", acc->prkey_id, acc->cert_id, acc->pubkey_id); rv = awp_new_file(p15card, profile, COSM_CONTAINER_LIST, 0, &clist, NULL); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Create container failed"); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "contaner cfile(rcount:%i,rlength:%i)", clist->record_count, clist->record_length); rv = sc_select_file(p15card->card, &clist->path, &file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Create container failed: cannot select container's list"); file->record_length = clist->record_length; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "contaner file(rcount:%i,rlength:%i)", file->record_count, file->record_length); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Append new record %i for private key", file->record_count + 1); rv = awp_create_container_record(p15card, profile, file, acc); if (clist) sc_file_free(clist); if (file) sc_file_free(file); if (list) free(list); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); } static int awp_update_container_entry (struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_file *list_file, int type, int file_id, int rec, int offs) { struct sc_context *ctx = p15card->card->ctx; int rv; unsigned char *buff = NULL; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "update container entry(type:%X,len:%i,count %i,rec %i,offs %i", type, file_id, rec, offs); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "container file(file-id:%X,rlen:%i,rcount:%i)", list_file->id, list_file->record_length, list_file->record_count); buff = malloc(list_file->record_length); if (!buff) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); memset(buff, 0, list_file->record_length); if (rec > list_file->record_count) { rv = awp_new_container_entry(p15card, buff, list_file->record_length); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot create container"); } else { rv = sc_select_file(p15card->card, &list_file->path, NULL); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot select list_file"); rv = sc_read_record(p15card->card, rec, buff, list_file->record_length, SC_RECORD_BY_REC_NR); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot read record"); } switch (type) { case SC_PKCS15_TYPE_PUBKEY_RSA: case COSM_TYPE_PUBKEY_RSA: if (*(buff + offs + 4)) sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Insert public key to container that contains certificate %02X%02X", *(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)) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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: SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INCORRECT_PARAMETERS, "invalid object type"); } 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, rec, buff, list_file->record_length, SC_RECORD_BY_REC_NR); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "rv:%i", rv); } free(buff); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "return after failure"); rv = 0; SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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, rec, rec_offs; unsigned char *list = NULL; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "update container(type:%X,obj_id:%X)", type, obj_id); 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; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "contaner cfile(rcount:%i,rlength:%i)", clist->record_count, clist->record_length); rv = sc_select_file(p15card->card, &clist->path, &file); if (rv) goto done; file->record_length = clist->record_length; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "contaner file(rcount:%i,rlength:%i)", file->record_count, file->record_length); if (type == SC_PKCS15_TYPE_PRKEY_RSA || type == COSM_TYPE_PRKEY_RSA) { rec_offs = 0; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Append new record %i for private key", file->record_count + 1); 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, rec + 1, 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "rec %i; rec_offs %i", rec, rec_offs); offs = 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; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "container contains PrKey %02X%02X", *(list + offs + 2), *(list + offs + 3)); 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; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "file id %X; size %i", ff->id, ff->size); buff = malloc(ff->size); if (!buff) { rv = SC_ERROR_OUT_OF_MEMORY; break; } rv = sc_pkcs15init_authenticate(profile, p15card, ff, SC_AC_OP_READ); if (rv) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "sc_pkcs15init_authenticate(READ) failed"); break; } rv = sc_read_binary(p15card->card, 0, buff, ff->size, 0); if ((unsigned)rv == ff->size) { rv = 0; id_offs = 5 + *(buff+3); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "rec %i; id offset %i",rec, id_offs); if (key_id->len == *(buff + id_offs) && !memcmp(key_id->value, buff + id_offs + 1, key_id->len)) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "found key file friend"); 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); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "*prkey_id 0x%X", *prkey_id); } } } free(buff); sc_file_free(ff); } } } done: if (clist) sc_file_free(clist); if (file) sc_file_free(file); if (list) free(list); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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"; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); 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); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "Not supported file type %X", type); return SC_ERROR_INVALID_ARGUMENTS; } sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "obj_name %s; num 0x%X",obj_name, num); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "lst_name %s",lst_name); if (sc_profile_get_file(profile, obj_name, &obj_file) < 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "No profile template '%s'", obj_name); rv = SC_ERROR_NOT_SUPPORTED; goto done; } else if (sc_profile_get_file(profile, lst_name, &lst_file) < 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "No profile template '%s'", lst_name); 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; rv = sc_read_binary(p15card->card, 0, buff, lst_file->size, lst_file->ef_structure); 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "ii %i, rv %i; %X; %i", 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); ERR_load_ERR_strings(); ERR_load_crypto_strings(); key_info = (struct sc_pkcs15_prkey_info *)obj->data; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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; if (obj->label) { ki->label.value = (unsigned char *)strdup(obj->label); ki->label.len = strlen(obj->label); } sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "cosm_encode_key_info() label(%i):%s",ki->label.len, ki->label.value); /* * Oberthur saves modulus value without tag and length. */ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "pubkey->modulus.len %i",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) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP encode cert failed: ID allocation error"); memcpy(ki->id.value, key_info->id.value, key_info->id.len); ki->id.len = key_info->id.len; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "cosm_encode_key_info() label:%s",ki->label.value); done: ERR_load_ERR_strings(); ERR_load_crypto_strings(); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); } static void awp_free_key_info(struct awp_key_info *ki) { if (ki->modulus.value) free(ki->modulus.value); if (ki->exponent.value) free(ki->exponent.value); if (ki->id.value) free(ki->id.value); } 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "file:%p, kinfo:%p, cinfo:%p", file, ki, ci); blob_size = 2; blob = malloc(blob_size); if (!blob) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP set key info failed: blob allocation error"); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); ERR_load_ERR_strings(); ERR_load_crypto_strings(); if (!obj || !ci) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "AWP encode cert failed: invalid parameters"); cert_info = (struct sc_pkcs15_cert_info *)obj->data; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Encode cert(%s,id:%s,der(%p,%i))", obj->label, sc_pkcs15_print_id(&cert_info->id), obj->content.value, obj->content.len); memset(&pubkey, 0, sizeof(pubkey)); if (obj->label) { ci->label.value = (unsigned char *)strdup(obj->label); ci->label.len = strlen(obj->label); } mem = BIO_new_mem_buf(obj->content.value, obj->content.len); if (!mem) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA, "AWP encode cert failed: invalid data"); x = d2i_X509_bio(mem, NULL); if (!x) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA, "AWP encode cert failed: x509 parse error"); buff = OPENSSL_malloc(i2d_X509(x,NULL) + EVP_MAX_MD_SIZE); if (!buff) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP encode cert failed: memory allocation error"); /* * subject commonName. */ ptr = awp_get_commonName(x); if (!ptr) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL, "AWP encode cert failed: cannot get CommonName"); 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_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL, "AWP encode cert failed: cannot get SubjectName"); ci->subject.value = malloc(r); if (!ci->subject.value) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP encode cert failed: subject allocation error"); 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_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL, "AWP encode cert failed: cannot get IssuerName"); ci->issuer.value = malloc(r); if (!ci->issuer.value) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP encode cert failed: issuer allocation error"); memcpy(ci->issuer.value, buff, r); ci->issuer.len = r; /* * ID */ ci->id.value = calloc(1, cert_info->id.len); if (!ci->id.value) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP encode cert failed: ID allocation error"); memcpy(ci->id.value, cert_info->id.value, cert_info->id.len); ci->id.len = cert_info->id.len; /* * serial number */ 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 done; } 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "cert. serial encoded length %i", encoded_len); } while (0); ci->x509 = X509_dup(x); done: ERR_print_errors_fp(stderr); ERR_clear_error(); ERR_free_strings(); if (pubkey.exponent.data) free(pubkey.exponent.data); if (pubkey.modulus.data) free(pubkey.modulus.data); if (x) X509_free(x); if (mem) BIO_free(mem); if (buff) OPENSSL_free(buff); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); } static void awp_free_cert_info(struct awp_cert_info *ci) { if (ci->cn.len && ci->cn.value) free(ci->cn.value); if (ci->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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); if (!obj || !di) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "AWP encode data failed: invalid parameters"); data_info = (struct sc_pkcs15_data_info *)obj->data; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Encode data(%s,id:%s,der(%p,%i))", obj->label, sc_pkcs15_print_id(&data_info->id), obj->content.value, obj->content.len); di->flags = 0x0000; if (obj->label) { 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) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP encode data failed: cannot allocate App.Label"); } r = sc_asn1_encode_object_id(&buf, &buflen, &data_info->app_oid); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "AWP encode data failed: cannot encode OID"); di->oid.len = buflen + 2; di->oid.value = malloc(di->oid.len); if (!di->oid.value) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); sc_debug (ctx, SC_LOG_DEBUG_NORMAL, "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); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); offs = 0; /* Flags */ if (buf_len - offs < 2) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); ikey->flags = *(buf + offs) * 0x100 + *(buf + offs + 1); offs += 2; /* Label */ len = awp_get_lv(ctx, buf, buf_len, offs, 2, &ikey->label); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, len, "AWP parse key info failed: label"); if (!len) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); offs += len; /* Ignore Key ID */ len = awp_get_lv(ctx, buf, buf_len, offs, 2, &ikey->id); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, len, "AWP parse key info failed: ID"); if (!len) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); offs += len; while (*(buf + offs) == '0') offs++; /* Subject */ len = awp_get_lv(ctx, buf, buf_len, offs, 2, &ikey->subject); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, len, "AWP parse key info failed: subject"); if (!len) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, len, "AWP parse key info failed: modulus"); if (!len) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); offs += len; /* Exponent */ len = awp_get_lv(ctx, buf, buf_len, offs, 1, &ikey->exponent); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, len, "AWP parse key info failed: exponent"); if (!len) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); rv = awp_new_file(p15card, profile, SC_PKCS15_TYPE_PRKEY_RSA, prvkey_id & 0xFF, &info_file, &key_file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP update key info failed: instantiation error"); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "key id %X; info id%X", key_file->id, info_file->id); rv = sc_pkcs15init_authenticate(profile, p15card, info_file, SC_AC_OP_READ); if (rv) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update key info failed: 'READ' authentication error"); goto done; } rv = sc_select_file(p15card->card, &info_file->path, &file); if (rv) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update key info failed: cannot select info file"); goto done; } buf = calloc(1,file->size); if (!buf) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP update key info failed: allocation error"); rv = sc_read_binary(p15card->card, 0, buf, file->size, 0); if (rv < 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update key info failed: read info file error"); goto done; } buf_len = rv; memset(&ikey, 0, sizeof(ikey)); rv = awp_parse_key_info(ctx, buf, buf_len, &ikey); if (rv < 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update key info failed: parse key info error"); goto done; } free(buf); rv = awp_set_key_info(p15card, profile, info_file, &ikey, ci); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP update key info failed: set key info error"); done: sc_file_free(file); sc_file_free(key_file); sc_file_free(info_file); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); der = obj->content; path = ((struct sc_pkcs15_cert_info *)obj->data)->path; obj_id = (path.value[path.len-1] & 0xFF) + (path.value[path.len-2] & 0xFF) * 0x100; rv = awp_new_file(p15card, profile, SC_PKCS15_TYPE_CERT_X509, obj_id & 0xFF, &info_file, &obj_file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "COSM new file error"); memset(&icert, 0, sizeof(icert)); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Cert Der(%p,%i)", der.value, der.len); rv = awp_encode_cert_info(p15card, obj, &icert); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "'Create Cert' update DF failed: cannot encode info"); rv = awp_set_certificate_info(p15card, profile, info_file, &icert); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "'Create Cert' update DF failed: cannot set info"); rv = awp_update_object_list(p15card, profile, SC_PKCS15_TYPE_CERT_X509, obj_id & 0xFF); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "'Create Cert' update DF failed: cannot update list"); rv = awp_update_container(p15card, profile, SC_PKCS15_TYPE_CERT_X509, &icert.id, obj_id, &prvkey_id); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "'Create Cert' update DF failed: cannot update container"); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "PrvKeyID:%04X", prvkey_id); if (prvkey_id) rv = awp_update_key_info(p15card, profile, prvkey_id, &icert); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "'Create Cert' update DF failed: cannot update key info"); awp_free_cert_info(&icert); if (info_file) sc_file_free(info_file); if (obj_file) sc_file_free(obj_file); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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, *obj_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; int rv; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); 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; struct sc_pkcs15_cert *p15cert; path = cert_info->path; cc.cert_id = (path.value[path.len-1] & 0xFF) + (path.value[path.len-2] & 0xFF) * 0x100; rv = sc_pkcs15_read_certificate(p15card, cert_info, &p15cert); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update private key' DF failed: cannot allocate content"); rv = awp_encode_cert_info(p15card, cert_obj, &icert); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update private key' DF failed: cannot encode cert info"); sc_pkcs15_free_certificate(p15cert); } 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, &obj_file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "New private key info file error"); pubkey.algorithm = SC_ALGORITHM_RSA; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "PrKey Der(%p,%i)", der.value, der.len); rv = sc_pkcs15_decode_pubkey(ctx, &pubkey, der.value, der.len); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update private key' DF failed: decode public key error"); memset(&ikey, 0, sizeof(ikey)); rv = awp_encode_key_info(p15card, key_obj, &pubkey.u.rsa, &ikey); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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_RET(ctx, SC_LOG_DEBUG_NORMAL, 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_RET(ctx, SC_LOG_DEBUG_NORMAL, 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_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update private key' DF failed: update container error"); if (cert_obj) awp_free_cert_info(&icert); awp_free_key_info(&ikey); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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, *obj_file=NULL; struct sc_path path; unsigned obj_id; int index, rv; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); 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; rv = awp_new_file(p15card, profile, obj->type, index, &info_file, &obj_file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "New public key info file error"); pubkey.algorithm = SC_ALGORITHM_RSA; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "PrKey Der(%p,%i)", der.value, der.len); rv = sc_pkcs15_decode_pubkey(ctx, &pubkey, der.value, der.len); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update public key' DF failed: decode public key error"); memset(&ikey, 0, sizeof(ikey)); rv = awp_encode_key_info(p15card, obj, &pubkey.u.rsa, &ikey); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update public key' DF failed: encode info error"); rv = awp_set_key_info(p15card, profile, info_file, &ikey, NULL); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update public key' DF failed: set info error"); rv = awp_update_object_list(p15card, profile, obj->type, index); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update public key' DF failed: update container error"); awp_free_key_info(&ikey); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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_pkcs15_der der; struct sc_path path; unsigned obj_id, obj_type = obj->auth_id.len ? COSM_TYPE_PRIVDATA_OBJECT : SC_PKCS15_TYPE_DATA_OBJECT; int rv; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); der = obj->content; path = ((struct sc_pkcs15_data_info *)obj->data)->path; obj_id = (path.value[path.len-1] & 0xFF) + (path.value[path.len-2] & 0xFF) * 0x100; rv = awp_new_file(p15card, profile, obj_type, obj_id & 0xFF, &info_file, &obj_file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "COSM new file error"); memset(&idata, 0, sizeof(idata)); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Data Der(%p,%i)", der.value, der.len); rv = awp_encode_data_info(p15card, obj, &idata); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "'Create Data' update DF failed: cannot encode info"); rv = awp_set_data_info(p15card, profile, info_file, &idata); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "'Create Data' update DF failed: cannot set info"); rv = awp_update_object_list(p15card, profile, obj_type, obj_id & 0xFF); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "'Create Data' update DF failed: cannot update list"); awp_free_data_info(&idata); if (info_file) sc_file_free(info_file); if (obj_file) sc_file_free(obj_file); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); if (!object) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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: SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "'Create' update DF failed: unsupported object type"); } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "update container entry (type:%X,file-id:%X)", type, file_id); rv = awp_new_file(p15card, profile, COSM_CONTAINER_LIST, 0, &clist, NULL); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP update contaner entry: cannot get allocate AWP file"); rv = sc_select_file(p15card->card, &clist->path, &file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP update contaner entry: cannot select container list file"); buff = malloc(file->record_length); if (!buff) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP update container entry: allocation error"); for (rec = 1; rec <= (unsigned)file->record_count; rec++) { rv = sc_read_record(p15card->card, rec, buff, file->record_length, SC_RECORD_BY_REC_NR); if (rv < 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update contaner 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update contaner entry: 'erase' authentication error %i", rv); break; } rv = sc_delete_record(p15card->card, rec); if (rv < 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update contaner entry: delete record error %i", rv); break; } } else { rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); if (rv < 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update contaner entry: 'update' authentication error %i", rv); break; } rv = sc_update_record(p15card->card, rec, buff, rec_len, SC_RECORD_BY_REC_NR); if (rv < 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update contaner entry: update record error %i", rv); break; } } } if (rv > 0) rv = 0; if (buff) free(buff); if (file) sc_file_free(file); if (clist) sc_file_free(clist); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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]; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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: SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INCORRECT_PARAMETERS, "AWP update object list: invalid type"); } sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update object list: select '%s' file", lst_name); rv = sc_profile_get_file(profile, lst_name, &lst_file); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP update object list: cannot instantiate list file"); rv = sc_select_file(p15card->card, &lst_file->path, &lst); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP update object list: cannot select list file"); rv = sc_pkcs15init_authenticate(profile, p15card, lst, SC_AC_OP_READ); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP update object list: 'read' authentication failed"); buff = malloc(lst->size); if (!buff) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP update object list: allocation error"); 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); if (lst) sc_file_free(lst); if (lst_file) sc_file_free(lst_file); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); path = ((struct sc_pkcs15_cert_info *) obj->data)->path; file_id = path.value[path.len-2] * 0x100 + path.value[path.len-1]; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "file-id:%X", file_id); rv = awp_new_file(p15card, profile, obj->type, file_id & 0xFF, &info_file, NULL); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete cert' update DF failed: cannt get allocate new AWP file"); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "info file-id:%X", info_file->id); rv = cosm_delete_file(p15card, profile, info_file); if (rv != SC_ERROR_FILE_NOT_FOUND) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete cert' update DF failed: delete info file error"); rv = awp_delete_from_container(p15card, profile, obj->type, file_id); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete cert' update DF failed: cannot update container"); rv = awp_remove_from_object_list(p15card, profile, obj->type, file_id); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete cert' update DF failed: cannot remove object"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); path = ((struct sc_pkcs15_prkey_info *) obj->data)->path; file_id = path.value[path.len-2] * 0x100 + path.value[path.len-1]; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "file-id:%X", file_id); rv = awp_new_file(p15card, profile, obj->type, file_id & 0xFF, &info_file, NULL); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete prkey' update DF failed: cannt get allocate new AWP file"); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "info file-id:%X", info_file->id); rv = cosm_delete_file(p15card, profile, info_file); if (rv != SC_ERROR_FILE_NOT_FOUND) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete prkey' update DF failed: delete info file error"); rv = awp_delete_from_container(p15card, profile, obj->type, file_id); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete prkey' update DF failed: cannot update container"); rv = awp_remove_from_object_list(p15card, profile, obj->type, file_id); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete prkey' update DF failed: cannot remove object"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); path = ((struct sc_pkcs15_pubkey_info *) obj->data)->path; file_id = path.value[path.len-2] * 0x100 + path.value[path.len-1]; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "file-id:%X", file_id); rv = awp_new_file(p15card, profile, obj->type, file_id & 0xFF, &info_file, NULL); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete pubkey' update DF failed: cannt get allocate new AWP file"); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "info file-id:%X", info_file->id); rv = cosm_delete_file(p15card, profile, info_file); if (rv != SC_ERROR_FILE_NOT_FOUND) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete pubkey' update DF failed: delete info file error"); rv = awp_delete_from_container(p15card, profile, obj->type, file_id); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete pubkey' update DF failed: cannot update container"); rv = awp_remove_from_object_list(p15card, profile, obj->type, file_id); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete pubkey' update DF failed: cannot remove object"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); path = ((struct sc_pkcs15_data_info *) obj->data)->path; file_id = path.value[path.len-2] * 0x100 + path.value[path.len-1]; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "file-id:%X", file_id); rv = awp_new_file(p15card, profile, obj->type, file_id & 0xFF, &info_file, NULL); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete DATA' update DF failed: cannt get allocate new AWP file"); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "info file-id:%X", info_file->id); rv = cosm_delete_file(p15card, profile, info_file); if (rv != SC_ERROR_FILE_NOT_FOUND) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete DATA' update DF failed: delete info file error"); rv = awp_remove_from_object_list(p15card, profile, obj->type, file_id); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete DATA' update DF failed: cannot remove object"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); if (!object) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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: SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "'Create' update DF failed: unsupported object type"); } SC_FUNC_RETURN(ctx, 1, rv); } #endif /* #ifdef ENABLE_OPENSSL */ opensc-0.13.0/src/pkcs15init/flex.profile0000644000015201777760000000613312057406034015131 00000000000000# # 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.13.0/src/pkcs15init/pkcs15-entersafe.c0000644000015201777760000004076312057406034016044 00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,ret,"Create EF(DIR) failed"); /* fill file by 0 */ 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,ret,"Crate 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,ret,"Create pkcs15 file failed"); } } {/* Preinstall keys */ ret = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_PREINSTALL_KEYS, 0); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,ret,"Preinstall keys failed"); } 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 (pin_obj) { /* 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 DSA 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 %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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "cant 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 %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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "cant verify pin"); /* generate key pair */ gendat.key_id = (u8) kinfo->key_reference; gendat.key_length = (size_t) kinfo->modulus_length; gendat.modulus = NULL; r = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_GENERATE_KEY, &gendat); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "EnterSafe generate RSA key pair failed"); /* 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; struct sc_pkcs15_object *objs[32]; int rv, nn, ii, update_df = 0; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Check and if needed update PinFlags"); rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 32); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to get PINs"); nn = rv; sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &profile_auth); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to get PIN info"); 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OBJECT_NOT_FOUND, "Cannot find AODF"); rv = sc_pkcs15init_update_any_df(p15card, profile, df, 0); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Update AODF error"); } 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.13.0/src/pkcs15init/miocos.profile0000644000015201777760000000224012057406034015457 00000000000000# # PKCS15 r/w profile for MioCOS cards # cardinfo { max-pin-length = 8; pin-encoding = ascii-numeric; pin-pad-char = 0x00; } # Define reasonable limits for PINs and PUK # Note that we do not set a file path or reference # here; that is done dynamically. PIN user-pin { attempts = 3; } PIN user-puk { attempts = 10; } # Additional filesystem info. # This is added to the file system info specified in the # main profile. filesystem { DF MF { DF PKCS15-AppDF { EF template-private-key { type = internal-ef; file-id = 4B01; # This is the base FileID size = 266; # 266 is enough for 1024-bit keys ACL = *=NEVER, CRYPTO=$PIN, UPDATE=$PIN; } EF template-public-key { file-id = 5501; ACL = *=NEVER, READ=NONE, UPDATE=$PIN; } EF template-certificate { file-id = 4301; ACL = *=NEVER, READ=NONE, UPDATE=$PIN; } EF template-extractable-key { file-id = 7000; ACL = *=NEVER, READ=$PIN, UPDATE=$PIN; } } } } # Define an SO pin # This PIN is not used yet. #PIN sopin { # file = sopinfile; # reference = 0; #} opensc-0.13.0/src/pkcs15init/ias_adele_common.profile0000644000015201777760000001153612057406034017454 00000000000000# # 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 insted 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.13.0/src/pkcs15init/rutoken.profile0000644000015201777760000001260212057406034015660 00000000000000# # 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.13.0/src/pkcs15init/pkcs15-sc-hsm.c0000644000015201777760000006636312057406034015266 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "pkcs15-init.h" #include "profile.h" static u8 pubexp[] = { 0x01, 0x00, 0x01 }; #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 } }; 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; }; static struct ec_curve curves[] = { { { "\x2A\x86\x48\xCE\x3D\x03\x01\x01", 8}, // secp192r1 aka prime192r1 { "\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}, { "\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}, { "\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}, { "\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}, { "\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}, { "\x01", 1} }, { { "\x2A\x86\x48\xCE\x3D\x03\x01\x07", 8}, // secp256r1 aka prime256r1 { "\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}, { "\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}, { "\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}, { "\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}, { "\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}, { "\x01", 1} }, { { "\x2B\x24\x03\x03\x02\x08\x01\x01\x03", 9}, // brainpoolP192r1 { "\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}, { "\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}, { "\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}, { "\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}, { "\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}, { "\x01", 1} }, { { "\x2B\x24\x03\x03\x02\x08\x01\x01\x05", 9}, // brainpoolP224r1 { "\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}, { "\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}, { "\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}, { "\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}, { "\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}, { "\x01", 1} }, { { "\x2B\x24\x03\x03\x02\x08\x01\x01\x07", 9}, // brainpoolP256r1 { "\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}, { "\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}, { "\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}, { "\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}, { "\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}, { "\x01", 1} }, { { "\x2B\x24\x03\x03\x02\x08\x01\x01\x09", 9}, // brainpoolP320r1 { "\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}, { "\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}, { "\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}, { "\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}, { "\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}, { "\x01", 1} }, { { NULL, 0} } }; 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_file_t newfile; 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_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 < 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, int 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 = keysize; LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); } static int sc_hsm_get_curve(struct sc_pkcs15_card *p15card, struct ec_curve **curve, u8 *oid, size_t oidlen) { int i; LOG_FUNC_CALLED(p15card->card->ctx); 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; } } sc_log(p15card->card->ctx, "Unknown curve"); LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_INVALID_DATA); } 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_pkcs15_ec_parameters *ecparams = (struct sc_pkcs15_ec_parameters *)key_info->params.data; struct ec_curve *curve; u8 *curveoid; int curveoidlen; int 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_hsm_get_curve(p15card, &curve, curveoid, curveoidlen); 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_decode_gakp_rsa(struct sc_pkcs15_card *p15card, sc_cvc_t *cvc, struct sc_pkcs15_prkey_info *key_info, struct sc_pkcs15_pubkey *pubkey) { u8 *buf; size_t buflen; int r; LOG_FUNC_CALLED(p15card->card->ctx); if (((key_info->modulus_length + 7) / 8) != cvc->primeOrModuluslen) { sc_log(p15card->card->ctx, "Modulus size in request does not match generated public key"); LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); } pubkey->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 = sizeof(pubexp); pubkey->u.rsa.exponent.data = malloc(pubkey->u.rsa.exponent.len); if (!pubkey->u.rsa.modulus.data || !pubkey->u.rsa.exponent.data) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(pubkey->u.rsa.exponent.data, pubexp, pubkey->u.rsa.exponent.len); memcpy(pubkey->u.rsa.modulus.data, cvc->primeOrModulus, pubkey->u.rsa.modulus.len); LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); } static int sc_hsm_decode_gakp_ec(struct sc_pkcs15_card *p15card, sc_cvc_t *cvc, struct sc_pkcs15_prkey_info *key_info, struct sc_pkcs15_pubkey *pubkey) { struct sc_asn1_entry asn1_ec_pointQ[C_ASN1_EC_POINTQ_SIZE]; struct sc_pkcs15_ec_parameters *ecparams = (struct sc_pkcs15_ec_parameters *)(key_info->params.data); struct sc_ec_params *ecp; u8 *buf; size_t buflen; int r; LOG_FUNC_CALLED(p15card->card->ctx); pubkey->algorithm = SC_ALGORITHM_EC; pubkey->u.ec.params.named_curve = strdup(ecparams->named_curve); sc_pkcs15_fix_ec_parameters(p15card->card->ctx, &pubkey->u.ec.params); ecp = calloc(1, sizeof(struct sc_ec_params)); if (!ecp) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); } ecp->der = malloc(ecparams->der.len); if (!ecp->der) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); } ecp->der_len = ecparams->der.len; memcpy(ecp->der, ecparams->der.value, ecp->der_len); pubkey->alg_id = (struct sc_algorithm_id *)calloc(1, sizeof(struct sc_algorithm_id)); if (!pubkey->alg_id) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); } pubkey->alg_id->algorithm = SC_ALGORITHM_EC; pubkey->alg_id->params = ecp; sc_copy_asn1_entry(c_asn1_ec_pointQ, asn1_ec_pointQ); sc_format_asn1_entry(asn1_ec_pointQ + 0, cvc->publicPoint, &cvc->publicPointlen, 1); r = sc_asn1_encode(p15card->card->ctx, asn1_ec_pointQ, &pubkey->u.ec.ecpointQ.value, &pubkey->u.ec.ecpointQ.len); LOG_TEST_RET(p15card->card->ctx, r, "ASN.1 encoding failed"); 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_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; 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)); strcpy(cvc.car, "UTCA00001"); strcpy(cvc.chr, "UTTM00001"); 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: LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_IMPLEMENTED); break; } 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) { switch(object->type) { case SC_PKCS15_TYPE_PRKEY_RSA: r = sc_hsm_decode_gakp_rsa(p15card, &cvc, key_info, pubkey); break; case SC_PKCS15_TYPE_PRKEY_EC: r = sc_hsm_decode_gakp_ec(p15card, &cvc, key_info, pubkey); break; } } 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_context *ctx = p15card->card->ctx; 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_context *ctx = p15card->card->ctx; struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) object->data; struct sc_pkcs15_object *prkey; 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_delete_ef(p15card, CA_CERTIFICATE_PREFIX, cert_info->path.value[1]); } else { LOG_TEST_RET(p15card->card->ctx, r, "Error locating matching private key"); r = sc_hsm_delete_ef(p15card, EE_CERTIFICATE_PREFIX, ((struct sc_pkcs15_prkey_info *)prkey->data)->key_reference); } return r; } 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_context *ctx = p15card->card->ctx; 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_context *ctx = p15card->card->ctx; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)object->data; u8 *buf; size_t buflen; int r; 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_context *ctx = p15card->card->ctx; 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[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_context *ctx = p15card->card->ctx; 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[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_context *ctx = p15card->card->ctx; 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; } return sc_hsm_delete_ef(p15card, CD_PREFIX, ((struct sc_pkcs15_data_info *)object->data)->path.value[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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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, NULL, /* 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.13.0/src/pkcs15init/westcos.profile0000644000015201777760000000707412057406034015667 00000000000000 cardinfo { label = "westcos"; manufacturer = "CEV"; max-pin-length = 8; min-pin-length = 4; pin-encoding = BCD; pin-pad-char = 0xff; } # Default settings. # This option block will always be processed. option default { macros { protected = *=$PIN, READ=NONE; unprotected = *=NONE; private = *=$PIN; so-pin-flags = local, initialized, needs-padding; #, soPin; so-min-pin-length = 6; so-pin-attempts = 2; so-auth-id = 1; #FF; so-puk-attempts = 4; so-min-puk-length = 6; unusedspace-size = 128; odf-size = 256; aodf-size = 256; cdf-size = 512; prkdf-size = 256; pukdf-size = 256; dodf-size = 256; } } PIN so-pin { auth-id = 1; reference = 1; attempts = 3; min-length = 4; max-length = 8; flags = local, initialized, needs-padding; } PIN so-puk { auth-id = 2; reference = 2; attempts = 10; min-length = 4; max-length = 8; flags = local, initialized, needs-padding; } PIN user-pin { auth-id = 1; reference = 1; attempts = 3; min-length = 4; max-length = 8; flags = local, initialized, needs-padding; } PIN user-puk { auth-id = 2; reference = 2; attempts = 10; min-length = 4; max-length = 8; flags = local, initialized, needs-padding; } filesystem { DF MF { path = 3F00; type = DF; # This is the DIR file EF DIR { type = EF; file-id = 2F00; size = 128; acl = $unprotected; } # Here comes the application DF DF PKCS15-AppDF { type = DF; file-id = 5015; aid = A0:00:00:00:63:50:4B:43:53:2D:31:35; acl = $unprotected; size = 5000; EF PINFILE { file-id = AAAA; type = INTERNAL-EF; structure = TRANSPARENT; size = 100; acl = *=NEVER; } EF PKCS15-ODF { file-id = 5031; size = $odf-size; acl = $unprotected; } EF PKCS15-TokenInfo { file-id = 5032; acl = $unprotected; } EF PKCS15-UnusedSpace { file-id = 5033; size = $unusedspace-size; acl = $unprotected; } EF PKCS15-AODF { file-id = 4401; size = $aodf-size; acl = $protected; } EF PKCS15-PrKDF { file-id = 4402; size = $prkdf-size; acl = $protected; } EF PKCS15-PuKDF { file-id = 4403; size = $pukdf-size; acl = $protected; } EF PKCS15-CDF { file-id = 4404; size = $cdf-size; acl = $protected; } EF PKCS15-DODF { file-id = 4405; size = $dodf-size; ACL = $protected; } # This template defines files for keys, certificates etc. # # When instantiating the template, each file id will be # combined with the last octet of the object's pkcs15 id # to form a unique file ID. template key-domain { EF private-key { file-id = 0100; structure = transparent; acl = *=NEVER, READ=$PIN, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; } EF public-key { file-id = 0200; structure = transparent; acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; } # Certificate template EF certificate { file-id = 0300; structure = transparent; acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; } # data objects are stored in transparent EFs. EF data { file-id = 0400; structure = transparent; acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; } # private data objects are stored in transparent EFs. EF privdata { file-id = 0500; structure = transparent; acl = *=NEVER, READ=$PIN, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; } } } } } opensc-0.13.0/src/pkcs15init/pkcs15-cardos.c0000644000015201777760000005576312057406034015351 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" #include "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 void tlv_next(struct tlv *tlv, u8 tag) { assert(tlv->next + 2 < tlv->end); tlv->current = tlv->next; *(tlv->next++) = tag; *(tlv->next++) = 0; } static void tlv_add(struct tlv *tlv, u8 val) { assert(tlv->next + 1 < tlv->end); *(tlv->next++) = val; tlv->current[1]++; } static size_t tlv_len(struct tlv *tlv) { return tlv->next - tlv->base; } /* * Try to delete pkcs15 structure * This is not quite the same as erasing the whole token, but * it's close enough to be useful. */ static int 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 autometically 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; } else { preferred = current; /* PINs are even numbered, PUKs are odd */ if (!(preferred & 1)) preferred++; if (preferred >= 126) return SC_ERROR_TOO_MANY_OBJECTS; } if (current > preferred || preferred > CARDOS_PIN_ID_MAX) return SC_ERROR_TOO_MANY_OBJECTS; auth_info->attrs.pin.reference = preferred; return 0; } /* * 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, &df->path, NULL); if (r < 0) return r; if (puk && puk_len) { struct sc_pkcs15_auth_info puk_ainfo; sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, &puk_ainfo); puk_ainfo.attrs.pin.reference = puk_id = auth_info->attrs.pin.reference + 1; r = 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "CardOS supports RSA keys only."); return SC_ERROR_NOT_SUPPORTED; } if (cardos_key_algorithm(key_info->usage, key_info->modulus_length, &algorithm) < 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "CardOS does not support keys " "that can both sign _and_ decrypt."); return SC_ERROR_NOT_SUPPORTED; } r = sc_select_file(p15card->card, &key_info->path, &file); if (r) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Failed to store key: cannot select parent DF"); return r; } r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); sc_file_free(file); if (r) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Failed to store key: 'UPDATE' authentication failed"); 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "CardOS does not support keys " "that can both sign _and_ decrypt."); return SC_ERROR_NOT_SUPPORTED; } if (sc_profile_get_file(profile, "tempfile", &temp) < 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Profile doesn't define temporary file " "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; } /* * 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, minlen, maxlen; 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid pin length: %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 = auth_info->attrs.pin.min_length; tlv_init(&tlv, buffer, sizeof(buffer)); /* object address: class, id */ tlv_next(&tlv, 0x83); tlv_add(&tlv, 0x00); /* class byte: usage TEST, k=0 */ tlv_add(&tlv, auth_info->attrs.pin.reference); /* parameters */ tlv_next(&tlv, 0x85); tlv_add(&tlv, 0x02); /* options byte */ hasverifyrc = cardos_have_verifyrc_package(card); if (hasverifyrc == 1) /* Use 9 byte OCI parameters to be able to set VerifyRC bit */ tlv_add(&tlv, 0x04); /* options_2 byte with bit 2 set to return CurrentErrorCounter */ tlv_add(&tlv, attempts & 0xf); /* flags byte */ tlv_add(&tlv, CARDOS_ALGO_PIN); /* algorithm = pin-test */ tlv_add(&tlv, attempts & 0xf); /* errcount = attempts */ /* usecount: not documented, but seems to work like this: * - value of 0xff means pin can be presented any number * of times * - anything less: max # of times before BS object is blocked. */ tlv_add(&tlv, 0xff); /* DEK: not documented, no idea what it means */ tlv_add(&tlv, 0xff); /* ARA counter: number of times the test object can be used before * another verification is required (~ user consent) * (0x00 unlimited usage) */ tlv_add(&tlv, 0x00); tlv_add(&tlv, minlen); /* minlen */ /* AC conditions */ tlv_next(&tlv, 0x86); tlv_add(&tlv, 0x00); /* use: always */ tlv_add(&tlv, auth_info->attrs.pin.reference); /* change: PIN */ tlv_add(&tlv, puk_id); /* unblock: PUK */ /* data: PIN */ tlv_next(&tlv, 0x8f); while (pin_len--) tlv_add(&tlv, *pin++); 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)); tlv_next(&tlv, 0x83); tlv_add(&tlv, se_id); tlv_next(&tlv, 0x86); tlv_add(&tlv, 0); tlv_add(&tlv, 0); tlv_next(&tlv, 0x8f); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); args.data = buffer; args.len = tlv_len(&tlv); /* 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 */ tlv_next(&tlv, 0x83); tlv_add(&tlv, 0x20|num); /* PSO, n-th component */ tlv_add(&tlv, key_id); /* Object parameters */ tlv_next(&tlv, 0x85); tlv_add(&tlv, CARDOS_KEY_OPTIONS|(last? 0x00 : 0x20)); tlv_add(&tlv, CARDOS_KEY_FLAGS); tlv_add(&tlv, algorithm); tlv_add(&tlv, 0x00); tlv_add(&tlv, 0xFF); /* use count */ tlv_add(&tlv, 0xFF); /* DEK (whatever this is) */ tlv_add(&tlv, 0x00); tlv_add(&tlv, 0x00); /* AC bytes */ tlv_next(&tlv, 0x86); tlv_add(&tlv, pin_id); /* AC USE */ tlv_add(&tlv, pin_id); /* AC CHANGE */ tlv_add(&tlv, pin_id); /* UNKNOWN */ tlv_add(&tlv, 0); /* rfu */ tlv_add(&tlv, 0); /* rfu */ tlv_add(&tlv, 0); /* rfu */ #if 0 tlv_add(&tlv, pin_id); /* AC GENKEY */ #else tlv_add(&tlv, 0); #endif #ifdef SET_SM_BYTES /* it shouldn't be necessary to set the default value */ /* SM bytes */ tlv_next(&tlv, 0x8B); for (n = 0; n < 16; n++) tlv_add(&tlv, 0xFF); #endif /* key component */ tlv_next(&tlv, 0x8f); if (use_prefix != 0) { tlv_add(&tlv, len+1); tlv_add(&tlv, 0); } while (len--) tlv_add(&tlv, *data++); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid public key data: missing tag"); return SC_ERROR_INTERNAL; } p = sc_asn1_find_tag(card->ctx, data, ilen, 0x81, &tlen); if (p == NULL) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid public key data: missing modulus"); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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, 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; 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if ((len = apdu.resplen) == 0) /* looks like no package has been installed */ return 0; while (len != 0) { p = sc_asn1_find_tag(card->ctx, p, len, 0xe1, &tlen); if (p == NULL) return 0; if (card->type == SC_CARD_TYPE_CARDOS_M4_3) { /* the verifyRC package on CardOS 4.3B use Manufacturer ID 0x01 */ /* and Package Number 0x07 */ q = sc_asn1_find_tag(card->ctx, p, tlen, 0x01, &ilen); 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, p, 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 */ NULL, /* 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.13.0/src/pkcs15init/iasecc_generic_oberthur.profile0000644000015201777760000001107112057406034021025 00000000000000# # 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 insted 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.13.0/src/pkcs15init/pkcs15-openpgp.c0000755000015201777760000002457312057406034015544 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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" /** * 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 (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 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; struct sc_pkcs15_prkey_rsa *rsa = &(key->u.rsa); sc_cardctl_openpgp_keystore_info_t key_info; int r; LOG_FUNC_CALLED(card->ctx); if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "only RSA is currently supported"); return SC_ERROR_NOT_SUPPORTED; } memset(&key_info, 0, sizeof(sc_cardctl_openpgp_keystore_info_t)); key_info.keytype = kinfo->id.value[0]; key_info.e = rsa->exponent.data; key_info.e_len = rsa->exponent.len; key_info.p = rsa->p.data; key_info.p_len = rsa->p.len; key_info.q = rsa->q.data; key_info.q_len = rsa->q.len; key_info.n = rsa->modulus.data; key_info.n_len = rsa->modulus.len; r = sc_card_ctl(card, SC_CARDCTL_OPENPGP_STORE_KEY, &key_info); LOG_FUNC_RETURN(card->ctx, r); } /** * Generates a new (RSA) key pair using an existing key file. * @param profile IN profile information for this card * @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; 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.keytype = 3; } if (!key_info.keytype && (kid->len > 1 || kid->value[0] > 3)) { sc_log(ctx, "Key ID must be 1, 2 or 3!"); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); } if (!key_info.keytype) key_info.keytype = kid->value[0]; /* Prepare buffer */ key_info.modulus_len = required->modulus_length; key_info.modulus = calloc(required->modulus_length >> 3, 1); if (key_info.modulus == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_ENOUGH_MEMORY); /* The OpenPGP supports only 32-bit exponent. */ key_info.exponent_len = 32; key_info.exponent = calloc(4, 1); if (key_info.exponent == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_ENOUGH_MEMORY); r = sc_card_ctl(card, SC_CARDCTL_OPENPGP_GENERATE_KEY, &key_info); if (r < 0) goto out; sc_log(ctx, "Set output modulus info"); pubkey->u.rsa.modulus.len = key_info.modulus_len; pubkey->u.rsa.modulus.data = calloc(key_info.modulus_len, 1); if (pubkey->u.rsa.modulus.data == NULL) goto out; memcpy(pubkey->u.rsa.modulus.data, key_info.modulus, key_info.modulus_len); sc_log(ctx, "Set output exponent info"); pubkey->u.rsa.exponent.len = key_info.exponent_len; pubkey->u.rsa.exponent.data = calloc(key_info.exponent_len, 1); if (pubkey->u.rsa.exponent.data == NULL) goto out; memcpy(pubkey->u.rsa.exponent.data, key_info.exponent, key_info.exponent_len); out: if (key_info.modulus) free(key_info.modulus); if (key_info.exponent) free(key_info.exponent); 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_file_t *file; sc_pkcs15_cert_info_t *cinfo; sc_pkcs15_id_t *cid; 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); 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) { sc_log(card->ctx, "This version does not support certificate ID = %d (only ID=3 is supported).", cid->value[0]); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } /* Just update the certificate DO */ 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); 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; } 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.13.0/src/pkcs15init/Makefile.in0000644000015201777760000005412212057406056014663 00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program 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. @SET_MAKE@ # Required to build Windows resource file VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ DIST_COMMON = README $(dist_pkgdata_DATA) $(noinst_HEADERS) \ $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ $(top_srcdir)/win32/ltrc.inc subdir = src/pkcs15init ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libpkcs15init_la_LIBADD = am_libpkcs15init_la_OBJECTS = pkcs15-lib.lo profile.lo \ pkcs15-westcos.lo pkcs15-gpk.lo pkcs15-miocos.lo \ pkcs15-cflex.lo pkcs15-cardos.lo pkcs15-jcop.lo \ pkcs15-starcos.lo pkcs15-setcos.lo pkcs15-incrypto34.lo \ pkcs15-muscle.lo pkcs15-asepcos.lo pkcs15-rutoken.lo \ pkcs15-entersafe.lo pkcs15-epass2003.lo pkcs15-rtecp.lo \ pkcs15-myeid.lo pkcs15-oberthur.lo pkcs15-oberthur-awp.lo \ pkcs15-authentic.lo pkcs15-iasecc.lo pkcs15-openpgp.lo \ pkcs15-sc-hsm.lo libpkcs15init_la_OBJECTS = $(am_libpkcs15init_la_OBJECTS) AM_V_lt = $(am__v_lt_$(V)) am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) am__v_lt_0 = --silent DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_$(V)) am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) am__v_CC_0 = @echo " CC " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_$(V)) am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) am__v_CCLD_0 = @echo " CCLD " $@; AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; SOURCES = $(libpkcs15init_la_SOURCES) DIST_SOURCES = $(libpkcs15init_la_SOURCES) am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__installdirs = "$(DESTDIR)$(pkgdatadir)" DATA = $(dist_pkgdata_DATA) HEADERS = $(noinst_HEADERS) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUG_FILE = @DEBUG_FILE@ DEFAULT_PCSC_PROVIDER = @DEFAULT_PCSC_PROVIDER@ DEFAULT_SM_MODULE = @DEFAULT_SM_MODULE@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GIT = @GIT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBRARY_BITNESS = @LIBRARY_BITNESS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCT_CFLAGS = @OPENCT_CFLAGS@ OPENCT_LIBS = @OPENCT_LIBS@ OPENSC_LT_AGE = @OPENSC_LT_AGE@ OPENSC_LT_CURRENT = @OPENSC_LT_CURRENT@ OPENSC_LT_OLDEST = @OPENSC_LT_OLDEST@ OPENSC_LT_REVISION = @OPENSC_LT_REVISION@ OPENSC_VERSION_FIX = @OPENSC_VERSION_FIX@ OPENSC_VERSION_MAJOR = @OPENSC_VERSION_MAJOR@ OPENSC_VERSION_MINOR = @OPENSC_VERSION_MINOR@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OPTIONAL_OPENCT_CFLAGS = @OPTIONAL_OPENCT_CFLAGS@ OPTIONAL_OPENCT_LIBS = @OPTIONAL_OPENCT_LIBS@ OPTIONAL_OPENSSL_CFLAGS = @OPTIONAL_OPENSSL_CFLAGS@ OPTIONAL_OPENSSL_LIBS = @OPTIONAL_OPENSSL_LIBS@ OPTIONAL_PCSC_CFLAGS = @OPTIONAL_PCSC_CFLAGS@ OPTIONAL_READLINE_CFLAGS = @OPTIONAL_READLINE_CFLAGS@ OPTIONAL_READLINE_LIBS = @OPTIONAL_READLINE_LIBS@ OPTIONAL_ZLIB_CFLAGS = @OPTIONAL_ZLIB_CFLAGS@ OPTIONAL_ZLIB_LIBS = @OPTIONAL_ZLIB_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCSC_CFLAGS = @PCSC_CFLAGS@ PCSC_LIBS = @PCSC_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RC = @RC@ READLINE_CFLAGS = @READLINE_CFLAGS@ READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ WIN_LIBPREFIX = @WIN_LIBPREFIX@ XSLTPROC = @XSLTPROC@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ git = @git@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkcs11dir = @pkcs11dir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ xslstylesheetsdir = @xslstylesheetsdir@ RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE) MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak noinst_LTLIBRARIES = libpkcs15init.la noinst_HEADERS = profile.h pkcs15-init.h pkcs15-oberthur.h dist_pkgdata_DATA = \ cyberflex.profile \ flex.profile \ gpk.profile \ miocos.profile \ cardos.profile \ incrypto34.profile \ jcop.profile \ oberthur.profile \ starcos.profile \ setcos.profile \ pkcs15.profile \ muscle.profile \ rutoken.profile \ asepcos.profile \ entersafe.profile \ epass2003.profile \ rutoken_ecp.profile \ westcos.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 AM_CPPFLAGS = -DSC_PKCS15_PROFILE_DIRECTORY=\"$(pkgdatadir)\" \ -I$(top_srcdir)/src AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) libpkcs15init_la_SOURCES = \ pkcs15-lib.c profile.c \ pkcs15-westcos.c \ pkcs15-gpk.c pkcs15-miocos.c pkcs15-cflex.c \ pkcs15-cardos.c pkcs15-jcop.c pkcs15-starcos.c \ pkcs15-setcos.c pkcs15-incrypto34.c pkcs15-muscle.c \ pkcs15-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 all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj .rc $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/win32/ltrc.inc $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/pkcs15init/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/pkcs15init/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ test "$$dir" != "$$p" || dir=.; \ echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done libpkcs15init.la: $(libpkcs15init_la_OBJECTS) $(libpkcs15init_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libpkcs15init_la_OBJECTS) $(libpkcs15init_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-asepcos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-authentic.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-cardos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-cflex.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-entersafe.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-epass2003.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-gpk.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-iasecc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-incrypto34.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-jcop.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-lib.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-miocos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-muscle.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-myeid.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-oberthur-awp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-oberthur.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-openpgp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-rtecp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-rutoken.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-sc-hsm.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-setcos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-starcos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-westcos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/profile.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-dist_pkgdataDATA: $(dist_pkgdata_DATA) @$(NORMAL_INSTALL) test -z "$(pkgdatadir)" || $(MKDIR_P) "$(DESTDIR)$(pkgdatadir)" @list='$(dist_pkgdata_DATA)'; test -n "$(pkgdatadir)" || list=; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgdatadir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgdatadir)" || exit $$?; \ done uninstall-dist_pkgdataDATA: @$(NORMAL_UNINSTALL) @list='$(dist_pkgdata_DATA)'; test -n "$(pkgdatadir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ test -n "$$files" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkgdatadir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkgdatadir)" && rm -f $$files ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: CTAGS CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(pkgdatadir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dist_pkgdataDATA install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dist_pkgdataDATA .MAKE: install-am install-strip .PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES ctags distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am \ install-dist_pkgdataDATA install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags uninstall uninstall-am uninstall-dist_pkgdataDATA .rc.lo: $(LTRCCOMPILE) -i "$<" -o "$@" .rc.o: $(RCCOMPILE) -i "$<" -o "$@" # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: opensc-0.13.0/src/pkcs15init/cyberflex.profile0000644000015201777760000000554712057406034016166 00000000000000# # 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.13.0/src/pkcs15init/pkcs15-asepcos.c0000644000015201777760000006115712057406034015525 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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 wether 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); /* Check wether 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; /* outter 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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) */ #if 0 *p++ = (pinfo->flags & SC_PKCS15_PIN_FLAG_SO_PIN) ? 0x08 : 0x00; #else *p++ = 0x00; #endif /* 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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; 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); 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; pid = (auth_info->attrs.pin.reference & 0xff) | (((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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to create PIN file, insufficent 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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; 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "unable to find file in profile"); return r; } r = sc_pkcs15init_authenticate(profile, p15card, prkey, op); sc_file_free(prkey); if (r != SC_SUCCESS) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "unable to authenticate"); 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[512], *p = buf; if (sizeof(buf) < kdlen + 11) 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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, 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 proctected by a PIN */ /* XXX use the pkcs15 structures for this */ sc_cardctl_asepcos_akn2fileid_t st; st.akn = sc_pkcs15init_get_pin_reference(p15card, profile, SC_AC_SYMBOLIC, SC_PKCS15INIT_USER_PIN); r = sc_card_ctl(p15card->card, SC_CARDCTL_ASEPCOS_AKN2FILEID, &st); if (r != SC_SUCCESS) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "unable to determine file id of the PIN"); return r; } afileid = st.fileid; } #if 0 /* select the DF */ r = sc_select_file(card, &profile->df_info->file->path, NULL); if (r != SC_SUCCESS) return r; #endif /* 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; #if 0 /* private exponent */ *p++ = 0x92; SET_TLV_LENGTH(p, blen); memset(p, 0xff, blen); p += blen; #else /* public exponent */ *p++ = 0x90; SET_TLV_LENGTH(p, 3); memset(p, 0xff, 3); p += 3; #endif /* 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_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "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, 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_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "unable to select rsa key file"); return r; } /* store key parts in buffer */ *p++ = 0xc1; *p++ = 0x82; p += 2; #if 0 /* private exponent */ *p++ = 0x92; SET_TLV_LENGTH(p, key->d.len); memcpy(p, key->d.data, key->d.len); p += key->d.len; #else /* public exponent */ *p++ = 0x90; SET_TLV_LENGTH(p, key->exponent.len); memcpy(p, key->exponent.data, key->exponent.len); p += key->exponent.len; #endif /* 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_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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.13.0/src/pkcs15init/cardos.profile0000644000015201777760000000542112057406034015445 00000000000000# # 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.13.0/src/pkcs15init/muscle.profile0000644000015201777760000000660512057406034015467 00000000000000# # 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.13.0/src/pkcs15init/jcop.profile0000644000015201777760000000424212057406034015125 00000000000000# # PKCS15 r/w profile for JCOP cards # cardinfo { max-pin-length = 16; pin-encoding = ascii-numeric; pin-pad-char = 0x00; } filesystem { DF MF { DF PKCS15-AppDF { acl = *=NONE, CREATE=CHV3; EF PKCS15-AODF { file-id = 502E; } EF PKCS15-PrKDF { file-id = 502C; } EF PKCS15-PuKDF { file-id = 502B; } EF PKCS15-CDF { file-id = 502D; } EF PKCS15-DODF { file-id = 502F; } template key-domain { EF private-key { file-id = 3000; acl = *=NEVER, UPDATE=$PIN, CRYPTO=$PIN, ERASE=$SOPIN; } EF extractable-key { file-id = 3100; acl = *=NEVER, READ=$PIN, UPDATE=$PIN, ERASE=$SOPIN; } EF data { file-id = 3200; acl = *=NEVER, UPDATE=$PIN, READ=NONE, ERASE=$SOPIN; } EF privdata { file-id = 3500; acl = *=NEVER, UPDATE=$PIN, READ=$PIN, ERASE=$SOPIN; } EF public-key { file-id = 3300; acl = *=NEVER, UPDATE=$PIN, READ=NONE, ERASE=$SOPIN; } EF certificate { file-id = 3400; acl = *=NEVER, UPDATE=$PIN, READ=NONE, ERASE=$SOPIN; } } EF temp-pubkey { file-id = 0000; acl = *=NEVER, UPDATE=$PIN, READ=NONE, ERASE=$SOPIN; } } } } opensc-0.13.0/src/pkcs15init/entersafe.profile0000644000015201777760000000746012057406034016153 00000000000000# # 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.13.0/src/pkcs15init/profile.h0000644000015201777760000001044012057406034014416 00000000000000/* * Card profile information (internal) * * Copyright (C) 2002 Olaf Kirch */ #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; unsigned int dsa_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.13.0/src/scconf/0000755000015201777760000000000012057406120012143 500000000000000opensc-0.13.0/src/scconf/parse.c0000644000015201777760000002361712057406034013356 00000000000000/* * $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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #ifdef HAVE_STRINGS_H #include #endif #include #include "common/compat_strlcpy.h" #include "scconf.h" #include "internal.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->type == SCCONF_ITEM_TYPE_VALUE && 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) { if (parser->key) { free(parser->key); } parser->key = NULL; parser->current_item = item; return item; } } item = malloc(sizeof(scconf_item)); if (!item) { return NULL; } memset(item, 0, sizeof(scconf_item)); 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); } 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; } 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); block = malloc(sizeof(scconf_block)); if (!block) { return; } memset(block, 0, sizeof(scconf_block)); 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_parse_token(scconf_parser * parser, int token_type, const char *token) { scconf_item *item; int 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); 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 == '"') { /* quoted string, remove them */ token++; len = strlen(token); if (len < 1 || token[len - 1] != '"') { scconf_parse_warning_expect(parser, "\""); } else { /* stoken */ stoken = token ? strdup(token) : NULL; 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) { /* 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; } scconf_block_add_internal(parser); scconf_parse_reset_state(parser); break; case '}': 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 ';': #if 0 if ((parser->state & STATE_VALUE) == 0 || (parser->state & STATE_SET) == 0) { scconf_parse_error_not_expect(parser, ";"); break; } #endif 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; 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; 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; } if (r <= 0) config->errmsg = buffer; return r; } opensc-0.13.0/src/scconf/Makefile.mak0000644000015201777760000000034212057406034014275 00000000000000TOPDIR = ..\.. TARGET = scconf.lib OBJECTS = scconf.obj parse.obj write.obj sclex.obj .SUFFIXES : .l all: $(TARGET) $(TARGET): $(OBJECTS) lib $(LIBFLAGS) /out:$(TARGET) $(OBJECTS) !INCLUDE $(TOPDIR)\win32\Make.rules.mak opensc-0.13.0/src/scconf/README.scconf0000644000015201777760000002264312057406034014230 00000000000000A 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 heirarchical 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 heirarchy, it only finds blocks in the top level of either the context (the root block) or of the block given in the block paramter (if not NULL). 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.13.0/src/scconf/scconf.h0000644000015201777760000001467212057406034013525 00000000000000/* * $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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _SC_CONF_H #define _SC_CONF_H #ifdef __cplusplus extern "C" { #endif typedef struct _scconf_entry { const char *name; unsigned int type; unsigned int flags; void *parm; void *arg; } scconf_entry; /* Entry flags */ #define SCCONF_PRESENT 0x00000001 #define SCCONF_MANDATORY 0x00000002 #define SCCONF_ALLOC 0x00000004 #define SCCONF_ALL_BLOCKS 0x00000008 #define SCCONF_VERBOSE 0x00000010 /* For debugging purposes only */ /* Entry types */ #define SCCONF_CALLBACK 1 #define SCCONF_BLOCK 2 #define SCCONF_LIST 3 #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); /* Parse entries */ extern int scconf_parse_entries(const scconf_context * config, const scconf_block * block, scconf_entry * entry); /* 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); /* Write configuration entries to block */ extern int scconf_write_entries(scconf_context * config, scconf_block * block, scconf_entry * entry); /* 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.13.0/src/scconf/internal.h0000644000015201777760000000310612057406034014054 00000000000000/* * $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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _SCCONF_INTERNAL_H #define _SCCONF_INTERNAL_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 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]; } 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_parse_token(scconf_parser * parser, int token_type, const char *token); #ifdef __cplusplus } #endif #endif opensc-0.13.0/src/scconf/sclex.c0000644000015201777760000000770512057406034013362 00000000000000/* * $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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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); bp->bufmax = 256; bp->bufcur = 0; bp->buf[0] = '\0'; bp->saved_string = saved_string; } static void buf_addch(BUFHAN * bp, char ch) { if (bp->bufcur >= bp->bufmax) { bp->bufmax += 256; bp->buf = (char *) realloc(bp->buf, bp->bufmax); } #if 0 printf("pushback %c\n", ch); #endif 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 '{': 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.13.0/src/scconf/scconf.c0000644000015201777760000003753012057406034013516 00000000000000/* * $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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #ifdef HAVE_STRINGS_H #include #endif #include #include "scconf.h" scconf_context *scconf_new(const char *filename) { scconf_context *config; config = malloc(sizeof(scconf_context)); if (!config) { return NULL; } memset(config, 0, sizeof(scconf_context)); config->filename = filename ? strdup(filename) : NULL; config->root = malloc(sizeof(scconf_block)); if (!config->root) { if (config->filename) { free(config->filename); } free(config); return NULL; } memset(config->root, 0, sizeof(scconf_block)); 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; blocks = (scconf_block **) realloc(blocks, sizeof(scconf_block *) * alloc_size); for (item = block->items; item; item = item->next) { if (item->type == SCCONF_ITEM_TYPE_BLOCK && strcasecmp(item_name, item->key) == 0) { 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-configurated' 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; list = scconf_find_list(block, option); return !list ? def : strtol(list->data, NULL, 0); } 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 = malloc(sizeof(scconf_item)); if (!next) { return NULL; } memset(next, 0, sizeof(scconf_item)); ptr = next; _dst = next; while (src) { if (!next) { next = malloc(sizeof(scconf_item)); if (!next) { scconf_item_destroy(ptr); return NULL; } memset(next, 0, sizeof(scconf_item)); _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 = malloc(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 = malloc(sizeof(scconf_list)); if (!rec) { return NULL; } memset(rec, 0, sizeof(scconf_list)); 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); } buf = malloc(len); if (!buf) { return NULL; } memset(buf, 0, len); 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; } static scconf_block **getblocks(const scconf_context * config, const scconf_block * block, scconf_entry * entry) { scconf_block **blocks = NULL, **tmp; blocks = scconf_find_blocks(config, block, entry->name, NULL); if (blocks) { if (blocks[0] != NULL) { if (config->debug) { fprintf(stderr, "block found (%s)\n", entry->name); } return blocks; } free(blocks); blocks = NULL; } if (scconf_find_list(block, entry->name) != NULL) { if (config->debug) { fprintf(stderr, "list found (%s)\n", entry->name); } tmp = (scconf_block **) realloc(blocks, sizeof(scconf_block *) * 2); if (!tmp) { free(blocks); return NULL; } blocks = tmp; blocks[0] = (scconf_block *) block; blocks[1] = NULL; } return blocks; } static int parse_entries(const scconf_context * config, const scconf_block * block, scconf_entry * entry, int depth); static int parse_type(const scconf_context * config, const scconf_block * block, scconf_entry * entry, int depth) { void *parm = entry->parm; size_t *len = (size_t *) entry->arg; int (*callback_func) (const scconf_context * config, const scconf_block * block, scconf_entry * entry, int depth) = (int (*)(const scconf_context *, const scconf_block *, scconf_entry *, int)) parm; int r = 0; if (config->debug) { fprintf(stderr, "decoding '%s'\n", entry->name); } switch (entry->type) { case SCCONF_CALLBACK: if (parm) { r = callback_func(config, block, entry, depth); } break; case SCCONF_BLOCK: if (parm) { r = parse_entries(config, block, (scconf_entry *) parm, depth + 1); } break; case SCCONF_LIST: { const scconf_list *val = scconf_find_list(block, entry->name); if (!val) { r = 1; break; } if (parm) { if (entry->flags & SCCONF_ALLOC) { scconf_list *dest = NULL; for (; val != NULL; val = val->next) { if (!scconf_list_add(&dest, val->data)) { r = 1; break; } } *((scconf_list **) parm) = dest; } else { *((const scconf_list **) parm) = val; } } if (entry->flags & SCCONF_VERBOSE) { char *buf = scconf_list_strdup(val, ", "); printf("%s = %s\n", entry->name, buf); free(buf); } } break; case SCCONF_BOOLEAN: { int val = scconf_get_bool(block, entry->name, 0); if (parm) { *((int *) parm) = val; } if (entry->flags & SCCONF_VERBOSE) { printf("%s = %s\n", entry->name, val == 0 ? "false" : "true"); } } break; case SCCONF_INTEGER: { int val = scconf_get_int(block, entry->name, 0); if (parm) { *((int *) parm) = val; } if (entry->flags & SCCONF_VERBOSE) { printf("%s = %i\n", entry->name, val); } } break; case SCCONF_STRING: { const char *val = scconf_get_str(block, entry->name, NULL); int vallen = val ? strlen(val) : 0; if (!vallen) { r = 1; break; } if (parm) { if (entry->flags & SCCONF_ALLOC) { char **buf = (char **) parm; *buf = malloc(vallen + 1); if (*buf == NULL) { r = 1; break; } memset(*buf, 0, vallen + 1); if (len) { *len = vallen; } parm = *buf; } memcpy((char *) parm, val, vallen); } if (entry->flags & SCCONF_VERBOSE) { printf("%s = %s\n", entry->name, val); } } break; default: fprintf(stderr, "invalid configuration type: %d\n", entry->type); } if (r) { fprintf(stderr, "decoding of configuration entry '%s' failed.\n", entry->name); return r; } entry->flags |= SCCONF_PRESENT; return 0; } static int parse_entries(const scconf_context * config, const scconf_block * block, scconf_entry * entry, int depth) { int r, i, idx; scconf_entry *e; scconf_block **blocks = NULL; if (config->debug) { fprintf(stderr, "parse_entries called, depth %d\n", depth); } for (idx = 0; entry[idx].name; idx++) { e = &entry[idx]; blocks = getblocks(config, block, e); if (!blocks) { if (!(e->flags & SCCONF_MANDATORY)) { if (config->debug) fprintf(stderr, "optional configuration entry '%s' not present\n", e->name); continue; } fprintf(stderr, "mandatory configuration entry '%s' not found\n", e->name); return 1; } for (i = 0; blocks[i]; i++) { r = parse_type(config, blocks[i], e, depth); if (r) { free(blocks); return r; } if (!(e->flags & SCCONF_ALL_BLOCKS)) break; } free(blocks); } return 0; } int scconf_parse_entries(const scconf_context * config, const scconf_block * block, scconf_entry * entry) { if (!entry) return 1; if (!block) block = config->root; return parse_entries(config, block, entry, 0); } static int write_entries(scconf_context * config, scconf_block * block, scconf_entry * entry, int depth); static int write_type(scconf_context * config, scconf_block * block, scconf_entry * entry, int depth) { void *parm = entry->parm; void *arg = entry->arg; int (*callback_func) (scconf_context * config, scconf_block * block, scconf_entry * entry, int depth) = (int (*)(scconf_context *, scconf_block *, scconf_entry *, int)) parm; int r = 0; if (config->debug) { fprintf(stderr, "encoding '%s'\n", entry->name); } switch (entry->type) { case SCCONF_CALLBACK: if (parm) { r = callback_func(config, block, entry, depth); } break; case SCCONF_BLOCK: if (parm) { scconf_block *subblock; const scconf_list *name = (const scconf_list *) arg; subblock = scconf_block_add(config, block, entry->name, name); r = write_entries(config, subblock, (scconf_entry *) parm, depth + 1); } break; case SCCONF_LIST: if (parm) { const scconf_list *val = (const scconf_list *) parm; scconf_item_add(config, block, NULL, SCCONF_ITEM_TYPE_VALUE, entry->name, val); if (entry->flags & SCCONF_VERBOSE) { char *buf = scconf_list_strdup(val, ", "); printf("%s = %s\n", entry->name, buf); free(buf); } } break; case SCCONF_BOOLEAN: if (parm) { const int val = * (int* ) parm; scconf_put_bool(block, entry->name, val); if (entry->flags & SCCONF_VERBOSE) { printf("%s = %s\n", entry->name, val == 0 ? "false" : "true"); } } break; case SCCONF_INTEGER: if (parm) { const int val = * (int*) parm; scconf_put_int(block, entry->name, val); if (entry->flags & SCCONF_VERBOSE) { printf("%s = %i\n", entry->name, val); } } break; case SCCONF_STRING: if (parm) { const char *val = (const char *) parm; scconf_put_str(block, entry->name, val); if (entry->flags & SCCONF_VERBOSE) { printf("%s = %s\n", entry->name, val); } } break; default: fprintf(stderr, "invalid configuration type: %d\n", entry->type); } if (r) { fprintf(stderr, "encoding of configuration entry '%s' failed.\n", entry->name); return r; } entry->flags |= SCCONF_PRESENT; return 0; } static int write_entries(scconf_context * config, scconf_block * block, scconf_entry * entry, int depth) { int r, idx; scconf_entry *e; if (config->debug) { fprintf(stderr, "write_entries called, depth %d\n", depth); } for (idx = 0; entry[idx].name; idx++) { e = &entry[idx]; r = write_type(config, block, e, depth); if (r) { return r; } } return 0; } int scconf_write_entries(scconf_context * config, scconf_block * block, scconf_entry * entry) { if (!entry) return 1; if (!block) block = config->root; return write_entries(config, block, entry, 0); } opensc-0.13.0/src/scconf/lex-parse.l0000644000015201777760000000376712057406034014161 00000000000000%{ /* * $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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.13.0/src/scconf/Makefile.am0000644000015201777760000000074112057406034014125 00000000000000include $(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_PROGRAMS = test-conf noinst_LTLIBRARIES = libscconf.la AM_CPPFLAGS = -I$(top_srcdir)/src libscconf_la_SOURCES = scconf.c parse.c write.c sclex.c test_conf_SOURCES = test-conf.c test_conf_LDADD = libscconf.la $(top_builddir)/src/common/libcompat.la opensc-0.13.0/src/scconf/test-conf.c0000644000015201777760000001324312057406034014140 00000000000000/* * $Id$ * * Copyright (C) 2002 * Antti Tapaninen * * 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ #include "config.h" #include #include #include #include #include "scconf.h" #define ADD_TEST static int ldap_cb(const scconf_context * config, const scconf_block * block, scconf_entry * entry, int depth) { scconf_entry ldap_entry[] = { {"ldaphost", SCCONF_STRING, SCCONF_VERBOSE, NULL, NULL}, {"ldapport", SCCONF_INTEGER, SCCONF_VERBOSE, NULL, NULL}, {"scope", SCCONF_INTEGER, SCCONF_VERBOSE, NULL, NULL}, {"binddn", SCCONF_STRING, SCCONF_VERBOSE, NULL, NULL}, {"passwd", SCCONF_STRING, SCCONF_VERBOSE, NULL, NULL}, {"base", SCCONF_STRING, SCCONF_VERBOSE, NULL, NULL}, {"attributes", SCCONF_LIST, SCCONF_VERBOSE, NULL, NULL}, {"filter", SCCONF_STRING, SCCONF_VERBOSE, NULL, NULL}, {NULL, 0, 0, NULL, NULL} }; char *cardprefix = (char *) entry->arg; char *str = scconf_list_strdup(block->name, " "); if (!str) return 1; printf("LDAP entry[%s%s%s]\n", cardprefix ? cardprefix : "", cardprefix ? " " : "", str); free(str); if (scconf_parse_entries(config, block, ldap_entry) != 0) { printf("scconf_parse_entries failed\n"); return 1; } return 0; /* 0 for ok, 1 for error */ } static int card_cb(const scconf_context * config, const scconf_block * block, scconf_entry * entry, int depth) { char *str = scconf_list_strdup(block->name, " "); scconf_entry card_entry[] = { {"ldap", SCCONF_CALLBACK, SCCONF_VERBOSE | SCCONF_ALL_BLOCKS, (void *) ldap_cb, str}, {NULL, 0, 0, NULL, NULL} }; if (!str) return 1; printf("CARD entry[%s]\n", str); if (scconf_parse_entries(config, block, card_entry) != 0) { printf("scconf_parse_entries failed\n"); free(str); return 1; } free(str); return 0; /* 0 for ok, 1 for error */ } static int write_cb(scconf_context * config, scconf_block * block, scconf_entry * entry, int depth) { scconf_put_str(block, entry->name, "inside write_cb();"); scconf_item_add(config, block, NULL, SCCONF_ITEM_TYPE_COMMENT, NULL, "# commentN"); return 0; /* 0 for ok, 1 for error */ } static int write_entries(scconf_context *conf, scconf_list *list) { static int int42 = 42, int1 = 1; scconf_entry subblock[] = { {"stringIT", SCCONF_STRING, SCCONF_VERBOSE, (void *) "sexy", NULL}, {"callback_str", SCCONF_CALLBACK, SCCONF_VERBOSE, (void *) write_cb, NULL}, {NULL, 0, 0, NULL, NULL} }; scconf_entry wentry[] = { {"string", SCCONF_STRING, SCCONF_VERBOSE, (void *) "value1", NULL}, {"integer", SCCONF_INTEGER, SCCONF_VERBOSE, (void *) &int42, NULL}, {"sucks", SCCONF_BOOLEAN, SCCONF_VERBOSE, (void *) &int1, NULL }, {"listN", SCCONF_LIST, SCCONF_VERBOSE, (void *) list, NULL}, {"blockN", SCCONF_BLOCK, SCCONF_VERBOSE, (void *) subblock, (void *) list}, {NULL, 0, 0, NULL, NULL} }; return scconf_write_entries(conf, NULL, wentry); } int main(int argc, char **argv) { #ifdef ADD_TEST scconf_block *foo_block = NULL; scconf_item *foo_item = NULL; scconf_list *foo_list = NULL; #endif scconf_context *conf = NULL; scconf_entry entry[] = { {"ldap", SCCONF_CALLBACK, SCCONF_VERBOSE | SCCONF_ALL_BLOCKS, (void *) ldap_cb, NULL}, {"card", SCCONF_CALLBACK, SCCONF_VERBOSE | SCCONF_ALL_BLOCKS, (void *) card_cb, NULL}, {NULL, 0, 0, NULL, NULL} }; char *in = NULL, *out = NULL; int r; if (argc != 3) { printf("Usage: test-conf \n"); return 1; } in = argv[argc - 2]; out = argv[argc - 1]; conf = scconf_new(in); if (!conf) { printf("scconf_new failed\n"); return 1; } if (scconf_parse(conf) < 1) { printf("scconf_parse failed: %s\n", conf->errmsg); scconf_free(conf); return 1; } conf->debug = 1; if (scconf_parse_entries(conf, NULL, entry) != 0) { printf("scconf_parse_entries failed\n"); scconf_free(conf); return 1; } #ifdef ADD_TEST scconf_list_add(&foo_list, "value1"); scconf_list_add(&foo_list, "value2"); foo_block = (scconf_block *) scconf_find_block(conf, NULL, "foo"); foo_block = scconf_block_add(conf, foo_block, "block1", foo_list); foo_block = scconf_block_add(conf, foo_block, "block2", foo_list); scconf_list_add(&foo_list, "value3"); /* this will not segfault as type SCCONF_ITEM_TYPE_COMMENT is used */ scconf_item_add(conf, foo_block, foo_item, SCCONF_ITEM_TYPE_COMMENT, NULL, "# comment1"); scconf_item_add(conf, foo_block, foo_item, SCCONF_ITEM_TYPE_VALUE, "list1", foo_list); foo_block = NULL; scconf_item_add(conf, foo_block, foo_item, SCCONF_ITEM_TYPE_BLOCK, "block3", (void *) scconf_find_block(conf, NULL, "foo")); scconf_item_add(conf, foo_block, foo_item, SCCONF_ITEM_TYPE_VALUE, "list2", foo_list); scconf_item_add(conf, foo_block, foo_item, SCCONF_ITEM_TYPE_COMMENT, NULL, "# comment2"); if (write_entries(conf, foo_list) != 0) { printf("scconf_write_entries failed\n"); scconf_free(conf); return 1; } scconf_list_destroy(foo_list); #endif if ((r = scconf_write(conf, out)) != 0) { printf("scconf_write: %s\n", strerror(r)); } else { printf("Successfully rewrote file \"%s\" as \"%s\"\n", in, out); } scconf_free(conf); return 0; } opensc-0.13.0/src/scconf/Makefile.in0000644000015201777760000004405612057406056014151 00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program 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. @SET_MAKE@ # Required to build Windows resource file VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ DIST_COMMON = $(dist_noinst_DATA) $(noinst_HEADERS) \ $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ $(top_srcdir)/win32/ltrc.inc noinst_PROGRAMS = test-conf$(EXEEXT) subdir = src/scconf ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libscconf_la_LIBADD = am_libscconf_la_OBJECTS = scconf.lo parse.lo write.lo sclex.lo libscconf_la_OBJECTS = $(am_libscconf_la_OBJECTS) AM_V_lt = $(am__v_lt_$(V)) am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) am__v_lt_0 = --silent PROGRAMS = $(noinst_PROGRAMS) am_test_conf_OBJECTS = test-conf.$(OBJEXT) test_conf_OBJECTS = $(am_test_conf_OBJECTS) test_conf_DEPENDENCIES = libscconf.la \ $(top_builddir)/src/common/libcompat.la DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_$(V)) am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) am__v_CC_0 = @echo " CC " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_$(V)) am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) am__v_CCLD_0 = @echo " CCLD " $@; AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; SOURCES = $(libscconf_la_SOURCES) $(test_conf_SOURCES) DIST_SOURCES = $(libscconf_la_SOURCES) $(test_conf_SOURCES) DATA = $(dist_noinst_DATA) HEADERS = $(noinst_HEADERS) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUG_FILE = @DEBUG_FILE@ DEFAULT_PCSC_PROVIDER = @DEFAULT_PCSC_PROVIDER@ DEFAULT_SM_MODULE = @DEFAULT_SM_MODULE@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GIT = @GIT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBRARY_BITNESS = @LIBRARY_BITNESS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCT_CFLAGS = @OPENCT_CFLAGS@ OPENCT_LIBS = @OPENCT_LIBS@ OPENSC_LT_AGE = @OPENSC_LT_AGE@ OPENSC_LT_CURRENT = @OPENSC_LT_CURRENT@ OPENSC_LT_OLDEST = @OPENSC_LT_OLDEST@ OPENSC_LT_REVISION = @OPENSC_LT_REVISION@ OPENSC_VERSION_FIX = @OPENSC_VERSION_FIX@ OPENSC_VERSION_MAJOR = @OPENSC_VERSION_MAJOR@ OPENSC_VERSION_MINOR = @OPENSC_VERSION_MINOR@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OPTIONAL_OPENCT_CFLAGS = @OPTIONAL_OPENCT_CFLAGS@ OPTIONAL_OPENCT_LIBS = @OPTIONAL_OPENCT_LIBS@ OPTIONAL_OPENSSL_CFLAGS = @OPTIONAL_OPENSSL_CFLAGS@ OPTIONAL_OPENSSL_LIBS = @OPTIONAL_OPENSSL_LIBS@ OPTIONAL_PCSC_CFLAGS = @OPTIONAL_PCSC_CFLAGS@ OPTIONAL_READLINE_CFLAGS = @OPTIONAL_READLINE_CFLAGS@ OPTIONAL_READLINE_LIBS = @OPTIONAL_READLINE_LIBS@ OPTIONAL_ZLIB_CFLAGS = @OPTIONAL_ZLIB_CFLAGS@ OPTIONAL_ZLIB_LIBS = @OPTIONAL_ZLIB_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCSC_CFLAGS = @PCSC_CFLAGS@ PCSC_LIBS = @PCSC_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RC = @RC@ READLINE_CFLAGS = @READLINE_CFLAGS@ READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ WIN_LIBPREFIX = @WIN_LIBPREFIX@ XSLTPROC = @XSLTPROC@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ git = @git@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkcs11dir = @pkcs11dir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ xslstylesheetsdir = @xslstylesheetsdir@ RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE) 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 test_conf_SOURCES = test-conf.c test_conf_LDADD = libscconf.la $(top_builddir)/src/common/libcompat.la all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj .rc $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/win32/ltrc.inc $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/scconf/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/scconf/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ test "$$dir" != "$$p" || dir=.; \ echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done libscconf.la: $(libscconf_la_OBJECTS) $(libscconf_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libscconf_la_OBJECTS) $(libscconf_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-conf$(EXEEXT): $(test_conf_OBJECTS) $(test_conf_DEPENDENCIES) @rm -f test-conf$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_conf_OBJECTS) $(test_conf_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scconf.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sclex.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-conf.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/write.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: CTAGS CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(DATA) $(HEADERS) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ ctags distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags uninstall uninstall-am .rc.lo: $(LTRCCOMPILE) -i "$<" -o "$@" .rc.o: $(RCCOMPILE) -i "$<" -o "$@" # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: opensc-0.13.0/src/scconf/write.c0000644000015201777760000001047512057406034013374 00000000000000/* * $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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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; int datalen, len, alloc_len, quote; if (!list) { return strdup(""); } len = 0; alloc_len = 1024; buffer = (char *) realloc(buffer, alloc_len); if (!buffer) { return strdup(""); } 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.13.0/src/libopensc/0000755000015201777760000000000012057406120012646 500000000000000opensc-0.13.0/src/libopensc/pkcs15-cache.c0000644000015201777760000001033212057406034015104 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include "internal.h" #include "pkcs15.h" 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 pathname[SC_MAX_PATH_SIZE*2+1]; int r; const u8 *pathptr; size_t i, pathlen; if (path->type != SC_PATH_TYPE_PATH) 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; pathptr = path->value; pathlen = path->len; if (pathlen > 2 && memcmp(pathptr, "\x3F\x00", 2) == 0) { pathptr += 2; pathlen -= 2; } for (i = 0; i < pathlen; i++) sprintf(pathname + 2*i, "%02X", pathptr[i]); if (p15card->tokeninfo->serial_number != NULL) { char *last_update = sc_pkcs15_get_lastupdate(p15card); if (last_update != NULL) r = snprintf(buf, bufsize, "%s/%s_%s_%s", dir, p15card->tokeninfo->serial_number, last_update, pathname); else r = snprintf(buf, bufsize, "%s/%s_DATE_%s", dir, p15card->tokeninfo->serial_number, pathname); if (r < 0) return SC_ERROR_BUFFER_TOO_SMALL; } else return SC_ERROR_INVALID_ARGUMENTS; 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 r; FILE *f; size_t count, offset, got; struct stat stbuf; u8 *data = NULL; r = generate_cache_filename(p15card, path, fname, sizeof(fname)); if (r != 0) return r; r = stat(fname, &stbuf); if (r) return SC_ERROR_FILE_NOT_FOUND; if (path->count < 0) { count = stbuf.st_size; offset = 0; } else { count = path->count; offset = path->index; if (offset + count > (size_t)stbuf.st_size) return SC_ERROR_FILE_NOT_FOUND; /* cache file bad? */ } if (*buf == NULL) { data = malloc((size_t)stbuf.st_size); if (data == NULL) return SC_ERROR_OUT_OF_MEMORY; } else if (count > *bufsize) return SC_ERROR_BUFFER_TOO_SMALL; f = fopen(fname, "rb"); if (f == NULL) { if (data) free(data); return SC_ERROR_FILE_NOT_FOUND; } if (offset) fseek(f, (long)offset, SEEK_SET); if (data) *buf = data; got = fread(*buf, 1, count, f); fclose(f); if (got != count) { if (data) free(data); return SC_ERROR_BUFFER_TOO_SMALL; } *bufsize = count; if (data) *buf = data; return 0; } 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; FILE *f; size_t c; r = generate_cache_filename(p15card, path, fname, sizeof(fname)); if (r != 0) return r; f = fopen(fname, "wb"); /* If the open failed because the cache directory does * not exist, create it and a re-try the fopen() call. */ if (f == NULL && errno == ENOENT) { if ((r = sc_make_cache_dir(p15card->card->ctx)) < 0) return r; f = fopen(fname, "wb"); } if (f == NULL) return 0; c = fwrite(buf, 1, bufsize, f); fclose(f); if (c != bufsize) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "fwrite() wrote only %d bytes", c); unlink(fname); return SC_ERROR_INTERNAL; } return 0; } opensc-0.13.0/src/libopensc/asn1.h0000644000015201777760000001573612057406034013621 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. */ int sc_asn1_put_tag(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); /* non-inverting version */ int sc_asn1_decode_bit_string_ni(const u8 * inbuf, size_t inlen, void *outbuf, size_t outlen); int sc_asn1_decode_integer(const u8 * inbuf, size_t inlen, int *out); 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); #define SC_ASN1_CLASS_MASK 0x30000000 #define SC_ASN1_UNI 0x00000000 /* Universal */ #define SC_ASN1_APP 0x10000000 /* Application */ #define SC_ASN1_CTX 0x20000000 /* Context */ #define SC_ASN1_PRV 0x30000000 /* Private */ #define SC_ASN1_CONS 0x01000000 #define SC_ASN1_TAG_MASK 0x00FFFFFF #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 #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_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.13.0/src/libopensc/card-piv.c0000644000015201777760000023157112057406034014454 00000000000000/* * 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,2006,2007,2008,2009,2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #ifdef ENABLE_OPENSSL /* openssl only needed for card administration */ #include #include #include #include #endif /* ENABLE_OPENSSL */ #include "internal.h" #include "asn1.h" #include "cardctl.h" #ifdef ENABLE_ZLIB #include "compression.h" #endif 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_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. * These objects will only be present if the history object says * they are on the card, or the discovery or history object in not present. * If the file lilsted in the history object offCardCertURL was found, * 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_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; typedef struct piv_private_data { sc_file_t *aid_file; int enumtag; int selected_obj; /* The index into the piv_objects last selected */ int return_only_cert; /* return the cert from the object */ int rwb_state; /* first time -1, 0, in middle, 1 at eof */ int operation; /* saved from set_security_env */ int 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 */ } piv_private_data_t; #define PIV_DATA(card) ((piv_private_data_t*)card->drv_data) struct piv_aid { int enumtag; size_t len_short; /* min lenght without version */ size_t len_long; /* With version and other stuff */ u8 *value; }; /* * The Generic entry should be the "A0 00 00 03 08 00 00 01 00 " * NIST published this on 10/6/2005 * 800-73-2 Part 1 now refers to version "02 00" * 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. * * 800-73-3 Part 1 now referes to "01 00" i.e. going back to 800-73-1. * The main differences between 73-1, and 73-3 are the addition of the * key History object and keys, as well as Discovery and Iris objects. */ static struct piv_aid piv_aids[] = { {SC_CARD_TYPE_PIV_II_GENERIC, 9, 9, (u8 *) "\xA0\x00\x00\x03\x08\x00\x00\x10\x00" }, {0, 9, 0, NULL } }; /* The EC curves supported by PIV */ #if 0 static u8 oid_prime256v1[] = {"\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07"}; static u8 oid_secp384r1[] = {"\x06\x05\x2b\x81\x04\x00\x22"}; #endif /* * 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 1 #define PIV_OBJECT_TYPE_PUBKEY 2 #define PIV_OBJECT_NOT_PRESENT 4 struct piv_object { int enumtag; const char * name; 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 */ static const struct piv_object piv_objects[] = { { PIV_OBJ_CCC, "Card Capability Container", "2.16.840.1.101.3.7.1.219.0", 3, "\x5F\xC1\x07", "\xDB\x00", 0}, { PIV_OBJ_CHUI, "Card Holder Unique Identifier", "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", "2.16.840.1.101.3.7.2.1.1", 3, "\x5F\xC1\x05", "\x01\x01", PIV_OBJECT_TYPE_CERT} , { PIV_OBJ_CHF, "Card Holder Fingerprints", "2.16.840.1.101.3.7.2.96.16", 3, "\x5F\xC1\x03", "\x60\x10", 0}, { PIV_OBJ_PI, "Printed Information", "2.16.840.1.101.3.7.2.48.1", 3, "\x5F\xC1\x09", "\x30\x01", 0}, { PIV_OBJ_CHFI, "Cardholder Facial Images", "2.16.840.1.101.3.7.2.96.48", 3, "\x5F\xC1\x08", "\x60\x30", 0}, { PIV_OBJ_X509_DS, "X.509 Certificate for Digital Signature", "2.16.840.1.101.3.7.2.1.0", 3, "\x5F\xC1\x0A", "\x01\x00", PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_X509_KM, "X.509 Certificate for Key Management", "2.16.840.1.101.3.7.2.1.2", 3, "\x5F\xC1\x0B", "\x01\x02", PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_X509_CARD_AUTH, "X.509 Certificate for Card Authentication", "2.16.840.1.101.3.7.2.5.0", 3, "\x5F\xC1\x01", "\x05\x00", PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_SEC_OBJ, "Security Object", "2.16.840.1.101.3.7.2.144.0", 3, "\x5F\xC1\x06", "\x90\x00", 0}, { PIV_OBJ_DISCOVERY, "Discovery Object", "2.16.840.1.101.3.7.2.96.80", 1, "\x7E", "\x60\x50", 0}, { PIV_OBJ_HISTORY, "Key History Object", "2.16.840.1.101.3.7.2.96.96", 3, "\x5F\xC1\x0C", "\x60\x60", 0}, /* 800-73-3, 21 new objects, 20 history certificates */ { PIV_OBJ_RETIRED_X509_1, "Retired X.509 Certificate for Key Management 1", "2.16.840.1.101.3.7.2.16.1", 3, "\x5F\xC1\x0D", "\x10\x01", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_2, "Retired X.509 Certificate for Key Management 2", "2.16.840.1.101.3.7.2.16.2", 3, "\x5F\xC1\x0E", "\x10\x02", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_3, "Retired X.509 Certificate for Key Management 3", "2.16.840.1.101.3.7.2.16.3", 3, "\x5F\xC1\x0F", "\x10\x03", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_4, "Retired X.509 Certificate for Key Management 4", "2.16.840.1.101.3.7.2.16.4", 3, "\x5F\xC1\x10", "\x10\x04", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_5, "Retired X.509 Certificate for Key Management 5", "2.16.840.1.101.3.7.2.16.5", 3, "\x5F\xC1\x11", "\x10\x05", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_6, "Retired X.509 Certificate for Key Management 6", "2.16.840.1.101.3.7.2.16.6", 3, "\x5F\xC1\x12", "\x10\x06", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_7, "Retired X.509 Certificate for Key Management 7", "2.16.840.1.101.3.7.2.16.7", 3, "\x5F\xC1\x13", "\x10\x07", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_8, "Retired X.509 Certificate for Key Management 8", "2.16.840.1.101.3.7.2.16.8", 3, "\x5F\xC1\x14", "\x10\x08", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_9, "Retired X.509 Certificate for Key Management 9", "2.16.840.1.101.3.7.2.16.9", 3, "\x5F\xC1\x15", "\x10\x09", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_10, "Retired X.509 Certificate for Key Management 10", "2.16.840.1.101.3.7.2.16.10", 3, "\x5F\xC1\x16", "\x10\x0A", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_11, "Retired X.509 Certificate for Key Management 11", "2.16.840.1.101.3.7.2.16.11", 3, "\x5F\xC1\x17", "\x10\x0B", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_12, "Retired X.509 Certificate for Key Management 12", "2.16.840.1.101.3.7.2.16.12", 3, "\x5F\xC1\x18", "\x10\x0C", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_13, "Retired X.509 Certificate for Key Management 13", "2.16.840.1.101.3.7.2.16.13", 3, "\x5F\xC1\x19", "\x10\x0D", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_14, "Retired X.509 Certificate for Key Management 14", "2.16.840.1.101.3.7.2.16.14", 3, "\x5F\xC1\x1A", "\x10\x0E", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_15, "Retired X.509 Certificate for Key Management 15", "2.16.840.1.101.3.7.2.16.15", 3, "\x5F\xC1\x1B", "\x10\x0F", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_16, "Retired X.509 Certificate for Key Management 16", "2.16.840.1.101.3.7.2.16.16", 3, "\x5F\xC1\x1C", "\x10\x10", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_17, "Retired X.509 Certificate for Key Management 17", "2.16.840.1.101.3.7.2.16.17", 3, "\x5F\xC1\x1D", "\x10\x11", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_18, "Retired X.509 Certificate for Key Management 18", "2.16.840.1.101.3.7.2.16.18", 3, "\x5F\xC1\x1E", "\x10\x12", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_19, "Retired X.509 Certificate for Key Management 19", "2.16.840.1.101.3.7.2.16.19", 3, "\x5F\xC1\x1F", "\x10\x13", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_RETIRED_X509_20, "Retired X.509 Certificate for Key Management 20", "2.16.840.1.101.3.7.2.16.20", 3, "\x5F\xC1\x20", "\x10\x14", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_IRIS_IMAGE, "Cardholder Iris Images", "2.16.840.1.101.3.7.2.16.21", 3, "\x5F\xC1\x21", "\x10\x15", 0}, /* following not standard , to be used by piv-tool only for testing */ { PIV_OBJ_9B03, "3DES-ECB ADM", "2.16.840.1.101.3.7.2.9999.3", 2, "\x9B\x03", "\x9B\x03", 0}, /* Only used when signing a cert req, usually from engine * after piv-tool generated the key and saved the pub key * to a file. Note RSA key can be 1024, 2048 or 3072 * but still use the "9x06" name. */ { PIV_OBJ_9A06, "RSA 9A Pub key from last genkey", "2.16.840.1.101.3.7.2.9999.20", 2, "\x9A\x06", "\x9A\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9C06, "Pub 9C key from last genkey", "2.16.840.1.101.3.7.2.9999.21", 2, "\x9C\x06", "\x9C\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9D06, "Pub 9D key from last genkey", "2.16.840.1.101.3.7.2.9999.22", 2, "\x9D\x06", "\x9D\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9E06, "Pub 9E key from last genkey", "2.16.840.1.101.3.7.2.9999.23", 2, "\x9E\x06", "\x9E\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8206, "Pub 82 key ", "2.16.840.1.101.3.7.2.9999.101", 2, "\x82\x06", "\x82\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8306, "Pub 83 key ", "2.16.840.1.101.3.7.2.9999.102", 2, "\x83\x06", "\x83\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8406, "Pub 84 key ", "2.16.840.1.101.3.7.2.9999.103", 2, "\x84\x06", "\x84\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8506, "Pub 85 key ", "2.16.840.1.101.3.7.2.9999.104", 2, "\x85\x06", "\x85\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8606, "Pub 86 key ", "2.16.840.1.101.3.7.2.9999.105", 2, "\x86\x06", "\x86\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8706, "Pub 87 key ", "2.16.840.1.101.3.7.2.9999.106", 2, "\x87\x06", "\x87\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8806, "Pub 88 key ", "2.16.840.1.101.3.7.2.9999.107", 2, "\x88\x06", "\x88\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8906, "Pub 89 key ", "2.16.840.1.101.3.7.2.9999.108", 2, "\x89\x06", "\x89\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8A06, "Pub 8A key ", "2.16.840.1.101.3.7.2.9999.109", 2, "\x8A\x06", "\x8A\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8B06, "Pub 8B key ", "2.16.840.1.101.3.7.2.9999.110", 2, "\x8B\x06", "\x8B\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8C06, "Pub 8C key ", "2.16.840.1.101.3.7.2.9999.111", 2, "\x8C\x06", "\x8C\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8D06, "Pub 8D key ", "2.16.840.1.101.3.7.2.9999.112", 2, "\x8D\x06", "\x8D\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8E06, "Pub 8E key ", "2.16.840.1.101.3.7.2.9999.113", 2, "\x8E\x06", "\x8E\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8F06, "Pub 8F key ", "2.16.840.1.101.3.7.2.9999.114", 2, "\x8F\x06", "\x8F\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9006, "Pub 90 key ", "2.16.840.1.101.3.7.2.9999.115", 2, "\x90\x06", "\x90\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9106, "Pub 91 key ", "2.16.840.1.101.3.7.2.9999.116", 2, "\x91\x06", "\x91\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9206, "Pub 92 key ", "2.16.840.1.101.3.7.2.9999.117", 2, "\x92\x06", "\x92\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9306, "Pub 93 key ", "2.16.840.1.101.3.7.2.9999.118", 2, "\x93\x06", "\x93\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9406, "Pub 94 key ", "2.16.840.1.101.3.7.2.9999.119", 2, "\x94\x06", "\x94\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9506, "Pub 95 key ", "2.16.840.1.101.3.7.2.9999.120", 2, "\x95\x06", "\x95\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_LAST_ENUM, "", "", 0, "", "", 0} }; static struct sc_card_operations piv_ops; static struct sc_card_driver piv_drv = { "PIV-II for multiple cards", "piv", &piv_ops, NULL, 0, NULL }; static int piv_find_obj_by_containerid(sc_card_t *card, const u8 * str) { int i; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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]) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, i); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, -1); } /* * If ptr == NULL, just return the size of the tag and lenght and data * otherwise, store tag and length at **ptr, and increment */ static size_t put_tag_and_len(unsigned int tag, size_t len, u8 **ptr) { int i; u8 *p; if (len < 128) { i = 2; } else if (len < 256) { i = 3; } else { i = 4; } if (ptr) { p = *ptr; *p++ = (u8)tag; switch (i) { case 2: *p++ = len; break; case 3: *p++ = 0x81; *p++ = len; break; case 4: *p++ = 0x82; *p++ = (u8) (len >> 8); *p++ = (u8) (len & 0xff); break; } *ptr = p; } else { i += len; } return i; } /* * Send a command and receive data. There is always something to send. * Used by GET DATA, PUT DATA, GENERAL AUTHENTICATE * and GENERATE ASYMMETRIC KEY PAIR. * GET DATA may call to get the first 128 bytes to get the lenght from the tag. * * A caller may provide a buffer, and length to read. If not provided, * an internal 4096 byte buffer is used, and a copy is returned to the * caller. that need to be freed by the caller. */ static int piv_general_io(sc_card_t *card, int ins, int p1, int p2, const u8 * sendbuf, size_t sendbuflen, u8 ** recvbuf, size_t * recvbuflen) { int r; sc_apdu_t apdu; u8 rbufinitbuf[4096]; u8 *rbuf; size_t rbuflen; unsigned int cla_out, tag_out; const u8 *body; size_t bodylen; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%02x %02x %02x %d : %d %d\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; } r = sc_lock(card); if (r != SC_SUCCESS) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; apdu.lc = sendbuflen; apdu.datalen = sendbuflen; apdu.data = sendbuf; if (recvbuf) { apdu.resp = rbuf; apdu.le = (rbuflen > 256) ? 256 : rbuflen; apdu.resplen = rbuflen; } else { apdu.resp = rbuf; apdu.le = 0; apdu.resplen = 0; } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"calling sc_transmit_apdu flags=%x le=%d, resplen=%d, resp=%p", apdu.flags, apdu.le, apdu.resplen, apdu.resp); /* with new adpu.c and chaining, this actually reads the whole object */ r = sc_transmit_apdu(card, &apdu); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DEE r=%d apdu.resplen=%d sw1=%02x sw2=%02x", r, apdu.resplen, apdu.sw1, apdu.sw2); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Transmit failed"); goto err; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); /* TODO: - DEE look later at tag vs size read too */ if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Card returned error "); goto err; } /* * See how much we read and make sure it is asn1 * if not, return 0 indicating no data found */ rbuflen = 0; /* in case rseplen < 3 i.e. not parseable */ if ( recvbuflen && recvbuf && apdu.resplen > 3) { *recvbuflen = 0; /* we should have all the tag data, so we have to tell sc_asn1_find_tag * the buffer is bigger, so it will not produce "ASN1.tag too long!" */ body = rbuf; if (sc_asn1_read_tag(&body, 0xffff, &cla_out, &tag_out, &bodylen) != SC_SUCCESS) { /* only early beta cards had this problem */ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "***** received buffer tag MISSING "); body = rbuf; /* some readers/cards might return 6c 00 */ if (apdu.sw1 == 0x61 || apdu.sw2 == 0x6c ) bodylen = 12000; else bodylen = apdu.resplen; } rbuflen = body - rbuf + bodylen; /* if using internal buffer, alloc new one */ if (rbuf == rbufinitbuf) { *recvbuf = malloc(rbuflen); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DEE got buffer %p len %d",*recvbuf, rbuflen); if (*recvbuf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } memcpy(*recvbuf, rbuf, rbuflen); /* copy tag too */ } } if (recvbuflen) { *recvbuflen = rbuflen; r = *recvbuflen; } err: sc_unlock(card); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } /* 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 = NULL; size_t rbuflen = 0; u8 *p; const u8 *tag; u8 tagbuf[16]; u8 outdata[3]; /* we could also add tag 81 for exponent */ size_t taglen, i; size_t out_len; size_t in_len; unsigned int cla_out, tag_out; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); keydata->exponent = 0; keydata->pubkey = NULL; keydata->pubkey_len = 0; keydata->ecparam = NULL; /* will show size as we only support 2 curves */ keydata->ecparam_len = 0; keydata->ecpoint = NULL; keydata->ecpoint_len = 0; out_len = 3; outdata[0] = 0x80; outdata[1] = 0x01; outdata[2] = keydata->key_algid; switch (keydata->key_algid) { case 0x05: keydata->key_bits = 3072; break; case 0x06: keydata->key_bits = 1024; break; case 0x07: keydata->key_bits = 2048; break; /* TODO: - DEE For EC, also set the curve parameter as the OID */ case 0x11: keydata->key_bits = 0; keydata->ecparam =0; /* we only support prime256v1 for 11 */ keydata->ecparam_len =0; break; case 0x14: keydata->key_bits = 0; keydata->ecparam = 0; /* we only support secp384r1 */ keydata->ecparam_len = 0; break; default: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); } p = tagbuf; put_tag_and_len(0xAC, out_len, &p); memcpy(p, outdata, out_len); p+=out_len; r = piv_general_io(card, 0x47, 0x00, keydata->key_num, tagbuf, p - tagbuf, &rbuf, &rbuflen); if (r >= 0) { const u8 *cp; keydata->exponent = 0; /* expected tag is 7f49. */ /* we will whatever tag is present */ cp = rbuf; in_len = rbuflen; r = sc_asn1_read_tag(&cp, rbuflen, &cla_out, &tag_out, &in_len); if (r != SC_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"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 = 0; for (i = 0; i < taglen;i++) { keydata->exponent = (keydata->exponent<<8) + tag[i]; } } 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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: if (rbuf) free(rbuf); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Got args: aid=%x, aidlen=%d, response=%x, responselen=%d\n", aid, aidlen, response, responselen ? *responselen : 0); sc_format_apdu(card, &apdu, response == NULL ? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, 0xA4, 0x04, 0x00); apdu.lc = aidlen; apdu.data = aid; apdu.datalen = aidlen; apdu.resp = response; apdu.resplen = responselen ? *responselen : 0; apdu.le = response == NULL ? 0 : 256; /* could be 21 for fci */ r = sc_transmit_apdu(card, &apdu); if (responselen) *responselen = apdu.resplen; SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "PIV select failed"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 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 * Assumes that priv may not be present */ static int piv_find_aid(sc_card_t * card, sc_file_t *aid_file) { sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; int r,i; const u8 *tag; size_t taglen; const u8 *pix; size_t pixlen; size_t resplen = sizeof(rbuf); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* first see if the default applcation will return a template * that we know about. */ if (card->type == SC_CARD_TYPE_PIV_II_GENERIC) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 0); r = piv_select_aid(card, piv_aids[0].value, piv_aids[0].len_short, rbuf, &resplen); if (r >= 0 && resplen > 2 ) { tag = sc_asn1_find_tag(card->ctx, rbuf, resplen, 0x61, &taglen); if (tag != NULL) { pix = sc_asn1_find_tag(card->ctx, tag, taglen, 0x4F, &pixlen); if (pix != NULL ) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"found PIX"); /* early cards returned full AID, rather then just the pix */ for (i = 0; piv_aids[i].len_long != 0; i++) { if ((pixlen >= 6 && memcmp(pix, piv_aids[i].value + 5, piv_aids[i].len_long - 5 ) == 0) || ((pixlen >= piv_aids[i].len_short && memcmp(pix, piv_aids[i].value, piv_aids[i].len_short) == 0))) { if (card->type > SC_CARD_TYPE_PIV_II_BASE && card->type < SC_CARD_TYPE_PIV_II_BASE+1000 && card->type == piv_aids[i].enumtag) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, i); } else { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, i); } } } } } } /* for testing, we can force the use of a specific AID * by using the card= parameter in conf file */ for (i = 0; piv_aids[i].len_long != 0; i++) { if (card->type > SC_CARD_TYPE_PIV_II_BASE && card->type < SC_CARD_TYPE_PIV_II_BASE+1000 && card->type != piv_aids[i].enumtag) { continue; } sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x04, 0x00); apdu.lc = piv_aids[i].len_long; apdu.data = piv_aids[i].value; apdu.datalen = apdu.lc; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->type != 0 && card->type == piv_aids[i].enumtag) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, i); } continue; } if ( apdu.resplen == 0 && r == 0) { /* could be the MSU card */ continue; /* other cards will return a FCI */ } if (apdu.resp[0] != 0x6f || apdu.resp[1] > apdu.resplen - 2 ) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NO_CARD_SUPPORT); card->ops->process_fci(card, aid_file, apdu.resp+2, apdu.resp[1]); if (aid_file->name == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NO_CARD_SUPPORT); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, i); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unable to load PIV off card file: \"%s\"\n",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_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Problem with \"%s\"\n",filename); r = SC_ERROR_DATA_OBJECT_NOT_FOUND; goto err; } body = tagbuf; if (sc_asn1_read_tag(&body, 0xfffff, &cla_out, &tag_out, &bodylen) != SC_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DER problem\n"); r = SC_ERROR_INVALID_ASN1_OBJECT; 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 */ if (rbuflen > len) { 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 = rbuflen; *buf_len = rbuflen; err: if (f >= 0) close(f); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } /* the tag is the PIV_OBJ_* */ static int piv_get_data(sc_card_t * card, int enumtag, u8 **buf, size_t *buf_len) { u8 *p; int r = 0; u8 tagbuf[8]; size_t tag_len; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "#%d \n", enumtag); /* assert(enumtag >= 0 && enumtag < PIV_OBJ_LAST_ENUM); */ tag_len = piv_objects[enumtag].tag_len; p = tagbuf; put_tag_and_len(0x5c, tag_len, &p); memcpy(p, piv_objects[enumtag].tag_value, tag_len); p += tag_len; if (*buf_len == 1 && *buf == NULL) { /* we need to get the length */ u8 rbufinitbuf[8]; /* tag of 53 with 82 xx xx will fit in 4 */ u8 *rbuf; size_t rbuflen; size_t bodylen; unsigned int cla_out, tag_out; const u8 *body; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"get len of #%d", enumtag); rbuf = rbufinitbuf; rbuflen = sizeof(rbufinitbuf); r = piv_general_io(card, 0xCB, 0x3F, 0xFF, tagbuf, p - tagbuf, &rbuf, &rbuflen); if (r > 0) { body = rbuf; if (sc_asn1_read_tag(&body, 0xffff, &cla_out, &tag_out, &bodylen) != SC_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "***** received buffer tag MISSING "); r = SC_ERROR_FILE_NOT_FOUND; goto err; } *buf_len = r; } else if ( r == 0) { r = SC_ERROR_FILE_NOT_FOUND; goto err; } else { goto err; } } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"get buffer for #%d len %d", enumtag, *buf_len); if (*buf == NULL && *buf_len > 0) { *buf = malloc(*buf_len); if (*buf == NULL ) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } } r = piv_general_io(card, 0xCB, 0x3F, 0xFF, tagbuf, p - tagbuf, buf, buf_len); err: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "#%d", enumtag); assert(enumtag >= 0 && enumtag < PIV_OBJ_LAST_ENUM); /* see if we have it cached */ if (priv->obj_cache[enumtag].flags & PIV_OBJ_CACHE_VALID) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"found #%d %p:%d %p:%d", 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"#%d found but len=0", enumtag); goto err; } *buf = priv->obj_cache[enumtag].obj_data; *buf_len = priv->obj_cache[enumtag].obj_len; r = *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 */ if (priv->obj_cache[enumtag].flags & PIV_OBJ_CACHE_NOT_PRESENT) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"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_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"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_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"added #%d %p:%d %p:%d", enumtag, priv->obj_cache[enumtag].obj_data, priv->obj_cache[enumtag].obj_len, priv->obj_cache[enumtag].internal_obj_data, priv->obj_cache[enumtag].internal_obj_len); } else if (r == 0 || r == SC_ERROR_FILE_NOT_FOUND) { r = SC_ERROR_FILE_NOT_FOUND; priv->obj_cache[enumtag].flags |= PIV_OBJ_CACHE_VALID; priv->obj_cache[enumtag].obj_len = 0; } else if ( r < 0) { goto err; } ok: err: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; /* if already cached */ if (priv->obj_cache[enumtag].internal_obj_data && priv->obj_cache[enumtag].internal_obj_len) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"#%d found internal %p:%d", enumtag, priv->obj_cache[enumtag].internal_obj_data, priv->obj_cache[enumtag].internal_obj_len); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 0); } body = sc_asn1_find_tag(card->ctx, priv->obj_cache[enumtag].obj_data, priv->obj_cache[enumtag].obj_len, 0x53, &bodylen); if (body == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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 */ if (tag && (((*tag) & 0x80) || ((*tag) & 0x01))) { compressed = 1; } tag = sc_asn1_find_tag(card->ctx, body, bodylen, 0x70, &taglen); if (tag == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OBJECT_NOT_VALID); if (taglen == 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_NOT_FOUND); if(compressed) { #ifdef ENABLE_ZLIB size_t len; u8* newBuf = NULL; if(SC_SUCCESS != sc_decompress_alloc(&newBuf, &len, tag, taglen, COMPRESSION_AUTO)) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OBJECT_NOT_VALID); } priv->obj_cache[enumtag].internal_obj_data = newBuf; priv->obj_cache[enumtag].internal_obj_len = len; #else sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"PIV compression not supported, no zlib"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); #endif } else { if (!(priv->obj_cache[enumtag].internal_obj_data = malloc(taglen))) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); memcpy(priv->obj_cache[enumtag].internal_obj_data, tag, taglen); priv->obj_cache[enumtag].internal_obj_len = taglen; } /* convert pub key to internal */ /* TODO: -DEE need to fix ... would only be used if we cache the pub key, but we don't today */ } else if (piv_objects[enumtag].flags & PIV_OBJECT_TYPE_PUBKEY) { tag = sc_asn1_find_tag(card->ctx, body, bodylen, *body, &taglen); if (tag == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OBJECT_NOT_VALID); if (taglen == 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_NOT_FOUND); if (!(priv->obj_cache[enumtag].internal_obj_data = malloc(taglen))) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); memcpy(priv->obj_cache[enumtag].internal_obj_data, tag, taglen); priv->obj_cache[enumtag].internal_obj_len = taglen; } else { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"added #%d internal %p:%d", enumtag, priv->obj_cache[enumtag].internal_obj_data, priv->obj_cache[enumtag].internal_obj_len); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 0); } /* * 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DEE rbuf=%p,rbuflen=%d,",rbuf, rbuflen); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL," ***** tag 0x53 MISSING \n"); r = SC_ERROR_INVALID_DATA; goto err; } if (bodylen > body - rbuf + rbuflen) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL," ***** tag length > then data: %d>%d+%d", 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; } 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 = count; } err: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; sbuflen = put_tag_and_len(0x5c, tag_len, NULL) + buf_len; if (!(sbuf = malloc(sbuflen))) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); p = sbuf; put_tag_and_len(0x5c, tag_len, &p); memcpy(p, piv_objects[tag].tag_value, tag_len); p += tag_len; memcpy(p, buf, buf_len); p += buf_len; r = piv_general_io(card, 0xDB, 0x3F, 0xFF, sbuf, p - sbuf, NULL, NULL); if (sbuf) free(sbuf); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; int r = SC_SUCCESS; u8 *sbuf = NULL; u8 *p; size_t sbuflen; size_t taglen; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DEE cert len=%d",count); taglen = put_tag_and_len(0x70, count, NULL) + put_tag_and_len(0x71, 1, NULL) + put_tag_and_len(0xFE, 0, NULL); sbuflen = put_tag_and_len(0x53, taglen, NULL); sbuf = malloc(sbuflen); if (sbuf == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); p = sbuf; put_tag_and_len(0x53, taglen, &p); put_tag_and_len(0x70, count, &p); memcpy(p, buf, count); p += count; put_tag_and_len(0x71, 1, &p); *p++ = (flags)? 0x80:0x00; /* certinfo, i.e. gziped? */ put_tag_and_len(0xFE,0,&p); /* LRC tag */ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DEE buf %p len %d %d", sbuf, p -sbuf, sbuflen); enumtag = piv_objects[priv->selected_obj].enumtag; r = piv_put_data(card, enumtag, sbuf, sbuflen); if (sbuf) free(sbuf); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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 * uncompresed 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (priv->selected_obj < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); enumtag = piv_objects[priv->selected_obj].enumtag; if (priv->rwb_state == 1) /* trying to write at end */ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 0); if (priv->rwb_state == -1) { /* if cached, remove old entry */ if (priv->obj_cache[enumtag].flags & PIV_OBJ_CACHE_VALID) { priv->obj_cache[enumtag].flags = 0; if (priv->obj_cache[enumtag].obj_data) { free(priv->obj_cache[enumtag].obj_data); priv->obj_cache[enumtag].obj_data = NULL; priv->obj_cache[enumtag].obj_len = 0; } if (priv->obj_cache[enumtag].internal_obj_data) { free(priv->obj_cache[enumtag].internal_obj_data); priv->obj_cache[enumtag].internal_obj_data = NULL; priv->obj_cache[enumtag].internal_obj_len = 0; } } if (idx != 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NO_CARD_SUPPORT); priv->w_buf_len = flags>>8; if (priv->w_buf_len == 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); if (idx + count > priv->w_buf_len) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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 = 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; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, (r < 0)? r : (int)count); } /* * Card initialization is not standard. * Some cards use mutual or external authentication using s 3des key. We * will read in the key from a file. * This is only needed during initialization/personalization of the card */ static int piv_get_3des_key(sc_card_t *card, u8 *key) { int r; int f = -1; char keybuf[24*3]; /* 3des key as three sets of xx:xx:xx:xx:xx:xx:xx:xx * with a : between which is 71 bytes */ char * keyfilename = NULL; size_t outlen; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); keyfilename = (char *)getenv("PIV_EXT_AUTH_KEY"); if (keyfilename == NULL) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unable to get PIV_EXT_AUTH_KEY=filename for general_external_authenticate\n"); r = SC_ERROR_FILE_NOT_FOUND; goto err; } if ((f = open(keyfilename, O_RDONLY)) < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL," Unable to load 3des key for general_external_authenticate\n"); r = SC_ERROR_FILE_NOT_FOUND; goto err; } if (read(f, keybuf, 71) != 71) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL," Unable to read 3des key for general_external_authenticate\n"); r = SC_ERROR_WRONG_LENGTH; goto err; } keybuf[23] = '\0'; keybuf[47] = '\0'; keybuf[71] = '\0'; outlen = 8; r = sc_hex_to_bin(keybuf, key, &outlen); if (r) goto err; outlen = 8; r = sc_hex_to_bin(keybuf+24, key+8, &outlen); if (r) goto err; outlen = 8; r = sc_hex_to_bin(keybuf+48, key+16, &outlen); if (r) goto err; err: if (f >=0) close(f); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } /* * 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, outl, outl2; u8 *rbuf = NULL; size_t rbuflen; u8 nonce[8] = {0xDE, 0xE0, 0xDE, 0xE1, 0xDE, 0xE2, 0xDE, 0xE3}; u8 sbuf[255], key[24]; u8 *p, *q; EVP_CIPHER_CTX ctx; const EVP_CIPHER *cipher; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); EVP_CIPHER_CTX_init(&ctx); switch (alg_id) { case 1: cipher=EVP_des_ede3_ecb(); break; case 2: cipher=EVP_des_ede3_cbc(); break; case 3: cipher=EVP_des_ede3_ecb(); break; case 4: cipher=EVP_des_ede3_cbc(); break; default: cipher=EVP_des_ede3_ecb(); break; } r = piv_get_3des_key(card, key); if (r != SC_SUCCESS) goto err; r = sc_lock(card); if (r != SC_SUCCESS) goto err; 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, &rbuflen); if (r < 0) goto err; q = rbuf; if ( (*q++ != 0x7C) || (*q++ != rbuflen - 2) || (*q++ != 0x80) || (*q++ != rbuflen - 4)) { r = SC_ERROR_INVALID_DATA; goto err; } N = *(rbuf + 3); /* assuming N + sizeof(nonce) + 6 < 128 */ /* prepare the response */ p = sbuf; *p++ = 0x7c; *p++ = N + sizeof(nonce)+ 4; *p++ = 0x80; *p++ = (u8)N; /* 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 */ r = SC_ERROR_INTERNAL; goto err; } EVP_CIPHER_CTX_set_padding(&ctx,0); if (!EVP_DecryptUpdate(&ctx, p, &outl, q, N)) { r = SC_ERROR_INTERNAL; goto err; } if(!EVP_DecryptFinal(&ctx, p+outl, &outl2)) { r = SC_ERROR_INTERNAL; goto err; } if (outl+outl2 != N) { r = SC_ERROR_INTERNAL; goto err; } p += N; *p++ = 0x81; *p++ = sizeof(nonce); memcpy(p, &nonce, sizeof(nonce)); /* we use a fixed nonce for now */ p += sizeof(nonce); free(rbuf); rbuf = NULL; r = piv_general_io(card, 0x87, alg_id, key_ref, sbuf, p - sbuf, &rbuf, &rbuflen); if (r < 0) goto err; q = rbuf; if ( (*q++ != 0x7C) || (*q++ != rbuflen - 2) || ((*q++ | 0x02) != 0x82) /* SP800-73 not clear if 80 or 82 */ || (*q++ != rbuflen - 4)) { r = SC_ERROR_INVALID_DATA; goto err; } N = *(rbuf + 3); p = sbuf; EVP_CIPHER_CTX_cleanup(&ctx); EVP_CIPHER_CTX_init(&ctx); if (!EVP_DecryptInit(&ctx, cipher, key, NULL)) { r = SC_ERROR_INTERNAL; goto err; } EVP_CIPHER_CTX_set_padding(&ctx,0); if (!EVP_DecryptUpdate(&ctx, p, &outl, q, N)) { r = SC_ERROR_INTERNAL; goto err; } if(!EVP_DecryptFinal(&ctx, p+outl, &outl2)) { r = SC_ERROR_INTERNAL; goto err; } if (outl+outl2 != sizeof(nonce) || memcmp(nonce, p, sizeof(nonce)) != 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "mutual authentication failed, card returned wrong value"); r = SC_ERROR_DECRYPT_FAILED; goto err; } r = SC_SUCCESS; err: EVP_CIPHER_CTX_cleanup(&ctx); if (locked) sc_unlock(card); if (rbuf) free(rbuf); #else sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"OpenSSL Required"); r = SC_ERROR_NOT_SUPPORTED; #endif /* ENABLE_OPENSSL */ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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 int outl, outl2; int N; int locked = 0; u8 *rbuf = NULL; size_t rbuflen; u8 sbuf[255], key[24]; u8 *p, *q; EVP_CIPHER_CTX ctx; const EVP_CIPHER *cipher; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); EVP_CIPHER_CTX_init(&ctx); switch (alg_id) { case 1: cipher=EVP_des_ede3_ecb(); break; case 2: cipher=EVP_des_ede3_cbc(); break; case 3: cipher=EVP_des_ede3_ecb(); break; case 4: cipher=EVP_des_ede3_cbc(); break; default: cipher=EVP_des_ede3_ecb(); break; } r = piv_get_3des_key(card, key); if (r != SC_SUCCESS) goto err; r = sc_lock(card); if (r != SC_SUCCESS) goto err; 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, &rbuflen); if (r < 0) goto err; q = rbuf; if ( (*q++ != 0x7C) || (*q++ != rbuflen - 2) || (*q++ != 0x81) || (*q++ != rbuflen - 4)) { r = SC_ERROR_INVALID_DATA; goto err; } /* assuming challenge and response are same size i.e. des3 */ p = sbuf; *p++ = 0x7c; *p++ = *(rbuf + 1); *p++ = 0x82; *p++ = *(rbuf + 3); N = *(rbuf + 3); /* assuming 2 * N + 6 < 128 */ if (!EVP_EncryptInit(&ctx, cipher, key, NULL)) { r = SC_ERROR_INTERNAL; goto err; } EVP_CIPHER_CTX_set_padding(&ctx,0); if (!EVP_EncryptUpdate(&ctx, p, &outl, q, N)) { r = SC_ERROR_INTERNAL; goto err; } if(!EVP_EncryptFinal(&ctx, p+outl, &outl2)) { r = SC_ERROR_INTERNAL; goto err; } if (outl+outl2 != N) { r = SC_ERROR_INTERNAL; goto err; } p += N; r = piv_general_io(card, 0x87, alg_id, key_ref, sbuf, p - sbuf, NULL, NULL); err: if (locked) sc_unlock(card); EVP_CIPHER_CTX_cleanup(&ctx); sc_mem_clear(key, sizeof(key)); if (rbuf) free(rbuf); #else sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"OpenSSL Required"); r = SC_ERROR_NOT_SUPPORTED; #endif /* ENABLE_OPENSSL */ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } 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; u8 temp[2000]; size_t templen = sizeof(temp); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (card->serialnr.len) { *serial = card->serialnr; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); } /* ensure we've got the PIV selected, and nothing else is in process */ /* This fixes several problems due to previous incomplete APDUs during card detection */ /* Note: We need the temp because (some?) Oberthur cards don't like selecting an applet without response data */ /* 800-73-3 part1 draft, and CIO Council docs imply for PIV Compatible card * The FASC-N Agency code should be 9999 and there should be a GUID * based on RFC 4122. RIf so and the GUID is not all 0's * we will use the GUID as the serial number. */ piv_select_aid(card, piv_aids[0].value, piv_aids[0].len_short, temp, &templen); r = piv_get_cached_data(card, PIV_OBJ_CHUI, &rbuf, &rbuflen); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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) { 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"fascn=%p,fascnlen=%d,guid=%p,guidlen=%d,gbits=%2.2x\n", fascn, fascnlen, guid, guidlen, gbits); if (fascn && fascnlen == 25) { /* test if guid and the fascn starts with ;9999 (in ISO 4bit + partiy code) */ if (!(gbits && fascn[0] == 0xD4 && fascn[1] == 0xE7 && fascn[2] == 0x39 && (fascn[3] | 0x7F) == 0xFF)) { serial->len = fascnlen < SC_MAX_SERIALNR ? fascnlen : SC_MAX_SERIALNR; memcpy (serial->value, fascn, serial->len); r = SC_SUCCESS; gbits = 0; /* set to skip using guid below */ } } if (guid && gbits) { serial->len = guidlen < SC_MAX_SERIALNR ? guidlen : SC_MAX_SERIALNR; memcpy (serial->value, guid, serial->len); r = SC_SUCCESS; } } } card->serialnr = *serial; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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 *ptr) { piv_private_data_t * priv = PIV_DATA(card); *ptr = priv->pin_preference; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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 */ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"cmd=%ld ptr=%p"); if (priv == NULL) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); } static int piv_get_challenge(sc_card_t *card, u8 *rnd, size_t len) { u8 sbuf[16]; u8 *rbuf = NULL; size_t rbuflen = 0; u8 *p, *q; int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"challenge len=%d",len); sc_lock(card); p = sbuf; *p++ = 0x7c; *p++ = 0x02; *p++ = 0x81; *p++ = 0x00; /* assuming 8 byte response ? */ /* should take what the card returns */ while (len > 0) { size_t n = len > 8 ? 8 : len; /* NIST 800-73-3 says use 9B, previous verisons used 00 */ r = piv_general_io(card, 0x87, 0x00, 0x9B, sbuf, p - sbuf, &rbuf, &rbuflen); if (r < 0) { sc_unlock(card); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } q = rbuf; if ( (*q++ != 0x7C) || (*q++ != rbuflen - 2) || (*q++ != 0x81) || (*q++ != rbuflen - 4)) { r = SC_ERROR_INVALID_DATA; sc_unlock(card); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } memcpy(rnd, q, n); len -= n; rnd += n; free(rbuf); rbuf = NULL; } sc_unlock(card); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 0); } 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"flags=%08x op=%d alg=%d algf=%08x algr=%08x kr0=%02x, krfl=%d\n", 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]; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int piv_restore_security_env(sc_card_t *card, int se_num) { SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 0); } 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; u8 *p; const u8 *tag; size_t taglen; const u8 *body; size_t bodylen; unsigned int real_alg_id; u8 sbuf[4096]; /* needs work. for 3072 keys, needs 384+10 or so */ u8 *rbuf = NULL; size_t rbuflen; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* should assume large send data */ p = sbuf; put_tag_and_len(0x7c, (2 + put_tag_and_len(0, datalen, NULL)) , &p); put_tag_and_len(0x82, 0, &p); if (priv->operation == SC_SEC_OPERATION_DERIVE && priv->algorithm == SC_ALGORITHM_EC) put_tag_and_len(0x85, datalen, &p); else put_tag_and_len(0x81, datalen, &p); memcpy(p, data, datalen); p += datalen; /* * alg_id=06 is a place holder for all RSA keys. * Derive the real alg_id based on the size of the * the data, as we are always using raw mode. * Non RSA keys needs some work in thia area. */ 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, &rbuflen); if ( r >= 0) { body = sc_asn1_find_tag(card->ctx, rbuf, rbuflen, 0x7c, &bodylen); if (body) { tag = sc_asn1_find_tag(card->ctx, body, bodylen, 0x82, &taglen); if (tag) { memcpy(out, tag, taglen); r = taglen; } } else r = SC_ERROR_INVALID_DATA; } if (rbuf) free(rbuf); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; int i; int nLen; u8 rbuf[128]; /* For EC conversions 384 will fit */ size_t rbuflen = sizeof(rbuf); const u8 * body; size_t bodylen; const u8 * tag; size_t taglen; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* The PIV returns a DER SEQUENCE{INTEGER, INTEGER} * Which may have leading 00 to force positive * TODO: -DEE should check if PKCS15 want the same * But PKCS11 just wants 2* filed_length in bytes * So we have to strip out the integers * if present 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL," output too small for EC signature %d < %d", outlen, 2*nLen); r = SC_ERROR_INVALID_DATA; goto err; } memset(out, 0, outlen); r = piv_validate_general_authentication(card, data, datalen, rbuf, rbuflen); if (r < 0) goto err; if ( r >= 0) { body = sc_asn1_find_tag(card->ctx, rbuf, rbuflen, 0x30, &bodylen); for (i = 0; i<2; i++) { if (body) { tag = sc_asn1_find_tag(card->ctx, body, bodylen, 0x02, &taglen); if (tag) { bodylen -= taglen - (tag - body); body = tag + taglen; if (taglen > nLen) { /* drop leading 00 if present */ if (*tag != 0x00) { r = SC_ERROR_INVALID_DATA; goto err; } tag++; taglen--; } memcpy(out + nLen*i + nLen - taglen , tag, taglen); } else { r = SC_ERROR_INVALID_DATA; goto err; } } else { r = SC_ERROR_INVALID_DATA; goto err; } } r = 2 * nLen; } } 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; int 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 */ if (memcmp(path, "\x3F\x00", 2) == 0) { if (pathlen == 2) { r = piv_select_aid(card, piv_aids[0].value, piv_aids[0].len_short, NULL, NULL); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot select PIV AID"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } else if (pathlen > 2) { path += 2; pathlen -= 2; } } i = piv_find_obj_by_containerid(card, path); if (i < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } file = sc_file_new(); if (file == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 0); } static int piv_process_discovery(sc_card_t *card) { piv_private_data_t * priv = PIV_DATA(card); int r; u8 * rbuf = NULL; size_t rbuflen = 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; r = piv_get_cached_data(card, PIV_OBJ_DISCOVERY, &rbuf, &rbuflen); if (r <= 0) { priv->obj_cache[PIV_OBJ_DISCOVERY].flags |= PIV_OBJ_CACHE_NOT_PRESENT; /* Discovery object is only object that has 3 byte Lc= 50017E * and pree 800-73-3 cards may treat this as a strange error. * So treat any error as not present */ r = 0; goto err; } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Discovery = %p:%d",rbuf, rbuflen); /* the object is now cached, see what we have */ if (rbuflen != 0) { body = rbuf; if ((r = sc_asn1_read_tag(&body, rbuflen, &cla_out, &tag_out, &bodylen)) != SC_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DER problem %d\n",r); r = SC_ERROR_INVALID_ASN1_OBJECT; goto err; } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Discovery 0x%2.2x 0x%2.2x %p:%d", cla_out, tag_out, body, bodylen); if ( cla_out+tag_out == 0x7E && body != NULL && bodylen != 0) { aidlen = 0; aid = sc_asn1_find_tag(card->ctx, body, bodylen, 0x4F, &aidlen); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Discovery aid=%p:%d",aid,aidlen); if (aid == NULL || aidlen < piv_aids[0].len_short || memcmp(aid,piv_aids[0].value,piv_aids[0].len_short) != 0) { /*TODO look at long */ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Discovery object not PIV"); r = SC_SUCCESS; /* not an error could be some other appl */ goto err; } pinp = sc_asn1_find_tag(card->ctx, body, bodylen, 0x5F2F, &pinplen); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Discovery pinp=%p:%d",pinp,pinplen); if (pinp && pinplen == 2) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Discovery pinp flags=0x%2.2x 0x%2.2x",*pinp, *(pinp+1)); r = SC_SUCCESS; if (*pinp == 0x60 && *(pinp+1) == 0x20) { /* use Global pin */ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Pin Preference - Global"); priv->pin_preference = 0x00; } } } } err: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; 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) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DER problem %d\n",r); r = SC_ERROR_INVALID_ASN1_OBJECT; goto err; } if ( cla_out+tag_out == 0x53 && body != NULL && bodylen != 0) { numlen = 0; num = sc_asn1_find_tag(card->ctx, body, bodylen, 0xC1, &numlen); if (num) { if (numlen != 1 || *num > PIV_OBJ_RETIRED_X509_20-PIV_OBJ_RETIRED_X509_1+1) { r = SC_ERROR_INTERNAL; /* TODO some other error */ 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_INTERNAL; /* TODO some other error */ 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); memcpy(priv->offCardCertURL, url, urllen); } } else { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Problem with Histroy object\n"); goto err; } } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "History on=%d off=%d URL=%s\n", 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 || cla_out+tag_out != 0x30) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DER problem\n"); 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 || cla_out+tag_out != 0x30) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DER problem\n"); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DER problem\n"); 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 */ i2 = put_tag_and_len(0x70,certlen, NULL) + put_tag_and_len(0x71, 1, NULL) + put_tag_and_len(0xFE, 0, NULL); certobjlen = put_tag_and_len(0x53, i2, NULL); certobj = malloc(certobjlen); if (certobj == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } cp = certobj; put_tag_and_len(0x53, i2, &cp); put_tag_and_len(0x70,certlen, &cp); memcpy(cp, cert, certlen); cp += certlen; put_tag_and_len(0x71, 1,&cp); *cp++ = 0x00; put_tag_and_len(0xFE, 0, &cp); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "got internal r=%d\n",r); certobj = NULL; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Added from off card file #%d %p:%d 0x%02X \n", 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); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } 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->aid_file) sc_file_free(priv->aid_file); if (priv->w_buf) free(priv->w_buf); if (priv->offCardCertURL) free(priv->offCardCertURL); for (i = 0; i < PIV_OBJ_LAST_ENUM - 1; i++) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DEE freeing #%d, 0x%02x %p:%d %p:%d", i, priv->obj_cache[i].flags, priv->obj_cache[i].obj_data, priv->obj_cache[i].obj_len, priv->obj_cache[i].internal_obj_data, priv->obj_cache[i].internal_obj_len); if (priv->obj_cache[i].obj_data) free(priv->obj_cache[i].obj_data); if (priv->obj_cache[i].internal_obj_data) free(priv->obj_cache[i].internal_obj_data); } free(priv); } return 0; } static int piv_match_card(sc_card_t *card) { int i; sc_file_t aidfile; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* Since we send an APDU, the card's logout function may be called... * however it may be in dirty memory */ card->ops->logout = NULL; /* Detect by selecting applet */ i = !(piv_find_aid(card, &aidfile)); return i; /* never match */ } static int piv_init(sc_card_t *card) { int r, i; unsigned long flags; unsigned long ext_flags; piv_private_data_t *priv; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); priv = calloc(1, sizeof(piv_private_data_t)); if (!priv) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); priv->aid_file = sc_file_new(); priv->selected_obj = -1; priv->pin_preference = 0x80; /* 800-73-3 part 1, table 3 */ /* Some objects will only be present if Histroy object says so */ for (i=0; i < PIV_OBJ_LAST_ENUM -1; i++) { if(piv_objects[i].flags & PIV_OBJECT_NOT_PRESENT) priv->obj_cache[i].flags |= PIV_OBJ_CACHE_NOT_PRESENT; } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Max send = %d recv = %d\n", card->max_send_size, card->max_recv_size); card->drv_data = priv; card->cla = 0x00; card->name = "PIV-II card"; r = piv_find_aid(card, priv->aid_file); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Failed to initialize %s\n", card->name); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } priv->enumtag = piv_aids[r].enumtag; card->type = piv_aids[r].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; _sc_card_add_rsa_alg(card, 1024, flags, 0); /* manditory */ _sc_card_add_rsa_alg(card, 2048, flags, 0); /* optional */ _sc_card_add_rsa_alg(card, 3072, flags, 0); /* optional */ flags = SC_ALGORITHM_ECDSA_RAW; ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE | SC_ALGORITHM_EXT_EC_UNCOMPRESES; _sc_card_add_ec_alg(card, 256, flags, ext_flags); _sc_card_add_ec_alg(card, 384, flags, ext_flags); card->caps |= SC_CARD_CAP_RNG; /* * 800-73-3 cards may have a history object and/or a discovery object * We want to process them now as this has information on what * keys and certs the card has and how the pin might be used. */ r = piv_process_history(card); r = piv_process_discovery(card); if (r > 0) r = 0; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } static int piv_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { /* 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(); if (data->cmd == SC_PIN_CMD_CHANGE) { int 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; } } } return iso_drv->ops->pin_cmd(card, data, tries_left); } 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.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.card_ctl = piv_card_ctl; piv_ops.pin_cmd = piv_pin_cmd; return &piv_drv; } #if 1 struct sc_card_driver * sc_get_piv_driver(void) { return sc_get_driver(); } #endif opensc-0.13.0/src/libopensc/cardctl.h0000644000015201777760000005467212057406034014375 00000000000000/* * cardctl.h: card_ctl command numbers * * Copyright (C) 2003 Olaf Kirch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #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_SE_INFO, SC_CARDCTL_GET_CHV_REFERENCE_IN_SE, SC_CARDCTL_PKCS11_INIT_TOKEN, SC_CARDCTL_PKCS11_INIT_PIN, /* * GPK specific calls */ SC_CARDCTL_GPK_BASE = _CTL_PREFIX('G', 'P', 'K'), SC_CARDCTL_GPK_VARIANT, SC_CARDCTL_GPK_LOCK, SC_CARDCTL_GPK_PKINIT, SC_CARDCTL_GPK_PKLOAD, SC_CARDCTL_GPK_IS_LOCKED, SC_CARDCTL_GPK_GENERATE_KEY, /* * Cryptoflex specific calls */ SC_CARDCTL_CRYPTOFLEX_BASE = _CTL_PREFIX('C', 'F', 'X'), SC_CARDCTL_CRYPTOFLEX_GENERATE_KEY, /* * MioCOS specific calls */ SC_CARDCTL_MIOCOS_BASE = _CTL_PREFIX('M', 'I', 'O'), SC_CARDCTL_MIOCOS_CREATE_AC, /* * TCOS specific calls */ SC_CARDCTL_TCOS_BASE = _CTL_PREFIX('T','C','S'), SC_CARDCTL_TCOS_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, /* * 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, /* * JCOP specific calls */ SC_CARDCTL_JCOP_BASE = _CTL_PREFIX('J', 'C', 'P'), SC_CARDCTL_JCOP_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, /* * Incrypto34 specific calls */ SC_CARDCTL_INCRYPTO34_BASE = _CTL_PREFIX('I', '3', '4'), SC_CARDCTL_INCRYPTO34_PUT_DATA_FCI, SC_CARDCTL_INCRYPTO34_PUT_DATA_OCI, SC_CARDCTL_INCRYPTO34_PUT_DATA_SECI, SC_CARDCTL_INCRYPTO34_GENERATE_KEY, SC_CARDCTL_INCRYPTO34_CHANGE_KEY_DATA, SC_CARDCTL_INCRYPTO34_ERASE_FILES, /* * Muscle specific calls */ SC_CARDCTL_MUSCLE_BASE = _CTL_PREFIX('M','S','C'), SC_CARDCTL_MUSCLE_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 STANDART */ 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, /* * 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, /* * 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, /* * 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 }; 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 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 char * pin; size_t pin_len; } sc_cardctl_pkcs11_init_pin_t; /* * GPK lock file. * Parent DF of file must be selected. */ struct sc_cardctl_gpk_lock { struct sc_file * file; unsigned int operation; }; /* * GPK initialize private key file. * Parent DF must be selected. */ struct sc_cardctl_gpk_pkinit { struct sc_file * file; unsigned int privlen; }; /* * GPK load private key portion. */ struct sc_cardctl_gpk_pkload { struct sc_file * file; u8 * data; unsigned int len; unsigned int datalen; }; struct sc_cardctl_gpk_genkey { unsigned int fid; unsigned int privlen; unsigned char * pubkey; unsigned int pubkey_len; }; enum { SC_CARDCTL_MIOCOS_AC_PIN, SC_CARDCTL_MIOCOS_AC_CHAL, SC_CARDCTL_MIOCOS_AC_LOGICAL, SC_CARDCTL_MIOCOS_AC_SMARTPIN }; /* * MioCOS AC info */ struct sc_cardctl_miocos_ac_info { int type; int ref; int max_tries; int enable_ac; /* only applicable to PINs */ u8 key_value[8]; int max_unblock_tries; /* same here */ u8 unblock_value[8]; /* and here */ }; /* * 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; unsigned int key_bits; unsigned short fid; }; /* * Incrypto34 PIN info */ struct sc_cardctl_incrypto34_obj_info { u8 * data; size_t len; unsigned int key_id; unsigned int key_class; }; struct sc_cardctl_incrypto34_genkey_info { unsigned int key_id; unsigned int key_bits; unsigned short fid; }; /* * Cryptoflex info */ struct sc_cardctl_cryptoflex_genkey_info { unsigned int key_num; unsigned int 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; struct sc_cardctl_jcop_genkey { unsigned long exponent; sc_path_t pub_file_ref; sc_path_t pri_file_ref; unsigned char * pubkey; unsigned int pubkey_len; }; /* * Oberthur ex_data stuff */ 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_DSA_PUBLIC, SC_CARDCTL_OBERTHUR_KEY_DSA_PRIVATE }; struct sc_cardctl_oberthur_genkey_info { unsigned int id_prv, id_pub; unsigned int key_bits; unsigned long exponent; unsigned char * pubkey; unsigned int 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]; unsigned int id_len; }; struct sc_cardctl_oberthur_createpin_info { unsigned int type; unsigned int ref; const unsigned char *pin; unsigned int 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; unsigned int mod_len; /* in bits */ unsigned int pubexp_len; /* in bits */ unsigned char *pubexp; unsigned int primep_len; /* in bits */ unsigned char *primep; unsigned int primeq_len; /* in bits */ unsigned char *primeq; }; /* * Muscle stuff */ typedef struct sc_cardctl_muscle_gen_key_info { int keyType; int keySize; int privateKeyLocation; int publicKeyLocation; } sc_cardctl_muscle_gen_key_info_t; typedef struct sc_cardctl_muscle_key_info { int keyType; int keyLocation; int keySize; int modLength; u8* modValue; int expLength; u8* expValue; int pLength; u8* pValue; int qLength; u8* qValue; int pqLength; u8* pqValue; int dp1Length; u8* dp1Value; int dq1Length; u8* dq1Value; int gLength; u8* gValue; int 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; } 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_EC = 0x21, /* SC_CARDCTL_MYEID_KEY_AES = 0x?, // for future use SC_CARDCTL_MYEID_KEY_DES = 0x?, SC_CARDCTL_MYEID_KEY_3DES = 0x?, */ }; 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 */ unsigned int key_len_bits; unsigned char *mod; unsigned int pubexp_len; unsigned char *pubexp; unsigned int primep_len; unsigned char *primep; unsigned int primeq_len; unsigned char *primeq; unsigned int dp1_len; unsigned char *dp1; unsigned int dq1_len; unsigned char *dq1; unsigned int invq_len; unsigned char *invq; /* new for MyEID > 3.6.0 */ unsigned char *d; /* EC private key */ unsigned int d_len; /* EC */ unsigned char *ecpublic_point; /* EC public key */ unsigned int 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 long exponent; /* RSA */ unsigned char * pubkey; /* RSA */ unsigned int pubkey_len; /* RSA */ unsigned char * ecparam; /* EC */ unsigned int ecparam_len; /* EC */ unsigned char * ecpoint; /* EC */ unsigned int 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_KEYFORMAT_STD 0 /* See 4.3.3.6 Algorithm Attributes */ #define SC_OPENPGP_KEYFORMAT_STDN 1 /* OpenPGP card spec v2 */ #define SC_OPENPGP_KEYFORMAT_CRT 2 #define SC_OPENPGP_KEYFORMAT_CRTN 3 typedef struct sc_cardctl_openpgp_keygen_info { u8 keytype; /* SC_OPENPGP_KEY_ */ u8 *modulus; /* New-generated pubkey info responded from the card */ size_t modulus_len; /* Length of modulus in bit */ u8 *exponent; size_t exponent_len; } sc_cardctl_openpgp_keygen_info_t; typedef struct sc_cardctl_openpgp_keystore_info { u8 keytype; u8 keyformat; u8 *e; size_t e_len; u8 *p; size_t p_len; u8 *q; size_t q_len; u8 *n; size_t n_len; time_t creationtime; } sc_cardctl_openpgp_keystore_info_t; /* * 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 */ u8 options[2]; /* Initilization options */ char dkek_shares; /* Number of DKEK shares, 0 for card generated, -1 for none */ } 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; #ifdef __cplusplus } #endif #endif /* _OPENSC_CARDCTL_H */ opensc-0.13.0/src/libopensc/pkcs15-gemsafeGPK.c0000644000015201777760000003553112057406034016022 00000000000000/* * partial PKCS15 emulation for gemsafe GPK cards * * Copyright (C) 2005, Douglas E. Engert * 2004, Nils Larsch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include "common/compat_strlcpy.h" #include "internal.h" #include "pkcs15.h" #include "log.h" #include "cardctl.h" #define MANU_ID "GemSAFE on GPK16000" int sc_pkcs15emu_gemsafeGPK_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); static int (*pin_cmd_save)(struct sc_card *, struct sc_pin_cmd_data *, int *tries_left); typedef struct cdata_st { const char *label; int authority; const char *path; const char *id; int obj_flags; } cdata; typedef struct pdata_st { const char *id; const char *label; const char *path; int ref; int type; unsigned int maxlen; unsigned int minlen; unsigned int storedlen; int flags; int tries_left; const char pad_char; int obj_flags; } pindata; typedef struct prdata_st { const char *id; const char *label; unsigned int modulus_len; int usage; const char *path; int ref; const char *auth_id; int obj_flags; } prdata; typedef struct keyinfo_st { int fileid; sc_pkcs15_id_t id; unsigned int modulus_len; u8 modulus[1024/8]; } keyinfo; #define USAGE_NONREP SC_PKCS15_PRKEY_USAGE_NONREPUDIATION #define USAGE_KE SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ SC_PKCS15_PRKEY_USAGE_DECRYPT | \ SC_PKCS15_PRKEY_USAGE_WRAP | \ SC_PKCS15_PRKEY_USAGE_UNWRAP #define USAGE_AUT SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ SC_PKCS15_PRKEY_USAGE_DECRYPT | \ SC_PKCS15_PRKEY_USAGE_WRAP | \ SC_PKCS15_PRKEY_USAGE_UNWRAP | \ SC_PKCS15_PRKEY_USAGE_SIGN static const u8 gemsafe_aid[] = {0xA0, 0x00, 0x00, 0x00, 0x18, 0x0F, 0x00, 0x00, 0x01, 0x63, 0x00, 0x01}; static int my_pin_cmd(sc_card_t * card, struct sc_pin_cmd_data * data, int *tries_left) { /* GemSAFE pin uses a null terminated string with 0xFF */ /* so we need to add the 0x00 to the pin then pad with 0xFF */ int r; const u8 *saved_data = NULL; int saved_len = 0; u8 newpin[8]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); memset(newpin, 0xff, sizeof(newpin)); if (data->pin1.data && data->pin1.len < 8 && data->pin1.len > 0) { memcpy(newpin,data->pin1.data, (size_t)data->pin1.len); newpin[data->pin1.len] = 0x00; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "pin len=%d", data->pin1.len); saved_data = data->pin1.data; saved_len = data->pin1.len; data->pin1.data = newpin; data->pin1.len = sizeof(newpin); } r = pin_cmd_save(card, data, tries_left); if (saved_data) { data->pin1.data = saved_data; data->pin1.len = saved_len; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int is_seq(unsigned char * seq, unsigned int *seq_size, unsigned int *seq_len) { int i,j,k; if (seq[0] != 0x30) return 0; /* not a sequence */ if (seq[1] & 0x80) { i = seq[1] & 0x7f; if (i > 2 || i == 0) return 0; /* cert would be bigger then 65k or zero */ if (seq[2] == 0) return 0; /* DER would not have extra zero */ k = 0; for (j = 0; j < i; j++) { k = (k << 8) + seq[j + 2]; } if (k < 128) return 0; /* DER would have used single byte for len */ } else { i = 0; k = seq[1]; } *seq_size = i + 2; *seq_len = k; return 1; } static int gemsafe_detect_card(sc_pkcs15_card_t *p15card) { sc_card_t *card = p15card->card; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (strcmp(card->name, "Gemplus GPK")) return SC_ERROR_WRONG_CARD; return SC_SUCCESS; } static int sc_pkcs15emu_gemsafeGPK_init(sc_pkcs15_card_t *p15card) { const cdata certs[] = { {"User certificate",0, "","1", 0}, {NULL, 0, NULL, NULL, 0} }; const pindata pins[] = { { "1", "pin", "3F000200", 0x00, SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 8, 4, 8, SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_LOCAL, -1, 0x00, SC_PKCS15_CO_FLAG_PRIVATE }, { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; const prdata prkeys[] = { { "1", "AUTH key", 1024, USAGE_AUT, "I0009", 0x00, "1", 0}, { NULL, NULL, 0, 0, NULL, 0, NULL, 0} }; int r, i, j; int dfpath; sc_path_t path; sc_file_t *file = NULL; sc_card_t *card = p15card->card; unsigned char *gsdata = NULL; unsigned int idxlen, idx1, idx2, seq_len1, seq_len2, seq_size1, seq_size2; sc_serial_number_t serial; u8 sysrec[7]; int num_keyinfo = 0; keyinfo kinfo[8]; /* will loook for 8 keys */ u8 modulus_buf[ 1 + 1024 / 8]; /* tag+modulus */ u8 *cp; char buf[256]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* need to limit to 248 */ card->max_send_size = 248; card->max_recv_size = 248; /* could read this off card if needed */ p15card->tokeninfo->label = strdup("GemSAFE"); p15card->tokeninfo->manufacturer_id = strdup(MANU_ID); /* get serial number */ r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); r = sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0); if (r != SC_SUCCESS) return SC_ERROR_INTERNAL; p15card->tokeninfo->serial_number = strdup(buf); /* test if we have a gemsafe app df */ memset(&path, 0, sizeof(path)); memcpy(path.value, gemsafe_aid, sizeof(gemsafe_aid)); path.len = sizeof(gemsafe_aid); path.type = SC_PATH_TYPE_DF_NAME; r = sc_select_file(card, &path, &file); if (r < 0) { /* OK, then lets try for 3f000200 */ sc_format_path("3F000200",&path); path.type = SC_PATH_TYPE_PATH; r = sc_select_file(card, &path, &file); } if (r < 0) return SC_ERROR_WRONG_CARD; /* we will use dfpath in all other references */ dfpath = file->id; free(file); file = NULL; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "GemSafe file found, id=%d",dfpath); /* There may be more then one key in the directory. */ /* we need to find them so we can associate them with the */ /* the certificate. The files are 0007 to 000f */ for (i = 7; i < 16; i++) { path.value[0] = 0x00; path.value[1] = i; path.len = 2; path.type = SC_PATH_TYPE_FILE_ID; r = sc_select_file(card, &path, NULL); if (r < 0) continue; r = sc_read_record(card, 1, sysrec, sizeof(sysrec), SC_RECORD_BY_REC_NR); if (r != 7 || sysrec[0] != 0) { continue; } if (sysrec[5] != 0x00) { continue; } switch (sysrec[1]) { case 0x00: kinfo[num_keyinfo].modulus_len = 512 / 8; break; case 0x10: kinfo[num_keyinfo].modulus_len = 768 / 8; break; case 0x11: kinfo[num_keyinfo].modulus_len = 1024 / 8; break; default: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported modulus length"); continue; } kinfo[num_keyinfo].fileid = i; sc_pkcs15_format_id("NONE", &kinfo[num_keyinfo].id); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"reading modulus"); r = sc_read_record(card, 2, modulus_buf, kinfo[num_keyinfo].modulus_len+1, SC_RECORD_BY_REC_NR); if (r < 0) continue; /* need to reverse the modulus skiping the tag */ j = kinfo[num_keyinfo].modulus_len; cp = kinfo[num_keyinfo].modulus; while (j--) *cp++ = modulus_buf[j + 1]; num_keyinfo++; } /* Get the gemsafe data with the cert */ sc_format_path("3F000200004", &path); /* file.id has the real DF of the GemSAFE file from above*/ path.value[2] = dfpath >> 8; path.value[3] = dfpath & 0xff; if (sc_select_file(card, &path, &file) < 0) { return SC_ERROR_WRONG_CARD; } /* the GemSAFE file has our cert, but we do not know the format */ /* of the file. But we do know a cert has SEQ SEQ SEQOF INT 2 */ /* so we will look for that. We assume cert is larger then 127 bytes */ /* and less then 65K, and must be fit in the file->size */ /* There is a chance that we might find something that is not */ /* a cert, but the chances are low. If GemPlus ever publishes */ /* the format of the file, we can used that instead. */ /* For performance reasons we will only */ /* read part of the file , as it is about 6100 bytes */ gsdata = malloc(file->size); if (!gsdata) return SC_ERROR_OUT_OF_MEMORY; /* set indcies of data in gsdata */ idx1 = 0; /* start point */ idx2 = 0; /* index of last data read so far */ /* set certs We only have one we are interested in */ /* but the read loop is set up to allow for more in future */ for (i = 0; certs[i].label; i++) { struct sc_pkcs15_cert_info cert_info; struct sc_pkcs15_object cert_obj; sc_pkcs15_cert_t *cert_out; memset(&cert_info, 0, sizeof(cert_info)); memset(&cert_obj, 0, sizeof(cert_obj)); sc_pkcs15_format_id(certs[i].id, &cert_info.id); cert_info.authority = certs[i].authority; strlcpy(cert_obj.label, certs[i].label, sizeof(cert_obj.label)); cert_obj.flags = certs[i].obj_flags; while (idx1 < file->size - 16) { /* actually 13 for all these tests */ if (idx1 + 16 > idx2 ) { /* need more data in buff */ idxlen = 248; /* read in next 248 bytes */ if (idxlen > file->size - idx2) idxlen = file->size - idx2; r = sc_read_binary(card, idx2, gsdata + idx2, idxlen, 0); if (r < 0) break; idx2 = idx2 + idxlen; } if ( gsdata[idx1] == 0x30 && is_seq(gsdata + idx1, &seq_size1, &seq_len1) && is_seq(gsdata + idx1 + seq_size1, &seq_size2, &seq_len2) && gsdata[idx1 + seq_size1 + seq_size2 + 0] == 0xa0 && gsdata[idx1 + seq_size1 + seq_size2 + 1] == 0x03 && gsdata[idx1 + seq_size1 + seq_size2 + 2] == 0x02 && gsdata[idx1 + seq_size1 + seq_size2 + 3] == 0x01 && gsdata[idx1 + seq_size1 + seq_size2 + 4] == 0x02 && idx1 + 4 + seq_len1 < file->size) { /* we have a cert (I hope) */ /* read in rest if needed */ idxlen = idx1 + seq_len1 + 4 - idx2; if (idxlen > 0) { idxlen = (idxlen + 3) & 0xfffffffc; r = sc_read_binary(card, idx2, gsdata + idx2, idxlen, 0); if (r < 0) break; /* can not read cert */ idx2 = idx2 + idxlen; } cert_info.value.len = seq_len1 + 4; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Found cert at offset %d", idx1); cert_info.value.value = (unsigned char *) malloc(cert_info.value.len); if (!cert_info.value.value) return SC_ERROR_OUT_OF_MEMORY; memcpy(cert_info.value.value, gsdata + idx1, cert_info.value.len); idx1 = idx1 + cert_info.value.len; break; } idx1++; } if (cert_info.value.value == NULL) break; /* cert not found, no more certs */ r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); if (r < 0) { free(gsdata); return SC_ERROR_INTERNAL; } /* now lets see if we have a matching key for this cert */ r = sc_pkcs15_read_certificate(p15card, &cert_info, &cert_out); if (r < 0) { free(gsdata); return SC_ERROR_INTERNAL; } for (j = 0; j < num_keyinfo; j++) { if (cert_out->key->u.rsa.modulus.len == kinfo[j].modulus_len && memcmp(cert_out->key->u.rsa.modulus.data, &kinfo[j].modulus, cert_out->key->u.rsa.modulus.len) == 0) { memcpy(&kinfo[j].id, &cert_info.id, sizeof(sc_pkcs15_id_t)); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found match"); } } sc_pkcs15_free_certificate(cert_out); } if (gsdata) free(gsdata); /* set pins */ /* GemSAFE uses different padding, so need to trap */ /* the pin_cmd and reset the padding */ pin_cmd_save = card->ops->pin_cmd; card->ops->pin_cmd = my_pin_cmd; for (i = 0; pins[i].label; i++) { struct sc_pkcs15_auth_info pin_info; struct sc_pkcs15_object pin_obj; memset(&pin_info, 0, sizeof(pin_info)); memset(&pin_obj, 0, sizeof(pin_obj)); sc_pkcs15_format_id(pins[i].id, &pin_info.auth_id); pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; pin_info.attrs.pin.reference = pins[i].ref; pin_info.attrs.pin.flags = pins[i].flags; pin_info.attrs.pin.type = pins[i].type; pin_info.attrs.pin.min_length = pins[i].minlen; pin_info.attrs.pin.stored_length = pins[i].storedlen; pin_info.attrs.pin.max_length = pins[i].maxlen; pin_info.attrs.pin.pad_char = pins[i].pad_char; sc_format_path(pins[i].path, &pin_info.path); pin_info.path.value[2] = dfpath >> 8; pin_info.path.value[3] = dfpath & 0xff; pin_info.tries_left = -1; strlcpy(pin_obj.label, pins[i].label, sizeof(pin_obj.label)); pin_obj.flags = pins[i].obj_flags; r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); if (r < 0) return SC_ERROR_INTERNAL; } /* needs work, as we may want to add more then one key */ /* but not sure what the other keys do */ /* set private keys */ for (i = 0; prkeys[i].label; i++) { struct sc_pkcs15_prkey_info prkey_info; struct sc_pkcs15_object prkey_obj; memset(&prkey_info, 0, sizeof(prkey_info)); memset(&prkey_obj, 0, sizeof(prkey_obj)); sc_pkcs15_format_id(prkeys[i].id, &prkey_info.id); prkey_info.usage = prkeys[i].usage; prkey_info.native = 1; prkey_info.key_reference = prkeys[i].ref; prkey_info.modulus_length= prkeys[i].modulus_len; sc_format_path(prkeys[i].path, &prkey_info.path); /*DEE need to look for them by reading and checking mudulus vs cert */ /* will use the default path, unless we found a key with */ /* the same modulus as the cert(s) we already added */ /* This allows us to have a card with a key but no cert */ for (j = 0; j < num_keyinfo; j++) { if (sc_pkcs15_compare_id(&kinfo[j].id, &prkey_info.id)) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found key in file %d for id %d", kinfo[j].fileid, prkey_info.id); prkey_info.path.value[0] = kinfo[j].fileid >> 8; prkey_info.path.value[1] = kinfo[j].fileid & 0xff; break; } } strlcpy(prkey_obj.label, prkeys[i].label, sizeof(prkey_obj.label)); prkey_obj.flags = prkeys[i].obj_flags; if (prkeys[i].auth_id) sc_pkcs15_format_id(prkeys[i].auth_id, &prkey_obj.auth_id); r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); if (r < 0) return SC_ERROR_INTERNAL; } return SC_SUCCESS; } int sc_pkcs15emu_gemsafeGPK_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts) { sc_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Entering %s", __FUNCTION__); if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) return sc_pkcs15emu_gemsafeGPK_init(p15card); else { int r = gemsafe_detect_card(p15card); if (r) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_gemsafeGPK_init(p15card); } } opensc-0.13.0/src/libopensc/pkcs15-tcos.c0000644000015201777760000004347112057406034015023 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include "common/compat_strlcpy.h" #include "internal.h" #include "pkcs15.h" #include "cardctl.h" #include "log.h" int sc_pkcs15emu_tcos_init_ex( sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts); 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]; 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "Select(%s) failed\n", path); return 1; } if(sc_read_binary(card, 0, cert, sizeof(cert), 0)<0){ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "ReadBinary(%s) failed\n", path); return 2; } if(cert[0]!=0x30 || cert[1]!=0x82){ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Invalid Cert: %02X:%02X:...\n", cert[0], cert[1]); return 3; } /* some certificates are prefixed by an OID */ if(cert[4]==0x06 && cert[5]<10 && cert[6+cert[5]]==0x30 && cert[7+cert[5]]==0x82){ 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "sc_pkcs15emu_add_x509_cert(%s) failed\n", path); return 4; } sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "%s: OK, Index=%d, Count=%d\n", path, cert_info.path.index, cert_info.path.count); 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "Select(%s) failed\n", sc_print_path(&prkey_info.path)); return 1; } sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Searching for Key-Ref %02X\n", key_reference); while((r=sc_read_record(card, ++rec_no, buf, sizeof(buf), SC_RECORD_BY_REC_NR))>0){ int found=0; if(buf[0]!=0xA0) continue; for(i=2;iprop_attr[1] & 0x04) can_crypt=1; if (f->prop_attr[1] & 0x08) can_sign=1; sc_file_free(f); } prkey_info.usage= SC_PKCS15_PRKEY_USAGE_SIGN; if(can_crypt) prkey_info.usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT|SC_PKCS15_PRKEY_USAGE_DECRYPT; if(can_sign) prkey_info.usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; r=sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); if(r!=SC_SUCCESS){ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "sc_pkcs15emu_add_rsa_prkey(%s) failed\n", path); return 4; } sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "%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; 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'; 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "Select(%s) failed\n", sc_print_path(&pin_info.path)); return 1; } sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Searching for PIN-Ref %02X\n", pin_reference); while((r=sc_read_record(card, ++rec_no, buf, sizeof(buf), SC_RECORD_BY_REC_NR))>0){ int found=0, fbz=-1; if(buf[0]!=0xA0) continue; for(i=2;iprop_attr[3]; sc_file_free(f); } r=sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); if(r!=SC_SUCCESS){ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "sc_pkcs15emu_add_pin_obj(%s) failed\n", path); return 4; } sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "%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]; strcpy(buf,dir); return strcat(buf,path); } 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); p15card->tokeninfo->manufacturer_id = strdup("TeleSec GmbH"); p15card->tokeninfo->label = strdup(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; p15card->tokeninfo->manufacturer_id = strdup("TeleSec GmbH"); p15card->tokeninfo->label = strdup("IDKey Card"); insert_cert(p15card, "DF074331", 0x45, 1, "Signatur Zertifikat 1"); insert_cert(p15card, "DF074332", 0x45, 1, "Signatur Zertifikat 2"); insert_cert(p15card, "DF074333", 0x45, 1, "Signatur Zertifikat 3"); insert_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_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; p15card->tokeninfo->manufacturer_id = strdup("Deutsche Post"); p15card->tokeninfo->label = strdup("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; p15card->tokeninfo->manufacturer_id = strdup("DATEV"); p15card->tokeninfo->label = strdup("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")){ p15card->tokeninfo->manufacturer_id = strdup("JLU Giessen"); p15card->tokeninfo->label = strdup("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")){ p15card->tokeninfo->manufacturer_id = strdup("TU Darmstadt"); p15card->tokeninfo->label = strdup("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, sc_pkcs15emu_opt_t *opts ){ sc_card_t *card = p15card->card; sc_context_t *ctx = p15card->card->ctx; sc_serial_number_t serialnr; char serial[30]; int i, r; /* check if we have the correct card OS unless SC_PKCS15EMU_FLAGS_NO_CHECK */ i=(opts && (opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)); if (!i && card->type!=SC_CARD_TYPE_TCOS_V2 && card->type!=SC_CARD_TYPE_TCOS_V3) return SC_ERROR_WRONG_CARD; /* get the card serial number */ r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serialnr); if (r < 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "unable to get ICCSN\n"); return SC_ERROR_WRONG_CARD; } sc_bin_to_hex(serialnr.value, serialnr.len , serial, sizeof(serial), 0); serial[19] = '\0'; p15card->tokeninfo->serial_number = strdup(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; return SC_ERROR_INTERNAL; } opensc-0.13.0/src/libopensc/ef-atr.c0000644000015201777760000001173312057406034014121 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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; unsigned char category; LOG_FUNC_CALLED(ctx); category = *buf; memset(&ef_atr, 0, sizeof(struct sc_ef_atr)); /* IAS/ECC specific: skip second 'zero' byte */ if (*(++buf) == 0x00) ++buf; 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 %X, card caps %X", ef_atr.df_selection, ef_atr.unit_size, ef_atr.card_capabilities); } 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); } } if (category == ISO7816_II_CATEGORY_TLV) { 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; int rv; unsigned char *buf = NULL; LOG_FUNC_CALLED(ctx); sc_format_path("3F002F01", &path); rv = sc_select_file(card, &path, &file); LOG_TEST_RET(ctx, rv, "Cannot select EF(ATR) file"); buf = malloc(file->size); if (!buf) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Memory allocation error"); rv = sc_read_binary(card, 0, buf, file->size, 0); LOG_TEST_RET(ctx, rv, "Cannot read EF(ATR) file"); rv = sc_parse_ef_atr_content(card, buf, file->size); LOG_TEST_RET(ctx, rv, "EF(ATR) parse error"); free(buf); sc_file_free(file); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } void sc_free_ef_atr(sc_card_t *card) { if (card->ef_atr) free(card->ef_atr); card->ef_atr = NULL; } opensc-0.13.0/src/libopensc/card-flex.c0000644000015201777760000011304712057406034014611 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include "internal.h" #include "cardctl.h" #define FLAG_KEYGEN 0x80000000 #define IS_CYBERFLEX(card) (card->type == SC_CARD_TYPE_FLEX_CYBER) static 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 similiar 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) { #if 0 sc_apdu_t apdu; u8 rbuf[3]; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xC4, 0x00, 0x00); apdu.cla = 0xF0 /* 0x00 for Cyberflex */; apdu.le = 3; apdu.resplen = 3; apdu.resp = rbuf; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 && apdu.sw2 != 0x00) return 0; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "AC Keys: %02X %02X %02X\n", rbuf[0], rbuf[1], rbuf[2]); #endif 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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: #if 0 /* FIXME */ file->acl[SC_AC_OP_DECREASE] = ac_to_acl(p[0] & 0x0F); #endif 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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: #if 0 file->ef_structure = SC_FILE_EF_PROGRAM; #endif break; default: sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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: #if 0 /* FIXME */ file->acl[SC_AC_OP_DECREASE] = ac_to_acl(pos[0] & 0x0F); #endif 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; char debug_buf[32]; sc_bin_to_hex(buf, buflen, debug_buf, sizeof(debug_buf), 0); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "called, p1=%u, path=%s\n", p1, debug_buf); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); if (file_out == NULL) return 0; if (apdu.resplen < 14) return SC_ERROR_UNKNOWN_DATA_RECEIVED; if (apdu.resp[0] == 0x6F) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unsupported: card returned FCI\n"); return SC_ERROR_UNKNOWN_DATA_RECEIVED; /* FIXME */ } file = sc_file_new(); if (file == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); /* 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "called, cached path=%s\n", pbuf); switch (path->type) { case SC_PATH_TYPE_PATH: if ((pathlen & 1) != 0) /* not divisible by 2 */ return SC_ERROR_INVALID_ARGUMENTS; magic_done = check_path(card, &pathptr, &pathlen, file_out != NULL); if (pathlen == 0) return 0; if (pathlen != 2 || memcmp(pathptr, "\x3F\x00", 2) != 0) { locked = 1; r = sc_lock(card); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed"); if (!magic_done && memcmp(pathptr, "\x3F\x00", 2) != 0) { r = select_file_id(card, (const u8 *) "\x3F\x00", 2, 0, NULL); if (r) sc_unlock(card); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to select Master File (MF)"); } while (pathlen > 2) { r = select_file_id(card, pathptr, 2, 0, NULL); if (r) sc_unlock(card); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to select DF"); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "expected 4 bytes, got %d.\n", apdu.resplen); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } memcpy(buf, rbuf + 2, 2); buf += 2; count += 2; buflen -= 2; } return 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; size_t count = 0, p2 = 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "expected 6 bytes, got %d.\n", apdu.resplen); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } memcpy(buf, rbuf + 4, 2); buf += 2; count += 2; buflen -= 2; } return 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File type has to be SC_PATH_TYPE_FILE_ID\n"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); } sc_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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; int i; size_t size = file->size; int ops[6]; /* 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Creating %02x:%02x, size %d %02x:%02x\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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid EF structure\n"); return -1; } p[5] = 0x01; /* status?? */ 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[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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File structure encoding failed.\n"); return SC_ERROR_INVALID_ARGUMENTS; } if (file->type != SC_FILE_TYPE_DF && file->ef_structure != SC_FILE_EF_TRANSPARENT) rec_nr = file->record_count; 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); if (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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid crypto operation supplied.\n"); return SC_ERROR_NOT_SUPPORTED; } if (env->algorithm != SC_ALGORITHM_RSA) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid crypto algorithm supplied.\n"); return SC_ERROR_NOT_SUPPORTED; } if ((env->algorithm_flags & SC_ALGORITHM_RSA_PADS) || (env->algorithm_flags & SC_ALGORITHM_RSA_HASHES)) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Card supports only raw RSA.\n"); return SC_ERROR_NOT_SUPPORTED; } if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { if (env->key_ref_len != 1 || (env->key_ref[0] != 0 && env->key_ref[0] != 1)) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid key reference supplied.\n"); return SC_ERROR_NOT_SUPPORTED; } prv->rsa_key_ref = env->key_ref[0]; } if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Algorithm reference not supported.\n"); return SC_ERROR_NOT_SUPPORTED; } if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) if (memcmp(env->file_ref.value, "\x00\x12", 2) != 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File reference is not 0012.\n"); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Illegal input length: %d\n", data_len); return SC_ERROR_INVALID_ARGUMENTS; } if (outlen < data_len) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Output buffer too small.\n"); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); for (i = 0; i < apdu.resplen; i++) out[i] = sbuf[apdu.resplen-1-i]; return 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Illegal input length: %d\n", data_len); return SC_ERROR_INVALID_ARGUMENTS; } key_id = prv->rsa_key_ref + 1; /* Why? */ if (outlen < data_len) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Output buffer too small.\n"); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); return 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Illegal key length: %d\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; sbuf[1] = data->exponent >> 8; sbuf[2] = data->exponent >> 16; sbuf[3] = data->exponent >> 24; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); data->pubkey_len = 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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; 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } 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.13.0/src/libopensc/iso7816.c0000644000015201777760000007531412057406034014070 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include "internal.h" #include "asn1.h" #include "iso7816.h" static const struct sc_card_error iso7816_errors[] = { { 0x6200, SC_ERROR_MEMORY_FAILURE, "State of non-volatile memory 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_MEMORY_FAILURE, "State of non-volatile memory changed" }, { 0x6381, SC_ERROR_CARD_CMD_FAILED, "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" }, { 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" }, /* Possibly TCOS / Micardo specific 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"}, }; static int iso7816_check_sw(sc_card_t *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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Wrong length; correct length is %d", sw2); return SC_ERROR_WRONG_LENGTH; } if (sw1 == 0x90) return SC_SUCCESS; if (sw1 == 0x63U && (sw2 & ~0x0fU) == 0xc0U ) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Verification failed (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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s", iso7816_errors[i].errorstr); return iso7816_errors[i].errorno; } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown SWs; SW1=%02X, SW2=%02X", sw1, sw2); return SC_ERROR_CARD_CMD_FAILED; } static int iso7816_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 recvbuf[SC_MAX_APDU_BUFFER_SIZE]; int r; if (idx > 0x7fff) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "invalid EF offset: 0x%X > 0x7FFF", idx); return SC_ERROR_OFFSET_TOO_LARGE; } assert(count <= (card->max_recv_size > 0 ? card->max_recv_size : 256)); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB0, (idx >> 8) & 0x7F, idx & 0xFF); apdu.le = count; apdu.resplen = count; apdu.resp = recvbuf; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.resplen == 0) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); memcpy(buf, recvbuf, apdu.resplen); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r == SC_ERROR_FILE_END_REACHED) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Check SW error"); if (apdu.resplen < count) { r = iso7816_read_binary(card, idx + apdu.resplen, buf + apdu.resplen, count - apdu.resplen, flags); /* Ignore all but 'corrupted data' errors */ if (r == SC_ERROR_CORRUPTED_DATA) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_CORRUPTED_DATA); else if (r > 0) apdu.resplen += r; } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen); } static int iso7816_read_record(sc_card_t *card, unsigned int rec_nr, u8 *buf, size_t count, unsigned long flags) { sc_apdu_t apdu; u8 recvbuf[SC_MAX_APDU_BUFFER_SIZE]; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB2, rec_nr, 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; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.resplen == 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); memcpy(buf, recvbuf, apdu.resplen); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen); } static int iso7816_write_record(sc_card_t *card, unsigned int rec_nr, const u8 *buf, size_t count, unsigned long flags) { sc_apdu_t apdu; int r; if (count > 256) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Trying to send too many bytes"); return SC_ERROR_INVALID_ARGUMENTS; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD2, rec_nr, 0); apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3; if (flags & SC_RECORD_BY_REC_NR) apdu.p2 |= 0x04; apdu.lc = count; apdu.datalen = count; apdu.data = buf; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "Card returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, count); } static int iso7816_append_record(sc_card_t *card, const u8 *buf, size_t count, unsigned long flags) { sc_apdu_t apdu; int r; if (count > 256) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Trying to send too many bytes"); return SC_ERROR_INVALID_ARGUMENTS; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE2, 0, 0); apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3; apdu.lc = count; apdu.datalen = count; apdu.data = buf; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "Card returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, count); } static int iso7816_update_record(sc_card_t *card, unsigned int rec_nr, const u8 *buf, size_t count, unsigned long flags) { sc_apdu_t apdu; int r; if (count > 256) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Trying to send too many bytes"); return SC_ERROR_INVALID_ARGUMENTS; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDC, rec_nr, 0); apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3; if (flags & SC_RECORD_BY_REC_NR) apdu.p2 |= 0x04; apdu.lc = count; apdu.datalen = count; apdu.data = buf; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "Card returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, count); } static int iso7816_write_binary(sc_card_t *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) { sc_apdu_t apdu; int r; assert(count <= (card->max_send_size > 0 ? card->max_send_size : 255)); if (idx > 0x7fff) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid EF offset: 0x%X > 0x7FFF", idx); return SC_ERROR_OFFSET_TOO_LARGE; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD0, (idx >> 8) & 0x7F, idx & 0xFF); apdu.lc = count; apdu.datalen = count; apdu.data = buf; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "Card returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, count); } static int iso7816_update_binary(sc_card_t *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) { sc_apdu_t apdu; int r; assert(count <= (card->max_send_size > 0 ? card->max_send_size : 255)); if (idx > 0x7fff) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid EF offset: 0x%X > 0x7FFF", idx); return SC_ERROR_OFFSET_TOO_LARGE; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD6, (idx >> 8) & 0x7F, idx & 0xFF); apdu.lc = count; apdu.datalen = count; apdu.data = buf; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "Card returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 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; size_t taglen, len = buflen; const unsigned char *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: %d", 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; 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: type = "unknown"; break; } sc_log(ctx, " type: %s", type); sc_log(ctx, " EF structure: %d", byte & 0x07); } } tag = sc_asn1_find_tag(ctx, p, len, 0x84, &taglen); if (tag != NULL && taglen > 0 && taglen <= 16) { char tbuf[128]; memcpy(file->name, tag, taglen); file->namelen = taglen; sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, file->name, file->namelen, tbuf, sizeof(tbuf)); sc_log(ctx, " File name: %s", tbuf); 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) sc_file_set_sec_attr(file, tag, taglen); 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 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, pathlen; struct sc_file *file = NULL; 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, 0); switch (in_path->type) { 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 */ 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 = card->max_recv_size > 0 ? card->max_recv_size : 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 (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); } if ((size_t)apdu.resp[1] + 2 <= apdu.resplen) card->ops->process_fci(card, file, apdu.resp+2, apdu.resp[1]); *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(sc_card_t *card, u8 *rnd, size_t len) { int r; sc_apdu_t apdu; u8 buf[10]; if (!rnd && len) return SC_ERROR_INVALID_ARGUMENTS; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x84, 0x00, 0x00); apdu.le = 8; apdu.resp = buf; apdu.resplen = 8; /* include SW's */ while (len > 0) { size_t n = len > 8 ? 8 : len; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.resplen != 8) return sc_check_sw(card, apdu.sw1, apdu.sw2); memcpy(rnd, apdu.resp, n); len -= n; rnd += n; } return 0; } static int iso7816_construct_fci(sc_card_t *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; 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->prop_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(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; if (card->ops->construct_fci == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); r = card->ops->construct_fci(card, file, sbuf, &len); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } static int iso7816_get_response(sc_card_t *card, size_t *count, u8 *buf) { sc_apdu_t apdu; int r; size_t rlen; /* request at most max_recv_size bytes */ if (card->max_recv_size > 0 && *count > card->max_recv_size) rlen = card->max_recv_size; else rlen = *count; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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)); *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(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 != 0 && path->len != 2)) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File type has to be SC_PATH_TYPE_FILE_ID"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } static int iso7816_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, locked = 0; 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_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; 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_ASYMMETRIC) *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; } r = p - sbuf; apdu.lc = r; apdu.datalen = r; apdu.data = sbuf; if (se_num > 0) { r = sc_lock(card); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed"); locked = 1; } if (apdu.datalen != 0) { r = sc_transmit_apdu(card, &apdu); if (r) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s: APDU transmit failed", sc_strerror(r)); goto err; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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(sc_card_t *card, int se_num) { sc_apdu_t apdu; int r; assert(card != NULL); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 0xF3, se_num); r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } static int iso7816_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]; assert(card != NULL && data != NULL && out != NULL); if (datalen > 255) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); /* INS: 0x2A PERFORM SECURITY OPERATION * P1: 0x9E Resp: Digital Signature * P2: 0x9A Cmd: Input for Digital Signature */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); /* FIXME */ apdu.le = 256; memcpy(sbuf, data, datalen); apdu.data = sbuf; apdu.lc = datalen; apdu.datalen = datalen; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } static int iso7816_decipher(sc_card_t *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen) { int r; sc_apdu_t apdu; u8 *sbuf = NULL; assert(card != NULL && crgram != NULL && out != NULL); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); 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; /* 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; /* Use APDU chaining with 2048bit RSA keys if the card does not do extended APDU-s */ if ((crgram_len+1 > 255) && !(card->caps & SC_CARD_CAP_APDU_EXT)) apdu.flags |= SC_APDU_FLAGS_CHAINING; 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; r = sc_transmit_apdu(card, &apdu); sc_mem_clear(sbuf, crgram_len + 1); free(sbuf); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen); else SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } static int iso7816_build_pin_apdu(sc_card_t *card, sc_apdu_t *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; switch (data->pin_type) { case SC_AC_CHV: 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; 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) { 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; len += r; break; case SC_PIN_CMD_UNBLOCK: ins = 0x2C; if (data->pin1.len != 0 || use_pin_pad) { 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; } break; default: return SC_ERROR_NOT_SUPPORTED; } sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, ins, p1, data->pin_reference); apdu->lc = len; apdu->datalen = len; apdu->data = buf; apdu->resplen = 0; return 0; } static int iso7816_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { sc_apdu_t local_apdu, *apdu; int r; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; if (tries_left) *tries_left = -1; /* 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)) { /* 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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; SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu->sw1 == 0x63) { if ((apdu->sw2 & 0xF0) == 0xC0 && tries_left != NULL) *tries_left = apdu->sw2 & 0x0F; return SC_ERROR_PIN_CODE_INCORRECT; } return sc_check_sw(card, apdu->sw1, apdu->sw2); } static int no_match(sc_card_t *card) { return 0; } static struct sc_card_operations iso_ops = { no_match, NULL, /* 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, NULL, /* get_data */ NULL, /* put_data */ NULL, /* delete_record */ NULL /* read_public_key */ }; 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; } opensc-0.13.0/src/libopensc/pkcs15.c0000644000015201777760000022655412057406034014062 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "cardctl.h" #include "internal.h" #include "pkcs15.h" #include "asn1.h" #ifdef ENABLE_OPENSSL #include #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_NULL, SC_ASN1_TAG_NULL, 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 } }; /* * in src/libopensc/types.h SC_MAX_SUPPORTED_ALGORITHMS defined as 8 */ 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 }, { 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 *p15card); static void sc_pkcs15_remove_dfs(struct sc_pkcs15_card *p15card); static void sc_pkcs15_remove_objects(struct sc_pkcs15_card *p15card); 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); u8 label[SC_PKCS15_MAX_LABEL_SIZE]; size_t label_len = sizeof(label); 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]; size_t reference_len = sizeof(ti->supported_algos[0].reference); size_t mechanism_len = sizeof(ti->supported_algos[0].mechanism); 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)); 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, NULL, NULL, 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); LOG_TEST_RET(ctx, r, "ASN.1 parsing of EF(TokenInfo) failed"); if (asn1_toki_attrs[1].flags & SC_ASN1_PRESENT) { ti->serial_number = malloc(serial_len * 2 + 1); if (ti->serial_number == NULL) return SC_ERROR_OUT_OF_MEMORY; ti->serial_number[0] = 0; for (ii = 0; ii < serial_len; ii++) { char byte[3]; sprintf(byte, "%02X", serial[ii]); strcat(ti->serial_number, byte); } } 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]; size_t reference_len = sizeof(ti->supported_algos[0].reference); size_t mechanism_len = sizeof(ti->supported_algos[0].mechanism); 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]; 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_supported_algorithms, asn1_supported_algorithms); for (ii=0; iisupported_algos[ii].reference; ii++) { sc_format_asn1_entry(asn1_algo_infos[ii] + 0, &ti->supported_algos[ii].reference, &reference_len, 1); sc_format_asn1_entry(asn1_algo_infos[ii] + 1, &ti->supported_algos[ii].mechanism, &mechanism_len, 1); sc_format_asn1_entry(asn1_algo_infos[ii] + 2, NULL, NULL, 0); sc_format_asn1_entry(asn1_algo_infos[ii] + 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) { u8 serial[128]; 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) { if (p15card->file_odf != NULL) { sc_file_free(p15card->file_odf); p15card->file_odf = NULL; } if (p15card->file_tokeninfo != NULL) { sc_file_free(p15card->file_tokeninfo); p15card->file_tokeninfo = NULL; } } } static void fix_starcos_pkcs15_card(struct sc_pkcs15_card *p15card) { struct sc_context *ctx = p15card->card->ctx; /* set special flags based on card meta data */ if (strcmp(p15card->card->driver->short_name,"cardos") == 0) { /* D-Trust cards (D-TRUST, D-SIGN) */ if (strstr(p15card->tokeninfo->label,"D-TRUST") != NULL || strstr(p15card->tokeninfo->label,"D-SIGN") != NULL) { /* D-TRUST Card 2.0 2cc (standard cards, which always add * SHA1 prefix itself */ if (strstr(p15card->tokeninfo->label, "2cc") != NULL) { p15card->card->caps |= SC_CARD_CAP_ONLY_RAW_HASH_STRIPPED; sc_log(ctx, "D-TRUST 2cc card detected, only SHA1 works with this card"); /* XXX: add detection when other hash than SHA1 is used with * such a card, as this produces invalid signatures. */ } /* D-SIGN multicard 2.0 2ca (cards working with all types of hashes * and no addition of prefix) */ else if (strstr(p15card->tokeninfo->label, "2ca") != NULL) { p15card->card->caps |= SC_CARD_CAP_ONLY_RAW_HASH; sc_log(ctx, "D-TRUST 2ca card detected"); } /* XXX: probably there are more D-Trust card in the wild, * which also need these flags to produce valid signatures */ } } } static int parse_ddo(struct sc_pkcs15_card *p15card, const u8 * buf, size_t buflen) { 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) { 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) { 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) { 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: if (p15card->file_odf != NULL) { sc_file_free(p15card->file_odf); p15card->file_odf = NULL; } if (p15card->file_tokeninfo != NULL) { sc_file_free(p15card->file_tokeninfo); p15card->file_tokeninfo = NULL; } if (p15card->file_unusedspace != NULL) { sc_file_free(p15card->file_unusedspace); p15card->file_unusedspace = NULL; } LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } #if 0 static int encode_ddo(struct sc_pkcs15_card *p15card, u8 **buf, size_t *buflen) { struct sc_asn1_entry asn1_ddo[5]; int r; size_t label_len; sc_copy_asn1_entry(c_asn1_ddo, asn1_ddo); sc_format_asn1_entry(asn1_ddo + 1, &card->file_odf.path, NULL, 0); sc_format_asn1_entry(asn1_ddo + 2, &card->file_tokeninfo.path, NULL, 0); r = sc_asn1_encode(ctx, asn1_dir, buf, buflen); if (r) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "sc_asn1_encode() failed: %s", sc_strerror(r)); return r; } return 0; } #endif 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]; size_t lupdate_len = sizeof(last_update) - 1; int r, content_len; 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; content = calloc(file->size, 1); if (!content) return NULL; r = sc_read_binary(p15card->card, 0, content, file->size, 0); if (r < 0) return NULL; content_len = r; sc_file_free(file); sc_copy_asn1_entry(c_asn1_last_update, asn1_last_update); sc_format_asn1_entry(asn1_last_update + 0, last_update, &lupdate_len, 0); r = sc_asn1_decode(ctx, asn1_last_update, content, content_len, NULL, NULL); free(content); if (r < 0) return NULL; 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 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_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(&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(sc_context_t *ctx, struct sc_pkcs15_card *p15card, u8 **buf, size_t *buflen) { 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_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; } sc_init_oid(&p15card->tokeninfo->profile_indication.oid); p15card->magic = SC_PKCS15_CARD_MAGIC; return p15card; } void sc_pkcs15_free_tokeninfo(struct sc_pkcs15_card *p15card) { if (!p15card || !p15card->tokeninfo) return; if (p15card->tokeninfo->label != NULL) free(p15card->tokeninfo->label); if (p15card->tokeninfo->serial_number != NULL) free(p15card->tokeninfo->serial_number); if (p15card->tokeninfo->manufacturer_id != NULL) free(p15card->tokeninfo->manufacturer_id); if (p15card->tokeninfo->last_update.gtime != NULL) free(p15card->tokeninfo->last_update.gtime); if (p15card->tokeninfo->preferred_language != NULL) free(p15card->tokeninfo->preferred_language); if (p15card->tokeninfo->profile_indication.name != NULL) free(p15card->tokeninfo->profile_indication.name); if (p15card->tokeninfo->seInfo != NULL) { unsigned i; for (i = 0; i < p15card->tokeninfo->num_seInfo; i++) free(p15card->tokeninfo->seInfo[i]); free(p15card->tokeninfo->seInfo); } free(p15card->tokeninfo); p15card->tokeninfo = NULL; } void sc_pkcs15_free_app(struct sc_pkcs15_card *p15card) { if (!p15card || !p15card->app) return; if (p15card->app->label) free(p15card->app->label); if (p15card->app->ddo.value) free(p15card->app->ddo.value); free(p15card->app); p15card->app = NULL; } void sc_pkcs15_card_free(struct sc_pkcs15_card *p15card) { if (p15card == NULL) return; assert(p15card->magic == SC_PKCS15_CARD_MAGIC); if (p15card->ops.clear) p15card->ops.clear(p15card); sc_pkcs15_remove_objects(p15card); sc_pkcs15_remove_dfs(p15card); sc_pkcs15_free_unusedspace(p15card); p15card->unusedspace_read = 0; if (p15card->file_app != NULL) sc_file_free(p15card->file_app); if (p15card->file_tokeninfo != NULL) sc_file_free(p15card->file_tokeninfo); if (p15card->file_odf != NULL) sc_file_free(p15card->file_odf); if (p15card->file_unusedspace != NULL) sc_file_free(p15card->file_unusedspace); p15card->magic = 0; sc_pkcs15_free_tokeninfo(p15card); sc_pkcs15_free_app(p15card); free(p15card); } void sc_pkcs15_card_clear(sc_pkcs15_card_t *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; if (p15card->file_app != NULL) { sc_file_free(p15card->file_app); p15card->file_app = NULL; } if (p15card->file_tokeninfo != NULL) { sc_file_free(p15card->file_tokeninfo); p15card->file_tokeninfo = NULL; } if (p15card->file_odf != NULL) { sc_file_free(p15card->file_odf); p15card->file_odf = NULL; } if (p15card->file_unusedspace != NULL) { sc_file_free(p15card->file_unusedspace); p15card->file_unusedspace = NULL; } if (p15card->tokeninfo->label != NULL) { free(p15card->tokeninfo->label); p15card->tokeninfo->label = NULL; } if (p15card->tokeninfo->serial_number != NULL) { free(p15card->tokeninfo->serial_number); p15card->tokeninfo->serial_number = NULL; } if (p15card->tokeninfo->manufacturer_id != NULL) { free(p15card->tokeninfo->manufacturer_id); p15card->tokeninfo->manufacturer_id = NULL; } if (p15card->tokeninfo->last_update.gtime != NULL) { free(p15card->tokeninfo->last_update.gtime); p15card->tokeninfo->last_update.gtime = NULL; } if (p15card->tokeninfo->preferred_language != NULL) { free(p15card->tokeninfo->preferred_language); p15card->tokeninfo->preferred_language = NULL; } if (p15card->tokeninfo->profile_indication.name != NULL) { free(p15card->tokeninfo->profile_indication.name); p15card->tokeninfo->profile_indication.name = NULL; } 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; } } 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) return NULL; } else out->label = NULL; out->ddo.value = malloc(info->ddo.len); if (!out->ddo.value) return NULL; memcpy(out->ddo.value, info->ddo.value, 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; break; } } free(blocks); } } return out; } static int sc_pkcs15_bind_internal(sc_pkcs15_card_t *p15card, struct sc_aid *aid) { sc_path_t tmppath; sc_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; sc_pkcs15_tokeninfo_t tokeninfo; sc_pkcs15_df_t *df; const sc_app_info_t *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)); } 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)); 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(aid:'%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) 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; } buf = malloc(len); if(buf == NULL) return SC_ERROR_OUT_OF_MEMORY; err = sc_read_binary(card, 0, buf, len, 0); if (err < 0) goto end; if (err < 2) { err = SC_ERROR_PKCS15_APP_NOT_FOUND; goto end; } len = err; 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) goto end; if ((len = p15card->file_tokeninfo->size) == 0) { sc_log(ctx, "EF(TokenInfo) is empty"); goto end; } buf = malloc(len); if(buf == NULL) return SC_ERROR_OUT_OF_MEMORY; err = sc_read_binary(card, 0, buf, len, 0); if (err < 0) goto end; if (err <= 2) { err = SC_ERROR_PKCS15_APP_NOT_FOUND; goto end; } memset(&tokeninfo, 0, sizeof(tokeninfo)); err = sc_pkcs15_parse_tokeninfo(ctx, &tokeninfo, buf, (size_t)err); if (err != SC_SUCCESS) goto end; *(p15card->tokeninfo) = tokeninfo; if (!p15card->tokeninfo->serial_number && card->serialnr.len) { char *serial = calloc(1, card->serialnr.len*2 + 1); size_t ii; 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); return err; } return SC_SUCCESS; } int sc_pkcs15_bind(sc_card_t *card, struct sc_aid *aid, struct sc_pkcs15_card **p15card_out) { struct sc_pkcs15_card *p15card = NULL; sc_context_t *ctx = card->ctx; scconf_block *conf_block = NULL; int r, emu_first, enable_emu; LOG_FUNC_CALLED(ctx); sc_log(ctx, "application(aid:'%s')", aid ? sc_dump_hex(aid->value, aid->len) : "empty"); assert(p15card_out != NULL); p15card = sc_pkcs15_card_new(); if (p15card == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); p15card->card = card; p15card->opts.use_file_cache = 0; p15card->opts.use_pin_cache = 1; p15card->opts.pin_cache_counter = 10; p15card->opts.pin_cache_ignore_user_consent = 0; conf_block = sc_get_conf_block(ctx, "framework", "pkcs15", 1); if (conf_block) { p15card->opts.use_file_cache = scconf_get_bool(conf_block, "use_file_caching", p15card->opts.use_file_cache); 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); } sc_log(ctx, "PKCS#15 options: use_file_cache=%d use_pin_cache=%d pin_cache_counter=%d pin_cache_ignore_user_consent=%d", p15card->opts.use_file_cache, p15card->opts.use_pin_cache, p15card->opts.pin_cache_counter, p15card->opts.pin_cache_ignore_user_consent); 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) { 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); 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); if (r < 0) goto error; } } else { r = sc_pkcs15_bind_internal(p15card, aid); if (r < 0) goto error; } done: fix_starcos_pkcs15_card(p15card); *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) { assert(p15card != NULL && p15card->magic == SC_PKCS15_CARD_MAGIC); 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(sc_pkcs15_card_t *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) { sc_pkcs15_object_t *obj; sc_pkcs15_df_t *df; unsigned int df_mask = 0; size_t match_count = 0; int r = 0; 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. */ r = sc_pkcs15_parse_df(p15card, df); } /* 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 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 sc_pkcs15_id_t *id) { void *data = obj->data; switch (obj->type) { case SC_PKCS15_TYPE_CERT_X509: return sc_pkcs15_compare_id(&((struct sc_pkcs15_cert_info *) data)->id, id); case SC_PKCS15_TYPE_PRKEY_RSA: case SC_PKCS15_TYPE_PRKEY_DSA: case SC_PKCS15_TYPE_PRKEY_GOSTR3410: case SC_PKCS15_TYPE_PRKEY_EC: return sc_pkcs15_compare_id(&((struct sc_pkcs15_prkey_info *) data)->id, id); case SC_PKCS15_TYPE_PUBKEY_RSA: case SC_PKCS15_TYPE_PUBKEY_DSA: case SC_PKCS15_TYPE_PUBKEY_GOSTR3410: case SC_PKCS15_TYPE_PUBKEY_EC: return sc_pkcs15_compare_id(&((struct sc_pkcs15_pubkey_info *) data)->id, id); case SC_PKCS15_TYPE_SKEY_DES: case SC_PKCS15_TYPE_SKEY_2DES: case SC_PKCS15_TYPE_SKEY_3DES: return sc_pkcs15_compare_id(&((struct sc_pkcs15_skey_info *) data)->id, id); case SC_PKCS15_TYPE_AUTH_PIN: case SC_PKCS15_TYPE_AUTH_BIO: case SC_PKCS15_TYPE_AUTH_AUTHKEY: 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_DATA_OBJECT) return sc_compare_oid(&((struct sc_pkcs15_data_info *) obj->data)->app_oid, app_oid); return 0; } static int compare_obj_usage(sc_pkcs15_object_t *obj, unsigned int mask, unsigned int value) { void *data = obj->data; unsigned int usage; switch (obj->type) { case SC_PKCS15_TYPE_PRKEY_RSA: case SC_PKCS15_TYPE_PRKEY_DSA: case SC_PKCS15_TYPE_PRKEY_GOSTR3410: case SC_PKCS15_TYPE_PRKEY_EC: usage = ((struct sc_pkcs15_prkey_info *) data)->usage; break; case SC_PKCS15_TYPE_PUBKEY_RSA: case SC_PKCS15_TYPE_PUBKEY_DSA: case SC_PKCS15_TYPE_PUBKEY_GOSTR3410: case SC_PKCS15_TYPE_PUBKEY_EC: usage = ((struct sc_pkcs15_pubkey_info *) data)->usage; break; default: return 0; } return (usage & mask & value) != 0; } static int compare_obj_flags(sc_pkcs15_object_t *obj, unsigned int mask, unsigned int value) { struct sc_pkcs15_auth_info *auth_info; unsigned int flags; switch (obj->type) { case SC_PKCS15_TYPE_AUTH_PIN: 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(sc_pkcs15_object_t *obj, int value) { struct sc_pkcs15_auth_info *auth_info; void *data = obj->data; int reference; switch (obj->type) { case SC_PKCS15_TYPE_AUTH_PIN: 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_RSA: case SC_PKCS15_TYPE_PRKEY_DSA: case SC_PKCS15_TYPE_PRKEY_GOSTR3410: case SC_PKCS15_TYPE_PRKEY_EC: reference = ((struct sc_pkcs15_prkey_info *) data)->key_reference; break; default: return 0; } return reference == value; } static int compare_obj_path(sc_pkcs15_object_t *obj, const sc_path_t *path) { void *data = obj->data; switch (obj->type) { case SC_PKCS15_TYPE_CERT_X509: return sc_compare_path(&((struct sc_pkcs15_cert_info *) data)->path, path); case SC_PKCS15_TYPE_PRKEY_RSA: case SC_PKCS15_TYPE_PRKEY_DSA: case SC_PKCS15_TYPE_PRKEY_GOSTR3410: case SC_PKCS15_TYPE_PRKEY_EC: return sc_compare_path(&((struct sc_pkcs15_prkey_info *) data)->path, path); case SC_PKCS15_TYPE_PUBKEY_RSA: case SC_PKCS15_TYPE_PUBKEY_DSA: case SC_PKCS15_TYPE_PUBKEY_GOSTR3410: case SC_PKCS15_TYPE_PUBKEY_EC: return sc_compare_path(&((struct sc_pkcs15_pubkey_info *) data)->path, path); case SC_PKCS15_TYPE_AUTH_PIN: 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(sc_pkcs15_object_t *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 !strcmp(cinfo->app_label, app_label) && !strcmp(obj->label, 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(sc_pkcs15_card_t *p15card, sc_pkcs15_search_key_t *sk, sc_pkcs15_object_t **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(sc_pkcs15_card_t *p15card, unsigned int type, const sc_pkcs15_id_t *id, sc_pkcs15_object_t **out) { sc_pkcs15_search_key_t 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 sc_path_t *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) { sc_context_t *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) { sc_pkcs15_search_key_t 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) { sc_pkcs15_search_key_t 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(sc_pkcs15_card_t *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_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: 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_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); } if (obj->guid != NULL) free(obj->guid); 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(sc_context_t *ctx, struct sc_pkcs15_card *p15card, struct sc_pkcs15_df *df, u8 **buf_out, size_t *bufsize_out) { u8 *buf = NULL, *tmp = NULL; size_t bufsize = 0, tmpsize; const struct sc_pkcs15_object *obj; int (* func)(sc_context_t *, const struct sc_pkcs15_object *nobj, u8 **nbuf, size_t *nbufsize) = NULL; int r; assert(p15card != NULL && p15card->magic == SC_PKCS15_CARD_MAGIC); 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_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; } buf = (u8 *) realloc(buf, bufsize + tmpsize); memcpy(buf + bufsize, tmp, tmpsize); free(tmp); 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) { sc_context_t *ctx = p15card->card->ctx; u8 *buf; const u8 *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 (p15card->ops.parse_df) { r = p15card->ops.parse_df(p15card, df); LOG_FUNC_RETURN(ctx, r); } 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); 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; } }; 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 sc_path_t *path, const sc_pkcs15_id_t *auth_id) { struct sc_context *ctx = p15card->card->ctx; sc_pkcs15_unusedspace_t *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, sc_pkcs15_unusedspace_t *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(sc_context_t *ctx, struct sc_pkcs15_card *p15card, u8 **buf, size_t *buflen) { sc_path_t 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; sc_pkcs15_unusedspace_t *unusedspace; 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); 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 u8 * buf, size_t buflen, struct sc_pkcs15_card *p15card) { const u8 *p = buf; size_t left = buflen; int r; sc_path_t path, dummy_path; sc_pkcs15_id_t 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_path("3F00", &dummy_path); dummy_path.index = dummy_path.count = 0; sc_format_asn1_entry(asn1_unusedspace, asn1_unusedspace_values, NULL, 1); sc_format_asn1_entry(asn1_unusedspace_values, &path, NULL, 1); sc_format_asn1_entry(asn1_unusedspace_values+1, &auth_id, NULL, 0); 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) { 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; } int sc_pkcs15_read_file(struct sc_pkcs15_card *p15card, const sc_path_t *in_path, u8 **buf, size_t *buflen) { struct sc_context *ctx = p15card->card->ctx; sc_file_t *file = NULL; u8 *data = NULL; size_t len = 0, offset = 0; int r; assert(p15card != NULL && in_path != NULL && buf != NULL); 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) { r = sc_pkcs15_read_cached_file(p15card, in_path, &data, &len); } if (r) { r = sc_lock(p15card->card); LOG_TEST_RET(ctx, r, "sc_lock() failed"); r = sc_select_file(p15card->card, in_path, &file); if (r) goto fail_unlock; /* Handle the case where the ASN.1 Path object specified * index and length values */ if (in_path->count < 0) { len = file->size; 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; } } 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) { int i; size_t l, record_len; unsigned char *head; head = data; for (i=1; ; i++) { l = len - (head - data); if (l > 256) { l = 256; } r = sc_read_record(p15card->card, i, head, l, SC_RECORD_BY_REC_NR); if (r == SC_ERROR_RECORD_NOT_FOUND) break; if (r < 0) { free(data); 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 { r = sc_read_binary(p15card->card, offset, data, len, 0); if (r < 0) { free(data); goto fail_unlock; } /* sc_read_binary may return less than requested */ len = r; } sc_unlock(p15card->card); sc_file_free(file); } *buf = data; *buflen = len; LOG_FUNC_RETURN(ctx, SC_SUCCESS); fail_unlock: if (file) sc_file_free(file); sc_unlock(p15card->card); LOG_FUNC_RETURN(ctx, r); } int sc_pkcs15_compare_id(const struct sc_pkcs15_id *id1, const struct sc_pkcs15_id *id2) { assert(id1 != NULL && id2 != NULL); 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 = sizeof(id->value); if (sc_hex_to_bin(str, id->value, &len) >= 0) 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 sc_path_t *parent, sc_path_t *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) { sc_mem_clear(obj->content.value, obj->content.len); 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. */ tmp_buf = (unsigned char *)sc_mem_alloc_secure(ctx, 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; } 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) { case SC_PKCS15_TYPE_PRKEY_RSA: algo_refs = ((struct sc_pkcs15_prkey_info *)obj->data)->algo_refs; break; case SC_PKCS15_TYPE_PUBKEY_RSA: algo_refs = ((struct sc_pkcs15_pubkey_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) { case SC_PKCS15_TYPE_CERT_X509: *out = ((struct sc_pkcs15_cert_info *) obj->data)->id; break; case SC_PKCS15_TYPE_PRKEY_RSA: case SC_PKCS15_TYPE_PRKEY_DSA: case SC_PKCS15_TYPE_PRKEY_GOSTR3410: case SC_PKCS15_TYPE_PRKEY_EC: *out = ((struct sc_pkcs15_prkey_info *) obj->data)->id; break; case SC_PKCS15_TYPE_PUBKEY_RSA: case SC_PKCS15_TYPE_PUBKEY_DSA: case SC_PKCS15_TYPE_PUBKEY_GOSTR3410: case SC_PKCS15_TYPE_PUBKEY_EC: *out = ((struct sc_pkcs15_pubkey_info *) obj->data)->id; break; case SC_PKCS15_TYPE_AUTH_PIN: *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 . */ static 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_guid(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_object *obj, unsigned flags, 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]; int rv; if (p15card->ops.get_guid) return p15card->ops.get_guid(p15card, obj, out, out_size); if (obj->guid) { if (out_size < strlen(obj->guid)) return SC_ERROR_BUFFER_TOO_SMALL; memset(out, 0, out_size); if (out_size > strlen(obj->guid)) out_size = strlen(obj->guid); memcpy(out, obj->guid, out_size); return SC_SUCCESS; } rv = sc_pkcs15_get_object_id(obj, &id); if (rv) return rv; rv = sc_card_ctl(p15card->card, SC_CARDCTL_GET_SERIALNR, &serialnr); if (rv) return rv; memset(guid_bin, 0, sizeof(guid_bin)); memcpy(guid_bin, id.value, id.len); memcpy(guid_bin + id.len, serialnr.value, 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, id.len + serialnr.len, guid_bin); id.len = SHA_DIGEST_LENGTH; serialnr.len = 0; #endif return sc_pkcs15_serialize_guid(guid_bin, id.len + serialnr.len, flags, out, out_size); } 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.13.0/src/libopensc/pkcs15-gemsafeV1.c0000644000015201777760000003617012057406034015667 00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Initially written by David Mattes (david.mattes@boeing.com) */ #include "config.h" #include #include #include #include "internal.h" #include "pkcs15.h" #define MANU_ID "Gemplus" #define APPLET_NAME "GemSAFE V1" #define DRIVER_SERIAL_NUMBER "v0.9" int sc_pkcs15emu_gemsafeV1_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); 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 { const char *label; int authority; const char *path; const char *id; int obj_flags; } cdata; const cdata gemsafe_cert[] = { {"DS certificate", 0, "3F0016000004","45", SC_PKCS15_CO_FLAG_MODIFIABLE}, {NULL, 0, NULL, 0, 0} }; typedef struct pdata_st { const char *id; const char *label; const char *path; int ref; int type; unsigned int maxlen; unsigned int minlen; int flags; int tries_left; const char pad_char; int obj_flags; } pindata; const pindata gemsafe_pin[] = { { "01", "DS pin", NULL, 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 }, { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0 } }; 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 const prdata gemsafe_prkeys[] = { { "45", "DS key", 1024, USAGE_AUT, NULL, 0x03, "01", SC_PKCS15_CO_FLAG_PRIVATE}, { NULL, NULL, 0, 0, NULL, 0, NULL, 0} }; static int gemsafe_get_cert_len(sc_card_t *card, sc_path_t *path, int *key_ref) { const char *fn_name = "gemsafe_get_cert_len"; int r; int ind; u8 ibuf[248]; struct sc_file *file; size_t objlen, certlen; unsigned int block=0; int found = 0; unsigned int offset=0, index_local, i=0; r = sc_select_file(card, path, &file); if (r < 0) return 0; /* Apparently, the Applet max read "quanta" is 248 bytes */ /* Initial read */ r = sc_read_binary(card, offset, ibuf, 248, 0); if (r < 0) return 0; /* 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s: Certificate object is of size: %d\n", fn_name, objlen); if (objlen < 1 || objlen > 10240) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s: Invalid object size: %d\n", fn_name, objlen); return 0; } /* * We need to find the private key associated with the cert * It looks like the first thing in the block is a table of * which keys are allocated. * We will look for the first allocated key, and save the * key_ref. The table is small and is in the first 248 bytes. * If for some reason this is not true, we can still override * the key_ref in the opensc.conf with flag = n. */ ind = 2; /* skip length */ while (ibuf[ind] == 0x01) { if (ibuf[ind+1] == 0xFE) { *key_ref = ibuf[ind+4]; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Using key_ref %d found at offset %d\n", *key_ref, ind); break; } ind = ind + 8; } /* Using (block+1) in while loop avoids using final cert object data block */ while (!found && ( (block+1) * 248 < objlen) ) { /* Check current buffer */ for (i = 0; i < 247; i++) { if (ibuf[i] == 0x30 && ibuf[i+1] == 0x82) { found = 1; break; } } /* Grab another buffer */ if (!found) { block++; offset = block*248; r = sc_read_binary(card, offset, ibuf, 248, 0); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s: Could not read cert object\n", fn_name); return 0; } } } index_local = block*248 + i; /* DER Cert len is encoded this way */ certlen = ((((size_t) ibuf[i+2]) << 8) | ibuf[i+3]) + 4; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s: certlen: %04X\n", fn_name, certlen); path->index = index_local; path->count = certlen; return 1; } 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) { const char *fn_name = "sc_pkcs15emu_gemsafe_init"; int r, i; int key_ref = 0x03; 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_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "%s: Setting pkcs15 parameters\n", fn_name); if (p15card->tokeninfo->label) free(p15card->tokeninfo->label); p15card->tokeninfo->label = malloc(strlen(APPLET_NAME) + 1); if (!p15card->tokeninfo->label) return SC_ERROR_INTERNAL; strcpy(p15card->tokeninfo->label, APPLET_NAME); if (p15card->tokeninfo->serial_number) free(p15card->tokeninfo->serial_number); p15card->tokeninfo->serial_number = malloc(strlen(DRIVER_SERIAL_NUMBER) + 1); if (!p15card->tokeninfo->serial_number) return SC_ERROR_INTERNAL; strcpy(p15card->tokeninfo->serial_number, DRIVER_SERIAL_NUMBER); /* the GemSAFE applet version number */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0xdf, 0x03); apdu.cla = 0x80; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); /* Manual says Le=0x05, but should be 0x08 to return full version numer */ apdu.le = 0x08; apdu.lc = 0; apdu.datalen = 0; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return SC_ERROR_INTERNAL; if (r != SC_SUCCESS) return SC_ERROR_INTERNAL; /* the manufacturer ID, in this case GemPlus */ if (p15card->tokeninfo->manufacturer_id) free(p15card->tokeninfo->manufacturer_id); p15card->tokeninfo->manufacturer_id = malloc(strlen(MANU_ID) + 1); if (!p15card->tokeninfo->manufacturer_id) return SC_ERROR_INTERNAL; strcpy(p15card->tokeninfo->manufacturer_id, MANU_ID); /* set certs */ sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "%s: Setting certificate\n", fn_name); for (i = 0; gemsafe_cert[i].label; i++) { struct sc_pkcs15_id p15Id; sc_format_path(gemsafe_cert[i].path, &path); if (!gemsafe_get_cert_len(card, &path, &key_ref)) /* skip errors */ continue; sc_pkcs15_format_id(gemsafe_cert[i].id, &p15Id); 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_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "%s: Setting PIN\n", fn_name); for (i = 0; gemsafe_pin[i].label; i++) { struct sc_pkcs15_id p15Id; sc_pkcs15_format_id(gemsafe_pin[i].id, &p15Id); 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); } /* set private keys */ sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "%s: Setting private key\n", fn_name); for (i = 0; gemsafe_prkeys[i].label; i++) { struct sc_pkcs15_id p15Id, authId, *pauthId; 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; /* * 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_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Overriding key_ref with %d\n", key_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_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL,"%s: Selecting application DF\n", fn_name); sc_format_path("3F001600", &path); r = sc_select_file(card, &path, &file); if (r != SC_SUCCESS || !file) return SC_ERROR_INTERNAL; /* set the application DF */ if (p15card->file_app) free(p15card->file_app); p15card->file_app = file; return SC_SUCCESS; } int sc_pkcs15emu_gemsafeV1_init_ex( sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts) { if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) return sc_pkcs15emu_gemsafeV1_init(p15card); else { int r = gemsafe_detect_card(p15card); if (r) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_gemsafeV1_init(p15card); } } 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_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown PKCS15 object type %d\n", 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)); 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; 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) { /* const char *label = "Certificate"; */ sc_pkcs15_cert_info_t *info; info = calloc(1, sizeof(*info)); 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)); 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); } #if 0 static int sc_pkcs15emu_add_pubkey(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_pubkey_info_t *info; info = calloc(1, sizeof(*info)); info->id = *id; info->modulus_length = modulus_length; info->usage = usage; info->access_flags = SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE; info->key_reference = ref; if (path) info->path = *path; return sc_pkcs15emu_add_object(p15card, type, label, info, auth_id, obj_flags); } #endif /* SC_IMPLEMENT_DRIVER_VERSION("0.9.4") */ opensc-0.13.0/src/libopensc/libopensc.pc.in0000644000015201777760000000031212057406034015475 00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libopensc Description: libopensc Version: @VERSION@ Libs: -L${libdir} -lopensc -lscconf Cflags: -I${includedir} opensc-0.13.0/src/libopensc/card-gemsafeV1.c0000644000015201777760000004620012057406034015465 00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Initially written by David Mattes (david.mattes@boeing.com) */ /* Portuguese eID card support by Joao Poupino (joao.poupino@ist.utl.pt) */ #include "config.h" #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" static struct sc_card_operations gemsafe_ops; static struct sc_card_operations *iso_ops = NULL; static struct sc_card_driver gemsafe_drv = { "driver for the Gemplus GemSAFE V1 applet", "gemsafeV1", &gemsafe_ops, NULL, 0, NULL }; /* Known ATRs */ static 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}, {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_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[0] != NULL) conf_block = blocks[0]; free(blocks); } if (!conf_block) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "no card specific options configured, trying default AID\n"); return SC_ERROR_INTERNAL; } str_aid = scconf_get_str(conf_block, "aid", NULL); if (!str_aid) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "no aid configured, trying default AID\n"); return SC_ERROR_INTERNAL; } return sc_hex_to_bin(str_aid, aid, len); } static int gp_select_applet(sc_card_t *card, const u8 *aid, size_t aid_len) { int r; u8 buf[SC_MAX_APDU_BUFFER_SIZE]; struct sc_context *ctx = card->ctx; struct sc_apdu apdu; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xa4, 0x04, 0x00); apdu.lc = aid_len; apdu.data = aid; apdu.datalen = aid_len; apdu.resp = buf; apdu.le = 256; apdu.resplen = sizeof(buf); r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); return SC_SUCCESS; } static int gemsafe_match_card(sc_card_t *card) { int i; 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); } /* 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "applet selection failed\n"); return SC_ERROR_INTERNAL; } card->lock_count--; /* set the supported algorithm */ r = gemsafe_match_card(card); if (r > 0) { unsigned long flags; flags = SC_ALGORITHM_RSA_PAD_PKCS1; flags |= SC_ALGORITHM_RSA_PAD_ISO9796; flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; flags |= SC_ALGORITHM_RSA_HASH_NONE; _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->drv_data = exdata; return 0; } 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "unable to set ACL\n"); return SC_ERROR_INTERNAL; } } else sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_PAD_PKCS1) ret = card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID ? 0x02 : 0x12; 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) ret = card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID ? 0x02 : 0x12; } 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "unknown algorithm flags '%x'\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, len; struct sc_apdu apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_context_t *ctx = card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (data_len > 36) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "error: input data too long: %lu bytes\n", data_len); return SC_ERROR_INVALID_ARGUMENTS; } /* the Portuguese eID card requires a two-phase exchange */ if(card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID) { 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { if(card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID) { /* 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); r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if(apdu.sw1 != 0x90 || apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 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[SC_MAX_APDU_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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { int len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } static int 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_build_pin_apdu(struct sc_card *card, struct sc_apdu *apdu, struct sc_pin_cmd_data *data) { static u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; int r, len = 0, pad = 0, use_pin_pad = 0, ins, p1 = 0; switch (data->pin_type) { case SC_AC_CHV: 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; if ((r = sc_build_pin(sbuf, sizeof(sbuf), &data->pin1, pad)) < 0) return r; len = r; break; case SC_PIN_CMD_CHANGE: ins = 0x24; if (data->pin1.len != 0 || use_pin_pad) { if ((r = sc_build_pin(sbuf, sizeof(sbuf), &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(sbuf+len, sizeof(sbuf)-len, &data->pin2, pad)) < 0) return r; len += r; break; case SC_PIN_CMD_UNBLOCK: ins = 0x2C; if (data->pin1.len != 0 || use_pin_pad) { if ((r = sc_build_pin(sbuf, sizeof(sbuf), &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(sbuf+len, sizeof(sbuf)-len, &data->pin2, pad)) < 0) return r; len += r; } else { p1 |= 0x01; } break; default: return SC_ERROR_NOT_SUPPORTED; } sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, ins, p1, data->pin_reference); apdu->lc = len; apdu->datalen = len; apdu->data = sbuf; apdu->resplen = 0; return 0; } static int gemsafe_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { struct sc_apdu local_apdu, *apdu; int r; if (tries_left) *tries_left = -1; /* 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 = gemsafe_build_pin_apdu(card, &local_apdu, data); if (r < 0) return r; data->apdu = &local_apdu; } apdu = data->apdu; if (!(data->flags & SC_PIN_CMD_USE_PINPAD)) { /* Transmit the APDU to the card */ r = sc_transmit_apdu(card, apdu); /* Clear the buffer - it may contain pins */ memset((void *) apdu->data, 0, apdu->datalen); } else { /* Call the reader driver to collect * the PIN and pass on the APDU to the card */ if (data->pin1.offset == 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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; SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu->sw1 == 0x63) { if ((apdu->sw2 & 0xF0) == 0xC0 && tries_left != NULL) *tries_left = apdu->sw2 & 0x0F; return SC_ERROR_PIN_CODE_INCORRECT; } 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) 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 = gemsafe_pin_cmd; return &gemsafe_drv; } struct sc_card_driver *sc_get_gemsafeV1_driver(void) { return sc_get_driver(); } opensc-0.13.0/src/libopensc/pkcs15-piv.c0000644000015201777760000010772112057406034014650 00000000000000/* * 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 * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include "internal.h" #include "cardctl.h" #include "asn1.h" #include "pkcs15.h" #define MANU_ID "piv_II " int sc_pkcs15emu_piv_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); 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; int authority; const char *path; int obj_flags; } cdata; typedef struct pdata_st { const char *id; const char *label; const char *path; int ref; int type; unsigned int maxlen; unsigned int minlen; unsigned int storedlen; int flags; int tries_left; const unsigned char pad_char; int obj_flags; } 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; int key_alg; unsigned int pubkey_len; 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 sereal 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, 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; int r, i; 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; 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 uniquness as possible. * FASC-N is stored like a ISO 7811 Magnetic Strip Card * Using the ANSI/ISO BCD Data Format * 4 data bit + 1 parity bit (odd) least significant bit first. * It starts with the Start Sentinel 0x0b ";" * Fields are seperated by 0x0d "=" * 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_GENERIC || card->type >= SC_CARD_TYPE_PIV_II_GENERIC+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 */ static const objdata objects[] = { {"1", "Card Capability Container", "2.16.840.1.101.3.7.1.219.0", NULL, "DB00", 0}, {"2", "Card Holder Unique Identifier", "2.16.840.1.101.3.7.2.48.0", NULL, "3000", 0}, {"3", "Unsigned Card Holder Unique Identifier", "2.16.840.1.101.3.7.2.48.2", NULL, "3010", 0}, {"4", "X.509 Certificate for PIV Authentication", "2.16.840.1.101.3.7.2.1.1", NULL, "0101", 0}, {"5", "Cardholder Fingerprints", "2.16.840.1.101.3.7.2.96.16", "1", "6010", SC_PKCS15_CO_FLAG_PRIVATE}, {"6", "Printed Information", "2.16.840.1.101.3.7.2.48.1", "1", "3001", SC_PKCS15_CO_FLAG_PRIVATE}, {"7", "Cardholder Facial Image", "2.16.840.1.101.3.7.2.96.48", "1", "6030", SC_PKCS15_CO_FLAG_PRIVATE}, {"8", "X.509 Certificate for Digital Signature", "2.16.840.1.101.3.7.2.1.0", NULL, "0100", 0}, {"9", "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}, {NULL, NULL, NULL, NULL, NULL, 0} }; /* * NIST 800-73-1 lifted the restriction on * requering pin protected certs. Thus the default is to * not require this. */ /* certs will be pulled out from the cert objects */ /* the number of cert, pubkey and prkey triplets */ #define PIV_NUM_CERTS_AND_KEYS 24 static const cdata certs[PIV_NUM_CERTS_AND_KEYS] = { {"1", "Certificate for PIV Authentication", 0, "0101cece", 0}, {"2", "Certificate for Digital Signature", 0, "0100cece", 0}, {"3", "Certificate for Key Management", 0, "0102cece", 0}, {"4", "Certificate for Card Authentication", 0, "0500cece", 0}, {"5", "Retired Certificate for Key Management 1", 0, "1001cece", 0}, {"6", "Retired Certificate for Key Management 2", 0, "1002cece", 0}, {"7", "Retired Certificate for Key Management 3", 0, "1003cece", 0}, {"8", "Retired Certificate for Key Management 4", 0, "1004cece", 0}, {"9", "Retired Certificate for Key Management 5", 0, "1005cece", 0}, {"10", "Retired Certificate for Key Management 6", 0, "1006cece", 0}, {"11", "Retired Certificate for Key Management 7", 0, "1007cece", 0}, {"12", "Retired Certificate for Key Management 8", 0, "1008cece", 0}, {"13", "Retired Certificate for Key Management 9", 0, "1009cece", 0}, {"14", "Retired Certificate for Key Management 10", 0, "100Acece", 0}, {"15", "Retired Certificate for Key Management 11", 0, "100Bcece", 0}, {"16", "Retired Certificate for Key Management 12", 0, "100Ccece", 0}, {"17", "Retired Certificate for Key Management 13", 0, "100Dcece", 0}, {"18", "Retired Certificate for Key Management 14", 0, "100Ecece", 0}, {"19", "Retired Certificate for Key Management 15", 0, "100Fcece", 0}, {"20", "Retired Certificate for Key Management 16", 0, "1010cece", 0}, {"21", "Retired Certificate for Key Management 17", 0, "1011cece", 0}, {"22", "Retired Certificate for Key Management 18", 0, "1012cece", 0}, {"23", "Retired Certificate for Key Management 19", 0, "1013cece", 0}, {"24", "Retired Certificate for Key Management 20", 0, "1014cece", 0} }; static const pindata pins[] = { { "1", "PIV Card Holder 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 }, { "2", "PIV PUK", "", 0x81, SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 8, 4, 8, SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_LOCAL | SC_PKCS15_PIN_FLAG_SO_PIN | SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN, -1, 0xFF, SC_PKCS15_CO_FLAG_PRIVATE }, { 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 pkcs15v1_1 Table 2 * RSA and EC hav differents set of usage */ static const pubdata pubkeys[PIV_NUM_CERTS_AND_KEYS] = { { "1", "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, "1", 0, "PIV_9A_KEY"}, { "2", "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, "1", 0, "PIV_9C_KEY"}, { "3", "KEY MAN pubkey", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT| SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "9D06", 0x9D, "1", 0, "PIV_9D_KEY"}, { "4", "CARD AUTH pubkey", /*RSA*/SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER, /*EC*/SC_PKCS15_PRKEY_USAGE_VERIFY, "9E06", 0x9E, "0", 0, "PIV_9E_KEY"}, /* no pin, and avail in contactless */ { "5", "Retired KEY MAN 1", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "8206", 0x82, "1", 0, NULL}, { "6", "Retired KEY MAN 2", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "8306", 0x83, "1", 0, NULL}, { "7", "Retired KEY MAN 3", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "8406", 0x84, "1", 0, NULL}, { "8", "Retired KEY MAN 4", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "8506", 0x85, "1", 0, NULL}, { "9", "Retired KEY MAN 5", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "8606", 0x86, "1", 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, "1", 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, "1", 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, "1", 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, "1", 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, "1", 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, "1", 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, "1", 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, "1", 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, "1", 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, "1", 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, "1", 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, "1", 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, "1", 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, "1", 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, "1", 0, NULL} }; /* * note some of the SC_PKCS15_PRKEY values are dependent * on the key algorithm, and will be reset. */ static const prdata prkeys[PIV_NUM_CERTS_AND_KEYS] = { { "1", "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, "1", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "2", "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, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "3", "KEY MAN key", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x9D, "1", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "4", "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 */ { "5", "Retired KEY MAN 1", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x82, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "6", "Retired KEY MAN 2", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x83, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "7", "Retired KEY MAN 3", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x84, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "8", "Retired KEY MAN 4", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x85, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "9", "Retired KEY MAN 5", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x86, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "10", "Retired KEY MAN 6", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x87, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "11", "Retired KEY MAN 7", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x88, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "12", "Retired KEY MAN 8", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x89, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "13", "Retired KEY MAN 9", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x8A, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "14", "Retired KEY MAN 10", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x8B, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "15", "Retired KEY MAN 11", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x8C, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "16", "Retired KEY MAN 12", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x8D, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "17", "Retired KEY MAN 13", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x8E, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "18", "Retired KEY MAN 14", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x8F, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "19", "Retired KEY MAN 15", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x90, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "20", "Retired KEY MAN 16", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x91, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "21", "Retired KEY MAN 17", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x92, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "22", "Retired KEY MAN 18", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x93, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "23", "Retired KEY MAN 19", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x94, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1}, { "24", "Retired KEY MAN 20", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x95, "1", SC_PKCS15_CO_FLAG_PRIVATE, 1} }; int r, i; sc_card_t *card = p15card->card; sc_serial_number_t serial; char buf[SC_MAX_SERIALNR * 2 + 1]; common_key_info ckis[PIV_NUM_CERTS_AND_KEYS]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* could read this off card if needed */ /* CSP does not like a - in the name */ p15card->tokeninfo->label = strdup("PIV_II"); p15card->tokeninfo->manufacturer_id = strdup(MANU_ID); /* * 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"sc_card_ctl rc=%d",r); p15card->tokeninfo->serial_number = strdup("00000000"); } else { sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0); p15card->tokeninfo->serial_number = strdup(buf); } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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 = (card->ops->card_ctl)(card, SC_CARDCTL_PIV_OBJECT_PRESENT, &obj_info.path); if (r == 1) continue; /* Not on card, do not define the object */ strncpy(obj_info.app_label, objects[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); r = sc_format_oid(&obj_info.app_oid, objects[i].aoid); if (r != SC_SUCCESS) return r; 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); /* TODO * PIV key 9C requires the pin verify be done just before any * crypto operation using the key. * * Nss 3.12.7 does not check the CKA_ALWAYS_AUTHENTICATE attribute of a key * and will do a C_FindObjects with only CKA_VALUE looking for a certificate * it had found earlier after c_Login. The template does not add CKA_TYPE=cert. * This will cause the card-piv to read all the objects and will reset * the security status for the 9C key. * Mozilla Bug 357025 * Mozilla Bug 613507 * on 5/16/2012, both scheduled for NSS 3.14 * * We can not read all the objects, as some need the PIN! */ } /* * certs, pubkeys and priv keys are related and we assume * they are in order * We need to read the cert, get modulus and keylen * We use those for the pubkey, and priv key objects. * If no cert, then see if pubkey (i.e. we are initilizing, * and the pubkey is in a file,) then add pubkey and privkey * If no cert and no pubkey, skip adding them. */ /* set certs */ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "PIV-II adding certs..."); for (i = 0; i < PIV_NUM_CERTS_AND_KEYS; i++) { 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; 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; 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 = (card->ops->card_ctl)(card, SC_CARDCTL_PIV_OBJECT_PRESENT, &cert_info.path); if (r == 1) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Cert can not be present,i=%d", i); continue; } r = sc_pkcs15_read_file(p15card, &cert_info.path, &cert_der.value, &cert_der.len); if (r) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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; cert_info.path.len = 0; /* use in mem cert from now on */ } /* following will find the cached cert in cert_info */ r = sc_pkcs15_read_certificate(p15card, &cert_info, &cert_out); if (r < 0 || cert_out->key == NULL) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Failed to read/parse the certificate r=%d",r); continue; } 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; break; case SC_ALGORITHM_EC: ckis[i].pubkey_len = cert_out->key->u.ec.params.field_length; break; default: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsuported key.algorithm %d", cert_out->key->algorithm); ckis[i].pubkey_len = 0; /* set some value for now */ } sc_pkcs15_free_certificate(cert_out); r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, " Failed to add cert obj r=%d",r); continue; } } /* set pins */ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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; 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; if (i == 0 && (card->ops->card_ctl)(card, SC_CARDCTL_PIV_PIN_PREFERENCE, &pin_ref) == 0 && pin_ref == 0x00) { /* must be 80 for PIV pin, or 00 for Global PIN */ pin_info.attrs.pin.reference = pin_ref; pin_info.attrs.pin.flags &= ~SC_PKCS15_PIN_FLAG_LOCAL; label = "Global PIN"; } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DEE Adding pin %d label=%s",i, label); strncpy(pin_obj.label, label, SC_PKCS15_MAX_LABEL_SIZE - 1); pin_obj.flags = pins[i].obj_flags; r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); if (r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } /* set public keys */ /* We may only need this during initialzation when genkey * gets the pubkey, but it can not be read from the card * at a later time. The piv-tool can stach pubkey in file */ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "PIV-II adding pub keys..."); for (i = 0; i < PIV_NUM_CERTS_AND_KEYS; i++) { struct sc_pkcs15_pubkey_info pubkey_info; struct sc_pkcs15_object pubkey_obj; struct sc_pkcs15_pubkey *p15_key; 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 stached 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"No cert for this pub key i=%d",i); /* * If we used the piv-tool to generate a key, * we would have saved the public key as a file. * This code is only used while signing a request * After the certificate is loaded on the card, * the public key is extracted from the certificate. */ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DEE look for env %s", pubkeys[i].getenvname?pubkeys[i].getenvname:"NULL"); if (pubkeys[i].getenvname == NULL) continue; filename = getenv(pubkeys[i].getenvname); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DEE look for file %s", filename?filename:"NULL"); if (filename == NULL) continue; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Adding pubkey from file %s",filename); r = sc_pkcs15_pubkey_from_spki_filename(card->ctx, filename, &p15_key); if (r < 0) continue; /* 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Unsupported key_alg %d",p15_key->algorithm); continue; } pubkey_obj.emulated = p15_key; p15_key = NULL; } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"adding pubkey for %d keyalg=%d",i, ckis[i].key_alg); switch (ckis[i].key_alg) { case SC_ALGORITHM_RSA: 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); r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info); if (r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); /* should not fail */ ckis[i].pubkey_found = 1; break; case SC_ALGORITHM_EC: 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); r = sc_pkcs15emu_add_ec_pubkey(p15card, &pubkey_obj, &pubkey_info); if (r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); /* should not fail */ ckis[i].pubkey_found = 1; break; default: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"key_alg %d not supported", ckis[i].key_alg); continue; } } /* set private keys */ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "PIV-II adding private keys..."); for (i = 0; i < PIV_NUM_CERTS_AND_KEYS; i++) { 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; if (prkeys[i].auth_id) sc_pkcs15_format_id(prkeys[i].auth_id, &prkey_obj.auth_id); /* * When no cert is present and a pubkey in a file was found, * means the caller is initilaizeing a card. A sign operation * will be required to sign a certificate request even if * normal usage would not allow it. Set SC_PKCS15_PRKEY_USAGE_SIGN * TODO if code is added to allow key generation and reqest * sign in the same session, similiar code will be needed. */ if (ckis[i].pubkey_from_file == 1) { prkey_info.usage = SC_PKCS15_PRKEY_USAGE_SIGN; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Adding SC_PKCS15_PRKEY_USAGE_SIGN"); } switch (ckis[i].key_alg) { case SC_ALGORITHM_RSA: 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: prkey_info.usage |= prkeys[i].usage_ec; prkey_info.field_length = ckis[i].pubkey_len; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DEE added key_alg %2.2x prkey_obj.flags %8.8x", ckis[i].key_alg, prkey_obj.flags); r = sc_pkcs15emu_add_ec_prkey(p15card, &prkey_obj, &prkey_info); break; default: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported key_alg %d", ckis[i].key_alg); r = 0; /* we just skip this one */ } if (r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } p15card->ops.get_guid = piv_get_guid; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); } int sc_pkcs15emu_piv_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts) { sc_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) return sc_pkcs15emu_piv_init(p15card); else { int r = piv_detect_card(p15card); if (r) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_piv_init(p15card); } } opensc-0.13.0/src/libopensc/iso7816.h0000644000015201777760000000206612057406034014067 00000000000000/* * 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_TYPE 0x82 #define ISO7816_TAG_FCP_ID 0x83 #define ISO7816_TAG_FCP_ACLS 0x86 /* 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 /* Other interindustry data tags */ #define IASECC_TAG_II_IO_BUFFER_SIZES 0xE0 #ifdef __cplusplus } #endif #endif opensc-0.13.0/src/libopensc/sc.c0000644000015201777760000005130412057406034013346 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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 const char *sc_get_version(void) { return sc_version; } int sc_hex_to_bin(const char *in, u8 *out, size_t *outlen) { int err = 0; size_t left, count = 0; assert(in != NULL && out != NULL && outlen != NULL); left = *outlen; while (*in != '\0') { int byte = 0, nybbles = 2; while (nybbles-- && *in && *in != ':' && *in != ' ') { char c; byte <<= 4; c = *in++; if ('0' <= c && c <= '9') c -= '0'; else if ('a' <= c && c <= 'f') c = c - 'a' + 10; else if ('A' <= c && c <= 'F') c = c - 'A' + 10; else { err = SC_ERROR_INVALID_ARGUMENTS; goto out; } byte |= c; } if (*in == ':' || *in == ' ') in++; if (left <= 0) { err = SC_ERROR_BUFFER_TOO_SMALL; break; } out[count++] = (u8) byte; left--; } out: *outlen = count; return err; } int sc_bin_to_hex(const u8 *in, size_t in_len, char *out, size_t out_len, int in_sep) { unsigned int n, sep_len; char *pos, *end, sep; sep = (char)in_sep; sep_len = sep > 0 ? 1 : 0; pos = out; end = out + out_len; for (n = 0; n < in_len; n++) { if (pos + 3 + sep_len >= end) return SC_ERROR_BUFFER_TOO_SMALL; if (n && sep_len) *pos++ = sep; sprintf(pos, "%02x", in[n]); pos += 2; } *pos = '\0'; return 0; } 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 | buf[1] << 16 | buf[2] << 8 | buf[3]); } unsigned short bebytes2ushort(const u8 *buf) { if (buf == NULL) return 0U; return (unsigned short) (buf[0] << 8 | buf[1]); } 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] = strtol(p, &q, 10); if (!*q) break; if (!(q[0] == '.' && isdigit(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; assert(oid1 != NULL && oid2 != NULL); 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); 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; 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; return; } 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 0; } 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 + 1) 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; assert(file != NULL); assert(operation < SC_MAX_AC_OPS); switch (method) { case SC_AC_NEVER: sc_file_clear_acl_entries(file, operation); file->acl[operation] = (sc_acl_entry_t *) 1; return 0; case SC_AC_NONE: sc_file_clear_acl_entries(file, operation); file->acl[operation] = (sc_acl_entry_t *) 2; return 0; case SC_AC_UNKNOWN: sc_file_clear_acl_entries(file, operation); file->acl[operation] = (sc_acl_entry_t *) 3; return 0; 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 0; 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 0; } _new = malloc(sizeof(sc_acl_entry_t)); if (_new == NULL) return SC_ERROR_OUT_OF_MEMORY; _new->method = method; _new->key_ref = key_ref; _new->next = NULL; p = file->acl[operation]; if (p == NULL) { file->acl[operation] = _new; return 0; } while (p->next != NULL) p = p->next; p->next = _new; return 0; } 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, {{0, 0, 0, {0}}}, NULL }; static const sc_acl_entry_t e_none = { SC_AC_NONE, SC_AC_KEY_REF_NONE, {{0, 0, 0, {0}}}, NULL }; static const sc_acl_entry_t e_unknown = { SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE, {{0, 0, 0, {0}}}, NULL }; assert(file != NULL); assert(operation < SC_MAX_AC_OPS); 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; assert(file != NULL); assert(operation < SC_MAX_AC_OPS); 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; assert(sc_file_valid(file)); 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); 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; assert(sc_file_valid(src)); *dest = NULL; 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; return; err: if (newf != NULL) 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; assert(sc_file_valid(file)); if (sec_attr == NULL) { 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; assert(sc_file_valid(file)); if (prop_attr == NULL) { if (file->prop_attr != NULL) free(file->prop_attr); file->prop_attr = NULL; file->prop_attr_len = 0; return 0; } 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 0; } int sc_file_set_type_attr(sc_file_t *file, const u8 *type_attr, size_t type_attr_len) { u8 *tmp; assert(sc_file_valid(file)); if (type_attr == NULL) { if (file->type_attr != NULL) free(file->type_attr); file->type_attr = NULL; file->type_attr_len = 0; return 0; } 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 0; } int sc_file_valid(const sc_file_t *file) { #ifndef NDEBUG assert(file != NULL); #endif 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 0; if (n_hist > atr_len) n_hist = atr_len; reader->atr_info.hist_bytes_len = n_hist; reader->atr_info.hist_bytes = p; return 0; } void *sc_mem_alloc_secure(sc_context_t *ctx, size_t len) { void *pointer; int locked = 0; pointer = calloc(len, sizeof(unsigned char)); if (!pointer) return NULL; #ifdef HAVE_SYS_MMAN_H /* TODO Windows support and mprotect too */ /* Do not swap the memory */ if (mlock(pointer, len) >= 0) locked = 1; #endif if (!locked) { if (ctx->paranoid_memory) { sc_do_log (ctx, 0, NULL, 0, NULL, "cannot lock memory, failing allocation because paranoid set"); free (pointer); pointer = NULL; } else { sc_do_log (ctx, 0, NULL, 0, NULL, "cannot lock memory, sensitive data may be paged to disk"); } } return pointer; } void sc_mem_clear(void *ptr, size_t len) { #ifdef ENABLE_OPENSSL /* FIXME: Bug in 1.0.0-beta series crashes with 0 length */ if (len > 0) 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 0; } 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(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; } /**************************** 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(); } opensc-0.13.0/src/libopensc/muscle-filesystem.c0000644000015201777760000001400712057406034016412 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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)); memset(fs, 0, sizeof(mscfs_t)); memcpy(fs->currentPath, "\x3F\x00", 2); return fs; } void mscfs_free(mscfs_t *fs) { mscfs_clear_cache(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; } int mscfs_push_file(mscfs_t* fs, mscfs_file_t *file) { mscfs_cache_t *cache = &fs->cache; 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 0; } 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 */ } mscfs_push_file(fs, &file); } r = fs->listFile(&file, 0, fs->udata); if(r == 0) break; else if(r < 0) return r; } return fs->cache.size; } void mscfs_check_cache(mscfs_t* fs) { if(!fs->cache.array) { mscfs_update_cache(fs); } } int mscfs_lookup_path(mscfs_t* fs, const u8 *path, int pathlen, msc_id* objectId, int isDirectory) { u8* oid = objectId->id; if ((pathlen & 1) != 0) /* not divisble by 2 */ return MSCFS_INVALID_ARGS; if(isDirectory) { /* Directory must be right next to root */ if((0 == memcmp(path, "\x3F\x00", 2) && pathlen == 4) || (0 == memcmp(fs->currentPath, "\x3F\x00", 2) && pathlen == 2)) { 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(0 == memcmp(path, "\x3F\x00", 2) && pathlen == 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, int pathlen, mscfs_file_t **file_data, int* idx) { msc_id fullPath; int x; assert(fs != NULL && path != NULL && file_data != NULL); mscfs_lookup_path(fs, path, pathlen, &fullPath, 0); /* Obtain file information while checking if it exists */ mscfs_check_cache(fs); if(idx) *idx = -1; for(x = 0; x < fs->cache.size; x++) { msc_id objectId; *file_data = &fs->cache.array[x]; objectId = (*file_data)->objectId; if(0 == memcmp(objectId.id, fullPath.id, 4)) { if(idx) *idx = x; break; } *file_data = NULL; } if(*file_data == NULL && (0 == memcmp("\x3F\x00\x00\x00", fullPath.id, 4) || 0 == memcmp("\x3F\x00\x3F\x00", fullPath.id, 4 ))) { 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.13.0/src/libopensc/pkcs15-oberthur.c0000644000015201777760000010711112057406034015675 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "pkcs15.h" #include "log.h" #include "asn1.h" #include "internal.h" #ifdef ENABLE_OPENSSL #include #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); int sc_pkcs15emu_oberthur_init_ex(struct sc_pkcs15_card *, struct sc_pkcs15emu_opt *); static int sc_oberthur_parse_tokeninfo (struct sc_pkcs15_card *, unsigned char *, size_t, int); static int sc_oberthur_parse_containers (struct sc_pkcs15_card *, unsigned char *, size_t, int); static int sc_oberthur_parse_publicinfo (struct sc_pkcs15_card *, unsigned char *, size_t, int); 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(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_MEMORY_FAILURE; memcpy(buf_mem.data, der->value, der->len); buf_mem.max = buf_mem.length = der->len; bio = BIO_new(BIO_s_mem()); if(!bio) return SC_ERROR_MEMORY_FAILURE; BIO_set_mem_buf(bio, &buf_mem, BIO_NOCLOSE); x = d2i_X509_bio(bio, 0); BIO_free(bio); if (!x) 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (!in_path || !out || !out_len) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "Cannot read oberthur file"); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot select oberthur file to read"); if (file->ef_structure == SC_FILE_EF_TRANSPARENT) sz = file->size; else sz = (file->record_length + 2) * file->record_count; *out = calloc(sz, 1); if (*out == NULL) SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_MEMORY_FAILURE, "Cannot read oberthur file"); if (file->ef_structure == SC_FILE_EF_TRANSPARENT) { rv = sc_read_binary(card, 0, *out, sz, 0); } else { int rec; int offs = 0; int rec_len = file->record_length; for (rec = 1; ; rec++) { rv = sc_read_record(card, rec, *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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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; rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 0x10); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot read oberthur file: get AUTH objects error"); for (ii=0; iidata; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (!buff || len < 0x24) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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); p15card->tokeninfo->label = strdup(label); p15card->tokeninfo->manufacturer_id = strdup("Oberthur/OpenSC"); if (flags & 0x01) p15card->tokeninfo->flags |= SC_PKCS15_TOKEN_PRN_GENERATION; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "label %s", p15card->tokeninfo->label); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "manufacturer_id %s", p15card->tokeninfo->manufacturer_id); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); while (Containers) { struct container *next = Containers->next; free (Containers); Containers = next; } for (offs=0; offs < len;) { struct container *cont; unsigned char *ptr = buff + offs + 2; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "parse contaniers offs:%i, len:%i", offs, len); if (*(buff + offs) != 'R') return SC_ERROR_INVALID_DATA; cont = (struct container *)calloc(sizeof(struct container), 1); if (!cont) return SC_ERROR_MEMORY_FAILURE; 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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; } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); for (ii=0; iicard->ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot parse public key info"); break; case BASE_ID_CERT : rv = sc_pkcs15emu_oberthur_add_cert(p15card, file_id); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot parse data info"); break; default: SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Public object parse error"); } } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); for (ii=0; ii), * 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; size_t len, info_len, offs; unsigned flags; int rv; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to add public key: read oberthur file error"); /* Flags */ offs = 2; if (offs > info_len) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "Public key key-usage:%04X", key_info.usage); /* Label */ if (offs + 2 > info_len) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add public key: no 'Label'"); len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100; 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 > info_len) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add public key: no 'ID'"); len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100; if (!len || len > sizeof(key_info.id.value)) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA, "Failed to add public key: invalie 'ID' length"); memcpy(key_info.id.value, info_blob + offs + 2, len); key_info.id.len = len; /* 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); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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, *cert_blob; size_t info_len, cert_len, len, offs; unsigned flags; int rv; char ch_tmp[0x20]; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to add certificate: read oberthur file error"); if (info_len < 2) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add certificate: no 'CN'"); len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100; 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 > info_len) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add certificate: no 'ID'"); len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100; if (len > sizeof(cinfo.id.value)) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA, "Failed to add certificate: invalie 'ID' length"); memcpy(cinfo.id.value, info_blob + offs + 2, len); cinfo.id.len = len; /* 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to add certificate: read certificate error"); cinfo.value.value = cert_blob; cinfo.value.len = cert_len; rv = sc_oberthur_get_certificate_authority(&cinfo.value, &cinfo.authority); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to add certificate: get certificate attributes error"); if (flags & OBERTHUR_ATTR_MODIFIABLE) cobj.flags |= SC_PKCS15_CO_FLAG_MODIFIABLE; rv = sc_pkcs15emu_add_x509_cert(p15card, &cobj, &cinfo); SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, rv); } /* 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to add private key: get friends error"); if (ccont.id_cert) { struct sc_pkcs15_object *objs[32]; int ii; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "friend certificate %04X", ccont.id_cert); rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_CERT_X509, objs, 32); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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) { strncpy(kobj.label, objs[ii]->label, sizeof(kobj.label) - 1); break; } } if (ii == rv) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to add private key: read oberthur file error"); if (info_len < 2) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add private key: no 'tag'"); flags = *(info_blob + 0) * 0x100 + *(info_blob + 1); offs = 2; /* CN */ if (offs > info_len) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add private key: no 'CN'"); len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100; 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 > info_len) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add private key: no 'ID'"); len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100; if (!len) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add private key: zero length ID"); else if (len > sizeof(kinfo.id.value)) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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 > info_len) return SC_ERROR_UNKNOWN_DATA_RECEIVED; len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100; if (len) { kinfo.subject.value = malloc(len); if (!kinfo.subject.value) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_MEMORY_FAILURE, "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 */ snprintf(ch_tmp, sizeof(ch_tmp), "%s%04X", AWP_OBJECTS_DF_PRV, file_id); sc_format_path(ch_tmp, &kinfo.path); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Private key info path %s", ch_tmp); kinfo.modulus_length = size; kinfo.native = 1; kinfo.access_flags = SC_PKCS15_PRKEY_ACCESS_SENSITIVE | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE | SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE | SC_PKCS15_PRKEY_ACCESS_LOCAL; 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to add data: read oberthur file error"); if (info_len < 2) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add certificate: no 'tag'"); flags = *(info_blob + 0) * 0x100 + *(info_blob + 1); offs = 2; /* Label */ if (offs > info_len) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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 (label_len > sizeof(dobj.label) - 1) label_len = sizeof(dobj.label) - 1; offs += 2 + *(info_blob + offs + 1); /* Application */ if (offs > info_len) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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 (app_len > sizeof(dinfo.app_label) - 1) app_len = sizeof(dinfo.app_label) - 1; offs += 2 + app_len; /* OID encode like DER(ASN.1(oid)) */ if (offs > info_len) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add data: no 'OID'"); oid_len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100; if (oid_len) { oid = info_blob + offs + 2; if (*oid != 0x06 || (*(oid + 1) != oid_len - 2)) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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); SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, 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); p15card->tokeninfo->serial_number = strdup(serial); p15card->ops.parse_df = sc_awp_parse_df; p15card->ops.clear = sc_awp_clear; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Oberthur init: serial %s", p15card->tokeninfo->serial_number); sc_format_path(AWP_PIN_DF, &path); rv = sc_select_file(card, &path, NULL); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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; strncpy(obj.label, "SO PIN", SC_PKCS15_MAX_LABEL_SIZE-1); obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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; sc_format_path(AWP_PIN_DF, &auth_info.path); auth_info.path.type = SC_PATH_TYPE_PATH; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Oberthur init failed: cannot add PIN object"); } else if (rv != SC_ERROR_DATA_OBJECT_NOT_FOUND) { SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Oberthur init failed: cannot verify PIN"); } for (ii=0; oberthur_infos[ii].name; ii++) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Oberthur init: read %s file", oberthur_infos[ii].name); rv = sc_oberthur_read_file(p15card, oberthur_infos[ii].path, &oberthur_infos[ii].content, &oberthur_infos[ii].len, 1); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Oberthur init failed: read oberthur file error"); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Oberthur init: parse %s file, content length %i", 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Oberthur init failed: parse error"); } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); } 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) SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_WRONG_CARD); SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); } int sc_pkcs15emu_oberthur_init_ex(struct sc_pkcs15_card * p15card, struct sc_pkcs15emu_opt * opts) { int rv; SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) { rv = sc_pkcs15emu_oberthur_init(p15card); } else { rv = oberthur_detect_card(p15card); if (!rv) rv = sc_pkcs15emu_oberthur_init(p15card); } SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (df->type != SC_PKCS15_PRKDF && df->type != SC_PKCS15_DODF) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); if (df->enumerated) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); rv = sc_oberthur_read_file(p15card, AWP_OBJECTS_LIST_PRV, &buf, &buf_len, 1); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Parse DF: read pribate 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) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Parse DF: private info parse error"); df->enumerated = 1; SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); } static void sc_awp_clear(struct sc_pkcs15_card *p15card) { SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); } opensc-0.13.0/src/libopensc/padding.c0000644000015201777760000002165412057406034014354 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include "internal.h" /* TODO doxygen comments */ /* * 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(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) */ int sc_pkcs1_strip_02_padding(const u8 *data, size_t len, u8 *out, size_t *out_len) { unsigned int n = 0; if (data == NULL || len < 3) return SC_ERROR_INTERNAL; /* skip leading zero byte */ if (*data == 0) { data++; len--; } if (data[0] != 0x02) return SC_ERROR_WRONG_PADDING; /* skip over padding bytes */ for (n = 1; n < len && data[n]; n++) ; /* Must be at least 8 pad bytes */ if (n >= len || n < 9) return SC_ERROR_WRONG_PADDING; n++; if (out == NULL) /* just check the padding */ return SC_SUCCESS; /* Now move decrypted contents to head of buffer */ if (*out_len < len - n) return SC_ERROR_INTERNAL; memmove(out, data + n, len - n); return len - n; } /* 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; } /* general PKCS#1 encoding function */ int sc_pkcs1_encode(sc_context_t *ctx, unsigned long flags, const u8 *in, size_t in_len, u8 *out, size_t *out_len, size_t mod_len) { int rv, i; size_t tmp_len = *out_len; const u8 *tmp = in; unsigned int hash_algo, pad_algo; LOG_FUNC_CALLED(ctx); hash_algo = flags & (SC_ALGORITHM_RSA_HASHES | SC_ALGORITHM_RSA_HASH_NONE); pad_algo = flags & SC_ALGORITHM_RSA_PADS; sc_log(ctx, "hash algorithm 0x%X, pad algorithm 0x%X", hash_algo, pad_algo); if (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: /* add pkcs1 bt01 padding */ rv = sc_pkcs1_add_01_padding(tmp, tmp_len, out, out_len, mod_len); LOG_FUNC_RETURN(ctx, rv); default: /* currently only pkcs1 padding is supported */ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Unsupported padding algorithm 0x%x", pad_algo); LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } } int sc_get_encoding_flags(sc_context_t *ctx, unsigned long iflags, unsigned long caps, unsigned long *pflags, unsigned long *sflags) { size_t i; LOG_FUNC_CALLED(ctx); if (pflags == NULL || sflags == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); sc_log(ctx, "iFlags 0x%X, card capabilities 0x%X", iflags, caps); for (i = 0; digest_info_prefix[i].algorithm != 0; i++) { if (iflags & digest_info_prefix[i].algorithm) { if (digest_info_prefix[i].algorithm != SC_ALGORITHM_RSA_HASH_NONE && caps & digest_info_prefix[i].algorithm) *sflags |= digest_info_prefix[i].algorithm; else *pflags |= digest_info_prefix[i].algorithm; break; } } if (iflags & SC_ALGORITHM_RSA_PAD_PKCS1) { if (caps & SC_ALGORITHM_RSA_PAD_PKCS1) *sflags |= SC_ALGORITHM_RSA_PAD_PKCS1; else *pflags |= SC_ALGORITHM_RSA_PAD_PKCS1; } else if ((iflags & SC_ALGORITHM_RSA_PADS) == SC_ALGORITHM_RSA_PAD_NONE) { /* Work with RSA, EC and maybe GOSTR? */ if (!(caps & SC_ALGORITHM_RAW_MASK)) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "raw encryption is not supported"); *sflags |= (caps & SC_ALGORITHM_RAW_MASK); /* adds in the one raw type */ *pflags = 0; } else { LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "unsupported algorithm"); } sc_log(ctx, "pad flags 0x%X, secure algorithm flags 0x%X", *pflags, *sflags); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } opensc-0.13.0/src/libopensc/pkcs15-postecert.c0000644000015201777760000002450512057406034016060 00000000000000/* * PKCS15 emulation layer for Postecert and Cnipa card. * To see how this works, run p15dump on your Postecert or Cnipa Card. * * Copyright (C) 2004, Antonino Iacono * Copyright (C) 2003, Olaf Kirch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include "common/compat_strlcpy.h" #include "internal.h" #include "pkcs15.h" #include "log.h" int sc_pkcs15emu_postecert_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); static int (*set_security_env) (sc_card_t *, const sc_security_env_t *, int); static int set_sec_env(sc_card_t * card, const sc_security_env_t *env, int se_num) { sc_security_env_t tenv = *env; if (tenv.operation == SC_SEC_OPERATION_SIGN) tenv.operation = SC_SEC_OPERATION_DECIPHER; return set_security_env(card, &tenv, se_num); } static int do_sign(sc_card_t * card, const u8 * in, size_t inlen, u8 * out, size_t outlen) { return card->ops->decipher(card, in, inlen, out, outlen); } static void set_string(char **strp, const char *value) { if (*strp) free(*strp); *strp = value ? strdup(value) : NULL; } #if 1 /* XXX: temporary copy of the old pkcs15emu functions, * to be removed */ static int sc_pkcs15emu_add_pin(sc_pkcs15_card_t *p15card, const sc_pkcs15_id_t *id, const char *label, const sc_path_t *path, int ref, int type, unsigned int min_length, unsigned int max_length, int flags, int tries_left, const char pad_char, int obj_flags) { sc_pkcs15_auth_info_t info; sc_pkcs15_object_t obj; memset(&info, 0, sizeof(info)); memset(&obj, 0, sizeof(obj)); info.auth_id = *id; info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; info.attrs.pin.min_length = min_length; info.attrs.pin.max_length = max_length; info.attrs.pin.stored_length = max_length; info.attrs.pin.type = type; info.attrs.pin.reference = ref; info.attrs.pin.flags = flags; info.attrs.pin.pad_char = pad_char; info.tries_left = tries_left; 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.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; obj.flags = obj_flags; strlcpy(obj.label, label, sizeof(obj.label)); if (auth_id != NULL) obj.auth_id = *auth_id; return sc_pkcs15emu_add_rsa_prkey(p15card, &obj, &info); } static int sc_pkcs15emu_add_cert(sc_pkcs15_card_t *p15card, int type, int authority, const sc_path_t *path, const sc_pkcs15_id_t *id, const char *label, int obj_flags) { /* const char *label = "Certificate"; */ sc_pkcs15_cert_info_t info; sc_pkcs15_object_t obj; memset(&info, 0, sizeof(info)); memset(&obj, 0, sizeof(obj)); info.id = *id; info.authority = authority; if (path) info.path = *path; strlcpy(obj.label, label, sizeof(obj.label)); obj.flags = obj_flags; return sc_pkcs15emu_add_x509_cert(p15card, &obj, &info); } #endif static int sc_pkcs15emu_postecert_init(sc_pkcs15_card_t * p15card) { static int prkey_usage = SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; static int authprkey_usage = SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER | SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT; sc_card_t *card = p15card->card; sc_path_t path; sc_pkcs15_id_t id, auth_id; unsigned char certlen[2]; unsigned char *certi = NULL; int index_cert[4]; int count_cert[4]; int flags; int authority; size_t i, count; int r; int o = 0; const char *label = "User Non-repudiation Certificate"; const char *calabel = "CA Certificate"; const char *catmslabel = "CA TimeStamper Certificate"; const char *authlabel = "User Authentication Certificate"; const char *postecert_auth_cert_path = "504B0001"; const char *authPIN = "Authentication PIN"; const char *nonrepPIN = "Non-repudiation PIN"; const char *authPRKEY = "Authentication Key"; const char *nonrepPRKEY = "Non repudiation Key"; memset(index_cert, 0, sizeof(index_cert)); memset(count_cert, 0, sizeof(count_cert)); /* Get the non-repudiation certificate length */ sc_format_path(postecert_auth_cert_path, &path); if (sc_select_file(card, &path, NULL) < 0) { r = SC_ERROR_WRONG_CARD; goto failed; } set_string(&p15card->tokeninfo->label, "Postecert & Cnipa Card"); set_string(&p15card->tokeninfo->manufacturer_id, "Postecert"); set_string(&p15card->tokeninfo->serial_number, "0000"); sc_read_binary(card, 0, certlen, 2, 0); /* Now set the certificate offset/len */ count = (certlen[0] << 8) + certlen[1]; if (count < 256) return SC_ERROR_INTERNAL; certi = malloc(count); if (!certi) return SC_ERROR_OUT_OF_MEMORY; sc_read_binary(card, 0, certi, count - 500, 0); for (i = 2; i < (count - 256); i++) { /* this file contain more than one certificate */ if (*(certi + i) == 0x30 && *(certi + i + 1) == 0x82 && *(certi + i + 4) == 0x30 && *(certi + i + 5) == 0x82 && *(certi + i + 2) > 1 && *(certi + i + 2) < 8 && *(certi + i + 6) <= *(certi + i + 2)) { index_cert[o] = i; count_cert[o] = (*(certi + i + 2) << 8) + *(certi + i + 3) + 4; o++; if (o > 4) break; i += (*(certi + i + 2) << 8) + *(certi + i + 3); } } free(certi); path.index = index_cert[0]; path.count = count_cert[0]; id.value[0] = 1; id.len = 1; authority = 1; sc_pkcs15emu_add_cert(p15card, SC_PKCS15_TYPE_CERT_X509, authority, &path, &id, calabel, SC_PKCS15_CO_FLAG_MODIFIABLE); path.index = index_cert[1]; path.count = count_cert[1]; id.value[0] = 2; id.len = 1; authority = 1; sc_pkcs15emu_add_cert(p15card, SC_PKCS15_TYPE_CERT_X509, authority, &path, &id, catmslabel, SC_PKCS15_CO_FLAG_MODIFIABLE); path.index = index_cert[2]; path.count = count_cert[2]; id.value[0] = 3; id.len = 1; authority = 0; sc_pkcs15emu_add_cert(p15card, SC_PKCS15_TYPE_CERT_X509, authority, &path, &id, label, SC_PKCS15_CO_FLAG_MODIFIABLE); path.index = index_cert[3]; path.count = count_cert[3]; id.value[0] = 4; id.len = 1; sc_pkcs15emu_add_cert(p15card, SC_PKCS15_TYPE_CERT_X509, authority, &path, &id, authlabel, SC_PKCS15_CO_FLAG_MODIFIABLE); flags = SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_NEEDS_PADDING; /* add authentication PIN */ sc_format_path("3F00504B", &path); id.value[0] = 1; sc_pkcs15emu_add_pin(p15card, &id, authPIN, &path, 0x82, SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 6, 14, flags, 3, 0, SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE); /* add authentication private key */ id.value[0] = 4; auth_id.value[0] = 1; auth_id.len = 1; sc_pkcs15emu_add_prkey(p15card, &id, authPRKEY, SC_PKCS15_TYPE_PRKEY_RSA, 1024, authprkey_usage, &path, 0x06, &auth_id, SC_PKCS15_CO_FLAG_PRIVATE); /* add non repudiation PIN */ sc_format_path("3F00504B", &path); id.value[0] = 2; sc_pkcs15emu_add_pin(p15card, &id, nonrepPIN, &path, 0x82, SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 6, 14, flags, 3, 0, SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE); /* add non repudiation private key */ id.value[0] = 3; auth_id.value[0] = 2; sc_pkcs15emu_add_prkey(p15card, &id, nonrepPRKEY, SC_PKCS15_TYPE_PRKEY_RSA, 1024, prkey_usage, &path, 0x01, &auth_id, SC_PKCS15_CO_FLAG_PRIVATE); /* return to MF */ sc_format_path("3F00", &path); r = sc_select_file(card, &path, NULL); if (r != SC_SUCCESS) return r; { /* save old signature funcs */ set_security_env = card->ops->set_security_env; /* set new one */ card->ops->set_security_env = set_sec_env; card->ops->compute_signature = do_sign; } return 0; failed: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Failed to initialize Postecert and Cnipa emulation: %s\n", sc_strerror(r)); return r; } static int postecert_detect_card(sc_pkcs15_card_t * p15card) { sc_card_t *card = p15card->card; /* check if we have the correct card OS */ if (strcmp(card->name, "CardOS M4")) return SC_ERROR_WRONG_CARD; return SC_SUCCESS; } int sc_pkcs15emu_postecert_init_ex(sc_pkcs15_card_t * p15card, sc_pkcs15emu_opt_t * opts) { if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) return sc_pkcs15emu_postecert_init(p15card); else { int r = postecert_detect_card(p15card); if (r) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_postecert_init(p15card); } } opensc-0.13.0/src/libopensc/pkcs15-data.c0000644000015201777760000001307212057406034014756 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include "internal.h" #include "asn1.h" #include "pkcs15.h" static const struct sc_asn1_entry c_asn1_data_object[] = { { "dataObject", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; int sc_pkcs15_read_data_object(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_data_info *info, struct sc_pkcs15_data **data_object_out) { int r; struct sc_pkcs15_data *data_object; u8 *data = NULL; size_t len; if (p15card == NULL || info == NULL || data_object_out == NULL) return SC_ERROR_INVALID_ARGUMENTS; SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); r = sc_pkcs15_read_file(p15card, &info->path, &data, &len); if (r) return r; data_object = malloc(sizeof(struct sc_pkcs15_data)); if (data_object == NULL) { free(data); return SC_ERROR_OUT_OF_MEMORY; } memset(data_object, 0, sizeof(struct sc_pkcs15_data)); data_object->data = data; data_object->data_len = len; *data_object_out = data_object; return 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); int r; 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; SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "ASN.1 decoding failed"); if (!p15card->app || !p15card->app->ddo.aid.len) { 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) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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 = strlen(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(sc_pkcs15_data_info_t *data) { free(data); } opensc-0.13.0/src/libopensc/Makefile.mak0000644000015201777760000000460612057406034015007 00000000000000TOPDIR = ..\.. TARGET = opensc.dll opensc_a.lib OBJECTS = \ sc.obj ctx.obj log.obj errors.obj \ asn1.obj base64.obj sec.obj card.obj iso7816.obj dir.obj ef-atr.obj padding.obj apdu.obj \ \ 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 \ \ muscle.obj muscle-filesystem.obj \ \ ctbcs.obj reader-ctapi.obj reader-pcsc.obj reader-openct.obj \ \ card-setcos.obj card-miocos.obj card-flex.obj card-gpk.obj \ card-cardos.obj card-tcos.obj card-default.obj \ card-mcrd.obj card-starcos.obj card-openpgp.obj card-jcop.obj \ card-oberthur.obj card-belpic.obj card-atrust-acos.obj \ card-entersafe.obj card-epass2003.obj \ card-incrypto34.obj card-piv.obj card-muscle.obj card-acos5.obj \ card-asepcos.obj card-akis.obj card-gemsafeV1.obj card-rutoken.obj \ card-rtecp.obj card-westcos.obj card-myeid.obj card-ias.obj \ card-javacard.obj card-itacns.obj card-authentic.obj \ card-iasecc.obj iasecc-sdo.obj iasecc-sm.obj \ card-sc-hsm.obj \ \ pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \ pkcs15-tcos.obj pkcs15-esteid.obj pkcs15-postecert.obj pkcs15-gemsafeGPK.obj \ pkcs15-actalis.obj pkcs15-atrust-acos.obj pkcs15-tccardos.obj pkcs15-piv.obj \ pkcs15-esinit.obj pkcs15-westcos.obj pkcs15-pteid.obj pkcs15-oberthur.obj \ pkcs15-itacns.obj pkcs15-gemsafeV1.obj pkcs15-sc-hsm.obj \ compression.obj p15card-helper.obj \ $(TOPDIR)\win32\versioninfo.res all: $(TOPDIR)\win32\versioninfo.res $(TARGET) !INCLUDE $(TOPDIR)\win32\Make.rules.mak opensc.dll: $(OBJECTS) ..\scconf\scconf.lib ..\common\common.lib ..\common\libscdl.lib ..\pkcs15init\pkcs15init.lib echo LIBRARY $* > $*.def echo EXPORTS >> $*.def type lib$*.exports >> $*.def link $(LINKFLAGS) /dll /def:$*.def /implib:$*.lib /out:opensc.dll $(OBJECTS) ..\scconf\scconf.lib ..\common\common.lib ..\common\libscdl.lib ..\pkcs15init\pkcs15init.lib $(OPENSSL_LIB) $(ZLIB_LIB) gdi32.lib advapi32.lib ws2_32.lib if EXIST opensc.dll.manifest mt -manifest opensc.dll.manifest -outputresource:opensc.dll;2 opensc_a.lib: $(OBJECTS) ..\scconf\scconf.lib ..\common\common.lib ..\common\libscdl.lib ..\pkcs15init\pkcs15init.lib lib $(LIBFLAGS) /out:opensc_a.lib $(OBJECTS) ..\scconf\scconf.lib ..\common\common.lib ..\common\libscdl.lib ..\pkcs15init\pkcs15init.lib $(ZLIB_LIB) user32.lib ws2_32.lib opensc-0.13.0/src/libopensc/card-ias.c0000644000015201777760000003511712057406034014430 00000000000000/* * Driver for IAS based cards, e.g. Portugal's eID card. * * Copyright (C) 2009, Joao Poupino * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Partially based on the ISO7816 driver. * * Thanks to Andre Cruz, Jorge Ferreira and Paulo F. Andrade */ #include "config.h" #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" /* Portugal eID uses 1024 bit keys */ #define PTEID_RSA_KEYSIZE 128 #define DRVDATA(card) ((struct ias_priv_data *) ((card)->drv_data)) static struct sc_card_operations ias_ops; static struct sc_card_operations *iso_ops = NULL; static struct sc_card_driver ias_drv = { "IAS", "ias", &ias_ops, NULL, 0, NULL }; /* Known ATRs */ static struct sc_atr_table ias_atrs[] = { /* Portugal eID cards */ {"3B:65:00:00:D0:00:54:01:31", NULL, NULL, SC_CARD_TYPE_IAS_PTEID, 0, NULL}, {"3B:65:00:00:D0:00:54:01:32", NULL, NULL, SC_CARD_TYPE_IAS_PTEID, 0, NULL}, {"3B:95:95:40:FF:D0:00:54:01:31", NULL, NULL, SC_CARD_TYPE_IAS_PTEID, 0, NULL}, {"3B:95:95:40:FF:D0:00:54:01:32", NULL, NULL, SC_CARD_TYPE_IAS_PTEID, 0, NULL}, {NULL, NULL, NULL, 0, 0, NULL} }; /* Known AIDs */ static const u8 ias_aid_pteid[] = {0x60, 0x46, 0x32, 0xFF, 0x00, 0x01, 0x02}; static int ias_select_applet(sc_card_t *card, const u8 *aid, size_t aid_len) { int r; sc_path_t tpath; memset(&tpath, 0, sizeof(sc_path_t)); tpath.type = SC_PATH_TYPE_DF_NAME; tpath.len = aid_len; memcpy(tpath.value, aid, aid_len); r = iso_ops->select_file(card, &tpath, NULL); if (r != SC_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to select applet"); return r; } return SC_SUCCESS; } static int ias_init(sc_card_t *card) { unsigned long flags; assert(card != NULL); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); card->name = "IAS"; card->cla = 0x00; /* Card version detection */ if (card->type == SC_CARD_TYPE_IAS_PTEID) { int r = ias_select_applet(card, ias_aid_pteid, sizeof(ias_aid_pteid)); if (r != SC_SUCCESS) return r; /* Add other cards if necessary */ } else { return SC_ERROR_INTERNAL; } /* Set card capabilities */ card->caps |= SC_CARD_CAP_RNG; /* Set the supported algorithms */ flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE; /* Only 1024 bit key sizes were tested */ _sc_card_add_rsa_alg(card, 1024, flags, 0); return SC_SUCCESS; } static int ias_match_card(sc_card_t *card) { int i; i = _sc_match_atr(card, ias_atrs, &card->type); if (i < 0) return 0; return 1; } static int ias_build_pin_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, pad, use_pin_pad, ins, p1; len = pad = use_pin_pad = p1 = 0; assert(card != NULL); switch (data->pin_type) { case SC_AC_CHV: break; default: return SC_ERROR_INVALID_ARGUMENTS; } if (data->flags & SC_PIN_CMD_USE_PINPAD) use_pin_pad = 1; /* "needs-padding" necessary for the PTEID card, * but not defined in the pin structure */ if ((data->flags & SC_PIN_CMD_NEED_PADDING) || card->type == SC_CARD_TYPE_IAS_PTEID) pad = 1; data->pin1.offset = 5; switch (data->cmd) { case SC_PIN_CMD_VERIFY: ins = 0x20; if ( (r = sc_build_pin(sbuf, sizeof(sbuf), &data->pin1, pad)) < 0) return r; len = r; break; case SC_PIN_CMD_CHANGE: ins = 0x24; if ((data->flags & SC_PIN_CMD_IMPLICIT_CHANGE) == 0 && (data->pin1.len != 0 || use_pin_pad)) { if ( (r = sc_build_pin(sbuf, sizeof(sbuf), &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(sbuf+len, sizeof(sbuf)-len, &data->pin2, pad)) < 0) return r; len += r; break; case SC_PIN_CMD_UNBLOCK: ins = 0x2C; if (data->pin1.len != 0 || use_pin_pad) { if ( (r = sc_build_pin(sbuf, sizeof(sbuf), &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(sbuf+len, sizeof(sbuf)-len, &data->pin2, pad)) < 0) return r; len += r; } else { p1 |= 0x01; } break; default: return SC_ERROR_NOT_SUPPORTED; } sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, ins, p1, data->pin_reference); apdu->lc = len; apdu->datalen = len; apdu->data = sbuf; apdu->resplen = 0; return SC_SUCCESS; } static int ias_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { int r; sc_apdu_t local_apdu; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* Check if a PIN change operation is being requested, * as it requires sending two separate APDUs */ if (data->cmd == SC_PIN_CMD_CHANGE) { /* Build a SC_PIN_CMD_VERIFY APDU */ data->cmd = SC_PIN_CMD_VERIFY; r = ias_build_pin_apdu(card, &local_apdu, data); if (r < 0) return r; data->apdu = &local_apdu; r = iso_ops->pin_cmd(card, data, tries_left); if (r < 0) return r; /* Continue processing */ data->cmd = SC_PIN_CMD_CHANGE; /* The IAS spec mandates an implicit change PIN operation */ data->flags |= SC_PIN_CMD_IMPLICIT_CHANGE; } r = ias_build_pin_apdu(card, &local_apdu, data); if (r < 0) return r; data->apdu = &local_apdu; return iso_ops->pin_cmd(card, data, tries_left); } static int ias_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { int r; sc_apdu_t apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "ias_set_security_env, keyRef = 0x%0x, algo = 0x%0x\n", *env->key_ref, env->algorithm_flags); 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; /* confidentiality template */ sbuf[0] = 0x95; /* tag for usage qualifier byte */ sbuf[1] = 0x01; /* tag length */ sbuf[2] = 0x40; /* data decryption */ sbuf[3] = 0x84; /* tag for private key reference */ sbuf[4] = 0x01; /* tag length */ sbuf[5] = *env->key_ref; /* key reference */ sbuf[6] = 0x80; /* tag for algorithm reference */ sbuf[7] = 0x01; /* tag length */ if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) sbuf[8] = 0x1A; /* RSA PKCS#1 with no data formatting */ else { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Set Sec Env: unsupported algo 0X%0X\n", env->algorithm_flags); return SC_ERROR_INVALID_ARGUMENTS; } apdu.lc = 9; apdu.datalen = 9; break; case SC_SEC_OPERATION_SIGN: apdu.p2 = 0xA4; /* authentication template */ sbuf[0] = 0x95; /* tag for usage qualifier byte */ sbuf[1] = 0x01; /* tag length */ sbuf[2] = 0x40; /* internal authentication */ sbuf[3] = 0x84; /* tag for private key reference */ sbuf[4] = 0x01; /* tag length */ sbuf[5] = *env->key_ref; /* key reference */ sbuf[6] = 0x80; /* tag for algorithm reference */ sbuf[7] = 0x01; /* tag length */ if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) sbuf[8] = 0x02; /* RSA PKCS#1 with no data formatting */ else { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Set Sec Env: unsupported algo 0X%0X\n", env->algorithm_flags); return SC_ERROR_INVALID_ARGUMENTS; } apdu.lc = 9; apdu.datalen = 9; break; default: return SC_ERROR_INVALID_ARGUMENTS; } apdu.le = 0; apdu.data = sbuf; apdu.resplen = 0; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Set Security Env APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card's Set Security Env command returned error"); return r; } static int ias_compute_signature(sc_card_t *card, const u8 * data, size_t data_len, u8 * out, size_t outlen) { int r; size_t len = 0; sc_apdu_t apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_context_t *ctx = card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (data_len > 64) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "error: input data too long: %lu bytes\n", data_len); return SC_ERROR_INVALID_ARGUMENTS; } /* Send the data */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x88, 0x02, 0x00); memcpy(sbuf, data, data_len); apdu.data = sbuf; apdu.lc = data_len; apdu.datalen = data_len; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); /* Get the result */ if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { len = card->type == SC_CARD_TYPE_IAS_PTEID ? PTEID_RSA_KEYSIZE : outlen; r = iso_ops->get_response(card, &len, out); if (r == 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); else SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } static int ias_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { int r, pathlen, stripped_len; u8 buf[SC_MAX_APDU_BUFFER_SIZE]; u8 pathbuf[SC_MAX_PATH_SIZE], *path; sc_apdu_t apdu; sc_file_t *file; stripped_len = 0; path = pathbuf; file = NULL; assert(card != NULL && in_path != NULL); if (in_path->len > SC_MAX_PATH_SIZE) return SC_ERROR_INVALID_ARGUMENTS; memcpy(path, in_path->value, in_path->len); pathlen = in_path->len; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0); apdu.p2 = 0; /* First record, return FCI */ switch (in_path->type) { 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; case SC_PATH_TYPE_PATH: apdu.p1 = 9; /* Strip the MF */ if (pathlen >= 2 && memcmp(path, "\x3f\x00", 2) == 0) { if (pathlen == 2) { /* Only 3f00 provided */ apdu.p1 = 0; break; } path += 2; pathlen -= 2; } /* Optimization based on the normal Portuguese eID usage pattern: * paths with len >= 4 shall be stripped - this avoids unnecessary * "file not found" errors. Other cards may benefit from this also. * * This works perfectly for the Portuguese eID card, but if you * are adapting this driver to another card, "false positives" may * occur depending, of course, on the file structure of the card. * * Please have this in mind if adapting this driver to another card. */ if (pathlen >= 4) { stripped_len = pathlen - 2; path += stripped_len; pathlen = 2; } else if (pathlen == 2) { apdu.p1 = 0; } break; case SC_PATH_TYPE_FROM_CURRENT: apdu.p1 = 9; break; case SC_PATH_TYPE_PARENT: apdu.p1 = 3; apdu.p2 = 0x0C; pathlen = 0; apdu.cse = SC_APDU_CASE_2_SHORT; break; default: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); } 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.p2 = 0x0C; apdu.cse = (apdu.lc == 0) ? SC_APDU_CASE_1 : SC_APDU_CASE_3_SHORT; } r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "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)); } /* A "file not found" error was received, this can mean two things: * 1) the file does not exist * 2) the current DF may be incorrect due to the optimization applied * earlier. If the path was previously stripped, select the first DF * and try to re-select the path with the full value. */ if (stripped_len > 0 && apdu.sw1 == 0x6A && apdu.sw2 == 0x82) { sc_path_t tpath; /* Restore original path value */ path -= stripped_len; pathlen += stripped_len; memset(&tpath, 0, sizeof(sc_path_t)); tpath.type = SC_PATH_TYPE_PATH; tpath.len = 2; tpath.value[0] = path[0]; tpath.value[1] = path[1]; /* Go up in the hierarchy to the correct DF */ r = ias_select_file(card, &tpath, NULL); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Error selecting parent."); /* We're now in the right place, reconstruct the APDU and retry */ path += 2; pathlen -= 2; apdu.lc = pathlen; apdu.data = path; apdu.datalen = pathlen; if (file_out != NULL) apdu.resplen = sizeof(buf); r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "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)); } } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); if (apdu.resplen < 2) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED); switch (apdu.resp[0]) { case 0x6F: file = sc_file_new(); if (file == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); file->path = *in_path; if (card->ops->process_fci == NULL) { sc_file_free(file); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 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]); *file_out = file; break; case 0x00: /* proprietary coding */ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED); default: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED); } 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; /* Use the standard iso operations as default */ ias_ops = *iso_drv->ops; /* IAS specific functions */ ias_ops.select_file = ias_select_file; ias_ops.match_card = ias_match_card; ias_ops.init = ias_init; ias_ops.set_security_env = ias_set_security_env; ias_ops.compute_signature = ias_compute_signature; ias_ops.pin_cmd = ias_pin_cmd; return &ias_drv; } struct sc_card_driver *sc_get_ias_driver(void) { return sc_get_driver(); } opensc-0.13.0/src/libopensc/pkcs15-sec.c0000644000015201777760000004176712057406034014633 00000000000000/* * pkcs15-sec.c: PKCS#15 cryptography functions * * Copyright (C) 2001, 2002 Juha Yrjölä * Copyrigth (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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include "internal.h" #include "pkcs15.h" static int select_key_file(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_prkey_info *prkey, sc_security_env_t *senv) { sc_context_t *ctx = p15card->card->ctx; sc_path_t path, file_id; int r; LOG_FUNC_CALLED(ctx); memset(&path, 0, sizeof(sc_path_t)); memset(&file_id, 0, sizeof(sc_path_t)); /* TODO: Why file_app may be NULL -- at least 3F00 has to be present? * Check validity of the following assumption. */ /* For pkcs15-emulated cards, the file_app may be NULL, in that case we allways assume an absolute path */ if (!prkey->path.len && prkey->path.aid.len) { /* Private key is a SDO allocated in application DF */ path = prkey->path; } else if (prkey->path.len == 2 && p15card->file_app != NULL) { /* Path is relative to app. DF */ path = p15card->file_app->path; file_id = prkey->path; sc_append_path(&path, &file_id); senv->file_ref = file_id; senv->flags |= SC_SEC_ENV_FILE_REF_PRESENT; } else if (prkey->path.len > 2) { path = prkey->path; memcpy(file_id.value, prkey->path.value + prkey->path.len - 2, 2); 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); } 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) { sc_context_t *ctx = p15card->card->ctx; int r; sc_algorithm_info_t *alg_info; 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); memset(&senv, 0, sizeof(senv)); /* Card driver should have the access to supported algorithms from 'tokenInfo'. So that * it can get value of card specific 'AlgorithmInfo::algRef'. */ memcpy(&senv.supported_algos, &p15card->tokeninfo->supported_algos, sizeof(senv.supported_algos)); /* 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"); 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"); switch (obj->type) { case SC_PKCS15_TYPE_PRKEY_RSA: alg_info = sc_card_find_rsa_alg(p15card->card, prkey->modulus_length); if (alg_info == NULL) { sc_log(ctx, "Card does not support RSA with key length %d", prkey->modulus_length); LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } senv.algorithm = SC_ALGORITHM_RSA; break; case SC_PKCS15_TYPE_PRKEY_GOSTR3410: alg_info = sc_card_find_gostr3410_alg(p15card->card, prkey->modulus_length); if (alg_info == NULL) { sc_log(ctx, "Card does not support GOSTR3410 with key length %d", prkey->modulus_length); LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } senv.algorithm = SC_ALGORITHM_GOSTR3410; break; default: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED,"Key type not supported"); } 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; senv.operation = SC_SEC_OPERATION_DECIPHER; senv.flags = 0; /* optional keyReference attribute (the default value is -1) */ if (prkey->key_reference >= 0) { senv.key_ref_len = 1; senv.key_ref[0] = prkey->key_reference & 0xFF; senv.flags |= SC_SEC_ENV_KEY_REF_PRESENT; } senv.flags |= SC_SEC_ENV_ALG_PRESENT; r = sc_lock(p15card->card); LOG_TEST_RET(ctx, r, "sc_lock() failed"); if (prkey->path.len != 0 || prkey->path.aid.len != 0) { r = select_key_file(p15card, prkey, &senv); if (r < 0) { sc_unlock(p15card->card); LOG_TEST_RET(ctx, r,"Unable to select private key file"); } } r = sc_set_security_env(p15card->card, &senv, 0); if (r < 0) { sc_unlock(p15card->card); LOG_TEST_RET(ctx, r, "sc_set_security_env() failed"); } r = sc_decipher(p15card->card, in, inlen, out, outlen); if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { if (sc_pkcs15_pincache_revalidate(p15card, obj) == SC_SUCCESS) r = sc_decipher(p15card->card, in, inlen, out, outlen); } sc_unlock(p15card->card); LOG_TEST_RET(ctx, r, "sc_decipher() failed"); /* Strip any padding */ if (pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { size_t s = r; r = sc_pkcs1_strip_02_padding(out, s, out, &s); LOG_TEST_RET(ctx, r, "Invalid PKCS#1 padding"); } LOG_FUNC_RETURN(ctx, 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, unsigned long *poutlen) { sc_context_t *ctx = p15card->card->ctx; int r; sc_algorithm_info_t *alg_info; 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); memset(&senv, 0, sizeof(senv)); /* Card driver should have the access to supported algorithms from 'tokenInfo'. So that * it can get value of card specific 'AlgorithmInfo::algRef'. */ memcpy(&senv.supported_algos, &p15card->tokeninfo->supported_algos, sizeof(senv.supported_algos)); /* 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"); 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: alg_info = sc_card_find_ec_alg(p15card->card, prkey->field_length); if (alg_info == NULL) { sc_log(ctx, "Card does not support EC with field_size %d", prkey->field_length); LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } if (out == NULL || *poutlen < (prkey->field_length +7) / 8) { *poutlen = (prkey->field_length +7) / 8; r = 0; /* say no data to return */ goto out; } senv.algorithm = SC_ALGORITHM_EC; senv.flags |= SC_SEC_ENV_ALG_PRESENT; senv.flags |= SC_SEC_ENV_ALG_REF_PRESENT; senv.algorithm_ref = prkey->field_length; break; default: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED,"Key type not supported"); } 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; senv.operation = SC_SEC_OPERATION_DERIVE; /* optional keyReference attribute (the default value is -1) */ if (prkey->key_reference >= 0) { senv.key_ref_len = 1; senv.key_ref[0] = prkey->key_reference & 0xFF; senv.flags |= SC_SEC_ENV_KEY_REF_PRESENT; } r = sc_lock(p15card->card); LOG_TEST_RET(ctx, r, "sc_lock() failed"); if (prkey->path.len != 0 || prkey->path.aid.len != 0) { r = select_key_file(p15card, prkey, &senv); if (r < 0) { sc_unlock(p15card->card); LOG_TEST_RET(ctx, r,"Unable to select private key file"); } } r = sc_set_security_env(p15card->card, &senv, 0); if (r < 0) { sc_unlock(p15card->card); LOG_TEST_RET(ctx, r, "sc_set_security_env() failed"); } /* TODO Do we need a sc_derive? PIV at least can use the decipher, * senv.operation = SC_SEC_OPERATION_DERIVE; */ r = sc_decipher(p15card->card, in, inlen, out, *poutlen); if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { if (sc_pkcs15_pincache_revalidate(p15card, obj) == SC_SUCCESS) r = sc_decipher(p15card->card, in, inlen, out, *poutlen); } sc_unlock(p15card->card); LOG_TEST_RET(ctx, r, "sc_decipher/derive() failed"); /* Strip any padding */ if (pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { size_t s = r; r = sc_pkcs1_strip_02_padding(out, s, out, &s); LOG_TEST_RET(ctx, r, "Invalid PKCS#1 padding"); } /* If card stores derived key on card, then no data is returned * and the key must be used on the card. */ *poutlen = r; out: 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) { sc_context_t *ctx = p15card->card->ctx; int r; sc_security_env_t senv; sc_algorithm_info_t *alg_info; const struct sc_pkcs15_prkey_info *prkey = (const struct sc_pkcs15_prkey_info *) obj->data; u8 buf[1024], *tmp; size_t modlen; unsigned long pad_flags = 0, sec_flags = 0; LOG_FUNC_CALLED(ctx); sc_log(ctx, "security operation flags 0x%X", flags); memset(&senv, 0, sizeof(senv)); /* Card driver should have the access to supported algorithms from 'tokenInfo'. So that * it can get value of card specific 'AlgorithmInfo::algRef'. */ memcpy(&senv.supported_algos, &p15card->tokeninfo->supported_algos, sizeof(senv.supported_algos)); if ((obj->type & SC_PKCS15_TYPE_CLASS_MASK) != SC_PKCS15_TYPE_PRKEY) LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "This is not a private key"); /* If 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"); 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"); switch (obj->type) { case SC_PKCS15_TYPE_PRKEY_RSA: modlen = prkey->modulus_length / 8; alg_info = sc_card_find_rsa_alg(p15card->card, prkey->modulus_length); if (alg_info == NULL) { sc_log(ctx, "Card does not support RSA with key length %d", prkey->modulus_length); LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } senv.flags |= SC_SEC_ENV_ALG_PRESENT; senv.algorithm = SC_ALGORITHM_RSA; break; case SC_PKCS15_TYPE_PRKEY_GOSTR3410: modlen = (prkey->modulus_length + 7) / 8 * 2; alg_info = sc_card_find_gostr3410_alg(p15card->card, prkey->modulus_length); if (alg_info == NULL) { sc_log(ctx, "Card does not support GOSTR3410 with key length %d", prkey->modulus_length); LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } senv.flags |= SC_SEC_ENV_ALG_PRESENT; senv.algorithm = SC_ALGORITHM_GOSTR3410; break; case SC_PKCS15_TYPE_PRKEY_EC: modlen = ((prkey->field_length +7) / 8) * 2; /* 2*nLen */ alg_info = sc_card_find_ec_alg(p15card->card, prkey->field_length); if (alg_info == NULL) { sc_log(ctx, "Card does not support EC with field_size %d", prkey->field_length); LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } senv.algorithm = SC_ALGORITHM_EC; senv.flags |= SC_SEC_ENV_ALG_PRESENT; senv.flags |= SC_SEC_ENV_ALG_REF_PRESENT; senv.algorithm_ref = prkey->field_length; break; /* add other crypto types here */ default: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Key type not supported"); } /* Probably never happens, but better make sure */ if (inlen > sizeof(buf) || outlen < modlen) LOG_FUNC_RETURN(ctx, SC_ERROR_BUFFER_TOO_SMALL); 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) sc_mem_reverse(buf, inlen); 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 */ /* TODO: -DEE assume only RSA keys will ever use _NEED_USAGE */ sc_log(ctx, "supported algorithm flags 0x%X, private key usage 0x%X", alg_info->flags, prkey->usage); if ((alg_info->flags & SC_ALGORITHM_NEED_USAGE) && ((prkey->usage & USAGE_ANY_SIGN) && (prkey->usage & USAGE_ANY_DECIPHER)) ) { size_t tmplen = sizeof(buf); if (flags & SC_ALGORITHM_RSA_RAW) { r = sc_pkcs15_decipher(p15card, obj,flags, in, inlen, out, outlen); LOG_FUNC_RETURN(ctx, r); } if (modlen > tmplen) LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "Buffer too small, needs recompile!"); r = sc_pkcs1_encode(ctx, flags, in, inlen, buf, &tmplen, modlen); /* no padding needed - already done */ flags &= ~SC_ALGORITHM_RSA_PADS; /* instead use raw rsa */ flags |= SC_ALGORITHM_RSA_RAW; LOG_TEST_RET(ctx, r, "Unable to add padding"); r = sc_pkcs15_decipher(p15card, obj,flags, buf, modlen, out, outlen); LOG_FUNC_RETURN(ctx, r); } /* If the card doesn't support the requested algorithm, see if we * can strip the input so a more restrictive algo can be used */ if ((flags == (SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE)) && !(alg_info->flags & (SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_HASH_NONE))) { unsigned int algo; size_t tmplen = sizeof(buf); r = sc_pkcs1_strip_digest_info_prefix(&algo, tmp, inlen, tmp, &tmplen); if (r != SC_SUCCESS || algo == SC_ALGORITHM_RSA_HASH_NONE) { sc_mem_clear(buf, sizeof(buf)); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); } flags &= ~SC_ALGORITHM_RSA_HASH_NONE; flags |= algo; inlen = tmplen; } r = sc_get_encoding_flags(ctx, flags, alg_info->flags, &pad_flags, &sec_flags); if (r != SC_SUCCESS) { sc_mem_clear(buf, sizeof(buf)); LOG_FUNC_RETURN(ctx, r); } senv.algorithm_flags = sec_flags; sc_log(ctx, "DEE flags:0x%8.8x alg_info->flags:0x%8.8x pad:0x%8.8x sec:0x%8.8x", flags, alg_info->flags, pad_flags, sec_flags); /* add the padding bytes (if necessary) */ if (pad_flags != 0) { size_t tmplen = sizeof(buf); r = sc_pkcs1_encode(ctx, pad_flags, tmp, inlen, tmp, &tmplen, modlen); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to add padding"); 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 > sizeof(buf)) return SC_ERROR_BUFFER_TOO_SMALL; memmove(tmp+modlen-inlen, tmp, inlen); memset(tmp, 0, modlen-inlen); } inlen = modlen; } senv.operation = SC_SEC_OPERATION_SIGN; /* optional keyReference attribute (the default value is -1) */ if (prkey->key_reference >= 0) { senv.key_ref_len = 1; senv.key_ref[0] = prkey->key_reference & 0xFF; senv.flags |= SC_SEC_ENV_KEY_REF_PRESENT; } r = sc_lock(p15card->card); LOG_TEST_RET(ctx, r, "sc_lock() failed"); sc_log(ctx, "Private key path '%s'", sc_print_path(&prkey->path)); if (prkey->path.len != 0 || prkey->path.aid.len != 0) { r = select_key_file(p15card, prkey, &senv); if (r < 0) { sc_unlock(p15card->card); LOG_TEST_RET(ctx, r,"Unable to select private key file"); } } r = sc_set_security_env(p15card->card, &senv, 0); if (r < 0) { sc_unlock(p15card->card); LOG_TEST_RET(ctx, r, "sc_set_security_env() failed"); } r = sc_compute_signature(p15card->card, tmp, inlen, out, outlen); if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) if (sc_pkcs15_pincache_revalidate(p15card, obj) == SC_SUCCESS) r = sc_compute_signature(p15card->card, tmp, inlen, out, outlen); sc_mem_clear(buf, sizeof(buf)); sc_unlock(p15card->card); LOG_TEST_RET(ctx, r, "sc_compute_signature() failed"); LOG_FUNC_RETURN(ctx, r); } opensc-0.13.0/src/libopensc/sec.c0000644000015201777760000001767512057406034013530 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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; assert(card != NULL && crgram != NULL && out != NULL); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); 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; assert(card != NULL); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); 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_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { int r; assert(card != NULL); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); 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; assert(card != NULL); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); 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; assert(card != NULL); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unsupported PIN operation (%d)", data->cmd); } else { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Use of pin pad not supported by card driver"); r = SC_ERROR_NOT_SUPPORTED; } 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 allways 16 nibbles (8 bytes) and look like this: * 0x2 + len + pin_in_BCD + paddingnibbles * in which the paddingnibble = 0xF * E.g. if PIN = 12345, then sbuf = {0x24, 0x12, 0x34, 0x5F, 0xFF, 0xFF, 0xFF, 0xFF} * E.g. if PIN = 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 | 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++) { 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 i; } opensc-0.13.0/src/libopensc/pkcs15-prkey.c0000644000015201777760000006735212057406034015211 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include "internal.h" #include "asn1.h" #include "pkcs15.h" #include "common/compat_strlcpy.h" #ifdef ENABLE_OPENSSL #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x10000000L #ifndef OPENSSL_NO_EC #include #endif #endif #endif /* * in src/libopensc/types.h SC_MAX_SUPPORTED_ALGORITHMS defined as 8 */ #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 }, { 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 } }; #define C_ASN1_DSAKEY_I_P_ATTR_SIZE 2 static const struct sc_asn1_entry c_asn1_dsakey_i_p_attr[C_ASN1_DSAKEY_I_P_ATTR_SIZE] = { { "path", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_DSAKEY_VALUE_ATTR_SIZE 3 static const struct sc_asn1_entry c_asn1_dsakey_value_attr[C_ASN1_DSAKEY_VALUE_ATTR_SIZE] = { { "path", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { "pathProtected",SC_ASN1_STRUCT, SC_ASN1_CTX | 1 | SC_ASN1_CONS, 0, NULL, NULL}, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_DSAKEY_ATTR_SIZE 2 static const struct sc_asn1_entry c_asn1_dsakey_attr[C_ASN1_DSAKEY_ATTR_SIZE] = { { "value", SC_ASN1_CHOICE, 0, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_PRK_DSA_ATTR_SIZE 2 static const struct sc_asn1_entry c_asn1_prk_dsa_attr[C_ASN1_PRK_DSA_ATTR_SIZE] = { { "privateDSAKeyAttributes", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; /* * The element fieldSize is a proprietary extension to ISO 7816-15, providing to the middleware * the size of the underlying ECC field. This value is required for determine a proper size for * buffer allocations. The field follows the definition for modulusLength in RSA keys */ #define C_ASN1_ECCKEY_ATTR 4 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 }, { 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 5 static const struct sc_asn1_entry c_asn1_prkey[C_ASN1_PRKEY_SIZE] = { { "privateRSAKey", SC_ASN1_PKCS15_OBJECT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "privateECCKey", SC_ASN1_PKCS15_OBJECT, 0 | SC_ASN1_CTX | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "privateDSAKey", SC_ASN1_PKCS15_OBJECT, 2 | SC_ASN1_CTX | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "privateGOSTR3410Key", SC_ASN1_PKCS15_OBJECT, 4 | SC_ASN1_CTX | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; 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_dsakey_attr[C_ASN1_DSAKEY_ATTR_SIZE]; struct sc_asn1_entry asn1_prk_dsa_attr[C_ASN1_PRK_DSA_ATTR_SIZE]; struct sc_asn1_entry asn1_dsakey_i_p_attr[C_ASN1_DSAKEY_I_P_ATTR_SIZE]; struct sc_asn1_entry asn1_dsakey_value_attr[C_ASN1_DSAKEY_VALUE_ATTR_SIZE]; struct sc_asn1_entry asn1_gostr3410key_attr[C_ASN1_GOSTR3410KEY_ATTR_SIZE]; struct sc_asn1_entry asn1_prk_gostr3410_attr[C_ASN1_PRK_GOSTR3410_ATTR_SIZE]; struct sc_asn1_entry asn1_ecckey_attr[C_ASN1_ECCKEY_ATTR]; 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 dsa_prkey_obj = {obj, asn1_com_key_attr, asn1_com_prkey_attr, asn1_prk_dsa_attr}; struct sc_asn1_pkcs15_object gostr3410_prkey_obj = {obj, asn1_com_key_attr, asn1_com_prkey_attr, asn1_prk_gostr3410_attr}; struct sc_asn1_pkcs15_object ecc_prkey_obj = { obj, asn1_com_key_attr, asn1_com_prkey_attr, asn1_prk_ecc_attr }; sc_copy_asn1_entry(c_asn1_prkey, asn1_prkey); sc_copy_asn1_entry(c_asn1_supported_algorithms, asn1_supported_algorithms); sc_copy_asn1_entry(c_asn1_prk_rsa_attr, asn1_prk_rsa_attr); sc_copy_asn1_entry(c_asn1_rsakey_attr, asn1_rsakey_attr); sc_copy_asn1_entry(c_asn1_prk_dsa_attr, asn1_prk_dsa_attr); sc_copy_asn1_entry(c_asn1_dsakey_attr, asn1_dsakey_attr); sc_copy_asn1_entry(c_asn1_dsakey_value_attr, asn1_dsakey_value_attr); sc_copy_asn1_entry(c_asn1_dsakey_i_p_attr, asn1_dsakey_i_p_attr); sc_copy_asn1_entry(c_asn1_prk_gostr3410_attr, asn1_prk_gostr3410_attr); sc_copy_asn1_entry(c_asn1_gostr3410key_attr, asn1_gostr3410key_attr); sc_copy_asn1_entry(c_asn1_prk_ecc_attr, asn1_prk_ecc_attr); 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, &dsa_prkey_obj, NULL, 0); sc_format_asn1_entry(asn1_prkey + 3, &gostr3410_prkey_obj, NULL, 0); sc_format_asn1_entry(asn1_prk_rsa_attr + 0, asn1_rsakey_attr, NULL, 0); sc_format_asn1_entry(asn1_prk_dsa_attr + 0, asn1_dsakey_attr, NULL, 0); sc_format_asn1_entry(asn1_prk_gostr3410_attr + 0, asn1_gostr3410key_attr, NULL, 0); sc_format_asn1_entry(asn1_prk_ecc_attr + 0, asn1_ecckey_attr, NULL, 0); sc_format_asn1_entry(asn1_rsakey_attr + 0, &info.path, NULL, 0); sc_format_asn1_entry(asn1_rsakey_attr + 1, &info.modulus_length, NULL, 0); sc_format_asn1_entry(asn1_dsakey_attr + 0, asn1_dsakey_value_attr, NULL, 0); sc_format_asn1_entry(asn1_dsakey_value_attr + 0, &info.path, NULL, 0); sc_format_asn1_entry(asn1_dsakey_value_attr + 1, asn1_dsakey_i_p_attr, NULL, 0); sc_format_asn1_entry(asn1_dsakey_i_p_attr + 0, &info.path, NULL, 0); sc_format_asn1_entry(asn1_gostr3410key_attr + 0, &info.path, NULL, 0); sc_format_asn1_entry(asn1_gostr3410key_attr + 1, &gostr3410_params[0], NULL, 0); sc_format_asn1_entry(asn1_gostr3410key_attr + 2, &gostr3410_params[1], NULL, 0); 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_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) return r; LOG_TEST_RET(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; } else if (asn1_prkey[2].flags & SC_ASN1_PRESENT) { obj->type = SC_PKCS15_TYPE_PRKEY_DSA; /* If the value was indirect-protected, mark the path */ if (asn1_dsakey_i_p_attr[0].flags & SC_ASN1_PRESENT) info.path.type = SC_PATH_TYPE_PATH_PROT; } else if (asn1_prkey[3].flags & SC_ASN1_PRESENT) { obj->type = SC_PKCS15_TYPE_PRKEY_GOSTR3410; assert(info.modulus_length == 0); info.modulus_length = SC_PKCS15_GOSTR3410_KEYSIZE; assert(info.params.len == 0); info.params.len = sizeof(struct sc_pkcs15_keyinfo_gostparams); info.params.data = malloc(info.params.len); if (info.params.data == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); assert(sizeof(*keyinfo_gostparams) == info.params.len); keyinfo_gostparams = info.params.data; keyinfo_gostparams->gostr3410 = gostr3410_params[0]; keyinfo_gostparams->gostr3411 = gostr3410_params[1]; keyinfo_gostparams->gost28147 = gostr3410_params[2]; } else { sc_log(ctx, "Neither RSA or DSA or GOSTR3410 or ECC key in PrKDF entry."); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ASN1_OBJECT); } if (!p15card->app || !p15card->app->ddo.aid.len) { r = sc_pkcs15_make_absolute_path(&p15card->file_app->path, &info.path); if (r < 0) { sc_pkcs15_free_key_params(&info.params); return r; } } 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) { sc_pkcs15_free_key_params(&info.params); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(obj->data, &info, sizeof(info)); sc_log(ctx, "Key Subject %s", sc_dump_hex(info.subject.value, info.subject.len)); sc_log(ctx, "Key path %s", sc_print_path(&info.path)); return 0; } 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_dsakey_attr[C_ASN1_DSAKEY_ATTR_SIZE]; struct sc_asn1_entry asn1_prk_dsa_attr[C_ASN1_PRK_DSA_ATTR_SIZE]; struct sc_asn1_entry asn1_dsakey_value_attr[C_ASN1_DSAKEY_VALUE_ATTR_SIZE]; struct sc_asn1_entry asn1_dsakey_i_p_attr[C_ASN1_DSAKEY_I_P_ATTR_SIZE]; struct sc_asn1_entry asn1_gostr3410key_attr[C_ASN1_GOSTR3410KEY_ATTR_SIZE]; struct sc_asn1_entry asn1_prk_gostr3410_attr[C_ASN1_PRK_GOSTR3410_ATTR_SIZE]; struct sc_asn1_entry asn1_ecckey_attr[C_ASN1_ECCKEY_ATTR]; 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 dsa_prkey_obj = { (struct sc_pkcs15_object *) obj, asn1_com_key_attr, asn1_com_prkey_attr, asn1_prk_dsa_attr }; struct sc_asn1_pkcs15_object gostr3410_prkey_obj = { (struct sc_pkcs15_object *) obj, asn1_com_key_attr, asn1_com_prkey_attr, 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_dsa_attr, asn1_prk_dsa_attr); sc_copy_asn1_entry(c_asn1_dsakey_attr, asn1_dsakey_attr); sc_copy_asn1_entry(c_asn1_dsakey_value_attr, asn1_dsakey_value_attr); sc_copy_asn1_entry(c_asn1_dsakey_i_p_attr, asn1_dsakey_i_p_attr); sc_copy_asn1_entry(c_asn1_prk_gostr3410_attr, asn1_prk_gostr3410_attr); sc_copy_asn1_entry(c_asn1_gostr3410key_attr, asn1_gostr3410key_attr); sc_copy_asn1_entry(c_asn1_prk_ecc_attr, asn1_prk_ecc_attr); 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_DSA: sc_format_asn1_entry(asn1_prkey + 2, &dsa_prkey_obj, NULL, 1); sc_format_asn1_entry(asn1_prk_dsa_attr + 0, asn1_dsakey_value_attr, NULL, 1); if (prkey->path.type != SC_PATH_TYPE_PATH_PROT) { /* indirect: just add the path */ sc_format_asn1_entry(asn1_dsakey_value_attr + 0, &prkey->path, NULL, 1); } else { /* indirect-protected */ sc_format_asn1_entry(asn1_dsakey_value_attr + 1, asn1_dsakey_i_p_attr, NULL, 1); sc_format_asn1_entry(asn1_dsakey_i_p_attr + 0, &prkey->path, NULL, 1); } break; case SC_PKCS15_TYPE_PRKEY_GOSTR3410: sc_format_asn1_entry(asn1_prkey + 3, &gostr3410_prkey_obj, NULL, 1); sc_format_asn1_entry(asn1_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; ERR_load_ERR_strings(); ERR_load_crypto_strings(); sc_log(ctx, "CertValue(%i) %p", cert_object->content.len, cert_object->content.value); mem = BIO_new_mem_buf(cert_object->content.value, cert_object->content.len); if (!mem) LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "MEM buffer allocation error"); x = d2i_X509_bio(mem, NULL); if (!x) LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "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, "OpenSSL allocation error"); ptr = buff; rv = i2d_X509_NAME(X509_get_subject_name(x), &ptr); if (rv <= 0) 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(); ERR_free_strings(); 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_free_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_DSA: free(key->u.dsa.pub.data); free(key->u.dsa.p.data); free(key->u.dsa.q.data); free(key->u.dsa.g.data); free(key->u.dsa.priv.data); break; case SC_ALGORITHM_GOSTR3410: assert(key->u.gostr3410.d.data); free(key->u.gostr3410.d.data); break; case SC_ALGORITHM_EC: if (key->u.ec.params.der.value) free(key->u.ec.params.der.value); if (key->u.ec.params.named_curve) free(key->u.ec.params.named_curve); if (key->u.ec.privateD.data) free(key->u.ec.privateD.data); if (key->u.ec.ecpointQ.value) free(key->u.ec.ecpointQ.value); break; } } void sc_pkcs15_free_prkey_info(sc_pkcs15_prkey_info_t *key) { if (key->subject.value) free(key->subject.value); sc_pkcs15_free_key_params(&key->params); 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); 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; switch (pk->type) { case EVP_PKEY_RSA: { struct sc_pkcs15_prkey_rsa *dst = &pkcs15_key->u.rsa; RSA *src = EVP_PKEY_get1_RSA(pk); 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)) 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); } RSA_free(src); break; } case EVP_PKEY_DSA: { struct sc_pkcs15_prkey_dsa *dst = &pkcs15_key->u.dsa; DSA *src = EVP_PKEY_get1_DSA(pk); pkcs15_key->algorithm = SC_ALGORITHM_DSA; sc_pkcs15_convert_bignum(&dst->pub, src->pub_key); sc_pkcs15_convert_bignum(&dst->p, src->p); sc_pkcs15_convert_bignum(&dst->q, src->q); sc_pkcs15_convert_bignum(&dst->g, src->g); sc_pkcs15_convert_bignum(&dst->priv, src->priv_key); DSA_free(src); break; } #if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) case NID_id_GostR3410_2001: { struct sc_pkcs15_prkey_gostr3410 *dst = &pkcs15_key->u.gostr3410; EC_KEY *src = EVP_PKEY_get0(pk); assert(src); pkcs15_key->algorithm = SC_ALGORITHM_GOSTR3410; assert(EC_KEY_get0_private_key(src)); sc_pkcs15_convert_bignum(&dst->d, EC_KEY_get0_private_key(src)); break; } case EVP_PKEY_EC: { struct sc_pkcs15_prkey_ec *dst = &pkcs15_key->u.ec; EC_KEY *src = NULL; const EC_GROUP *grp = NULL; unsigned char buf[255]; size_t buflen = 255; int nid; src = EVP_PKEY_get0(pk); assert(src); assert(EC_KEY_get0_private_key(src)); assert(EC_KEY_get0_public_key(src)); pkcs15_key->algorithm = SC_ALGORITHM_EC; if (!sc_pkcs15_convert_bignum(&dst->privateD, EC_KEY_get0_private_key(src))) return SC_ERROR_INCOMPATIBLE_KEY; grp = EC_KEY_get0_group(src); if(grp == 0) return SC_ERROR_INCOMPATIBLE_KEY; /* get curve name */ nid = EC_GROUP_get_curve_name(grp); if(nid != 0) dst->params.named_curve = strdup(OBJ_nid2sn(nid)); /* Decode EC_POINT from a octet string */ buflen = EC_POINT_point2oct(grp, (const EC_POINT *) EC_KEY_get0_public_key(src), POINT_CONVERSION_UNCOMPRESSED, buf, buflen, NULL); /* copy the public key */ if (buflen > 0) { dst->ecpointQ.value = malloc(buflen); 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 /* OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) */ default: return SC_ERROR_NOT_SUPPORTED; } return SC_SUCCESS; #else return SC_ERROR_NOT_IMPLEMENTED; #endif } opensc-0.13.0/src/libopensc/ctbcs.h0000644000015201777760000001503312057406034014043 00000000000000/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 paramaters 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 /* Synchoronous ICC, */ #define CTBCS_SW2_RESET_SYNC_OK 0x00 /* reset successful */ #define CTBCS_SW1_RESET_ASYNC_OK 0x90 /* Asynchoronous 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 /* Synchoronous ICC, */ #define CTBCS_SW2_REQUEST_SYNC_OK 0x00 /* reset successful */ #define CTBCS_SW1_REQUEST_ASYNC_OK 0x90 /* Asynchoronous 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 succesful, */ #define CTBCS_SW2_EJECT_OK 0x00 #define CTBCS_SW1_EJECT_REMOVED 0x90 /* Command succesful, */ #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.13.0/src/libopensc/iasecc.h0000644000015201777760000000716712057406034014205 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 #define ISO7812_PAN_LENGTH 0x0C #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; }; int sm_iasecc_rsa_generate(struct sc_card *card, unsigned security_condition, struct iasecc_sdo *sdo); #endif opensc-0.13.0/src/libopensc/card-mcrd.c0000644000015201777760000011776612057406034014614 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" #include "esteid.h" static struct sc_atr_table mcrd_atrs[] = { {"3B:FF:94:00:FF:80:B1:FE:45:1F:03:00:68:D2:76:00:00:28:FF:05:1E:31:80:00:90:00:23", NULL, "Micardo 2.1/German BMI/D-Trust", SC_CARD_TYPE_MCRD_GENERIC, 0, NULL}, {"3b:6f:00:ff:00:68:d2:76:00:00:28:ff:05:1e:31:80:00:90:00", NULL, "D-Trust", SC_CARD_TYPE_MCRD_DTRUST, 0, NULL}, {"3b:ff:11:00:ff:80:b1:fe:45:1f:03:00:68:d2:76:00:00:28:ff:05:1e:31:80:00:90:00:a6", NULL, "D-Trust", SC_CARD_TYPE_MCRD_DTRUST, 0, NULL}, /* Certain pcsc-lite versions (1.5.3 for example on Ubuntu 10.04) incorrectly trunkate the wram ATR to the length of the cold ATR */ /* See opensc.conf for further information */ {"3B:FE:94:00:FF:80:B1:FA:45:1F:03:45:73:74:45:49:44:20", NULL, "Broken EstEID 1.1 warm", SC_CARD_TYPE_MCRD_ESTEID_V11, 0, NULL}, {"3b:fe:94:00:ff:80:b1:fa:45:1f:03:45:73:74:45:49:44:20:76:65:72:20:31:2e:30:43", NULL, "EstEID 1.0 cold", SC_CARD_TYPE_MCRD_ESTEID_V10, 0, NULL}, {"3b:6e:00:ff:45:73:74:45:49:44:20:76:65:72:20:31:2e:30", NULL, "EstEID 1.0 cold", SC_CARD_TYPE_MCRD_ESTEID_V10, 0, NULL}, {"3b:de:18:ff:c0:80:b1:fe:45:1f:03:45:73:74:45:49:44:20:76:65:72:20:31:2e:30:2b", NULL, "EstEID 1.0 cold 2006", SC_CARD_TYPE_MCRD_ESTEID_V10, 0, NULL}, {"3b:5e:11:ff:45:73:74:45:49:44:20:76:65:72:20:31:2e:30", NULL, "EstEID 1.0 warm 2006", SC_CARD_TYPE_MCRD_ESTEID_V10, 0, NULL}, {"3b:6e:00:00:45:73:74:45:49:44:20:76:65:72:20:31:2e:30", NULL, "EstEID 1.1 cold", SC_CARD_TYPE_MCRD_ESTEID_V11, 0, NULL}, {"3B:FE:18:00:00:80:31:FE:45:45:73:74:45:49:44:20:76:65:72:20:31:2E:30:A8", NULL, "EstEID 3.0 (dev1) cold", SC_CARD_TYPE_MCRD_ESTEID_V30, 0, NULL}, {"3B:FE:18:00:00:80:31:FE:45:80:31:80:66:40:90:A4:56:1B:16:83:01:90:00:86", NULL, "EstEID 3.0 (dev1) warm", SC_CARD_TYPE_MCRD_ESTEID_V30, 0, NULL}, {"3b:fe:18:00:00:80:31:fe:45:80:31:80:66:40:90:a4:16:2a:00:83:01:90:00:e1", NULL, "EstEID 3.0 (dev2) warm", SC_CARD_TYPE_MCRD_ESTEID_V30, 0, NULL}, {"3b:fe:18:00:00:80:31:fe:45:80:31:80:66:40:90:a4:16:2a:00:83:0f:90:00:ef", NULL, "EstEID 3.0 (18.01.2011) warm", SC_CARD_TYPE_MCRD_ESTEID_V30, 0, NULL}, {NULL, NULL, NULL, 0, 0, NULL} }; static unsigned char EstEID_v3_AID[] = {0xF0, 0x45, 0x73, 0x74, 0x45, 0x49, 0x44, 0x20, 0x76, 0x65, 0x72, 0x20, 0x31, 0x2E, 0x30}; static struct sc_card_operations mcrd_ops; static struct sc_card_driver mcrd_drv = { "MICARDO 2.1 / EstEID 1.0 - 3.0", "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; int recno; size_t datalen; u8 data[1]; }; struct keyd_record_s { struct keyd_record_s *next; 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. */ size_t curpathlen; /* Length of this path or 0 if unknown. */ int is_ef; /* True if the path points to an EF. */ 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; assert(!priv->is_ef); if (!priv->curpathlen) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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; } } } /* Some functionality straight from the EstEID manual. * 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[SC_MAX_APDU_BUFFER_SIZE]; assert(card != NULL); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xA4); sbuf[0] = 0x83; sbuf[1] = 0x00; apdu.data = sbuf; apdu.lc = 2; apdu.datalen = 2; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } static int mcrd_delete_ref_to_signkey(sc_card_t * card) { sc_apdu_t apdu; int r; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; assert(card != NULL); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xB6); sbuf[0] = 0x83; sbuf[1] = 0x00; apdu.data = sbuf; apdu.lc = 2; apdu.datalen = 2; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } static int mcrd_set_decipher_key_ref(sc_card_t * card, int key_reference) { sc_apdu_t apdu; sc_path_t path; int r; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 keyref_data[SC_ESTEID_KEYREF_FILE_RECLEN]; assert(card != NULL); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xB8); /* track the active keypair */ sc_format_path("0033", &path); r = sc_select_file(card, &path, NULL); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Can't select keyref info file 0x0033"); r = sc_read_record(card, 1, keyref_data, SC_ESTEID_KEYREF_FILE_RECLEN, SC_RECORD_BY_REC_NR); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Can't read keyref info file!"); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "authkey reference 0x%02x%02x\n", keyref_data[9], keyref_data[10]); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "signkey reference 0x%02x%02x\n", keyref_data[19], keyref_data[20]); sbuf[0] = 0x83; sbuf[1] = 0x03; sbuf[2] = 0x80; switch (key_reference) { case 1: sbuf[3] = keyref_data[9]; sbuf[4] = keyref_data[10]; break; case 2: sbuf[3] = keyref_data[19]; sbuf[4] = keyref_data[20]; break; } apdu.data = sbuf; apdu.lc = 5; apdu.datalen = 5; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } int is_esteid_card(sc_card_t *card) { switch(card->type) { case SC_CARD_TYPE_MCRD_ESTEID_V10: case SC_CARD_TYPE_MCRD_ESTEID_V11: case SC_CARD_TYPE_MCRD_ESTEID_V30: return 1; } return 0; } static int mcrd_match_card(sc_card_t * card) { int i = 0; 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; int r; sc_path_t tmppath; sc_apdu_t apdu; priv = calloc(1, sizeof *priv); if (!priv) return SC_ERROR_OUT_OF_MEMORY; card->drv_data = priv; card->cla = 0x00; card->caps = SC_CARD_CAP_RNG; if (is_esteid_card(card)) { /* Reset the MULTOS card to get to a known state */ if (card->type == SC_CARD_TYPE_MCRD_ESTEID_V11) sc_reset(card, 0); /* Select the EstEID AID to get to a known state. * For some reason a reset is required as well... */ if (card->type == SC_CARD_TYPE_MCRD_ESTEID_V30) { flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_HASH_SHA1 | SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_SHA256; /* EstEID v3.0 has 2048 bit keys */ _sc_card_add_rsa_alg(card, 2048, flags, 0); sc_reset(card, 0); sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xA4, 0x04, 0x00); apdu.lc = sizeof(EstEID_v3_AID); apdu.data = EstEID_v3_AID; apdu.datalen = sizeof(EstEID_v3_AID); apdu.resplen = 0; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "SELECT AID: %02X%02X", apdu.sw1, apdu.sw2); if(apdu.sw1 != 0x90 && apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_CARD_CMD_FAILED); } else { /* EstEID v1.0 and 1.1 have 1024 bit keys */ flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_SHA1; _sc_card_add_rsa_alg(card, 1024, flags, 0); } } else { flags = SC_ALGORITHM_RSA_RAW |SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE; _sc_card_add_rsa_alg(card, 512, flags, 0); _sc_card_add_rsa_alg(card, 768, flags, 0); _sc_card_add_rsa_alg(card, 1024, flags, 0); } priv->curpath[0] = MFID; priv->curpathlen = 1; sc_format_path ("3f00", &tmppath); sc_select_file (card, &tmppath, NULL); /* Not needed for the fixed EstEID profile */ if (!is_esteid_card(card)) 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); 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, 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); /* Read rule file. Note that we bypass our cache here. */ r = select_part(card, MCRD_SEL_EF, EF_Rule, NULL); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "selecting EF_Rule failed"); for (recno = 1;; recno++) { u8 recbuf[256]; r = sc_read_record(card, recno, recbuf, sizeof(recbuf), SC_RECORD_BY_REC_NR); if (r == SC_ERROR_RECORD_NOT_FOUND) break; else if (r < 0) { SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } else { rule = malloc(sizeof *rule + r); if (!rule) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); rule->recno = recno; rule->datalen = r; memcpy(rule->data, recbuf, r); rule->next = dfi->rule_file; dfi->rule_file = rule; } } sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "new EF_Rule file loaded (%d records)\n", recno - 1); /* Read the KeyD file. Note that we bypass our cache here. */ r = select_part(card, MCRD_SEL_EF, EF_KeyD, NULL); if (r == SC_ERROR_FILE_NOT_FOUND) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "no EF_KeyD file available\n"); return 0; /* That is okay. */ } SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "selecting EF_KeyD failed"); for (recno = 1;; recno++) { u8 recbuf[256]; r = sc_read_record(card, recno, recbuf, sizeof(recbuf), SC_RECORD_BY_REC_NR); if (r == SC_ERROR_RECORD_NOT_FOUND) break; else if (r < 0) { SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } else { keyd = malloc(sizeof *keyd + r); if (!keyd) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); keyd->recno = recno; keyd->datalen = r; memcpy(keyd->data, recbuf, r); keyd->next = dfi->keyd_file; dfi->keyd_file = keyd; } } sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "new EF_KeyD file loaded (%d records)\n", recno - 1); /* FIXME: Do we need to restore the current DF? I guess it is not required, but we could try to do so by selecting 3fff? */ return 0; } /* Return the SE number from the keyD for the FID. If ref_data is not NULL the reference data is returned; this shoudl be an array of at least 2 bytes. Returns -1 on error. */ static int get_se_num_from_keyd(sc_card_t * card, unsigned short fid, u8 * ref_data) { sc_context_t *ctx = card->ctx; struct df_info_s *dfi; struct keyd_record_s *keyd; size_t len, taglen; const u8 *p, *tag; char dbgbuf[2048]; u8 fidbuf[2]; fidbuf[0] = fid >> 8; fidbuf[1] = fid; dfi = get_df_info(card); if (!dfi || !dfi->keyd_file) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "EF_keyD not loaded\n"); return -1; } for (keyd = dfi->keyd_file; keyd; keyd = keyd->next) { p = keyd->data; len = keyd->datalen; sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, p, len, dbgbuf, sizeof dbgbuf); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "keyd no %d:\n%s", keyd->recno, dbgbuf); tag = sc_asn1_find_tag(ctx, p, len, 0x83, &taglen); if (!tag || taglen != 4 || !(tag[2] == fidbuf[0] && tag[3] == fidbuf[1])) continue; /* Found a matching record. */ if (ref_data) { ref_data[0] = tag[0]; ref_data[1] = tag[1]; } /* Look for the SE-DO */ tag = sc_asn1_find_tag(ctx, p, len, 0x7B, &taglen); if (!tag || !taglen) continue; p = tag; len = taglen; /* And now look for the referenced SE. */ tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen); if (!tag || taglen != 1) continue; return *tag; /* found. */ } sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "EF_keyD for %04hx not found\n", fid); return -1; } /* Process an ARR (7816-9/8.5.4) and setup the ACL. */ static void process_arr(sc_card_t * card, sc_file_t * file, const u8 * buf, size_t buflen) { 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "referenced EF_rule record %d not found\n", *buf); return; } sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, rule->data, rule->datalen, dbgbuf, sizeof dbgbuf); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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) break; left -= (p - buf); tag |= cla; if (tag == 0x80 && taglen != 1) { skip = 1; } else if (tag == 0x80) { /* AM byte. */ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " AM_DO: %02x\n", *p); skip = 0; } else if (tag >= 0x81 && tag <= 0x8f) { /* Cmd description */ sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, p, taglen, dbgbuf, sizeof dbgbuf); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " AM_DO: cmd[%s%s%s%s] %s", (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) { sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, p, taglen, dbgbuf, sizeof dbgbuf); switch (tag) { case 0x90: /* Always */ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " SC: always\n"); break; case 0x97: /* Never */ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " SC: never\n"); break; case 0xA4: /* Authentication, value is a CRT. */ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " SC: auth %s", dbgbuf); break; case 0xB4: case 0xB6: case 0xB8: /* Cmd or resp with SM, value is a CRT. */ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " SC: cmd/resp %s", dbgbuf); break; case 0x9E: /* Security Condition byte. */ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " SC: condition %s", dbgbuf); break; case 0xA0: /* OR template. */ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " SC: OR\n"); break; case 0xAF: /* AND template. */ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "processing FCI bytes\n"); /* File identifier. */ tag = sc_asn1_find_tag(ctx, p, len, 0x83, &taglen); if (tag != NULL && taglen == 2) { file->id = (tag[0] << 8) | tag[1]; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " 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_debug(ctx, SC_LOG_DEBUG_NORMAL, " bytes in file: %d\n", bytes); file->size = bytes; } if (tag == NULL) { tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen); if (tag != NULL && taglen >= 2) { int bytes = (tag[0] << 8) + tag[1]; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " bytes in file: %d\n", bytes); file->size = 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_debug(ctx, SC_LOG_DEBUG_NORMAL, " 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_debug(ctx, SC_LOG_DEBUG_NORMAL, " type: %s\n", type); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " 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] = tag[i]; else name[i] = '?'; } name[taglen] = 0; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " 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 && !is_esteid_card(card)) { process_arr(card, file, 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, file, 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(card, &apdu, buflen?SC_APDU_CASE_4_SHORT:SC_APDU_CASE_2_SHORT, 0xA4, kind, p2); apdu.data = buf; apdu.datalen = buflen; apdu.lc = apdu.datalen; apdu.resp = resbuf; apdu.resplen = sizeof(resbuf); apdu.le = 256; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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(); (*file)->type = SC_FILE_TYPE_DF; return SC_SUCCESS; } } if (p2 == 0x04 && apdu.resp[0] == 0x62) { *file = sc_file_new(); if (!*file) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); /* EstEID v3.0 cards are buggy and sometimes return a double 0x62 tag */ if (card->type == SC_CARD_TYPE_MCRD_ESTEID_V30 && apdu.resp[2] == 0x62) process_fcp(card, *file, apdu.resp + 4, apdu.resp[3]); else process_fcp(card, *file, apdu.resp + 2, apdu.resp[1]); return SC_SUCCESS; } if (p2 != 0x0C && apdu.resp[0] == 0x6F) { *file = sc_file_new(); if (!*file) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); if (apdu.resp[1] <= apdu.resplen) 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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) SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to select DF"); priv->curpath[priv->curpathlen] = *pathptr; priv->curpathlen++; } priv->is_ef = found_ef; if (!found_ef && !is_esteid_card(card)) 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); assert(!priv->curpathlen || priv->curpath[0] == MFID); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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. */ assert(priv->curpathlen > 1); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to select MF"); priv->curpath[0] = pathptr[0]; priv->curpathlen = 1; priv->is_ef = 0; } if (priv->is_ef) { assert(priv->curpathlen > 1); priv->curpathlen--; priv->is_ef = 0; } 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); assert(!priv->curpathlen || priv->curpath[0] == MFID); 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 { assert(priv->curpathlen > 1); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to select MF"); priv->curpath[0] = pathptr[0]; priv->curpathlen = 1; priv->is_ef = 0; } if (priv->is_ef) { assert(priv->curpathlen > 1); priv->curpathlen--; priv->is_ef = 0; } 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); { char line[256], *linep; size_t i; linep = line; linep += sprintf(linep, "ef=%d, curpath=", priv->is_ef); for (i = 0; i < priv->curpathlen; i++) { sprintf(linep, "%04X", priv->curpath[i]); linep += 4; } strcpy(linep, "\n"); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, line); } 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; pathptr = pathtmp; for (n = 0; n < path->len; n += 2) pathptr[n >> 1] = (path->value[n] << 8) | path->value[n + 1]; pathlen = path->len >> 1; 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); } } } { char line[256], *linep = line; size_t i; linep += sprintf(linep, " result=%d, ef=%d, curpath=", r, priv->is_ef); for (i = 0; i < priv->curpathlen; i++) { sprintf(linep, "%04X", priv->curpath[i]); linep += 4; } strcpy(linep, "\n"); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, line); } return r; } /* Crypto operations */ static int mcrd_restore_se(sc_card_t * card, int se_num) { sc_apdu_t apdu; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 0xF3, se_num); r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } /* It seems that MICARDO does not fully comply with ISO, so I use values gathered from peeking actual signing opeations using a different system. It has been generalized [?] and modified by information coming from openpgp card implementation, EstEID 'manual' and some other sources. -mp */ static int mcrd_set_security_env(sc_card_t * card, const sc_security_env_t * env, int se_num) { struct mcrd_priv_data *priv = DRVDATA(card); sc_apdu_t apdu; sc_path_t tmppath; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 *p; int r, locked = 0; assert(card != NULL && env != NULL); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); /* special environment handling for esteid, stolen from openpgp */ if (is_esteid_card(card)) { /* some sanity checks */ if (env->flags & SC_SEC_ENV_ALG_PRESENT) { if (env->algorithm != SC_ALGORITHM_RSA) return SC_ERROR_INVALID_ARGUMENTS; } if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || env->key_ref_len != 1) return SC_ERROR_INVALID_ARGUMENTS; /* Make sure we always start from MF */ sc_format_path ("3f00", &tmppath); sc_select_file (card, &tmppath, NULL); /* We now know that cache is not valid */ select_esteid_df(card); switch (env->operation) { case SC_SEC_OPERATION_DECIPHER: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Using keyref %d to dechiper\n", env->key_ref[0]); mcrd_restore_se(card, 6); mcrd_delete_ref_to_authkey(card); mcrd_delete_ref_to_signkey(card); mcrd_set_decipher_key_ref(card, env->key_ref[0]); break; case SC_SEC_OPERATION_SIGN: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Using keyref %d to sign\n", env->key_ref[0]); mcrd_restore_se(card, 1); break; default: return SC_ERROR_INVALID_ARGUMENTS; } priv->sec_env = *env; return 0; } if (card->type == SC_CARD_TYPE_MCRD_DTRUST || card->type == SC_CARD_TYPE_MCRD_GENERIC) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Using SC_CARD_TYPE_MCRD_DTRUST\n"); /* some sanity checks */ if (env->flags & SC_SEC_ENV_ALG_PRESENT) { if (env->algorithm != SC_ALGORITHM_RSA) return SC_ERROR_INVALID_ARGUMENTS; } if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || env->key_ref_len != 1) return SC_ERROR_INVALID_ARGUMENTS; switch (env->operation) { case SC_SEC_OPERATION_DECIPHER: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Using keyref %d to dechiper\n", env->key_ref[0]); mcrd_delete_ref_to_authkey(card); mcrd_delete_ref_to_signkey(card); mcrd_set_decipher_key_ref(card, env->key_ref[0]); break; case SC_SEC_OPERATION_SIGN: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Using keyref %d to sign\n", env->key_ref[0]); break; default: return SC_ERROR_INVALID_ARGUMENTS; } priv->sec_env = *env; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0); apdu.le = 0; p = sbuf; switch (env->operation) { case SC_SEC_OPERATION_DECIPHER: apdu.p1 = 0x41; apdu.p2 = 0xB8; break; case SC_SEC_OPERATION_SIGN: apdu.p1 = 0x41; apdu.p2 = 0xB6; break; default: return SC_ERROR_INVALID_ARGUMENTS; } *p++ = 0x83; *p++ = 0x03; *p++ = 0x80; if (card->type == SC_CARD_TYPE_MCRD_DTRUST || card->type == SC_CARD_TYPE_MCRD_GENERIC) { unsigned short fid; fid = env->key_ref[0]; *p = fid; p++; *p = 0; p++; } else if (is_esteid_card(card)) { if ((env->flags & SC_SEC_ENV_FILE_REF_PRESENT) && env->file_ref.len > 1) { unsigned short fid; int num; fid = env->file_ref.value[env->file_ref.len - 2] << 8; fid |= env->file_ref.value[env->file_ref.len - 1]; num = get_se_num_from_keyd(card, fid, p); if (num != -1) { /* Need to restore the security environmnet. */ if (num) { r = mcrd_restore_se(card, num); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "mcrd_enable_se failed"); } p += 2; } } } else { return SC_ERROR_INVALID_ARGUMENTS; } r = p - sbuf; apdu.lc = r; apdu.datalen = r; apdu.data = sbuf; apdu.resplen = 0; if (se_num > 0) { r = sc_lock(card); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed"); locked = 1; } if (apdu.datalen != 0) { r = sc_transmit_apdu(card, &apdu); if (r) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s: APDU transmit failed", sc_strerror(r)); goto err; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s: Card returned error", sc_strerror(r)); goto err; } } if (se_num <= 0) return 0; sc_unlock(card); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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 = &priv->sec_env; int r; sc_apdu_t apdu; assert(card != NULL && data != NULL && out != NULL); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); if (env->operation != SC_SEC_OPERATION_SIGN) return SC_ERROR_INVALID_ARGUMENTS; if (datalen > 255) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Will compute signature (%d) for %d (0x%02x) bytes using key %d algorithm %d flags %d\n", env->operation, datalen, datalen, env->key_ref[0], env->algorithm, env->algorithm_flags); switch (env->key_ref[0]) { case SC_ESTEID_AUTH: /* authentication key */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x88, 0, 0); break; default: sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A); } apdu.lc = datalen; apdu.data = data; apdu.datalen = datalen; apdu.le = 0x80; apdu.resp = out; apdu.resplen = outlen; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 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) { int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); data->pin1.offset = 5; data->pin1.length_offset = 4; data->pin2.offset = 5; data->pin2.length_offset = 4; if (is_esteid_card(card) && data->cmd == SC_PIN_CMD_GET_INFO) { sc_path_t tmppath; u8 buf[16]; int ref_to_record[] = {3,1,2}; /* the file with key pin info (tries left) 4.5 EF_PwdC */ /* XXX: cheat the file path cache by always starting fresh from MF */ sc_format_path ("3f00", &tmppath); r = sc_select_file (card, &tmppath, NULL); if (r < 0) return SC_ERROR_INTERNAL; sc_format_path ("3f000016", &tmppath); r = sc_select_file (card, &tmppath, NULL); if (r < 0) return SC_ERROR_INTERNAL; /* read the number of tries left for the PIN */ r = sc_read_record (card, ref_to_record[data->pin_reference], buf, sizeof(buf), SC_RECORD_BY_REC_NR); if (r < 0) return SC_ERROR_INTERNAL; if (buf[0] != 0x80 || buf[3] != 0x90) return SC_ERROR_INTERNAL; data->pin1.tries_left = buf[5]; data->pin1.max_tries = buf[2]; return SC_SUCCESS; } if (card->type == SC_CARD_TYPE_MCRD_DTRUST || card->type == SC_CARD_TYPE_MCRD_GENERIC) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "modify pin reference for D-Trust\n"); if (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)); } /* 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; return &mcrd_drv; } struct sc_card_driver *sc_get_mcrd_driver(void) { return sc_get_driver(); } opensc-0.13.0/src/libopensc/authentic.h0000644000015201777760000001174312057406034014735 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.13.0/src/libopensc/reader-pcsc.c0000644000015201777760000021452712057406034015141 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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 "pace.h" /* Logging */ #define PCSC_TRACE(reader, desc, rv) do { sc_log(reader->ctx, "%s:" desc ": 0x%08lx\n", reader->name, rv); } while (0) #define PCSC_LOG(ctx, desc, rv) do { sc_log(ctx, desc ": 0x%08lx\n", rv); } while (0) /* Utility for handling big endian IOCTL codes. */ #define dw2i_be(a, x) ((((((a[x] << 8) + a[x+1]) << 8) + a[x+2]) << 8) + a[x+3]) #define GET_PRIV_DATA(r) ((struct pcsc_private_data *) (r)->drv_data) struct pcsc_global_private_data { SCARDCONTEXT pcsc_ctx; SCARDCONTEXT pcsc_wait_ctx; int enable_pinpad; int enable_pace; 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; }; 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 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 /* Older pcsc-lite does not have it */ case SCARD_E_NO_READERS_AVAILABLE: return SC_ERROR_NO_READERS_FOUND; #endif case SCARD_E_NO_SERVICE: /* 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 = GET_PRIV_DATA(reader); SCARD_IO_REQUEST sSendPci, sRecvPci; DWORD dwSendLength, dwRecvLength; LONG rv; SCARDHANDLE card; SC_FUNC_CALLED(reader->ctx, SC_LOG_DEBUG_NORMAL); card = priv->pcsc_card; 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 != NULL) { rv = priv->gpriv->SCardControlOLD(card, sendbuf, dwSendLength, recvbuf, &dwRecvLength); } else { 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; 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_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "reader '%s'", reader->name); sc_apdu_log(reader->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "unable to transmit"); goto out; } sc_apdu_log(reader->ctx, SC_LOG_DEBUG_NORMAL, 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; } /* 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 = GET_PRIV_DATA(reader); int old_flags = reader->flags; DWORD state, prev_state; LONG rv; sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "%s check", reader->name); if (priv->reader_state.szReader == NULL) { 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; 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_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "current state: 0x%08X", state); sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "previous state: 0x%08X", 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); return SC_ERROR_READER_DETACHED; } 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); } /* 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) reader->flags |= SC_READER_CARD_CHANGED; } } else { reader->flags |= SC_READER_CARD_CHANGED; } } else { reader->flags &= ~SC_READER_CARD_PRESENT; if (old_flags & SC_READER_CARD_PRESENT) reader->flags |= SC_READER_CARD_CHANGED; } sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "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; SC_FUNC_CALLED(reader->ctx, SC_LOG_DEBUG_NORMAL); rv = refresh_attributes(reader); if (rv != SC_SUCCESS) SC_FUNC_RETURN(reader->ctx, SC_LOG_DEBUG_VERBOSE, rv); SC_FUNC_RETURN(reader->ctx, SC_LOG_DEBUG_VERBOSE, reader->flags); } static int check_forced_protocol(sc_context_t *ctx, struct sc_atr *atr, DWORD *protocol) { scconf_block *atrblock = NULL; int ok = 0; atrblock = _sc_match_atr_block(ctx, NULL, atr); if (atrblock != NULL) { const char *forcestr; forcestr = scconf_get_str(atrblock, "force_protocol", "unknown"); if (!strcmp(forcestr, "t0")) { *protocol = SCARD_PROTOCOL_T0; ok = 1; } else if (!strcmp(forcestr, "t1")) { *protocol = SCARD_PROTOCOL_T1; ok = 1; } else if (!strcmp(forcestr, "raw")) { *protocol = SCARD_PROTOCOL_RAW; ok = 1; } if (ok) sc_log(ctx, "force_protocol: %s", forcestr); } return ok; } 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 = GET_PRIV_DATA(reader); int r; sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "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->ctx, &reader->atr, &tmp)) protocol = tmp; /* reconnect always unlocks transaction */ priv->locked = 0; rv = priv->gpriv->SCardReconnect(priv->pcsc_card, priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED, protocol, action, &active_proto); 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 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 = GET_PRIV_DATA(reader); int r; SC_FUNC_CALLED(reader->ctx, SC_LOG_DEBUG_NORMAL); r = refresh_attributes(reader); if (r != SC_SUCCESS) SC_FUNC_RETURN(reader->ctx, SC_LOG_DEBUG_VERBOSE, r); if (!(reader->flags & SC_READER_CARD_PRESENT)) SC_FUNC_RETURN(reader->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_CARD_NOT_PRESENT); 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; sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "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->ctx, &reader->atr, &tmp)) { if (active_proto != tmp) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Reconnecting to force protocol"); r = pcsc_reconnect(reader, SCARD_UNPOWER_CARD); if (r != SC_SUCCESS) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "pcsc_reconnect (to force protocol) failed", r); return r; } } sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Final protocol: %s", reader->active_protocol == SC_PROTO_T1 ? "T=1" : "T=0"); } /* 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 = GET_PRIV_DATA(reader); SC_FUNC_CALLED(reader->ctx, SC_LOG_DEBUG_NORMAL); priv->gpriv->SCardDisconnect(priv->pcsc_card, priv->gpriv->disconnect_action); reader->flags = 0; return SC_SUCCESS; } static int pcsc_lock(sc_reader_t *reader) { LONG rv; int r; struct pcsc_private_data *priv = GET_PRIV_DATA(reader); SC_FUNC_CALLED(reader->ctx, SC_LOG_DEBUG_NORMAL); rv = priv->gpriv->SCardBeginTransaction(priv->pcsc_card); switch (rv) { case SCARD_E_INVALID_HANDLE: case SCARD_E_READER_UNAVAILABLE: r = pcsc_connect(reader); if (r != SC_SUCCESS) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "pcsc_connect failed", r); return r; } /* return failure so that upper layers will be notified and try to lock again */ return SC_ERROR_READER_REATTACHED; case SCARD_W_RESET_CARD: /* try to reconnect if the card was reset by some other application */ r = pcsc_reconnect(reader, SCARD_LEAVE_CARD); if (r != SC_SUCCESS) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "pcsc_reconnect failed", r); return r; } /* 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 = GET_PRIV_DATA(reader); SC_FUNC_CALLED(reader->ctx, SC_LOG_DEBUG_NORMAL); 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 = GET_PRIV_DATA(reader); free(priv); return SC_SUCCESS; } static int pcsc_reset(sc_reader_t *reader, int do_cold_reset) { struct pcsc_private_data *priv = GET_PRIV_DATA(reader); int r; int old_locked = priv->locked; r = pcsc_reconnect(reader, do_cold_reset ? SCARD_UNPOWER_CARD : SCARD_RESET_CARD); if(r != SC_SUCCESS) return r; /* pcsc_reconnect unlocks card... try to lock it again if it was locked */ if(old_locked) r = pcsc_lock(reader); 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); #ifndef _WIN32 if (gpriv->pcsc_wait_ctx != -1) { rv = gpriv->SCardCancel(gpriv->pcsc_wait_ctx); if (rv == SCARD_S_SUCCESS) /* Also close and clear the waiting context */ rv = gpriv->SCardReleaseContext(gpriv->pcsc_wait_ctx); } #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, 0, 0, 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; } /* Defaults */ gpriv->connect_exclusive = 0; gpriv->disconnect_action = SCARD_RESET_CARD; gpriv->transaction_end_action = SCARD_LEAVE_CARD; gpriv->reconnect_action = SCARD_LEAVE_CARD; gpriv->enable_pinpad = 1; gpriv->enable_pace = 1; gpriv->provider_library = DEFAULT_PCSC_PROVIDER; gpriv->pcsc_ctx = -1; gpriv->pcsc_wait_ctx = -1; conf_block = sc_get_conf_block(ctx, "reader_driver", "pcsc", 1); if (conf_block) { gpriv->connect_exclusive = scconf_get_bool(conf_block, "connect_exclusive", gpriv->connect_exclusive); gpriv->disconnect_action = pcsc_reset_action(scconf_get_str(conf_block, "disconnect_action", "reset")); 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->enable_pace = scconf_get_bool(conf_block, "enable_pace", gpriv->enable_pace); gpriv->provider_library = scconf_get_str(conf_block, "provider_library", gpriv->provider_library); } sc_log(ctx, "PC/SC options: connect_exclusive=%d disconnect_action=%d transaction_end_action=%d reconnect_action=%d enable_pinpad=%d enable_pace=%d", gpriv->connect_exclusive, gpriv->disconnect_action, gpriv->transaction_end_action, 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 */ if (sc_dlsym(gpriv->dlhandle, "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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); if (gpriv) { if (gpriv->pcsc_ctx != -1) 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) { u8 pace_capabilities_buf[] = { PACE_FUNCTION_GetReaderPACECapabilities, /* idxFunction */ 0, 0, /* lengthInputData */ }; u8 rbuf[6]; u8 *p = rbuf; size_t rcount = sizeof rbuf; struct pcsc_private_data *priv; unsigned long flags = 0; if (!reader) goto err; priv = GET_PRIV_DATA(reader); if (!priv) goto err; if (priv->pace_ioctl) { pcsc_internal_transmit(reader, pace_capabilities_buf, sizeof pace_capabilities_buf, rbuf, &rcount, priv->pace_ioctl); 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 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 = GET_PRIV_DATA(reader); u8 feature_buf[256], rbuf[SC_MAX_APDU_BUFFER_SIZE]; DWORD rcount, feature_len, i; PCSC_TLV_STRUCTURE *pcsc_tlv; LONG rv; const char *log_disabled = "but it's disabled in configuration file"; const char *broken_readers[] = {"HP USB Smart Card Keyboard"}; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); if (gpriv->SCardControl == NULL) return; rv = gpriv->SCardControl(card_handle, CM_IOCTL_GET_FEATURE_REQUEST, NULL, 0, feature_buf, sizeof(feature_buf), &feature_len); if (rv != (LONG)SCARD_S_SUCCESS) { PCSC_TRACE(reader, "SCardControl failed", rv); return; } if ((feature_len % sizeof(PCSC_TLV_STRUCTURE)) != 0) { sc_log(ctx, "Inconsistent TLV from reader!"); return; } /* get the number of elements instead of the complete size */ feature_len /= sizeof(PCSC_TLV_STRUCTURE); pcsc_tlv = (PCSC_TLV_STRUCTURE *)feature_buf; for (i = 0; i < feature_len; i++) { sc_log(ctx, "Reader feature %02x found", pcsc_tlv[i].tag); if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_DIRECT) { priv->verify_ioctl = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_START) { priv->verify_ioctl_start = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_FINISH) { priv->verify_ioctl_finish = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_DIRECT) { priv->modify_ioctl = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_START) { priv->modify_ioctl_start = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_FINISH) { priv->modify_ioctl_finish = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_IFD_PIN_PROPERTIES) { priv->pin_properties_ioctl = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_GET_TLV_PROPERTIES) { priv->get_tlv_properties = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_EXECUTE_PACE) { priv->pace_ioctl = ntohl(pcsc_tlv[i].value); } else { sc_log(ctx, "Reader feature %02x is not supported", pcsc_tlv[i].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, 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, log_text); reader->capabilities |= SC_READER_CAP_PIN_PAD; } else { sc_log(ctx, "%s %s", log_text, log_disabled); } } /* Ignore advertised pinpad capability on readers known to be broken. Trac #340 */ for (i = 0; i < sizeof(broken_readers)/sizeof(broken_readers[0]); i++) { if (strstr(reader->name, broken_readers[i]) && (reader->capabilities & SC_READER_CAP_PIN_PAD)) { sc_log(ctx, "%s has a broken pinpad, ignoring", reader->name); reader->capabilities &= ~SC_READER_CAP_PIN_PAD; } } /* Detect display */ if (priv->pin_properties_ioctl) { rcount = sizeof(rbuf); rv = gpriv->SCardControl(card_handle, priv->pin_properties_ioctl, NULL, 0, rbuf, sizeof(rbuf), &rcount); if (rv == SCARD_S_SUCCESS) { #ifdef PIN_PROPERTIES_v5 if (rcount == sizeof(PIN_PROPERTIES_STRUCTURE_v5)) { PIN_PROPERTIES_STRUCTURE_v5 *caps = (PIN_PROPERTIES_STRUCTURE_v5 *)rbuf; 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 *)rbuf; 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 (%d/%d)", rcount, sizeof(PIN_PROPERTIES_STRUCTURE)); } } if (priv->pace_ioctl) { char *log_text = "Reader supports PACE"; if (priv->gpriv->enable_pace) { reader->capabilities |= part10_detect_pace_capabilities(reader); if (reader->capabilities & SC_READER_CAP_PACE_GENERIC) sc_log(ctx, log_text); } else { sc_log(ctx, "%s %s", log_text, log_disabled); } } } 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); if (!gpriv) { /* FIXME: this is not the correct error */ ret = SC_ERROR_NO_READERS_FOUND; goto out; } sc_log(ctx, "Probing pcsc readers"); do { if (gpriv->pcsc_ctx == -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); } 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 pcsc context"); rv = gpriv->SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &gpriv->pcsc_ctx); if (rv != SCARD_S_SUCCESS) { PCSC_LOG(ctx, "SCardEstablishContext failed", rv); ret = pcsc_to_opensc_error(rv); goto out; } rv = SCARD_E_INVALID_HANDLE; } } while (rv != SCARD_S_SUCCESS); reader_buf = malloc(sizeof(char) * reader_buf_size); 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; } 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; unsigned int i; int found = 0; for (i=0;i < sc_ctx_get_reader_count(ctx) && !found;i++) { sc_reader_t *reader2 = sc_ctx_get_reader(ctx, i); if (reader2 == NULL) { ret = SC_ERROR_INTERNAL; goto err1; } if (!strcmp(reader2->name, reader_name)) { found = 1; } } /* Reader already available, skip */ if (found) { continue; } sc_log(ctx, "Found new pcsc reader '%s'", reader_name); if ((reader = calloc(1, sizeof(sc_reader_t))) == NULL) { ret = SC_ERROR_OUT_OF_MEMORY; goto err1; } if ((priv = calloc(1, sizeof(struct pcsc_private_data))) == NULL) { ret = SC_ERROR_OUT_OF_MEMORY; goto err1; } 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; } priv->gpriv = gpriv; if (_sc_add_reader(ctx, reader)) { ret = SC_SUCCESS; /* silent ignore */ goto err1; } refresh_attributes(reader); /* check for pinpad support early, to allow opensc-tool -l display accurate information */ if (gpriv->SCardControl != NULL) { if (priv->reader_state.dwEventState & SCARD_STATE_EXCLUSIVE) continue; sc_log(ctx, "Requesting reader features ... "); 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 communcation 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); } if (rv == SCARD_S_SUCCESS) { detect_reader_features(reader, card_handle); gpriv->SCardDisconnect(card_handle, SCARD_LEAVE_CARD); } } continue; err1: if (priv != NULL) { free(priv); } if (reader != NULL) { if (reader->name) free(reader->name); free(reader); } goto out; } ret = SC_SUCCESS; out: if (reader_buf != NULL) free (reader_buf); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, 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; size_t i; unsigned int num_watch; int r = SC_ERROR_INTERNAL; DWORD dwtimeout; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); if (!event_reader && !event && reader_states) { sc_log(ctx, "free allocated reader states"); free(*reader_states); *reader_states = NULL; SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } if (reader_states == NULL || *reader_states == NULL) { rgReaderStates = calloc(sc_ctx_get_reader_count(ctx) + 2, sizeof(SCARD_READERSTATE)); if (!rgReaderStates) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); /* Find out the current status */ num_watch = sc_ctx_get_reader_count(ctx); sc_log(ctx, "Trying to watch %d readers", num_watch); for (i = 0; i < num_watch; i++) { rgReaderStates[i].szReader = sc_ctx_get_reader(ctx, i)->name; rgReaderStates[i].dwCurrentState = SCARD_STATE_UNAWARE; rgReaderStates[i].dwEventState = SCARD_STATE_UNAWARE; } #ifndef __APPLE__ /* OS X 10.6.2 does not support PnP notification */ if (event_mask & SC_EVENT_READER_ATTACHED) { rgReaderStates[i].szReader = "\\\\?PnP?\\Notification"; rgReaderStates[i].dwCurrentState = SCARD_STATE_UNAWARE; rgReaderStates[i].dwEventState = SCARD_STATE_UNAWARE; num_watch++; } #endif } else { rgReaderStates = (SCARD_READERSTATE *)(*reader_states); for (num_watch = 0; rgReaderStates[num_watch].szReader; num_watch++) sc_log(ctx, "re-use reader '%s'", rgReaderStates[num_watch].szReader); } #ifndef _WIN32 /* Establish a new context, assuming that it is called from a different thread with pcsc-lite */ if (gpriv->pcsc_wait_ctx == -1) { rv = gpriv->SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &gpriv->pcsc_wait_ctx); if (rv != SCARD_S_SUCCESS) { 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; } if (num_watch == 0) { sc_log(ctx, "No readers available, PnP notification not supported"); *event_reader = NULL; 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 */ *event = 0; 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, rsp->dwCurrentState, rsp->dwEventState); prev_state = rsp->dwCurrentState; state = rsp->dwEventState; rsp->dwCurrentState = rsp->dwEventState; if (state & SCARD_STATE_CHANGED) { /* check for hotplug events */ if (!strcmp(rgReaderStates[i].szReader, "\\\\?PnP?\\Notification")) { sc_log(ctx, "detected hotplug event"); *event |= SC_EVENT_READER_ATTACHED; *event_reader = NULL; } if ((state & SCARD_STATE_PRESENT) && !(prev_state & SCARD_STATE_PRESENT)) { sc_log(ctx, "card inserted event"); *event |= SC_EVENT_CARD_INSERTED; } if ((prev_state & SCARD_STATE_PRESENT) && !(state & SCARD_STATE_PRESENT)) { sc_log(ctx, "card removed event"); *event |= SC_EVENT_CARD_REMOVED; } if ((state & SCARD_STATE_UNKNOWN) && !(prev_state & SCARD_STATE_UNKNOWN)) { sc_log(ctx, "reader detached event"); *event |= SC_EVENT_READER_DETACHED; } if ((prev_state & SCARD_STATE_UNKNOWN) && !(state & SCARD_STATE_UNKNOWN)) { sc_log(ctx, "reader re-attached event"); *event |= SC_EVENT_READER_ATTACHED; } if (*event & event_mask) { sc_log(ctx, "Matching event 0x%02X in reader %s", *event, rsp->szReader); *event_reader = sc_ctx_get_reader_by_name(ctx, rsp->szReader); r = SC_SUCCESS; goto out; } } /* No match - copy the state so pcscd knows * what to watch out for */ /* rsp->dwCurrentState = rsp->dwEventState; */ } if (timeout == 0) { r = SC_ERROR_EVENT_TIMEOUT; 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: if (!reader_states) { free(rgReaderStates); } else if (*reader_states == NULL) { sc_log(ctx, "return allocated 'reader states'"); *reader_states = rgReaderStates; } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, 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; unsigned int tmp16; 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 effective PIN length offset is specified, use it */ if (data->pin1.length_offset > 4) { tmp |= SC_CCID_PIN_UNITS_BYTES; tmp |= (data->pin1.length_offset - 5) << 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) { 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 ) + data->pin1.max_length; 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 */ pin_verify->abData[offset++] = apdu->cla; pin_verify->abData[offset++] = apdu->ins; pin_verify->abData[offset++] = apdu->p1; pin_verify->abData[offset++] = apdu->p2; /* Copy data if not Case 1 */ if (data->pin1.length_offset != 4) { pin_verify->abData[offset++] = apdu->lc; memcpy(&pin_verify->abData[offset], apdu->data, apdu->datalen); offset += apdu->datalen; } pin_verify->ulDataLength = HOST_TO_CCID_32(offset); /* APDU size */ count = sizeof(PIN_VERIFY_STRUCTURE) + offset -1; *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; unsigned int tmp16; PIN_MODIFY_STRUCTURE *pin_modify = (PIN_MODIFY_STRUCTURE *)buf; /* PIN verification control message */ pin_modify->bTimerOut = SC_CCID_PIN_TIMEOUT; /* bTimeOut */ pin_modify->bTimerOut2 = SC_CCID_PIN_TIMEOUT; /* bTimeOut2 */ /* bmFormatString */ tmp = 0x00; if (data->pin1.encoding == SC_PIN_ENCODING_ASCII) { tmp |= SC_CCID_PIN_ENCODING_ASCII; /* if the effective PIN length offset is specified, use it */ if (data->pin1.length_offset > 4) { tmp |= SC_CCID_PIN_UNITS_BYTES; tmp |= (data->pin1.length_offset - 5) << 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_modify->bmFormatString = tmp; /* bmFormatString */ /* 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->pin1.pad_length) { tmp |= data->pin1.pad_length; } pin_modify->bmPINBlockString = tmp; /* bmPINBlockString */ /* bmPINLengthFormat */ tmp = 0x00; if (data->pin1.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 not Case 1 APDU */ if (data->pin1.length_offset != 4) { pin_modify->bInsertionOffsetOld = data->pin1.offset - 5; pin_modify->bInsertionOffsetNew = data->pin2.offset - 5; } else { pin_modify->bInsertionOffsetOld = 0x00; pin_modify->bInsertionOffsetNew = 0x00; } if (!data->pin1.min_length || !data->pin1.max_length) return SC_ERROR_INVALID_ARGUMENTS; tmp16 = (data->pin1.min_length << 8 ) + data->pin1.max_length; 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 = 0x00; /* Default message indexes */ pin_modify->bMsgIndex2 = 0x01; pin_modify->bMsgIndex3 = 0x02; pin_modify->bTeoPrologue[0] = 0x00; pin_modify->bTeoPrologue[1] = 0x00; pin_modify->bTeoPrologue[2] = 0x00; /* APDU itself */ pin_modify->abData[offset++] = apdu->cla; pin_modify->abData[offset++] = apdu->ins; pin_modify->abData[offset++] = apdu->p1; pin_modify->abData[offset++] = apdu->p2; /* Copy data if not Case 1 */ if (data->pin1.length_offset != 4) { pin_modify->abData[offset++] = apdu->lc; memcpy(&pin_modify->abData[offset], apdu->data, apdu->datalen); offset += apdu->datalen; } pin_modify->ulDataLength = HOST_TO_CCID_32(offset); /* APDU size */ count = sizeof(PIN_MODIFY_STRUCTURE) + offset -1; *size = count; return SC_SUCCESS; } /* Find a given PCSC v2 part 10 property */ static int part10_find_property_by_tag(unsigned char buffer[], int length, int tag_searched) { unsigned char *p; int found = 0, len, value = -1; p = buffer; while (p-buffer < 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 = GET_PRIV_DATA(reader); r = pcsc_internal_transmit(reader, NULL, 0, buffer, &length, priv->get_tlv_properties); SC_TEST_RET(reader->ctx, SC_LOG_DEBUG_NORMAL, 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 (data->pin1.min_length < value) data->pin1.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 (data->pin1.max_length > value) data->pin1.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 = GET_PRIV_DATA(reader); u8 rbuf[SC_MAX_APDU_BUFFER_SIZE], sbuf[SC_MAX_APDU_BUFFER_SIZE]; char dbuf[SC_MAX_APDU_BUFFER_SIZE * 3]; size_t rcount = sizeof(rbuf), scount = 0; int r; DWORD ioctl = 0; sc_apdu_t *apdu; SC_FUNC_CALLED(reader->ctx, SC_LOG_DEBUG_NORMAL); if (priv->gpriv->SCardControl == NULL) return SC_ERROR_NOT_SUPPORTED; /* The APDU must be provided by the card driver */ if (!data->apdu) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Unknown PIN command %d", data->cmd); return SC_ERROR_NOT_SUPPORTED; } /* If PIN block building failed, we fail too */ SC_TEST_RET(reader->ctx, SC_LOG_DEBUG_NORMAL, r, "PC/SC v2 pinpad block building failed!"); /* If not, debug it, just for fun */ sc_bin_to_hex(sbuf, scount, dbuf, sizeof(dbuf), ':'); sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "PC/SC v2 pinpad block: %s", dbuf); r = pcsc_internal_transmit(reader, sbuf, scount, rbuf, &rcount, ioctl); SC_TEST_RET(reader->ctx, SC_LOG_DEBUG_NORMAL, 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) { SC_FUNC_RETURN(reader->ctx, SC_LOG_DEBUG_VERBOSE, 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); SC_TEST_RET(reader->ctx, SC_LOG_DEBUG_NORMAL, r, "PC/SC v2 pinpad: finish operation failed!"); } /* We expect only two bytes of result data (SW1 and SW2) */ if (rcount != 2) { SC_FUNC_RETURN(reader->ctx, SC_LOG_DEBUG_VERBOSE, 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; } SC_TEST_RET(reader->ctx, SC_LOG_DEBUG_NORMAL, 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) { char dbuf[SC_MAX_APDU_BUFFER_SIZE * 3]; 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 (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_sw1 = 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 = GET_PRIV_DATA(reader); 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; } 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 = NULL; pcsc_ops.perform_pace = pcsc_perform_pace; return &pcsc_drv; } #ifdef ENABLE_MINIDRIVER #define SCARD_CLASS_SYSTEM 0x7fff #define SCARD_ATTR_VALUE(Class, Tag) ((((ULONG)(Class)) << 16) | ((ULONG)(Tag))) #define SCARD_ATTR_DEVICE_FRIENDLY_NAME_A SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0003) #define SCARD_ATTR_DEVICE_SYSTEM_NAME_A SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0004) static int cardmod_connect(sc_reader_t *reader) { DWORD active_proto, protocol; SCARDHANDLE card_handle; LONG rv; struct pcsc_private_data *priv = GET_PRIV_DATA(reader); int r; r = refresh_attributes(reader); if (r) return r; if (!(reader->flags & SC_READER_CARD_PRESENT)) return SC_ERROR_CARD_NOT_PRESENT; return SC_SUCCESS; } static int cardmod_disconnect(sc_reader_t * reader) { struct pcsc_private_data *priv = GET_PRIV_DATA(reader); reader->flags = 0; return SC_SUCCESS; } static struct sc_reader_operations cardmod_ops; static struct sc_reader_driver cardmod_drv = { "PC/SC cardmod reader", "cardmod", &cardmod_ops, 0, 0, NULL }; static int cardmod_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; } /* Defaults */ gpriv->enable_pinpad = 1; conf_block = sc_get_conf_block(ctx, "reader_driver", "cardmod", 1); if (conf_block) { scconf_get_bool(conf_block, "enable_pinpad", gpriv->enable_pinpad); } sc_log(ctx, "PC/SC options: enable_pinpad=%d", gpriv->enable_pinpad); gpriv->dlhandle = sc_dlopen("winscard.dll"); if (gpriv->dlhandle == NULL) { ret = SC_ERROR_CANNOT_LOAD_MODULE; goto out; } gpriv->SCardStatus = (SCardStatus_t)sc_dlsym(gpriv->dlhandle, "SCardStatus"); gpriv->SCardGetStatusChange = (SCardGetStatusChange_t)sc_dlsym(gpriv->dlhandle, "SCardGetStatusChange"); gpriv->SCardTransmit = (SCardTransmit_t)sc_dlsym(gpriv->dlhandle, "SCardTransmit"); 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"); gpriv->SCardGetAttrib = sc_dlsym(gpriv->dlhandle, "SCardGetAttrib"); /* If we have SCardGetAttrib it is correct API */ if (gpriv->SCardGetAttrib != NULL) { 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->SCardStatus == NULL || gpriv->SCardGetStatusChange == NULL || gpriv->SCardControl == NULL || gpriv->SCardTransmit == NULL || gpriv->SCardGetAttrib == 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 cardmod_finish(sc_context_t *ctx) { struct pcsc_global_private_data *gpriv = (struct pcsc_global_private_data *) ctx->reader_drv_data; if (gpriv) { if (gpriv->dlhandle != NULL) sc_dlclose(gpriv->dlhandle); free(gpriv); } return SC_SUCCESS; } int cardmod_use_reader(sc_context_t *ctx, void * pcsc_context_handle, void * pcsc_card_handle) { SCARDHANDLE card_handle; u8 feature_buf[256], rbuf[SC_MAX_APDU_BUFFER_SIZE]; PCSC_TLV_STRUCTURE *pcsc_tlv; struct pcsc_global_private_data *gpriv = (struct pcsc_global_private_data *) ctx->reader_drv_data; LONG rv; char reader_name[128]; DWORD rcount, feature_len, display_ioctl, reader_name_size = sizeof(reader_name); int ret = SC_ERROR_INTERNAL; HKEY key; unsigned int i; wchar_t b; char *p; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); if (!gpriv) { ret = SC_ERROR_NO_READERS_FOUND; goto out; } /* if we already had a reader, delete it */ if (sc_ctx_get_reader_count(ctx) > 0) { sc_reader_t *oldrdr = list_extract_at(&ctx->readers, 0); if (oldrdr) _sc_delete_reader(ctx, oldrdr); } sc_log(ctx, "Probing pcsc readers"); gpriv->pcsc_ctx = *(SCARDCONTEXT *)pcsc_context_handle; card_handle = *(SCARDHANDLE *)pcsc_card_handle; sc_log(ctx, "gpriv->pcsc_ctx = %X, card_handle = %X", gpriv->pcsc_ctx, card_handle); if(gpriv->SCardGetAttrib(card_handle, SCARD_ATTR_DEVICE_SYSTEM_NAME_A, \ reader_name, &reader_name_size) == SCARD_S_SUCCESS) { sc_reader_t *reader = NULL; struct pcsc_private_data *priv = NULL; DWORD readers_len = 0, state, prot, atr_len = SC_MAX_ATR_SIZE; unsigned char atr[SC_MAX_ATR_SIZE]; if(1) { char texte[2048]; sc_bin_to_hex(reader_name, reader_name_size, texte, sizeof(texte)-5, ':'); sc_log(ctx, "lecteur name = %s\n%s\n", reader_name,texte); } if ((reader = calloc(1, sizeof(sc_reader_t))) == NULL) { ret = SC_ERROR_OUT_OF_MEMORY; goto err1; } if ((priv = calloc(1, sizeof(struct pcsc_private_data))) == NULL) { ret = SC_ERROR_OUT_OF_MEMORY; goto err1; } reader->drv_data = priv; reader->ops = &cardmod_ops; reader->driver = &cardmod_drv; if ((reader->name = strdup(reader_name)) == NULL) { ret = SC_ERROR_OUT_OF_MEMORY; goto err1; } priv->gpriv = gpriv; /* attempt to detect protocol in use T0/T1/RAW */ rv = priv->gpriv->SCardStatus(card_handle, NULL, &readers_len, &state, &prot, atr, &atr_len); if (rv != SCARD_S_SUCCESS) { sc_log(ctx, "SCardStatus failed %08x", rv); prot = SCARD_PROTOCOL_T0; } sc_log(ctx, "Set protocole to %s", \ (prot==SCARD_PROTOCOL_T0)?"T0":((prot==SCARD_PROTOCOL_T1)?"T1":"RAW")); reader->active_protocol = pcsc_proto_to_opensc(prot); if (_sc_add_reader(ctx, reader)) { ret = SC_SUCCESS; /* silent ignore */ goto err1; } priv->pcsc_card = card_handle; /* check for pinpad support */ if (gpriv->SCardControl != NULL) { sc_log(ctx, "Requesting reader features ... "); rv = gpriv->SCardControl(card_handle, CM_IOCTL_GET_FEATURE_REQUEST, NULL, 0, feature_buf, sizeof(feature_buf), &feature_len); if (rv != SCARD_S_SUCCESS) { sc_log(ctx, "SCardControl failed %08x", rv); } else { if ((feature_len % sizeof(PCSC_TLV_STRUCTURE)) != 0) { sc_log(ctx, "Inconsistent TLV from reader!"); } else { char *log_disabled = "but it's disabled in configuration file"; /* get the number of elements instead of the complete size */ feature_len /= sizeof(PCSC_TLV_STRUCTURE); pcsc_tlv = (PCSC_TLV_STRUCTURE *)feature_buf; for (i = 0; i < feature_len; i++) { sc_log(ctx, "Reader feature %02x detected", pcsc_tlv[i].tag); if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_DIRECT) { priv->verify_ioctl = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_START) { priv->verify_ioctl_start = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_FINISH) { priv->verify_ioctl_finish = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_DIRECT) { priv->modify_ioctl = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_START) { priv->modify_ioctl_start = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_FINISH) { priv->modify_ioctl_finish = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_IFD_PIN_PROPERTIES) { display_ioctl = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_EXECUTE_PACE) { priv->pace_ioctl = ntohl(pcsc_tlv[i].value); } else { sc_log(ctx, "Reader feature %02x is not supported", pcsc_tlv[i].tag); } } /* Set slot capabilities based on detected IOCTLs */ if (priv->verify_ioctl || (priv->verify_ioctl_start && priv->verify_ioctl_finish)) { char *log_text = "Reader supports pinpad PIN verification"; if (priv->gpriv->enable_pinpad) { sc_log(ctx, 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)) { char *log_text = "Reader supports pinpad PIN modification"; if (priv->gpriv->enable_pinpad) { sc_log(ctx, log_text); reader->capabilities |= SC_READER_CAP_PIN_PAD; } else { sc_log(ctx, "%s %s", log_text, log_disabled); } } if (display_ioctl) { rcount = sizeof(rbuf); rv = gpriv->SCardControl(card_handle, display_ioctl, NULL, 0, rbuf, sizeof(rbuf), &rcount); if (rv == SCARD_S_SUCCESS) { if (rcount == sizeof(PIN_PROPERTIES_STRUCTURE)) { PIN_PROPERTIES_STRUCTURE *caps = (PIN_PROPERTIES_STRUCTURE *)rbuf; 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 (%d/%d)", rcount, sizeof(PIN_PROPERTIES_STRUCTURE)); } } } if (priv->pace_ioctl) { char *log_text = "Reader supports PACE"; if (priv->gpriv->enable_pace) { sc_log(ctx, log_text); reader->capabilities |= SC_READER_CAP_PACE_GENERIC; } else { sc_log(ctx, "%s %s", log_text, log_disabled); } } } } } refresh_attributes(reader); ret = SC_SUCCESS; goto out; err1: if (priv != NULL) { free(priv); } if (reader != NULL) { if (reader->name) free(reader->name); free(reader); } } out: SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, ret); } static int cardmod_release(sc_reader_t *reader) { struct pcsc_private_data *priv = GET_PRIV_DATA(reader); free(priv); return SC_SUCCESS; } struct sc_reader_driver * sc_get_cardmod_driver(void) { struct sc_reader_driver *pcsc_drv = sc_get_pcsc_driver(); cardmod_ops = *(pcsc_drv->ops); cardmod_ops.init = cardmod_init; cardmod_ops.finish = cardmod_finish; cardmod_ops.detect_readers = NULL; /* cardmod_ops.transmit = ; */ cardmod_ops.lock = NULL; cardmod_ops.unlock = NULL; cardmod_ops.release = cardmod_release; cardmod_ops.connect = cardmod_connect; cardmod_ops.disconnect = cardmod_disconnect; /* cardmod_ops.perform_verify = ; */ cardmod_ops.wait_for_event = NULL; cardmod_ops.reset = NULL; cardmod_ops.use_reader = cardmod_use_reader; cardmod_ops.perform_pace = NULL; return &cardmod_drv; } #endif #endif /* ENABLE_PCSC */ opensc-0.13.0/src/libopensc/card-cardos.c0000644000015201777760000010340412057406034015122 00000000000000/* * card-cardos.c: Support for Siemens CardOS 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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 struct sc_atr_table cardos_atrs[] = { /* 4.0 */ { "3b:e2:00:ff:c1:10:31:fe:55:c8:02:9c", NULL, NULL, SC_CARD_TYPE_CARDOS_GENERIC, 0, NULL }, /* Italian eID card, postecert */ { "3b:e9:00:ff:c1:10:31:fe:55:00:64:05:00:c8:02:31:80:00:47", NULL, NULL, SC_CARD_TYPE_CARDOS_CIE_V1, 0, NULL }, /* Italian eID card, infocamere */ { "3b:fb:98:00:ff:c1:10:31:fe:55:00:64:05:20:47:03:31:80:00:90:00:f3", NULL, NULL, SC_CARD_TYPE_CARDOS_GENERIC, 0, NULL }, /* Another Italian InfocamereCard */ { "3b:fc:98:00:ff:c1:10:31:fe:55:c8:03:49:6e:66:6f:63:61:6d:65:72:65:28", NULL, NULL, SC_CARD_TYPE_CARDOS_GENERIC, 0, NULL }, { "3b:f4:98:00:ff:c1:10:31:fe:55:4d:34:63:76:b4", NULL, NULL, SC_CARD_TYPE_CARDOS_GENERIC, 0, NULL}, /* cardos m4.2 and above */ { "3b:f2:18:00:ff:c1:0a:31:fe:55:c8:06:8a", "ff:ff:0f:ff:00:ff:00:ff:ff:00:00:00:00", NULL, SC_CARD_TYPE_CARDOS_M4_2, 0, NULL }, /* CardOS 4.4 */ { "3b:d2:18:02:c1:0a:31:fe:58:c8:0d:51", NULL, NULL, SC_CARD_TYPE_CARDOS_M4_4, 0, NULL}, { NULL, NULL, NULL, 0, 0, NULL } }; static int cardos_match_card(sc_card_t *card) { unsigned char atr[SC_MAX_ATR_SIZE]; int i; i = _sc_match_atr(card, cardos_atrs, &card->type); if (i < 0) return 0; memcpy(atr, card->atr.value, sizeof(atr)); /* 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_M4_2) { int rv; sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; /* 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "checking cardos version ..."); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x82); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; apdu.lc = 0; rv = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return 0; if (apdu.resp[0] != atr[10] || apdu.resp[1] != atr[11]) /* version mismatch */ return 0; if (atr[11] <= 0x04) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos m4.01"); card->type = SC_CARD_TYPE_CARDOS_M4_01; } else if (atr[11] == 0x08) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos v4.3b"); card->type = SC_CARD_TYPE_CARDOS_M4_3; } else if (atr[11] == 0x09) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos v4.2b"); card->type = SC_CARD_TYPE_CARDOS_M4_2B; } else if (atr[11] >= 0x0B) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos v4.2c or higher"); card->type = SC_CARD_TYPE_CARDOS_M4_2C; } else { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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; 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if ((len = apdu.resplen) == 0) /* looks like no package has been installed */ return 0; while (len != 0) { p = sc_asn1_find_tag(card->ctx, p, len, 0xe1, &tlen); if (p == NULL) return 0; q = sc_asn1_find_tag(card->ctx, p, tlen, 0x01, &ilen); if (q == NULL || ilen != 4) return 0; if (q[0] == 0x1c) return 1; p += tlen; len -= tlen + 2; } return 0; } static int cardos_init(sc_card_t *card) { unsigned long flags, rsa_2048 = 0; card->name = "CardOS M4"; card->cla = 0x00; /* Set up algorithm info. */ flags = SC_ALGORITHM_NEED_USAGE | SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_ONBOARD_KEY_GEN ; _sc_card_add_rsa_alg(card, 512, flags, 0); _sc_card_add_rsa_alg(card, 768, flags, 0); _sc_card_add_rsa_alg(card, 1024, flags, 0); if (card->type == SC_CARD_TYPE_CARDOS_M4_2) { int r = cardos_have_2048bit_package(card); if (r < 0) return r; if (r == 1) 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) { rsa_2048 = 1; card->caps |= SC_CARD_CAP_APDU_EXT; } if (rsa_2048 == 1) { _sc_card_add_rsa_alg(card, 1280, flags, 0); _sc_card_add_rsa_alg(card, 1536, flags, 0); _sc_card_add_rsa_alg(card, 1792, flags, 0); _sc_card_add_rsa_alg(card, 2048, flags, 0); } return 0; } 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 now thic combination of ins+cla+p1+p2 */ /* i.e. command will never work */ { 0x6881, SC_ERROR_NO_CARD_SUPPORT, "logical channel not supported"}, { 0x6a86, SC_ERROR_INCORRECT_PARAMETERS,"p1/p2 invalid"}, { 0x6d00, SC_ERROR_INS_NOT_SUPPORTED, "ins invalid"}, { 0x6e00, SC_ERROR_CLASS_NOT_SUPPORTED, "class invalid (hi nibble)"}, /* known command, but incorrectly used */ /* i.e. command could work, but you need to change something */ { 0x6981, SC_ERROR_CARD_CMD_FAILED, "command cannot be used for file structure"}, { 0x6a80, SC_ERROR_INCORRECT_PARAMETERS,"invalid parameters in data field"}, { 0x6a81, SC_ERROR_NOT_SUPPORTED, "function/mode not supported"}, { 0x6a85, SC_ERROR_INCORRECT_PARAMETERS,"lc does not fit the tlv structure"}, { 0x6986, SC_ERROR_INCORRECT_PARAMETERS,"no current ef selected"}, { 0x6a87, SC_ERROR_INCORRECT_PARAMETERS,"lc does not fit p1/p2"}, { 0x6c00, SC_ERROR_WRONG_LENGTH, "le does not fit the data to be sent"}, { 0x6f83, SC_ERROR_CARD_CMD_FAILED, "command must not be used in transaction"}, /* (something) not found */ { 0x6987, SC_ERROR_INCORRECT_PARAMETERS,"key object for sm not found"}, { 0x6f86, SC_ERROR_CARD_CMD_FAILED, "key object not found"}, { 0x6a82, SC_ERROR_FILE_NOT_FOUND, "file not found"}, { 0x6a83, SC_ERROR_RECORD_NOT_FOUND, "record not found"}, { 0x6a88, SC_ERROR_CARD_CMD_FAILED, "object not found"}, /* (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 useing in/decrease"} }; static int cardos_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) { const int err_count = sizeof(cardos_errors)/sizeof(cardos_errors[0]); int i; for (i = 0; i < err_count; i++) { if (cardos_errors[i].SWs == ((sw1 << 8) | sw2)) { if ( cardos_errors[i].errorstr ) sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s\n", cardos_errors[i].errorstr); return cardos_errors[i].errorno; } } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown SWs; SW1=%02X, SW2=%02X\n", sw1, sw2); 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 = rbuf, *q; 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "DIRECTORY command returned error"); if (apdu.resplen > 256) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "directory listing > 256 bytes, cutting"); } len = apdu.resplen; while (len != 0) { size_t tlen = 0, ilen = 0; /* is there a file informatin block (0x6f) ? */ p = sc_asn1_find_tag(card->ctx, p, len, 0x6f, &tlen); if (p == NULL) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "directory tag missing"); return SC_ERROR_INTERNAL; } if (tlen == 0) /* empty directory */ break; q = sc_asn1_find_tag(card->ctx, p, tlen, 0x86, &ilen); if (q == NULL || ilen != 2) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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 enought space left in buffer => break */ break; /* extract next offset */ q = sc_asn1_find_tag(card->ctx, p, tlen, 0x8a, &ilen); if (q != NULL && ilen == 1) { offset = (u8)ilen; if (offset != 0) goto get_next_part; } len -= tlen + 2; p += tlen; } r = fids; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } static void add_acl_entry(sc_file_t *file, int op, u8 byte) { unsigned int method, key_ref = SC_AC_KEY_REF_NONE; switch (byte) { case 0x00: method = SC_AC_NONE; break; case 0xFF: method = SC_AC_NEVER; break; default: if (byte > 0x7F) { method = SC_AC_UNKNOWN; } else { method = SC_AC_CHV; key_ref = byte; } break; } sc_file_add_acl_entry(file, op, method, key_ref); } static int acl_to_byte(const sc_acl_entry_t *e) { if (e != NULL) { switch (e->method) { case SC_AC_NONE: return 0x00; case SC_AC_NEVER: return 0xFF; case SC_AC_CHV: case SC_AC_TERM: case SC_AC_AUT: if (e->key_ref == SC_AC_KEY_REF_NONE) return -1; if (e->key_ref > 0x7F) return -1; return e->key_ref; } } return 0x00; } static const int df_acl[9] = { -1, /* LCYCLE (life cycle change) */ SC_AC_OP_UPDATE, /* UPDATE Objects */ -1, /* APPEND Objects */ SC_AC_OP_INVALIDATE, /* DF */ SC_AC_OP_REHABILITATE, /* DF */ SC_AC_OP_DELETE, /* DF */ 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 (-> cylic fixed files) */ -1 /* DEC */ }; static void parse_sec_attr(sc_file_t *file, const u8 *buf, size_t len) { size_t i; const int *idx; idx = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl; /* acl defaults to 0xFF if unspecified */ for (i = 0; i < 9; i++) if (idx[i] != -1) add_acl_entry(file, idx[i], (u8)((i < len) ? buf[i] : 0xFF)); } static int 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); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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; 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; status[2] = file->size; } 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); if (out == NULL || inlen < 64) return SC_ERROR_INVALID_ARGUMENTS; /* add FCP tag */ *p++ = 0x62; /* we will add the length later */ p++; /* 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to create FCP"); return r; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00); apdu.lc = len; apdu.datalen = len; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } /* * Set the security context * Things get a little messy here. It seems you cannot do any * crypto without a security environment - but there isn't really * a way to specify the security environment in PKCS15. * What I'm doing here (for now) is to assume that for a key * object with ID 0xNN there is always a corresponding SE object * with the same ID. * XXX Need to find out how the Aladdin drivers do it. */ static int cardos_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { sc_apdu_t apdu; u8 data[3]; int key_id, r; assert(card != NULL && env != NULL); if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || env->key_ref_len != 1) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No or invalid key reference\n"); return SC_ERROR_INVALID_ARGUMENTS; } key_id = env->key_ref[0]; 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 = 0x01; } switch (env->operation) { case SC_SEC_OPERATION_DECIPHER: apdu.p2 = 0xB8; break; case SC_SEC_OPERATION_SIGN: apdu.p2 = 0xB6; break; default: return SC_ERROR_INVALID_ARGUMENTS; } data[0] = 0x83; data[1] = 0x01; data[2] = key_id; apdu.lc = apdu.datalen = 3; apdu.data = data; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } /* * Compute digital signature */ /* internal function to do the actual signature computation */ static int do_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, u8 *out, size_t outlen) { int r; sc_apdu_t apdu; /* 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; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 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) { int r; u8 buf[SC_MAX_APDU_BUFFER_SIZE]; size_t buf_len = sizeof(buf), tmp_len = buf_len; sc_context_t *ctx; assert(card != NULL && data != NULL && out != NULL); ctx = card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (datalen > SC_MAX_APDU_BUFFER_SIZE) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); if (outlen < datalen) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_BUFFER_TOO_SMALL); outlen = datalen; /* XXX As we don't know what operations are allowed with a * certain key, let's try RSA_PURE etc. and see which operation * succeeds (this is not really beautiful, but currently the * only way I see) -- Nils * * We also check for several caps flags here to pervent generating * invalid signatures with duplicated hash prefixes with some cards */ if (card->caps & SC_CARD_CAP_ONLY_RAW_HASH_STRIPPED) sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Forcing RAW_HASH_STRIPPED\n"); if (card->caps & SC_CARD_CAP_ONLY_RAW_HASH) sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Forcing RAW_HASH\n"); if (!(card->caps & (SC_CARD_CAP_ONLY_RAW_HASH_STRIPPED | SC_CARD_CAP_ONLY_RAW_HASH))) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "trying RSA_PURE_SIG (padded DigestInfo)\n"); r = do_compute_signature(card, data, datalen, out, outlen); if (r >= SC_SUCCESS) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "trying RSA_SIG (just the DigestInfo)\n"); /* remove padding: first try pkcs1 bt01 padding */ r = sc_pkcs1_strip_01_padding(data, datalen, buf, &tmp_len); if (r != SC_SUCCESS) { const u8 *p = data; /* no pkcs1 bt01 padding => let's try zero padding * This can only work if the data tbs doesn't have a * leading 0 byte. */ tmp_len = buf_len; while (*p == 0 && tmp_len != 0) { ++p; --tmp_len; } memcpy(buf, p, tmp_len); } if (!(card->caps & (SC_CARD_CAP_ONLY_RAW_HASH_STRIPPED | SC_CARD_CAP_ONLY_RAW_HASH)) || card->caps & SC_CARD_CAP_ONLY_RAW_HASH ) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "trying to sign raw hash value with prefix\n"); r = do_compute_signature(card, buf, tmp_len, out, outlen); if (r >= SC_SUCCESS) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } if (card->caps & SC_CARD_CAP_ONLY_RAW_HASH) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Failed to sign raw hash value with prefix when forcing\n"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); } sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "trying to sign stripped raw hash value (card is responsible for prefix)\n"); r = sc_pkcs1_strip_digest_info_prefix(NULL,buf,tmp_len,buf,&buf_len); if (r != SC_SUCCESS) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); return do_compute_signature(card, buf, buf_len, out, outlen); } static int 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); if (apdu.resplen < 1) { SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Lifecycle byte not in response"); } r = SC_SUCCESS; switch (rbuf[0]) { case 0x10: *mode = SC_CARDCTRL_LIFECYCLE_USER; break; case 0x20: *mode = SC_CARDCTRL_LIFECYCLE_ADMIN; break; case 0x34: /* MANUFACTURING */ *mode = SC_CARDCTRL_LIFECYCLE_OTHER; break; default: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown lifecycle byte %d", rbuf[0]); r = SC_ERROR_INTERNAL; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } static int 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } static int 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } static int 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); return r; } static int 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "GENERATE_KEY failed"); return r; } static int 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return SC_ERROR_INTERNAL; if (apdu.resplen != 32) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unexpected response to GET DATA serial" " number\n"); return SC_ERROR_INTERNAL; } /* cache serial number */ memcpy(card->serialnr.value, &rbuf[10], 6); card->serialnr.len = 6; /* copy and return serial number */ memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } 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_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(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { data->flags |= SC_PIN_CMD_NEED_PADDING; data->pin_reference |= 0x80; /* FIXME: the following values depend on what pin length was * used when creating the BS objects */ if (data->pin1.max_length == 0) data->pin1.max_length = 8; if (data->pin2.max_length == 0) data->pin2.max_length = 8; return iso_ops->pin_cmd(card, data, tries_left); } static int cardos_logout(sc_card_t *card) { if (card->type == SC_CARD_TYPE_CARDOS_M4_01 || card->type == SC_CARD_TYPE_CARDOS_M4_2) { 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } else return SC_ERROR_NOT_SUPPORTED; } static int cardos_get_data(struct sc_card *card, unsigned int tag, u8 *buf, size_t len) { int r; struct sc_apdu apdu; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, (tag >> 8) & 0xff, tag & 0xff); apdu.lc = 0; apdu.datalen = 0; apdu.le = len; apdu.resp = buf; apdu.resplen = len; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "GET_DATA returned error"); if (apdu.resplen > len) r = SC_ERROR_WRONG_LENGTH; else r = apdu.resplen; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } /* 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.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.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; cardos_ops.get_data = cardos_get_data; return &cardos_drv; } #if 1 struct sc_card_driver * sc_get_cardos_driver(void) { return sc_get_driver(); } #endif opensc-0.13.0/src/libopensc/pkcs15-syn.c0000644000015201777760000003361712057406034014665 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include "common/libscdl.h" #include "internal.h" #include "asn1.h" #include "pkcs15.h" extern int sc_pkcs15emu_westcos_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts); extern int sc_pkcs15emu_openpgp_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); extern int sc_pkcs15emu_infocamere_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); extern int sc_pkcs15emu_starcert_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); extern int sc_pkcs15emu_tcos_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); extern int sc_pkcs15emu_esteid_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); extern int sc_pkcs15emu_postecert_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); extern int sc_pkcs15emu_piv_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts); extern int sc_pkcs15emu_gemsafeGPK_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts); extern int sc_pkcs15emu_gemsafeV1_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts); extern int sc_pkcs15emu_actalis_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts); extern int sc_pkcs15emu_atrust_acos_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts); extern int sc_pkcs15emu_tccardos_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); extern int sc_pkcs15emu_entersafe_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); extern int sc_pkcs15emu_pteid_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); extern int sc_pkcs15emu_oberthur_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); extern int sc_pkcs15emu_itacns_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); extern int sc_pkcs15emu_sc_hsm_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); static struct { const char * name; int (*handler)(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); } builtin_emulators[] = { { "westcos", sc_pkcs15emu_westcos_init_ex }, { "openpgp", sc_pkcs15emu_openpgp_init_ex }, { "infocamere", sc_pkcs15emu_infocamere_init_ex }, { "starcert", sc_pkcs15emu_starcert_init_ex }, { "tcos", sc_pkcs15emu_tcos_init_ex }, { "esteid", sc_pkcs15emu_esteid_init_ex }, { "itacns", sc_pkcs15emu_itacns_init_ex }, { "postecert", sc_pkcs15emu_postecert_init_ex }, { "PIV-II", sc_pkcs15emu_piv_init_ex }, { "gemsafeGPK", sc_pkcs15emu_gemsafeGPK_init_ex }, { "gemsafeV1", sc_pkcs15emu_gemsafeV1_init_ex }, { "actalis", sc_pkcs15emu_actalis_init_ex }, { "atrust-acos",sc_pkcs15emu_atrust_acos_init_ex}, { "tccardos", sc_pkcs15emu_tccardos_init_ex }, { "entersafe", sc_pkcs15emu_entersafe_init_ex }, { "pteid", sc_pkcs15emu_pteid_init_ex }, { "oberthur", sc_pkcs15emu_oberthur_init_ex }, { "sc-hsm", sc_pkcs15emu_sc_hsm_init_ex }, { NULL, NULL } }; static int parse_emu_block(sc_pkcs15_card_t *, 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"; int sc_pkcs15_is_emulation_only(sc_card_t *card) { switch (card->type) { case SC_CARD_TYPE_MCRD_ESTEID_V10: case SC_CARD_TYPE_MCRD_ESTEID_V11: case SC_CARD_TYPE_MCRD_ESTEID_V30: case SC_CARD_TYPE_IAS_PTEID: case SC_CARD_TYPE_GEMSAFEV1_PTEID: case SC_CARD_TYPE_OPENPGP_V1: case SC_CARD_TYPE_OPENPGP_V2: case SC_CARD_TYPE_SC_HSM: return 1; default: return 0; } } int sc_pkcs15_bind_synthetic(sc_pkcs15_card_t *p15card) { sc_context_t *ctx = p15card->card->ctx; scconf_block *conf_block, **blocks, *blk; sc_pkcs15emu_opt_t opts; int i, r = SC_ERROR_WRONG_CARD; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); memset(&opts, 0, sizeof(opts)); conf_block = NULL; conf_block = sc_get_conf_block(ctx, "framework", "pkcs15", 1); if (!conf_block) { /* no conf file found => try bultin drivers */ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "no conf file (or section), trying all builtin emulators\n"); for (i = 0; builtin_emulators[i].name; i++) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "trying %s\n", builtin_emulators[i].name); r = builtin_emulators[i].handler(p15card, &opts); 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, *item; builtin_enabled = scconf_get_bool(conf_block, "enable_builtin_emulation", 1); list = scconf_find_list(conf_block, "builtin_emulators"); /* FIXME: rename to enabled_emulators */ if (builtin_enabled && list) { /* get the list of enabled emulation drivers */ for (item = list; item; item = item->next) { /* go through the list of builtin drivers */ const char *name = item->data; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "trying %s\n", name); for (i = 0; builtin_emulators[i].name; i++) if (!strcmp(builtin_emulators[i].name, name)) { r = builtin_emulators[i].handler(p15card, &opts); if (r == SC_SUCCESS) /* we got a hit */ goto out; } } } else if (builtin_enabled) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "no emulator list in config file, trying all builtin emulators\n"); for (i = 0; builtin_emulators[i].name; i++) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "trying %s\n", builtin_emulators[i].name); r = builtin_emulators[i].handler(p15card, &opts); if (r == SC_SUCCESS) /* we got a hit */ goto out; } } /* search for 'emulate foo { ... }' entries in the conf file */ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "searching for 'emulate foo { ... }' blocks\n"); blocks = scconf_find_blocks(ctx->conf, conf_block, "emulate", NULL); for (i = 0; blocks && (blk = blocks[i]) != NULL; i++) { const char *name = blk->name->data; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "trying %s\n", name); r = parse_emu_block(p15card, blk); if (r == SC_SUCCESS) { free(blocks); goto out; } } if (blocks) free(blocks); } /* Total failure */ return SC_ERROR_WRONG_CARD; 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "Failed to load card emulator: %s\n", sc_strerror(r)); } return r; } static int parse_emu_block(sc_pkcs15_card_t *p15card, scconf_block *conf) { sc_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; sc_pkcs15emu_opt_t opts; void *handle = NULL; int (*init_func)(sc_pkcs15_card_t *); int (*init_func_ex)(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); int r, force = 0; const char *driver, *module_name; driver = conf->name->data; init_func = NULL; init_func_ex = NULL; memset(&opts, 0, sizeof(opts)); opts.blk = conf; if (force != 0) opts.flags = SC_PKCS15EMU_FLAGS_NO_CHECK; 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; } } } else { const char *(*get_version)(void); const char *name = NULL; void *address; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Loading %s\n", module_name); /* try to open dynamic library */ handle = sc_dlopen(module_name); if (!handle) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "unable to open dynamic library '%s': %s\n", 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 || strcmp(get_version(), "0.9.3") < 0) { /* 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 *, sc_pkcs15emu_opt_t *)) address; } } /* try to initialize the pkcs15 structures */ if (init_func_ex) r = init_func_ex(p15card, &opts); else if (init_func) r = init_func(p15card); else r = SC_ERROR_WRONG_CARD; if (r >= 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s succeeded, card bound\n", module_name); p15card->dll_handle = handle; } else { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s failed: %s\n", 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_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; obj = calloc(1, sizeof(*obj)); if (!obj) return 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_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown PKCS15 object type %d\n", type); free(obj); return SC_ERROR_INVALID_ARGUMENTS; } obj->data = calloc(1, data_len); if (obj->data == NULL) { free(obj); return SC_ERROR_OUT_OF_MEMORY; } memcpy(obj->data, data, data_len); obj->df = sc_pkcs15emu_get_df(p15card, df_type); sc_pkcs15_add_object(p15card, obj); return SC_SUCCESS; } opensc-0.13.0/src/libopensc/libopensc.exports0000644000015201777760000001543212057406034016203 00000000000000scconf_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_entries scconf_parse_string scconf_put_bool scconf_put_int scconf_put_str scconf_write scconf_write_entries _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_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_decipher sc_delete_file sc_delete_record sc_der_copy sc_detect_card_presence sc_disconnect_card sc_do_log sc_do_log_noframe _sc_debug sc_enum_apps 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_valid sc_format_apdu sc_bytes2apdu sc_format_asn1_entry sc_format_oid sc_init_oid sc_compare_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_reverse 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_dsa 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_dsa sc_pkcs15_encode_pubkey_rsa sc_pkcs15_encode_pubkey_ec sc_pkcs15_encode_pubkey_gostr3410 sc_pkcs15_encode_pukdf_entry sc_pkcs15_encode_tokeninfo sc_pkcs15_encode_unusedspace sc_pkcs15_erase_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_get_application_by_type sc_pkcs15_get_guid sc_pkcs15_get_object_id sc_pkcs15_get_objects sc_pkcs15_get_objects_cond sc_pkcs15_get_lastupdate 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_unbind sc_pkcs15_unblock_pin sc_pkcs15_verify_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_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_update_binary sc_update_dir sc_update_record sc_verify sc_wait_for_event sc_write_binary sc_write_record sc_erase_binary sc_get_iso7816_driver 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_get_asepcos_ops sc_pkcs15init_get_cardos_ops sc_pkcs15init_get_cryptoflex_ops sc_pkcs15init_get_cyberflex_ops sc_pkcs15init_get_gpk_ops sc_pkcs15init_get_incrypto34_ops sc_pkcs15init_get_jcop_ops sc_pkcs15init_get_manufacturer sc_pkcs15init_get_miocos_ops sc_pkcs15init_get_muscle_ops sc_pkcs15init_get_oberthur_ops sc_pkcs15init_get_pin_info 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_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_print_cache sc_find_app sc_remote_data_init sc_crc32 sc_pkcs15_convert_prkey sc_pkcs15_convert_pubkey iasecc_sdo_encode_update_field 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 opensc-0.13.0/src/libopensc/p15card-helper.h0000644000015201777760000001030212057406034015453 00000000000000/* * p15card-helper.h: Utility library to assist in PKCS#15 emulation on Non-filesystem cards * * Copyright (C) 2006, Identity Alliance, Thomas Harning * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef P15CARD_HELPER_H #define P15CARD_HELPER_H #include "libopensc/pkcs15.h" #define USAGE_NONREP SC_PKCS15_PRKEY_USAGE_NONREPUDIATION | \ SC_PKCS15_PRKEY_USAGE_SIGN #define USAGE_DS SC_PKCS15_PRKEY_USAGE_SIGN #define USAGE_CRYPTO SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ SC_PKCS15_PRKEY_USAGE_DECRYPT | \ SC_PKCS15_PRKEY_USAGE_WRAP | \ SC_PKCS15_PRKEY_USAGE_UNWRAP #define USAGE_KE SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ SC_PKCS15_PRKEY_USAGE_DECRYPT | \ SC_PKCS15_PRKEY_USAGE_WRAP | \ SC_PKCS15_PRKEY_USAGE_UNWRAP #define USAGE_AUT SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ SC_PKCS15_PRKEY_USAGE_DECRYPT | \ SC_PKCS15_PRKEY_USAGE_WRAP | \ SC_PKCS15_PRKEY_USAGE_UNWRAP | \ SC_PKCS15_PRKEY_USAGE_SIGN typedef struct objdata_st { const char *id; const char *label; const char *aoid; int authority; const char *path; int obj_flags; } objdata; typedef struct cdata_st { const char *id; const char *label; int authority; const char *path; int obj_flags; } cdata; typedef struct pdata_st { const char *id; const char *label; const char *path; int ref; int type; unsigned int maxlen; unsigned int minlen; unsigned int storedlen; int flags; int tries_left; const char pad_char; int obj_flags; } pindata; typedef struct pubdata_st { const char *id; const char *label; unsigned int modulus_len; int usage; const char *path; int ref; const char *auth_id; int obj_flags; } pubdata; typedef struct prdata_st { const char *id; const char *label; unsigned int modulus_len; int usage; const char *path; int ref; const char *auth_id; int obj_flags; } prdata; typedef struct keyinfo_st { int fileid; sc_pkcs15_id_t id; unsigned int modulus_len; u8 modulus[1024/8]; } keyinfo; typedef struct p15data_items p15data_items; typedef int (*cert_load_function)(sc_card_t *card, u8** data, size_t* length, int* shouldFree); #define CERT_LOAD_FUNCTION(x) int x(sc_card_t *card, u8** data, size_t*length, int *shouldFree) typedef int (*cert_handle_function)(sc_pkcs15_card_t *p15card, p15data_items* items, const cdata* cert, u8* data, size_t length); #define CERT_HANDLE_FUNCTION(x) int x(sc_pkcs15_card_t *p15card, p15data_items* items, const cdata* cert, u8* data, size_t length) struct p15data_items { const objdata* objects; const cdata* certs; const pindata* pins; const pubdata* public_keys; const prdata* private_keys; cert_load_function cert_load; cert_handle_function cert_handle; int cert_continue; /* Continue after cert failure */ int forced_private; /* Should add all private keys w/o cert-management */ int forced_public; /* Should add public keys (generally not needed..) */ }; CERT_HANDLE_FUNCTION(default_cert_handle); int sc_pkcs15emu_initialize_objects(sc_pkcs15_card_t *p15card, p15data_items* items); int sc_pkcs15emu_initialize_certificates(sc_pkcs15_card_t *p15card, p15data_items* items); int sc_pkcs15emu_initialize_pins(sc_pkcs15_card_t *p15card, p15data_items *items); int sc_pkcs15emu_initialize_private_keys(sc_pkcs15_card_t *p15card, p15data_items *items); int sc_pkcs15emu_initialize_public_keys(sc_pkcs15_card_t *p15card, p15data_items *items); int sc_pkcs15emu_initialize_all(sc_pkcs15_card_t *p15card, p15data_items *items); #endif opensc-0.13.0/src/libopensc/muscle-filesystem.h0000644000015201777760000000403312057406034016415 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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; } 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); void mscfs_check_cache(mscfs_t* fs); int mscfs_lookup_path(mscfs_t* fs, const u8 *path, int pathlen, msc_id* objectId, int isDirectory); int mscfs_lookup_local(mscfs_t* fs, const int id, msc_id* objectId); /* -1 any, 0 DF, 1 EF */ int mscfs_check_selection(mscfs_t *fs, int requiredItem); int mscfs_loadFileInfo(mscfs_t* fs, const u8 *path, int pathlen, mscfs_file_t **file_data, int* index); #endif opensc-0.13.0/src/libopensc/iasecc-sdo.h0000644000015201777760000002512612057406034014763 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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_REPUDATION 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.13.0/src/libopensc/card-belpic.c0000644000015201777760000014045412057406034015113 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* About the Belpic (Belgian Personal Identity Card) card * * The Belpic card is a Cyberflex Java card, so you normaly communicate * with an applet running on the card. In order to support a pkcs15 file * structure, an applet (the Belpic applet) has been build that emulates * this. So the card's behaviour is specific for this Belpic applet, that's * why a separate driver has been made. * * The card contains the citizen's ID data (name, address, photo, ...) and * her keys and certs. The ID data are in a seperate directory on the card and * 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 repudation purposes * (so it can be used as an alternative to manual signatures). * * There are some special things to note, which all have some consequences: * (1) the SELECT FILE command doesn't return any FCI (file length, type, ...) * (2) the NonRep key needs a VERIFY PIN before a signature can be done with it * (3) pin pad readers had to be supported by a proprietory interface (as at * that moment no other solution was known/avaiable/ready) * The consequences are: * * For (1): we let the SELECT FILE command return that the file length is * a fixed large number and that each file is a transparant working EF * (except the root dir 3F 00). This way however, there is a problem with the * sc_read_binary() function that will only stop reading untill it receivces * 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 allways be the GUI. * * For (3), we link dynamically against a pin pad lib (DLL) that implements the * proprietory API for a specific pin pad. For each pin pad reader (identified * by it's PC/SC reader name), a pin pad lib correspondends. Some reader/lib * 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. */ #include "config.h" #include #include #include "internal.h" #include "log.h" #ifdef BELPIC_PIN_PAD #ifndef HAVE_GUI #define HAVE_GUI #endif #endif #ifdef BELPIC_PIN_PAD #include "winscard.h" #include "scr.h" #endif /* These defines are disabled for OpenSC */ #if 0 #define GET_LANG_FROM_CARD #define HAVE_ALLOW_SSO #endif #ifdef HAVE_GUI #include "scgui.h" #ifndef SCR_USAGE_SIGN #define SCR_USAGE_SIGN 2 /* in scr.h */ #endif #ifndef SCR_USAGE_AUTH #define SCR_USAGE_AUTH 1 #endif #endif /* To be removed */ #include static long t1, t2, tot_read = 0, tot_dur = 0, dur; #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 /* Used for a trick in select file and read binary */ static size_t next_idx = (size_t)-1; static struct sc_atr_table belpic_atrs[] = { /* Applet V1.1 */ { "3B:98:13:40:0A:A5:03:01:01:01:AD:13:11", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL }, /* Applet V1.0 with new EMV-compatible ATR */ { "3B:98:94:40:0A:A5:03:01:01:01:AD:13:10", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL }, /* Applet beta 5 + V1.0 */ { "3B:98:94:40:FF:A5:03:01:01:01:AD:13:10", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL }, #if 0 /* Applet beta 3 + 4 */ { "3B:98:11:40:FF:A5:03:01:01:01:AD:13:04", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL }, /* Applet beta 2 */ { "3B:68:00:00:29:05:01:02:01:AD:13:03", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL }, #endif { NULL, NULL, NULL, 0, 0, NULL } }; struct belpic_priv_data { int lang; int options; #ifdef BELPIC_PIN_PAD FARPROC scr_init; FARPROC scr_verify_pin; FARPROC scr_change_pin; char szPinPadDll[64]; #endif }; #define DRVDATA(card) ((struct belpic_priv_data *) ((card)->drv_data)) /* Single Sign On */ #ifdef HAVE_ALLOW_SSO #define SSO_OK(drv) ((drv)->allow_sso) #else #define SSO_OK(drv) 0 #endif static struct sc_card_operations belpic_ops; static struct sc_card_driver belpic_drv = { "Belpic cards", "belpic", &belpic_ops, NULL, 0, NULL }; static const struct sc_card_operations *iso_ops = NULL; #define LNG_ENG 0 #define LNG_DUTCH 1 #define LNG_FRENCH 2 #define LNG_GERMAN 3 #define LNG_NONE 0xFFFF #ifdef BELPIC_PIN_PAD /* Option flags from the config file */ #define PP_MSG_AUTH_PIN 0x00000001 #define PP_MSG_WRONG_PIN 0x00000002 #define PP_MSG_CHANGEPIN_MISMATCH 0x00000004 #define PP_MSG_PIN_BLOCKED 0x00000008 /* Hardcoded pin pad reader names (PC/SC) and their pin pad lib */ static char *pp_reader_names[] = { "Xiring X Pass Serial", NULL }; static char *pp_reader_libs[] = { "xireid", NULL }; static BYTE aid_belpic[] = { 0xA0, 0x00, 0x00, 0x01, 0x77, 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 }; static SCR_Application scr_app_belpic = { {aid_belpic, sizeof(aid_belpic)}, "ID", NULL }; static char *app_id_longstr[] = { "Identity", "Identiteit", "Identit", "Identitt" }; #endif /* BELPIC_PIN_PAD */ #if defined(HAVE_GUI) ||defined(BELPIC_PIN_PAD) static char *pin_usg_sig[] = { "Signature", "Handtekening", "Signature", "Signatur" }; static char *pin_usg_auth[] = { "Authentication", "Authentificatie", "Authentification", "Authentifizierung" }; #endif /* defined(HAVE_GUI) ||defined(BELPIC_PIN_PAD) */ #ifdef BELPIC_PIN_PAD static char *lang_codes[4] = { "en", "nl", "fr", "de" }; static char *pp_msg_auth_sh[] = { "Authentication", "Authentificatie", "Authentification", "Kennzeichnung" }; static char *pp_msg_auth[] = { "Enter your PIN on the reader, in order to authenticate yourself", "Geef uw PIN in op de lezer, om u te authentificeren", "Entrez votre PIN sur le lecteur, pour vous authentifier", "Bitte geben Sie Ihre PIN am Kartenlesegert ein, um sich zu authentifizieren" }; static char *pp_msg_sign_sh[] = { "Signature", "Handtekening", "Signature", "Signatur" }; static char *pp_msg_sign[] = { "Caution: You are about to make a legally binding electronic signature with your identity card.\nPlease enter your PIN on the card reader to continue or click the Cancel button.\n\nIf you only want to log on to a web site or server, do NOT enter your PIN and click the Cancel button.", "Let op: u gaat een wettelijk bindende electronische handtekening plaatsen met uw identiteitskaart.\nGeef uw PIN in op de kaartlezer om verder te gaan of klik op Stoppen.\n\nAls u enkel wil aanloggen op een web site of een server, geef uw PIN NIET in en klik op Stoppen.", "Attention: vous allez apposer une signature lectronique juridiquement valide avec votre carte d'identit.\nVeuillez entrer votre PIN sur le lecteur externe pour continuer ou cliquez sur Annuler.\n\nSi vous dsirez seulement vous connecter un site ou un serveur, n'entrez PAS votre PIN et cliquez sur Annuler.", "Achtung: Mit Ihrem Personalausweis werden Sie eine rechtlich bindende elektronische Signatur setzen.\nBitte geben Sie Ihre PIN am Kartenlesgert ein zum weitergehen oder klicken Sie auf Abbrechen.\n\nWenn Sie nur auf das Internet gehen mchten, geben Sie bitte Ihre PIN NICHT ein, sondern klicken Sie auf Abbrechen." }; static char *pp_msg_change_sh[] = { "PIN change", "PIN verandering", "Modification du PIN ", "PIN ndern" }; static char *pp_msg_change[] = { "Change your PIN on the reader", "Verander uw PIN op de lezer", "Modifiez votre PIN sur le lecteur", "Bitte ndern Sie Ihre PIN am Kartenlesegert" }; static char *pp_msg_pin_mismatch[] = { "The new PINs you entered were different.\n\nRetry or cancel?", "De ingevoerde nieuwe PINs zijn verschillend.\n\nOpnieuw proberen of stoppen?", "Les nouveaux PIN entrs sont diffrents.\n\nRessayer ou annuler?", "Die von Ihnen eingegebenen PINs unterscheiden sich.\n\nErneut versuchen oder abbrechen?" }; #define PCSC_ERROR(ctx, desc, rv) sc_debug(ctx, SC_LOG_DEBUG_NORMAL, desc ": %lx\n", rv); #endif /* BELPIC_PIN_PAD */ /* Language support for the GUI messages */ #ifdef HAVE_GUI #ifdef WIN32 #define BTN_KEYB_SHORTCUT "&" #else #define BTN_KEYB_SHORTCUT "_" #endif static char *app_msg[] = { "Identity", "Identiteit", #ifdef _WIN32 "Identit", #else "Identite", #endif #ifdef _WIN32 "Identitt" #else "Identitat", #endif }; static char *btn_msg_retry[4] = { BTN_KEYB_SHORTCUT"Try again", BTN_KEYB_SHORTCUT"Opnieuw proberen", BTN_KEYB_SHORTCUT"Ressayer", BTN_KEYB_SHORTCUT"Erneut versuchen" }; static char *btn_msg_cancel[4] = { BTN_KEYB_SHORTCUT"Cancel", BTN_KEYB_SHORTCUT"Stoppen", BTN_KEYB_SHORTCUT"Annuler", BTN_KEYB_SHORTCUT"Abbrechen" }; static char *btn_msg_ok[4] = { BTN_KEYB_SHORTCUT"OK", BTN_KEYB_SHORTCUT"OK", BTN_KEYB_SHORTCUT"OK", BTN_KEYB_SHORTCUT"OK" }; static char *btn_msg_close[4] = { BTN_KEYB_SHORTCUT"Close", BTN_KEYB_SHORTCUT"Sluiten", BTN_KEYB_SHORTCUT"Fermer", BTN_KEYB_SHORTCUT"Schliessen" }; static char *enter_pin_msg_auth[] = { "Enter your PIN, in order to authenticate yourself", "Geef uw PIN in, om u te authentificeren", "Entrez votre PIN, pour vous authentifier", "Bitte geben Sie Ihre PIN ein, um sich zu authentifizieren" }; static char *enter_pin_msg_sign[4] = { #ifdef _WIN32 "Caution: You are about to make a legally binding electronic signature with your identity card.\nPlease enter your PIN to continue or click the Cancel button.\n\nWarning: if you only want to log on to a web site or server, do NOT enter your PIN and click the Cancel button.", "Let op: u gaat een wettelijk bindende electronische handtekening plaatsen met uw identiteitskaart.\nGeef uw PIN in om verder te gaan of klik op Stoppen.\n\nWaarschuwing: als u enkel wil aanloggen op een web site of een server, geef uw PIN NIET in en klik op Stoppen.", "Attention: vous allez apposer une signature lectronique juridiquement valide avec votre carte d'identit.\nVeuillez entrer votre PIN pour continuer ou cliquez sur Annuler.\n\nPrcaution: si vous dsirez seulement vous connecter un site ou un serveur, n'entrez PAS votre PIN et cliquez sur Annuler.", "Achtung: Mit Ihrem Personalausweis werden Sie eine rechtlich bindende elektronische Signatur setzen.\nBitte geben Sie Ihre PIN ein zum weitergehen oder klicken Sie auf Abbrechen.\n\nWarnung: Wenn Sie nur auf das Internet gehen mchten, geben Sie bitte Ihre PIN NICHT ein, sondern klicken Sie auf Abbrechen." #else #ifdef __APPLE__ "CAUTION: you are about to make a legally binding electronic signature with your identity card. Please enter your PIN to continue or press the Cancel button. If you only want to log on to a web site or a server, do NOT enter your PIN and press the Cancel button.", "LET OP: u gaat een wettelijk bindende electronische handtekening plaatsen met uw identiteitskaart. Geef uw PIN in om verder te gaan of klik op Stoppen. Als u enkel wil aanloggen op een web site of een server, geef uw PIN NIET in en klik op Stoppen.", "ATTENTION: vous allez apposer une signature electronique\njuridiquement valide avec votre carte d'identite.Veuillez entrer votre PIN pour continuer ou cliquez sur Annuler. Si vous desirez seulement vous connecter a un site ou un serveur, n' entrez PAS votre PIN et cliquez sur Annuler.", "ACHTUNG: Mit Ihrem Personalausweis werden Sie eine rechtlich bindende elektronische Signatur setzen. Geben Sie Ihre PIN ein zum weitergehen oder klicken Sie auf Abbrechen. Warnung: Wenn Sie nur auf das Internet gehen mochten, geben Sie bitte Ihre PIN NICHT ein, sondern klicken Sie auf Abbrechen." #else "Caution: you are about to make a legally binding electronic\nsignature with your identity card.\nPlease enter your PIN to continue or press the Cancel button.\n\nIf you only want to log on to a web site or a server,\ndo NOT enter your PIN and press the Cancel button.", "Let op: u gaat een wettelijk bindende electronische handtekening\nplaatsen met uw identiteitskaart.\nGeef uw PIN in om verder te gaan of klik op Stoppen.\n\nAls u enkel wil aanloggen op een web site\nof een server, geef uw PIN NIET in en klik op Stoppen.", "Attention: vous allez apposer une signature electronique\njuridiquement valide avec votre carte d'identite.\nVeuillez entrer votre PIN pour continuer ou cliquez sur Annuler.\n\nSi vous desirez seulement vous connecter a un site\nou un serveur, n'entrez PAS votre PIN et cliquez sur Annuler.", "Achtung: Mit Ihrem Personalausweis werden Sie eine rechtlich\r\nbindende elektronische Signatur setzen.\r\nGeben Sie Ihre PIN ein zum weitergehen oder klicken Sie auf Abbrechen.\r\n\r\nWarnung: Wenn Sie nur auf das Internet gehen mochten, geben\r\nSie bitte Ihre PIN NICHT ein, sondern klicken Sie auf Abbrechen." #endif #endif }; static char *wrong_pin_len_msgs[4] = { "Wrong PIN length", "Foute PIN lengte", "Longueur de PIN erron", "Falsche PIN-Lnge" }; static char *wrong_pin_msgs[4] = { "Wrong PIN, %d tries left\n\nRetry or cancel?", "Foute PIN, nog %d pogingen\n\nOpnieuw proberen of stoppen?", "PIN erron, %d essais restants\n\nRessayer ou annuler?", "Falsche PIN, %d verbleibende Versuche\n\nErneut versuchen oder abbrechen?" }; static char *pin_blocked_msgs[4] = { "PIN blocked", "PIN geblokkeerd", "PIN bloqu ", "PIN gesperrt" }; #endif /* HAVE_GUI */ /* To be removed */ #if 0 static void dumphex(char *msg, const u8 * buf, int len) { int i; printf("%s", msg); for (i = 0; i < len; i++) printf("%02X ", buf[i]); printf(" (%d bytes)\n", len); } #endif #ifdef BELPIC_PIN_PAD #define SCR_INIT_ID 100 #define SCR_VERIFY_ID 101 #define SCR_CHANGE_ID 102 #define SCR_CARD_HANDLE 999 struct tTLV { unsigned char *base; unsigned char *end; unsigned char *current; unsigned char *next; }; static void TLVInit(struct tTLV *tlv, u8 * base, size_t size) { tlv->base = base; tlv->end = base + size; tlv->current = tlv->next = base; } static void TLVNext(struct tTLV *tlv, u8 tag) { assert(tlv->next + 2 < tlv->end); tlv->current = tlv->next; *(tlv->next++) = tag; *(tlv->next++) = 0; } static void TLVAdd(struct tTLV *tlv, u8 val) { assert(tlv->next + 1 < tlv->end); *(tlv->next++) = val; tlv->current[1]++; } static void TLVAddBuffer(struct tTLV *tlv, u8 * val, size_t size) { assert(tlv->next + size < tlv->end); memcpy(tlv->next, val, size); tlv->current[1] = size; tlv->next = tlv->next + size; } static size_t TLVLen(struct tTLV *tlv) { return tlv->next - tlv->base; } static LONG SCR_SCardInit(LPCTSTR szPinPadDll, LPCTSTR szReader, DWORD version, SCR_SupportConstants * supported) { LONG rv; unsigned char sendbuf[256]; unsigned char recvbuf[2]; char szTemp[32]; DWORD dwRecvLength; struct tTLV tlv; memset(szTemp, 0, sizeof(szTemp)); memset(sendbuf, 0, sizeof(sendbuf)); memset(recvbuf, 0, sizeof(recvbuf)); dwRecvLength = sizeof(recvbuf); /* Make TLV buffer */ TLVInit(&tlv, sendbuf, sizeof(sendbuf)); TLVNext(&tlv, 0x01); /* Function ID */ sprintf(szTemp, "%ld", SCR_INIT_ID); TLVAddBuffer(&tlv, (u8 *) szTemp, strlen(szTemp)); TLVNext(&tlv, 0x02); /* PinPad Dll */ TLVAddBuffer(&tlv, (u8 *) szPinPadDll, strlen(szPinPadDll)); TLVNext(&tlv, 0x03); /* Reader Name */ TLVAddBuffer(&tlv, (u8 *) szReader, strlen(szReader)); TLVNext(&tlv, 0x04); /* Version */ sprintf(szTemp, "%ld", version); TLVAddBuffer(&tlv, (u8 *) szTemp, strlen(szTemp)); #ifdef HAVE_PCSC_OLD rv = SCardControl(SCR_CARD_HANDLE, sendbuf, TLVLen(&tlv), recvbuf, &dwRecvLength); #else rv = SCardControl(SCR_CARD_HANDLE, 0, sendbuf, TLVLen(&tlv), recvbuf, dwRecvLength, &dwRecvLength); #endif if (dwRecvLength > 0) { *supported = recvbuf[0]; } else { rv = SC_ERROR_UNKNOWN_DATA_RECEIVED; } return rv; } static LONG SCR_SCardPIN(long lAction, LPCTSTR szPinPadDll, const SCR_Card * pCard, BYTE pinID, const SCR_PinUsage * pUsage, const SCR_Application * pApp, BYTE * pCardStatus) { LONG rv; unsigned char sendbuf[256]; unsigned char recvbuf[2]; char szTemp[32]; DWORD dwRecvLength; struct tTLV tlv; memset(szTemp, 0, sizeof(szTemp)); memset(recvbuf, 0, sizeof(recvbuf)); dwRecvLength = sizeof(recvbuf); /* Make TLV buffer */ TLVInit(&tlv, sendbuf, sizeof(sendbuf)); TLVNext(&tlv, 0x01); /* Function ID */ sprintf(szTemp, "%ld", lAction); TLVAddBuffer(&tlv, (u8 *) szTemp, strlen(szTemp)); TLVNext(&tlv, 0x02); /* PinPad Dll */ TLVAddBuffer(&tlv, (u8 *) szPinPadDll, strlen(szPinPadDll)); TLVNext(&tlv, 0x03); /* SCR_Card Handle */ sprintf(szTemp, "%ld", pCard->hCard); TLVAddBuffer(&tlv, (u8 *) szTemp, strlen(szTemp)); if (pCard->language != NULL) { TLVNext(&tlv, 0x04); /* SCR_Card language */ TLVAddBuffer(&tlv, (u8 *) pCard->language, strlen(pCard->language)); } if (pCard->id.data != NULL) { TLVNext(&tlv, 0x05); /* SCR_Card id */ TLVAddBuffer(&tlv, pCard->id.data, pCard->id.length); } TLVNext(&tlv, 0x06); /* PinID */ TLVAdd(&tlv, pinID); if (pUsage != NULL) { TLVNext(&tlv, 0x07); /* SCR_PinUsage code */ sprintf(szTemp, "%ld", pUsage->code); TLVAddBuffer(&tlv, (u8 *) szTemp, strlen(szTemp)); if (pUsage->shortString != NULL) { TLVNext(&tlv, 0x08); /* SCR_PinUsage shortstring */ TLVAddBuffer(&tlv, (u8 *) pUsage->shortString, strlen(pUsage->shortString)); } if (pUsage->longString != NULL) { TLVNext(&tlv, 0x09); /* SCR_PinUsage longstring */ TLVAddBuffer(&tlv, (u8 *) pUsage->longString, strlen(pUsage->longString)); } } if (pApp->id.data != NULL) { TLVNext(&tlv, 0x0A); /* SCR_Application id */ TLVAddBuffer(&tlv, (u8 *) pApp->id.data, pApp->id.length); } if (pApp->shortString != NULL) { TLVNext(&tlv, 0x0B); /* SCR_Application shortstring */ TLVAddBuffer(&tlv, (u8 *) pApp->shortString, strlen(pApp->shortString)); } if (pApp->longString != NULL) { TLVNext(&tlv, 0x0C); /* SCR_Application longstring */ TLVAddBuffer(&tlv, (u8 *) pApp->longString, strlen(pApp->longString)); } #ifdef HAVE_PCSC_OLD rv = SCardControl(SCR_CARD_HANDLE, sendbuf, TLVLen(&tlv), recvbuf, &dwRecvLength); #else rv = SCardControl(SCR_CARD_HANDLE, 0, sendbuf, TLVLen(&tlv), recvbuf, dwRecvLength, &dwRecvLength); #endif if (dwRecvLength < 2) { rv = SC_ERROR_UNKNOWN_DATA_RECEIVED; } else { memcpy(pCardStatus, recvbuf, 2); } return rv; } static LONG SCR_SCardVerifyPIN(LPCTSTR szPinPadDll, const SCR_Card * pCard, BYTE pinID, const SCR_PinUsage * pUsage, const SCR_Application * pApp, BYTE * pCardStatus) { return SCR_SCardPIN(SCR_VERIFY_ID, szPinPadDll, pCard, pinID, pUsage, pApp, pCardStatus); } static LONG SCR_SCardChangePIN(LPCTSTR szPinPadDll, const SCR_Card * pCard, BYTE pinID, const SCR_Application * pApp, BYTE * pCardStatus) { return SCR_SCardPIN(SCR_CHANGE_ID, szPinPadDll, pCard, pinID, NULL, pApp, pCardStatus); } #endif /* BELPIC_PIN_PAD */ #if defined(HAVE_GUI) ||defined(BELPIC_PIN_PAD) static int belpic_calculate_lang(sc_card_t *card) { struct belpic_priv_data *priv = DRVDATA(card); int lang = priv->lang; return lang; } #endif /* defined(HAVE_GUI) ||defined(BELPIC_PIN_PAD) */ static int str2lang(sc_context_t *ctx, char *lang) { if (memcmp(lang, "en", 2) == 0) return LNG_ENG; else if (memcmp(lang, "nl", 2) == 0) return LNG_DUTCH; else if (memcmp(lang, "fr", 2) == 0) return LNG_FRENCH; else if (memcmp(lang, "de", 2) == 0) return LNG_GERMAN; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Unknown/unsupported language code: %c%c\n", lang[0], lang[1]); return -1; } #ifdef GET_LANG_FROM_CARD /* str is in lower case, the case of buf can be both, and buf is large enough */ static int match_string(const char *str, const char *buf) { int i = 0; while (str[i] != '\0') { if (str[i] != ((buf[i] >= 'A' && buf[i] <= 'Z') ? buf[i] + 32 : buf[i])) return 0; i++; } return 1; /* match */ } static int get_pref(const char *prefs, int prefs_len, const char *title, const char *key, int *len) { int i = 0; int title_len = strlen(title); int key_len = strlen(key); while (prefs[i] != '\0' && i < prefs_len) i++; prefs_len = i; i = 0; while (i < prefs_len) { while (i < prefs_len && prefs[i] != '[') i++; if (i + title_len >= prefs_len) return -1; if (!match_string(title, prefs + i)) { i++; continue; } i += title_len; while (i < prefs_len) { while (i < prefs_len && (prefs[i] == '\r' || prefs[i] == '\n')) i++; if (i < prefs_len && prefs[i] == '[') break; if (i + key_len + 1 >= prefs_len) return -2; if (!match_string(key, prefs + i)) { i++; continue; } i += key_len; if (prefs[i] != '=') return -3; *len = ++i; while (*len < prefs_len && prefs[*len] != '\r' && prefs[*len] != '\n') (*len)++; *len -= i; return i; } } return -1; } static int get_language(sc_card_t *card) { sc_apdu_t apdu; u8 prefs[240], *lg_value; u8 path[] = { 0x3F, 0x00, 0xDF, 0x01, 0x40, 0x39 }; int r, i, len; /* Get the language from the card's preferences file */ assert(card != NULL); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x08, 0x0C); apdu.lc = sizeof(path); apdu.data = path; apdu.datalen = sizeof(path); apdu.resplen = 0; apdu.le = 0; r = sc_lock(card); if (r < 0) goto prefs_error; r = sc_transmit_apdu(card, &apdu); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Select_File[prefs_file] command failed: %d\n", r); sc_unlock(card); goto prefs_error; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Select_File[prefs_file]: card returned %d\n", r); sc_unlock(card); goto prefs_error; } r = iso_ops->read_binary(card, 0, prefs, sizeof(prefs), 0); sc_unlock(card); if (r <= 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Read_Binary[prefs_file] returned %d\n", r); goto prefs_error; } #if 0 dumphex("Prefs: ", prefs, r); #endif i = get_pref(prefs, r, "[gen]", "lg", &len); if (i <= 0 || len < 2) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Couldn't find language in prefs file: %d\n", i); goto prefs_error; } lg_value = prefs + i; /* language code(s) found, starts here */ i = 0; while (1) { while (i <= len - 2 && (lg_value[i] == ' ' || lg_value[i] == '|')) i++; if (i > len - 2) goto prefs_error; r = str2lang(card->ctx, lg_value + i); if (r >= 0) return r; i += 2; } prefs_error: /* If troubles with the card's prefs file, get the language from the OS */ #ifdef _WIN32 switch (GetUserDefaultLangID() & 0x00FF) { case 0x13: return LNG_DUTCH; case 0x0C: return LNG_FRENCH; case 0x07: return LNG_GERMAN; default: return LNG_ENG; } #endif return LNG_ENG; /* default */ } #endif /* GET_LANG_FROM_CARD */ static scconf_block *get_belpic_conf(sc_context_t *ctx, const char *name) { scconf_block *conf_block = NULL, **blocks; int i; for (i = 0; ctx->conf_blocks[i] != NULL; i++) { blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], name, NULL); if (!blocks) return NULL; conf_block = blocks[0]; free(blocks); if (conf_block != NULL) break; } return conf_block; } #ifdef BELPIC_PIN_PAD static void load_pin_pad_err(const char *reader_name, const char *pp_reader_lib, char *msg) { char buf[300]; void *hDlg; if (strlen(reader_name) + strlen(pp_reader_lib) > 200) return; sprintf(buf, "Error while loading library \"%s\" for pin pad reader \"%s\": %s\n", pp_reader_lib, reader_name, msg); scgui_ask_message(app_msg[0], "Pin pad library error", buf, btn_msg_close[0], NULL, reader_name); } static int belpic_load_pin_pad_lib(sc_card_t *card, struct belpic_priv_data *priv_data, const char *reader_name, const char *pp_reader_lib) { LONG r; DWORD supported; memset(priv_data->szPinPadDll, 0, sizeof(priv_data->szPinPadDll)); strcpy(priv_data->szPinPadDll, pp_reader_lib); priv_data->scr_init = (FARPROC) SCR_SCardInit; priv_data->scr_verify_pin = (FARPROC) SCR_SCardVerifyPIN; priv_data->scr_change_pin = (FARPROC) SCR_SCardChangePIN; if (priv_data->scr_init == NULL || priv_data->scr_verify_pin == NULL) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Function not found in \"%s\" err = 0x%0x\n", pp_reader_lib, GetLastError()); load_pin_pad_err(reader_name, pp_reader_lib, "unsufficient functionality found in library"); return SC_ERROR_READER; } r = priv_data->scr_init(pp_reader_lib, reader_name, 1, &supported); if (r != SCARD_S_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SCR_Init() returned 0x%0x\n", r); load_pin_pad_err(reader_name, pp_reader_lib, "Initialization of library failed"); return SC_ERROR_READER; } if (supported) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SCR_init() returned not supported code 0x%0x\n", supported); load_pin_pad_err(reader_name, pp_reader_lib, "Initialization of library returned UNSUPPORTED"); return SC_ERROR_READER; } #if 0 HINSTANCE dll = LoadLibrary(pp_reader_lib); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Pin pad reader \"%s\" found, now loading corresponding lib \"%s\"\n", reader_name, pp_reader_lib); if (dll == NULL) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unable to load library \"%s\", err = 0x%0x\n", pp_reader_lib, GetLastError()); load_pin_pad_err(reader_name, pp_reader_lib, "library not found or unable to load it"); return SC_ERROR_READER; } priv_data->scr_init = GetProcAddress(dll, "SCR_Init"); priv_data->scr_verify_pin = GetProcAddress(dll, "SCR_VerifyPIN"); priv_data->scr_change_pin = GetProcAddress(dll, "SCR_ChangePIN"); if (priv_data->scr_init == NULL || priv_data->scr_verify_pin == NULL) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Function not found in \"%s\" err = 0x%0x\n", pp_reader_lib, GetLastError()); load_pin_pad_err(reader_name, pp_reader_lib, "unsufficient functionality found in library"); return SC_ERROR_READER; } r = priv_data->scr_init(reader_name, 1, &supported); if (r != SCARD_S_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SCR_Init() returned 0x%0x\n", r); load_pin_pad_err(reader_name, pp_reader_lib, "Initialization of library failed"); return SC_ERROR_READER; } if (supported) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SCR_init() returned not supported code 0x%0x\n", supported); load_pin_pad_err(reader_name, pp_reader_lib, "Initialization of library returned UNSUPPORTED"); return SC_ERROR_READER; } #endif return 1; } static int belpic_detect_pin_pad(sc_card_t *card, struct belpic_priv_data *priv_data) { int i = 0; char *reader_name = card->reader->name, *conf_reader, *conf_lib; scconf_block *conf_block = NULL; char *reader_i = "reader ", *lib_i = "lib "; int rn_len = strlen(reader_name); /* Hardcoded readers */ for (i = 0; pp_reader_names[i] != NULL; i++) { int pp_rn_len = strlen(pp_reader_names[i]); if (rn_len >= pp_rn_len && strncmp(reader_name, pp_reader_names[i], pp_rn_len) == 0) { return belpic_load_pin_pad_lib(card, priv_data, reader_name, pp_reader_libs[i]); } } /* From the config file */ conf_block = get_belpic_conf(card->ctx, "belpic_pin_pad"); if (conf_block == NULL) return 0; for (i = 0; i < 10; i++) { reader_i[6] = (char) (0x30 + i); conf_reader = (char *) scconf_get_str(conf_block, reader_i, NULL); if (conf_reader != NULL && rn_len >= strlen(conf_reader) && strncmp(reader_name, conf_reader, strlen(conf_reader)) == 0) { lib_i[3] = (char) (0x30 + i); conf_lib = (char *) scconf_get_str(conf_block, lib_i, NULL); if (conf_lib != NULL) return belpic_load_pin_pad_lib(card, priv_data, reader_name, conf_lib); } } return 0; } #endif /* BELPIC_PIN_PAD */ static int belpic_finish(sc_card_t *card) { free(DRVDATA(card)); return 0; } static int belpic_match_card(sc_card_t *card) { int i; i = _sc_match_atr(card, belpic_atrs, &card->type); if (i < 0) return 0; return 1; } static int belpic_init(sc_card_t *card) { struct belpic_priv_data *priv = NULL; scconf_block *conf_block; #ifdef BELPIC_PIN_PAD int r; #endif sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Belpic V%s", BELPIC_VERSION); #ifdef HAVE_GUI sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, " with GUI support"); #endif #ifdef BELPIC_PIN_PAD sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, " with support for pin pad reader libs"); #endif sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "\n"); if (card->type < 0) card->type = SC_CARD_TYPE_BELPIC_EID; /* Unknown card: assume it's the Belpic Card */ priv = calloc(1, sizeof(struct belpic_priv_data)); if (priv == NULL) return SC_ERROR_OUT_OF_MEMORY; card->drv_data = priv; card->cla = 0x00; if (card->type == SC_CARD_TYPE_BELPIC_EID) { _sc_card_add_rsa_alg(card, 1024, SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE, 0); } /* State that we have an RNG */ card->caps |= SC_CARD_CAP_RNG; /* Language prefences */ priv->lang = -1; conf_block = get_belpic_conf(card->ctx, "belpic_general"); if (conf_block != NULL) { char *lang = (char *) scconf_get_str(conf_block, "force_language", NULL); if (lang != NULL && strlen(lang) == 2) priv->lang = str2lang(card->ctx, lang); } #ifdef GET_LANG_FROM_CARD if (priv->lang == -1) priv->lang = get_language(card); #endif card->max_pin_len = BELPIC_MAX_USER_PIN_LEN; #ifdef HAVE_GUI r = scgui_init(); if (r != 0) sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "scgui_init() returned error %d\n", i); #endif #ifdef BELPIC_PIN_PAD r = belpic_detect_pin_pad(card, priv); if (r == 1) card->reader->capabilities |= SC_READER_CAP_PIN_PAD; else if (r < 0) return r; /* error loading/initing pin pad lib */ conf_block = get_belpic_conf(card->ctx, "belpic_pin_pad"); if (conf_block != NULL) { if (scconf_get_bool(conf_block, "msg_auth_pin", 1)) priv->options |= PP_MSG_AUTH_PIN; if (scconf_get_bool(conf_block, "msg_wrong_pin", 1)) priv->options |= PP_MSG_WRONG_PIN; if (scconf_get_bool(conf_block, "msg_changepin_mismatch", 1)) priv->options |= PP_MSG_CHANGEPIN_MISMATCH; if (scconf_get_bool(conf_block, "msg_pin_blocked", 1)) priv->options |= PP_MSG_PIN_BLOCKED; } #endif return 0; } static int belpic_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; u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; int r, pathlen; sc_file_t *file = NULL; 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_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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; #if 0 printf("%d bytes: %d ms - %d bytes total: %d ms\n", r, dur, tot_read, tot_dur); #endif return r; } #ifdef BELPIC_PIN_PAD /* Test the result code of the pin pad reader + the card's status bytes */ static int belpic_pp_test_res(sc_card_t *card, int r, const u8 * card_status, int *tries_left) { #if 0 printf("PP res: 0x%0x (%d), SW1-SW2 = %02x %02x\n", r, r, card_status[0], card_status[1]); #endif if (r != SCARD_S_SUCCESS) { switch (r) { case SCARD_E_CANCELLED: return SC_ERROR_KEYPAD_CANCELLED; case SCARD_W_REMOVED_CARD: return SC_ERROR_CARD_REMOVED; case SCR_I_PIN_CHECK_FAILED: return SC_ERROR_KEYPAD_PIN_MISMATCH; default: return SC_ERROR_TRANSMIT_FAILED; } } if (card_status[0] == 0xEC && card_status[1] == 0xD2) return SC_ERROR_KEYPAD_TIMEOUT; if (card_status[0] == 0xEC && card_status[1] == 0xD6) return SC_ERROR_KEYPAD_CANCELLED; if (card_status[0] == 0x63) { if ((card_status[1] & 0xF0) == 0xC0 && tries_left != NULL) *tries_left = card_status[1] & 0x0F; return SC_ERROR_PIN_CODE_INCORRECT; } return sc_check_sw(card, card_status[0], card_status[1]); } /* Send the verify pin command to the pin pad reader + optionally show message */ static int belpic_pp_verify(sc_card_t *card, SCR_Card * scr_card, struct belpic_priv_data *priv, int pin_ref, int pin_usage, int *tries_left) { BYTE card_status[2]; void *hDlg; int first_time = 1, r = SC_ERROR_PIN_CODE_INCORRECT; int lang = belpic_calculate_lang(card); SCR_PinUsage scr_pin_usage = { pin_usage, pin_usage == SCR_USAGE_SIGN ? "SIG" : "AUT", pin_usage == SCR_USAGE_SIGN ? pin_usg_sig[lang] : pin_usg_auth[lang] }; char *reader_name = card->reader->name; char *pp_msg_login_sh = (pin_usage == SCR_USAGE_SIGN ? pp_msg_sign_sh[lang] : pp_msg_auth_sh[lang]); char *pp_msg_login = (pin_usage == SCR_USAGE_SIGN ? pp_msg_sign[lang] : pp_msg_auth[lang]); scgui_param_t icon = (pin_usage == SCR_USAGE_SIGN ? SCGUI_SIGN_ICON : SCGUI_NO_ICON); int mesg_on_screen = (priv->options & PP_MSG_AUTH_PIN) || (pin_usage != SCR_USAGE_AUTH) || SSO_OK(card->ctx); while (r == SC_ERROR_PIN_CODE_INCORRECT) { if (!first_time) { if (priv->options & PP_MSG_WRONG_PIN) { int r1; char msg[200]; sprintf(msg, wrong_pin_msgs[lang], *tries_left); r1 = scgui_ask_message(app_msg[lang], pp_msg_login_sh, msg, btn_msg_retry[lang], btn_msg_cancel[lang], reader_name); if (r1 == SCGUI_CANCEL) return r; else if (r1 != SCGUI_OK) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "scgui_ask_message returned %d\n", r1); return SC_ERROR_INTERNAL; } } else return r; } first_time = 0; if (mesg_on_screen) { scgui_display_message(app_msg[lang], pp_msg_login_sh, pp_msg_login, NULL, &hDlg, icon, reader_name); } #if 0 printf("belpic_pp_verify(): reader=%s, hCard=0x%0x\n", card->reader->name, scr_card->hCard); #endif r = priv->scr_verify_pin(priv->szPinPadDll, scr_card, pin_ref, &scr_pin_usage, &scr_app_belpic, card_status); if (mesg_on_screen) scgui_remove_message(hDlg); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SCR_Verify_PIN(): res = 0x%0x, status = %2X %2X\n", r, card_status[0], card_status[1]); r = belpic_pp_test_res(card, r, card_status, tries_left); } return r; } /* Send the change pin command to the pin pad reader + show message */ static int belpic_pp_change(sc_card_t *card, SCR_Card * scr_card, struct belpic_priv_data *priv, int pin_ref, int *tries_left) { BYTE card_status[2]; void *hDlg; int first_time = 1, r = SC_ERROR_KEYPAD_PIN_MISMATCH, r1; int lang = belpic_calculate_lang(card); char *reader_name = card->reader->name; while (r == SC_ERROR_KEYPAD_PIN_MISMATCH || r == SC_ERROR_PIN_CODE_INCORRECT) { if (!first_time) { int r1 = SCGUI_OK; if (r == SC_ERROR_KEYPAD_PIN_MISMATCH) { if (!(priv->options & PP_MSG_CHANGEPIN_MISMATCH)) return r; r1 = scgui_ask_message(app_msg[lang], pp_msg_change_sh[lang], pp_msg_pin_mismatch[lang], btn_msg_retry[lang], btn_msg_cancel[lang], reader_name); } if (r == SC_ERROR_PIN_CODE_INCORRECT) { char msg[200]; if (!(priv->options & PP_MSG_WRONG_PIN)) return r; sprintf(msg, wrong_pin_msgs[lang], *tries_left); r1 = scgui_ask_message(app_msg[lang], pp_msg_change_sh[lang], msg, btn_msg_retry[lang], btn_msg_cancel[lang], reader_name); } if (r1 == SCGUI_CANCEL) return r; else if (r1 != SCGUI_OK) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "scgui_ask_message returned %d\n", r1); return SC_ERROR_INTERNAL; } } first_time = 0; scgui_display_message(app_msg[lang], pp_msg_change_sh[lang], pp_msg_change[lang], NULL, &hDlg, SCGUI_NO_ICON, reader_name); r = priv->scr_change_pin(priv->szPinPadDll, scr_card, pin_ref, &scr_app_belpic, card_status); scgui_remove_message(hDlg); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SCR_Change_PIN(): res = 0x%0x, status = %2X %2X\n", r, card_status[0], card_status[1]); r = belpic_pp_test_res(card, r, card_status, tries_left); } return r; } #endif /* BELPIC_PIN_PAD */ static int belpic_pin_cmd_usage(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left, int pin_usage) { #ifdef BELPIC_PIN_PAD sc_apdu_t apdu; int r; struct belpic_priv_data *priv = DRVDATA(card); int lang = belpic_calculate_lang(card); if (card->reader->capabilities & SC_READER_CAP_PIN_PAD && priv->scr_init != NULL) { LONG r; SCR_Card scr_card = { priv->pcsc_card, lang_codes[lang], {NULL, 0} , NULL }; scr_app_belpic.longString = app_id_longstr[lang]; switch (data->cmd) { case SC_PIN_CMD_VERIFY: r = belpic_pp_verify(card, &scr_card, priv, data->pin_reference, pin_usage, tries_left); break; case SC_PIN_CMD_CHANGE: r = belpic_pp_change(card, &scr_card, priv, data->pin_reference, tries_left); break; default: r = SC_ERROR_NOT_SUPPORTED; } if (r == SC_ERROR_AUTH_METHOD_BLOCKED && (priv->options & PP_MSG_PIN_BLOCKED)) scgui_ask_message(app_msg[lang], " ", pin_blocked_msgs[lang], btn_msg_close[lang], NULL, card->reader->name); return r; } #endif /* BELPIC_PIN_PAD */ data->pin1.encoding = data->pin2.encoding = BELPIC_PIN_ENCODING; data->pin1.pad_char = data->pin2.pad_char = BELPIC_PAD_CHAR; data->pin1.min_length = data->pin2.min_length = BELPIC_MIN_USER_PIN_LEN; data->pin1.max_length = data->pin2.max_length = BELPIC_MAX_USER_PIN_LEN; data->apdu = NULL; return iso_ops->pin_cmd(card, data, tries_left); } static int belpic_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { if (SSO_OK(card->ctx) && data->cmd == SC_PIN_CMD_VERIFY) return 0; /* Don't log in right now, just say it's OK */ else return belpic_pin_cmd_usage(card, data, tries_left, 1); /* SCR_USAGE_AUTH = 1 */ } #ifdef HAVE_GUI /* Called by belpic_set_security_env() when a NonRep signature will be done, * or by belpic-compute_signature the first fime an auth signature is done * and the allow_sso is true */ static int belpic_askpin_verify(sc_card_t *card, int pin_usage) { struct sc_pin_cmd_data data; sc_apdu_t apdu; u8 pin_data[BELPIC_MAX_USER_PIN_LEN + 1]; int pin_len; int tries_left; int r; struct belpic_priv_data *priv = DRVDATA(card); int lang = belpic_calculate_lang(card); char *enter_pin_msg = (pin_usage == SCR_USAGE_AUTH ? enter_pin_msg_auth[lang] : enter_pin_msg_sign[lang]); scgui_param_t icon = (pin_usage == SCR_USAGE_AUTH ? SCGUI_NO_ICON : SCGUI_SIGN_ICON); data.pin1.encoding = BELPIC_PIN_ENCODING; data.pin1.pad_char = BELPIC_PAD_CHAR; data.pin1.min_length = BELPIC_MIN_USER_PIN_LEN; data.pin1.max_length = BELPIC_MAX_USER_PIN_LEN; data.cmd = SC_PIN_CMD_VERIFY; data.flags = 0; data.pin_type = SC_AC_CHV; data.pin_reference = 1; #ifdef BELPIC_PIN_PAD /* In case of a pinpad reader */ if (card->reader->capabilities & SC_READER_CAP_PIN_PAD && priv->scr_init != NULL) { data.pin1.data = NULL; data.pin1.len = 0; return belpic_pin_cmd_usage(card, &data, &tries_left, pin_usage); } #endif pin_len = BELPIC_MAX_USER_PIN_LEN + 1; r = scgui_enterpin(app_msg[lang], enter_pin_msg, pin_data, &pin_len, btn_msg_ok[lang], btn_msg_cancel[lang], wrong_pin_len_msgs[lang], icon); if (r == SCGUI_CANCEL) return SC_ERROR_KEYPAD_CANCELLED; if (r != SCGUI_OK) return SC_ERROR_INTERNAL; data.pin1.data = pin_data; data.pin1.len = pin_len; r = belpic_pin_cmd_usage(card, &data, &tries_left, pin_usage); /* card->ctx->allow_sso = true: we do PIN mgmnt ourselves */ while (r == SC_ERROR_PIN_CODE_INCORRECT && SSO_OK(card->ctx)) { int r1; char msg[200]; sprintf(msg, wrong_pin_msgs[lang], tries_left); r1 = scgui_ask_message(app_msg[lang], pin_usg_auth[lang], msg, btn_msg_retry[lang], btn_msg_cancel[lang], card->reader->name); if (r1 == SCGUI_CANCEL) return r; else if (r1 != SCGUI_OK) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "scgui_ask_message returned %d\n", r1); return SC_ERROR_INTERNAL; } pin_len = BELPIC_MAX_USER_PIN_LEN + 1; r = scgui_enterpin(app_msg[lang], enter_pin_msg, pin_data, &pin_len, btn_msg_ok[lang], btn_msg_cancel[lang], wrong_pin_len_msgs[lang], icon); if (r == SCGUI_CANCEL) return SC_ERROR_KEYPAD_CANCELLED; if (r != SCGUI_OK) return SC_ERROR_INTERNAL; data.pin1.data = pin_data; data.pin1.len = pin_len; r = belpic_pin_cmd_usage(card, &data, &tries_left, pin_usage); if (tries_left == 0) r = SC_ERROR_AUTH_METHOD_BLOCKED; } if (r == SC_ERROR_AUTH_METHOD_BLOCKED && SSO_OK(card->ctx)) scgui_ask_message(app_msg[lang], " ", pin_blocked_msgs[lang], btn_msg_close[lang], NULL, card->reader->name); return r; } #endif /* HAVE_GUI */ static int belpic_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { sc_apdu_t apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; int r; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "belpic_set_security_env(), keyRef = 0x%0x, algo = 0x%0x\n", *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) 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Set Sec Env: unsupported algo 0X%0X\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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Set Security Env APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card's Set Security Env command returned error"); /* If a NonRep signature will be done, ask to enter a PIN. It would be more * logical to put the code below into the compute signature function because * a Verify Pin call must immediately preceed a Compute Signature call. * 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) { #ifdef HAVE_GUI r = belpic_askpin_verify(card, SCR_USAGE_SIGN); if (r != 0 && r != SC_ERROR_KEYPAD_CANCELLED) sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Verify PIN in SET command returned %d\n", r); else sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Verify PIN in SET command returned %d\n", r); #else sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No GUI for NonRep key present, signature cancelled\n"); return SC_ERROR_NOT_SUPPORTED; #endif } return r; } static int belpic_compute_signature(sc_card_t *card, const u8 * data, size_t data_len, u8 * out, size_t outlen) { int r; r = iso_ops->compute_signature(card, data, data_len, out, outlen); #ifdef HAVE_GUI if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED && SSO_OK(card->ctx)) { r = belpic_askpin_verify(card, SCR_USAGE_AUTH); if (r == 0) r = iso_ops->compute_signature(card, data, data_len, out, outlen); } #endif return r; } static int belpic_update_binary(sc_card_t *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) { int r; r = iso_ops->update_binary(card, idx, buf, count, flags); #ifdef HAVE_GUI if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED && SSO_OK(card->ctx)) { r = belpic_askpin_verify(card, SCR_USAGE_AUTH); if (r == 0) r = iso_ops->update_binary(card, idx, buf, count, flags); } #endif return r; } #if 0 static int belpic_logout(sc_card_t *card) { sc_apdu_t apdu; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xE6, 0x00, 0x00); apdu.cla = 0x80; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "LOGOFF: APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "LOGOFF returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } #endif 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.finish = belpic_finish; belpic_ops.update_binary = belpic_update_binary; belpic_ops.select_file = belpic_select_file; belpic_ops.read_binary = belpic_read_binary; belpic_ops.pin_cmd = belpic_pin_cmd; belpic_ops.set_security_env = belpic_set_security_env; belpic_ops.compute_signature = belpic_compute_signature; belpic_ops.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.13.0/src/libopensc/card-openpgp.c0000644000015201777760000021245012057406034015321 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Specifications: * http://www.g10code.de/docs/openpgp-card-1.0.pdf (obsolete) * http://www.g10code.de/docs/openpgp-card-1.1.pdf * http://www.g10code.de/docs/openpgp-card-2.0.pdf */ #include "config.h" #include #include #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" #include "errors.h" #ifdef ENABLE_OPENSSL #include #endif /* ENABLE_OPENSSL */ static struct sc_atr_table pgp_atrs[] = { { "3b:fa:13:00:ff:81:31:80:45:00:31:c1:73:c0:01:00:00:90:00:b1", NULL, "OpenPGP card v1.0/1.1", SC_CARD_TYPE_OPENPGP_V1, 0, NULL }, { "3b:da:18:ff:81:b1:fe:75:1f:03:00:31:c5:73:c0:01:40:00:90:00:0c", NULL, "CryptoStick v1.2 (OpenPGP v2.0)", SC_CARD_TYPE_OPENPGP_V2, 0, NULL }, { 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 }; /* * 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. */ enum _type { /* DO type */ SIMPLE = SC_FILE_TYPE_WORKING_EF, CONSTRUCTED = SC_FILE_TYPE_DF }; enum _version { /* 2-byte BCD-alike encoded version number */ OPENPGP_CARD_1_0 = 0x0100, OPENPGP_CARD_1_1 = 0x0101, OPENPGP_CARD_2_0 = 0x0200 }; enum _access { /* access flags for the respective DO/file */ READ_NEVER = 0x0010, READ_PIN1 = 0x0011, READ_PIN2 = 0x0012, READ_PIN3 = 0x0014, READ_ALWAYS = 0x0018, READ_MASK = 0x00FF, WRITE_NEVER = 0x1000, WRITE_PIN1 = 0x1100, WRITE_PIN2 = 0x1200, WRITE_PIN3 = 0x1400, WRITE_ALWAYS = 0x1800, WRITE_MASK = 0x1F00 }; enum _ext_caps { /* extended capabilities/features */ EXT_CAP_ALG_ATTR_CHANGEABLE = 0x0004, EXT_CAP_PRIVATE_DO = 0x0008, EXT_CAP_C4_CHANGEABLE = 0x0010, EXT_CAP_KEY_IMPORT = 0x0020, EXT_CAP_GET_CHALLENGE = 0x0040, EXT_CAP_SM = 0x0080, EXT_CAP_CHAINING = 0x1000, EXT_CAP_APDU_EXT = 0x2000 }; enum _card_state { CARD_STATE_UNKNOWN = 0x00, CARD_STATE_INITIALIZATION = 0x03, CARD_STATE_ACTIVATED = 0x05 }; struct blob { struct blob * next; /* pointer to next sibling */ struct blob * parent; /* pointer to parent */ struct do_info *info; sc_file_t * file; unsigned int id; int status; unsigned char * data; unsigned int len; struct blob * files; /* pointer to 1st child */ }; struct do_info { unsigned int id; /* ID of the DO in question */ enum _type type; /* constructed DO or not */ enum _access access; /* R/W access levels for the DO */ /* function to get the DO from the card: * only != NULL is DO if readable and not only a part of a constructed DO */ int (*get_fn)(sc_card_t *, unsigned int, u8 *, size_t); /* function to write the DO to the card: * only != NULL if DO is writeable under some conditions */ int (*put_fn)(sc_card_t *, unsigned int, const u8 *, size_t); }; static int pgp_get_card_features(sc_card_t *card); static int pgp_finish(sc_card_t *card); static void pgp_iterate_blobs(struct blob *, int, void (*func)()); static int pgp_get_blob(sc_card_t *card, struct blob *blob, unsigned int id, struct blob **ret); static struct blob * pgp_new_blob(sc_card_t *, struct blob *, unsigned int, sc_file_t *); static void pgp_free_blob(struct blob *); 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 struct do_info pgp1_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 }, { 0xa400, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, { 0xa401, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, { 0xb600, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, { 0xb601, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, { 0xb800, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, { 0xb801, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, { 0, 0, 0, NULL, NULL }, }; static struct do_info pgp2_objects[] = { /* 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 }, /* The 7F21 is constructed DO in spec, but in practice, its content can be retrieved * as simple DO (no need to parse TLV). */ { 0x7f21, 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 }, { 0xa400, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, /* The 0xA401, 0xB601, 0xB801 are just symbolic, it does 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). */ { 0xa401, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, { 0xb600, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, { 0xb601, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, { 0xb800, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, { 0xb801, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, { 0, 0, 0, NULL, NULL }, }; /* The DO holding X.509 certificate is constructed but does not contain child DO. * We should notice this when building fake file system later. */ #define DO_CERT 0x7f21 #define DRVDATA(card) ((struct pgp_priv_data *) ((card)->drv_data)) struct pgp_priv_data { struct blob * mf; struct blob * current; /* currently selected file */ enum _version bcd_version; struct do_info *pgp_objects; enum _card_state state; /* card state */ enum _ext_caps ext_caps; /* extended capabilities */ size_t max_challenge_size; size_t max_cert_size; sc_security_env_t sec_env; }; /* ABI: check if card's ATR matches one of driver's */ static int pgp_match_card(sc_card_t *card) { int i; i = _sc_match_atr(card, pgp_atrs, &card->type); if (i >= 0) { card->name = pgp_atrs[i].name; return 1; } return 0; } /* ABI: initialize driver */ static int pgp_init(sc_card_t *card) { struct pgp_priv_data *priv; sc_path_t aid; sc_file_t *file = NULL; struct do_info *info; int r; struct blob *child = NULL; priv = calloc (1, sizeof *priv); if (!priv) return SC_ERROR_OUT_OF_MEMORY; card->drv_data = priv; card->cla = 0x00; /* set pointer to correct list of card objects */ priv->pgp_objects = (card->type == SC_CARD_TYPE_OPENPGP_V2) ? pgp2_objects : pgp1_objects; /* set detailed card version */ priv->bcd_version = (card->type == SC_CARD_TYPE_OPENPGP_V2) ? OPENPGP_CARD_2_0 : OPENPGP_CARD_1_1; /* select application "OpenPGP" */ sc_format_path("D276:0001:2401", &aid); aid.type = SC_PATH_TYPE_DF_NAME; if ((r = iso_ops->select_file(card, &aid, &file)) < 0) { pgp_finish(card); return r; } /* read information from AID */ if (file && file->namelen == 16) { /* OpenPGP card spec 1.1 & 2.0, section 4.2.1 & 4.1.2.1 */ priv->bcd_version = bebytes2ushort(file->name + 6); /* kludge: get card's serial number from manufacturer ID + serial number */ memcpy(card->serialnr.value, file->name + 8, 6); card->serialnr.len = 6; } /* change file path to MF for re-use in MF */ sc_format_path("3f00", &file->path); /* set up the root of our fake file tree */ priv->mf = pgp_new_blob(card, NULL, 0x3f00, file); if (!priv->mf) { pgp_finish(card); return SC_ERROR_OUT_OF_MEMORY; } /* select MF */ priv->current = priv->mf; /* Populate MF - add matching blobs listed in the pgp_objects table. */ for (info = priv->pgp_objects; (info != NULL) && (info->id > 0); info++) { if (((info->access & READ_MASK) == READ_ALWAYS) && (info->get_fn != NULL)) { child = pgp_new_blob(card, priv->mf, info->id, sc_file_new()); /* catch out of memory condition */ if (child == NULL) { pgp_finish(card); return SC_ERROR_OUT_OF_MEMORY; } } } /* get card_features from ATR & DOs */ pgp_get_card_features(card); return SC_SUCCESS; } /* internal: get features of the card: capabilities, ... */ static int pgp_get_card_features(sc_card_t *card) { struct pgp_priv_data *priv = DRVDATA (card); unsigned char *hist_bytes = card->atr.value; size_t atr_len = card->atr.len; size_t i = 0; struct blob *blob, *blob6e, *blob73; /* parse card capabilities from historical bytes */ while ((i < atr_len) && (hist_bytes[i] != 0x73)) i++; /* IS07816-4 hist bytes 3rd function table */ if ((hist_bytes[i] == 0x73) && (atr_len > i+3)) { /* bit 0x40 in byte 3 of TL 0x73 means "extended Le/Lc" */ if (hist_bytes[i+3] & 0x40) { card->caps |= SC_CARD_CAP_APDU_EXT; priv->ext_caps |= EXT_CAP_APDU_EXT; } /* bit 0x80 in byte 3 of TL 0x73 means "Command chaining" */ if (hist_bytes[i+3] & 0x80) priv->ext_caps |= EXT_CAP_CHAINING; } 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)) { while ((i < blob->len) && (blob->data[i] != 0x73)) i++; /* IS07816-4 hist bytes 3rd function table */ if ((blob->data[i] == 0x73) && (blob->len > i+3)) { /* bit 0x40 in byte 3 of TL 0x73 means "extended Le/Lc" */ if (blob->data[i+3] & 0x40) { card->caps |= SC_CARD_CAP_APDU_EXT; priv->ext_caps |= EXT_CAP_APDU_EXT; } /* bit 0x80 in byte 3 of TL 0x73 means "Command chaining" */ if (hist_bytes[i+3] & 0x80) priv->ext_caps |= EXT_CAP_CHAINING; } /* get card status from historical bytes status indicator */ if ((blob->data[0] == 0x00) && (blob->len >= 4)) priv->state = blob->data[blob->len-3]; } } if ((pgp_get_blob(card, priv->mf, 0x006e, &blob6e) >= 0) && (pgp_get_blob(card, blob6e, 0x0073, &blob73) >= 0)) { /* get "extended capabilities" DO */ if ((pgp_get_blob(card, blob73, 0x00c0, &blob) >= 0) && (blob->data != NULL) && (blob->len > 0)) { /* in v2.0 bit 0x04 in first byte means "algorithm attributes changeable */ if ((blob->data[0] & 0x04) && (card->type == SC_CARD_TYPE_OPENPGP_V2)) 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; } /* in v2.0 bit 0x80 in first byte means "support Secure Messaging" */ if ((blob->data[0] & 0x80) && (card->type == SC_CARD_TYPE_OPENPGP_V2)) priv->ext_caps |= EXT_CAP_SM; if ((priv->bcd_version >= OPENPGP_CARD_2_0) && (blob->len >= 10)) { /* max. challenge size is at bytes 3-4 */ priv->max_challenge_size = bebytes2ushort(blob->data + 2); /* max. cert size it at bytes 5-6 */ priv->max_cert_size = bebytes2ushort(blob->data + 4); /* max. send/receive sizes are at bytes 7-8 resp. 9-10 */ card->max_send_size = bebytes2ushort(blob->data + 6); card->max_recv_size = bebytes2ushort(blob->data + 8); } } /* 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]; } /* get supported algorithms & key lengths from "algorithm attributes" DOs */ for (i = 0x00c1; i <= 0x00c3; i++) { unsigned long flags; /* Is this correct? */ /* OpenPGP card spec 1.1 & 2.0, section 2.1 */ flags = SC_ALGORITHM_RSA_RAW; /* OpenPGP card spec 1.1 & 2.0, section 7.2.9 & 7.2.10 */ flags |= SC_ALGORITHM_RSA_PAD_PKCS1; flags |= SC_ALGORITHM_RSA_HASH_NONE; /* Can be generated in card */ flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; if ((pgp_get_blob(card, blob73, i, &blob) >= 0) && (blob->data != NULL) && (blob->len >= 4)) { if (blob->data[0] == 0x01) { /* Algorithm ID [RFC4880]: RSA */ unsigned int keylen = bebytes2ushort(blob->data + 1); /* Measured in bit */ _sc_card_add_rsa_alg(card, keylen, flags, 0); } } } } return SC_SUCCESS; } /* ABI: terminate driver */ 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_iterate_blobs(priv->mf, 99, pgp_free_blob); /* delete private data */ free(priv); } card->drv_data = NULL; } return SC_SUCCESS; } /* internal: fill a blob's data */ static int pgp_set_blob(struct blob *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(len, 1); if (tmp == NULL) return SC_ERROR_OUT_OF_MEMORY; blob->data = tmp; blob->len = 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, struct do_info *info) { sc_acl_entry_t *acl; 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 struct blob * pgp_new_blob(sc_card_t *card, struct blob *parent, unsigned int file_id, sc_file_t *file) { struct blob *blob = NULL; if (file == NULL) return NULL; if ((blob = calloc(1, sizeof(struct blob))) != NULL) { struct pgp_priv_data *priv = DRVDATA (card); struct do_info *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) { struct blob **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 { u8 id_str[2]; /* no parent: set file's path = file's id */ sc_format_path(ushort2bebytes(id_str, file_id), &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(struct blob *blob) { if (blob) { if (blob->parent) { struct blob **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; } if (blob->file) sc_file_free(blob->file); if (blob->data) free(blob->data); free(blob); } } /* internal: iterate through the blob tree, calling a function for each blob */ static void pgp_iterate_blobs(struct blob *blob, int level, void (*func)()) { if (blob) { if (level > 0) { struct blob *child = blob->files; while (child != NULL) { struct blob *next = child->next; pgp_iterate_blobs(child, level-1, func); child = next; } } func(blob); } } /* internal: read a blob's contents from card */ static int pgp_read_blob(sc_card_t *card, struct blob *blob) { 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[2048]; size_t buf_len = (card->caps & SC_CARD_CAP_APDU_EXT) ? sizeof(buffer) : 256; int 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, struct blob *blob) { const u8 *in; int r; 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; struct blob *new; r = sc_asn1_read_tag(&data, blob->len - (in - blob->data), &cla, &tag, &len); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unexpected end of contents\n"); return SC_ERROR_OBJECT_NOT_VALID; } /* undo ASN1's split of tag & class */ for (tmptag = tag; tmptag > 0x0FF; tmptag >>= 8) { cla <<= 8; } tag |= cla; /* create fake file system hierarchy by * using constructed DOs as DF */ if ((new = pgp_new_blob(card, blob, tag, sc_file_new())) == NULL) return SC_ERROR_OUT_OF_MEMORY; pgp_set_blob(new, data, len); 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, struct blob *blob, unsigned int id, struct blob **ret) { struct blob *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; } } 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, struct blob *root, unsigned int id, struct blob **ret) { struct blob *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->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 struct blob * pgp_find_blob(sc_card_t *card, unsigned int tag) { struct pgp_priv_data *priv = DRVDATA(card); struct blob *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 struct do_info * pgp_get_info_by_tag(sc_card_t *card, unsigned int tag) { struct pgp_priv_data *priv = DRVDATA(card); struct do_info *info; for (info = priv->pgp_objects; (info != NULL) && (info->id > 0); info++) if (tag == info->id) return info; return NULL; } /** * 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->value == NULL || 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-AppDF (5015) */ start_point += (memcmp(path->value + start_point, "\x50\x15", 2) == 0) ? 2 : 0; return start_point; } /* ABI: SELECT FILE */ 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); struct blob *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 implemetation, sometimes a file at path "11001101" * need to be written (1 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 successfully. * 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; } priv->current = NULL; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /* ignore explicitely 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 refered when importing key&certificate via pkcs15init, like above. * We pretend to successfully find this inexistent file. */ if (id == 0x4402 || id == 0x5f48) { priv->current = NULL; 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 */ priv->current = NULL; 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: LIST FILES */ static int pgp_list_files(sc_card_t *card, u8 *buf, size_t buflen) { struct pgp_priv_data *priv = DRVDATA(card); struct blob *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: READ BINARY */ 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); struct blob *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, count); } /* ABI: WRITE BINARY */ static int pgp_write_binary(sc_card_t *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } /* internal: get public key from card: as DF + sub-wEFs */ static int pgp_get_pubkey(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len) { sc_apdu_t apdu; u8 idbuf[2]; int r; sc_log(card->ctx, "called, tag=%04x\n", tag); sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 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, 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); struct blob *blob, *mod_blob, *exp_blob; sc_pkcs15_pubkey_t pubkey; u8 *data; size_t len; int r; sc_log(card->ctx, "called, tag=%04x\n", tag); if ((r = pgp_get_blob(card, priv->mf, tag & 0xFFFE, &blob)) < 0 || (r = pgp_get_blob(card, blob, 0x7F49, &blob)) < 0 || (r = pgp_get_blob(card, blob, 0x0081, &mod_blob)) < 0 || (r = pgp_get_blob(card, blob, 0x0082, &exp_blob)) < 0 || (r = pgp_read_blob(card, mod_blob)) < 0 || (r = pgp_read_blob(card, exp_blob)) < 0) LOG_TEST_RET(card->ctx, r, "error getting elements"); memset(&pubkey, 0, sizeof(pubkey)); pubkey.algorithm = SC_ALGORITHM_RSA; pubkey.u.rsa.modulus.data = mod_blob->data; pubkey.u.rsa.modulus.len = mod_blob->len; pubkey.u.rsa.exponent.data = exp_blob->data; pubkey.u.rsa.exponent.len = exp_blob->len; r = sc_pkcs15_encode_pubkey(card->ctx, &pubkey, &data, &len); 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, len); } /* ABI: GET DATA */ 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); LOG_TEST_RET(card->ctx, r, "Card returned error"); LOG_FUNC_RETURN(card->ctx, apdu.resplen); } /* ABI: PUT DATA */ static int pgp_put_data(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len) { sc_apdu_t apdu; struct pgp_priv_data *priv = DRVDATA(card); struct blob *affected_blob = NULL; struct do_info *dinfo = NULL; u8 ins = 0xDA; u8 p1 = tag >> 8; u8 p2 = tag & 0xFF; int r; LOG_FUNC_CALLED(card->ctx); /* Check if the tag is writable */ 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; 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-codded and may change in various version of the card. If we check here, * the driver may be sticked 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 %ld exceeds DO size limit %ld.", buf_len, priv->max_cert_size); LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_LENGTH); } /* Extended Header list (004D DO) 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, SC_APDU_CASE_3, 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 = buf; apdu.datalen = buf_len; apdu.lc = buf_len; } else { 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); /* 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, buf_len); } /* ABI: 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) { 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 OpenGPG, 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 ver 1, the PINs are named CHV1, CHV2, CHV3. In ver 2, they * are named PW1, PW3 (PW1 operates in 2 modes). However, the PIN references (P2 in APDU) * are the same between 2 version: * 81 (CHV1 or PW1), 82 (CHV2 or PW1-mode 2), 83 (CHV3 or PW3). * * 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; /* Ensure pin_reference is 81, 82, 83 */ if (!(data->pin_reference == 0x81 || data->pin_reference == 0x82 || data->pin_reference == 0x83)) { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "key-id should be 1, 2, 3."); } LOG_FUNC_RETURN(card->ctx, iso_ops->pin_cmd(card, data, tries_left)); } /* ABI: set security environment */ 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); if ((env->flags & SC_SEC_ENV_ALG_PRESENT) && (env->algorithm != SC_ALGORITHM_RSA)) 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; default: LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "invalid operation"); } priv->sec_env = *env; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /* ABI: 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; 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, SC_APDU_CASE_4, 0x2A, 0x9E, 0x9A); break; case 0x02: /* authentication key */ /* INTERNAL AUTHENTICATE */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x88, 0, 0); break; case 0x01: default: LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "invalid key reference"); } apdu.lc = data_len; apdu.data = 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, apdu.resplen); } /* ABI: DECIPHER */ 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 *temp = NULL; int r; LOG_FUNC_CALLED(card->ctx); /* There's some funny padding indicator that must be * prepended... hmm. */ if (!(temp = malloc(inlen + 1))) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); temp[0] = '\0'; memcpy(temp + 1, in, inlen); in = temp; inlen += 1; if (env->operation != SC_SEC_OPERATION_DECIPHER) { 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, SC_APDU_CASE_4, 0x2A, 0x80, 0x86); break; case 0x00: /* signature key */ default: free(temp); LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "invalid key reference"); } apdu.lc = inlen; apdu.data = in; apdu.datalen = inlen; 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); 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"); LOG_FUNC_RETURN(card->ctx, 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); struct blob *algo_blob; unsigned int old_modulus_len; /* Measured in bit */ unsigned int old_exponent_len; const unsigned int tag = 0x00C0 | key_info->keytype; u8 changed = 0; int r = SC_SUCCESS; LOG_FUNC_CALLED(card->ctx); /* Get old algorithm attributes */ r = pgp_seek_blob(card, priv->mf, (0x00C0 | key_info->keytype), &algo_blob); LOG_TEST_RET(card->ctx, r, "Cannot get old algorithm attributes"); old_modulus_len = bebytes2ushort(algo_blob->data + 1); /* The modulus length is coded in byte 2 & 3 */ sc_log(card->ctx, "Old modulus length %d, new %d.", old_modulus_len, key_info->modulus_len); old_exponent_len = bebytes2ushort(algo_blob->data + 3); /* The exponent length is coded in byte 3 & 4 */ sc_log(card->ctx, "Old exponent length %d, new %d.", old_exponent_len, key_info->exponent_len); /* Modulus */ /* If passed modulus_len is zero, it means using old key size */ if (key_info->modulus_len == 0) { sc_log(card->ctx, "Use old modulus length (%d).", old_modulus_len); key_info->modulus_len = old_modulus_len; } /* To generate key with new key size */ else if (old_modulus_len != key_info->modulus_len) { algo_blob->data[1] = key_info->modulus_len >> 8; algo_blob->data[2] = key_info->modulus_len; changed = 1; } /* Exponent */ if (key_info->exponent_len == 0) { sc_log(card->ctx, "Use old exponent length (%d).", old_exponent_len); key_info->exponent_len = old_exponent_len; } else if (old_exponent_len != key_info->exponent_len) { algo_blob->data[3] = key_info->exponent_len >> 8; algo_blob->data[4] = key_info->exponent_len; changed = 1; } /* If to-be-generated key has different size, we will set this new value for * GENERATE ASYMMETRIC KEY PAIR to work */ if (changed) { r = pgp_put_data(card, tag, algo_blob->data, 6); /* Note: Don't use pgp_set_blob to set data, because it won't touch the real DO */ LOG_TEST_RET(card->ctx, r, "Cannot set new algorithm attributes"); } 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]; LOG_FUNC_CALLED(card->ctx); if (key_id == 0 || key_id > 3) { sc_log(card->ctx, "Invalid key ID %d.", key_id); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); } if (outtime != NULL && *outtime != 0) createtime = *outtime; else if (outtime != NULL) /* Set output */ *outtime = createtime = time(NULL); strftime(timestring, timestrlen, "%c %Z", gmtime(&createtime)); sc_log(card->ctx, "Creation time %s.", timestring); /* Code borrowed from GnuPG */ ulong2bebytes(buf, 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 PGP fingerprints. * Reference: GnuPG, app-openpgp.c. * modulus and exponent are passed separately from key_info * because key_info->exponent may be null. **/ static int pgp_calculate_and_store_fingerprint(sc_card_t *card, time_t ctime, u8* modulus, u8* exponent, sc_cardctl_openpgp_keygen_info_t *key_info) { u8 fingerprint[SHA_DIGEST_LENGTH]; size_t mlen = key_info->modulus_len >> 3; /* 1/8 */ size_t elen = key_info->exponent_len >> 3; /* 1/8 */ u8 *fp_buffer = NULL; /* Fingerprint buffer, not hashed */ size_t fp_buffer_len; u8 *p; /* Use this pointer to set fp_buffer content */ size_t pk_packet_len; unsigned int tag; struct blob *fpseq_blob; u8 *newdata; int r; LOG_FUNC_CALLED(card->ctx); if (modulus == NULL || exponent == NULL || mlen == 0 || elen == 0) { sc_log(card->ctx, "Null data (modulus or exponent)"); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } /* http://tools.ietf.org/html/rfc4880 page 41, 72 */ pk_packet_len = 1 /* For ver number */ + 4 /* Creation time */ + 1 /* Algorithm */ + 2 /* Algorithm-specific fields */ + mlen + 2 + elen; fp_buffer_len = 3 + pk_packet_len; p = fp_buffer = calloc(fp_buffer_len, 1); if (!p) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY); } p[0] = 0x99; /* http://tools.ietf.org/html/rfc4880 page 71 */ ushort2bebytes(++p, pk_packet_len); /* Start pk_packet */ p += 2; *p = 4; /* Version 4 key */ ulong2bebytes(++p, ctime); /* Creation time */ p += 4; *p = 1; /* RSA */ /* Algorithm-specific fields */ ushort2bebytes(++p, key_info->modulus_len); p += 2; memcpy(p, modulus, mlen); p += mlen; ushort2bebytes(++p, key_info->exponent_len); p += 2; memcpy(p, exponent, elen); p = NULL; /* Hash with SHA-1 */ SHA1(fp_buffer, fp_buffer_len, fingerprint); free(fp_buffer); /* Store to DO */ tag = 0x00C6 + key_info->keytype; sc_log(card->ctx, "Write to DO %04X.", tag); r = pgp_put_data(card, 0x00C6 + key_info->keytype, fingerprint, SHA_DIGEST_LENGTH); LOG_TEST_RET(card->ctx, r, "Cannot write to DO."); /* Update the blob containing fingerprints (00C5) */ sc_log(card->ctx, "Update the blob containing fingerprints (00C5)"); fpseq_blob = pgp_find_blob(card, 0x00C5); if (!fpseq_blob) { sc_log(card->ctx, "Not found 00C5"); goto exit; } /* Save the fingerprints sequence */ newdata = malloc(fpseq_blob->len); if (!newdata) { sc_log(card->ctx, "Not enough memory to update fingerprints blob."); goto exit; } memcpy(newdata, fpseq_blob->data, fpseq_blob->len); /* Move p to the portion holding the fingerprint of the current key */ p = newdata + 20*(key_info->keytype - 1); /* Copy new fingerprint value */ memcpy(p, fingerprint, 20); /* Set blob's data */ pgp_set_blob(fpseq_blob, newdata, fpseq_blob->len); free(newdata); exit: 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, u8* modulus, size_t modulus_len, u8* exponent, size_t exponent_len, u8 key_id) { struct pgp_priv_data *priv = DRVDATA(card); struct blob *pk_blob; unsigned int blob_id; sc_pkcs15_pubkey_t pubkey; u8 *data = NULL; size_t len; int r; LOG_FUNC_CALLED(card->ctx); if (key_id == SC_OPENPGP_KEY_SIGN) blob_id = 0xB601; else if (key_id == SC_OPENPGP_KEY_ENCR) blob_id = 0xB801; else if (key_id == SC_OPENPGP_KEY_AUTH) blob_id = 0xA401; else { sc_log(card->ctx, "Unknown key id %X.", key_id); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } sc_log(card->ctx, "Get the blob %X.", 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 */ memset(&pubkey, 0, sizeof(pubkey)); pubkey.algorithm = SC_ALGORITHM_RSA; pubkey.u.rsa.modulus.data = modulus; pubkey.u.rsa.modulus.len = modulus_len >> 3; /* 1/8 */ pubkey.u.rsa.exponent.data = exponent; pubkey.u.rsa.exponent.len = exponent_len >> 3; r = sc_pkcs15_encode_pubkey(card->ctx, &pubkey, &data, &len); sc_log(card->ctx, "Update blob content."); r = pgp_set_blob(pk_blob, data, len); 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) { unsigned int blob_id; time_t ctime = 0; u8 *in = data; u8 *modulus; u8 *exponent; int r; LOG_FUNC_CALLED(card->ctx); /* Store creation time */ r = pgp_store_creationtime(card, key_info->keytype, &ctime); LOG_TEST_RET(card->ctx, r, "Cannot store creation time"); /* Parse response. Ref: pgp_enumerate_blob() */ while (data_len > (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); 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; if (tag == 0x0081) { /* Set the output data */ if (key_info->modulus) { memcpy(key_info->modulus, part, len); } /* Always set output for modulus_len */ key_info->modulus_len = len*8; /* Remember the modulus to calculate fingerprint later */ modulus = part; } else if (tag == 0x0082) { /* Set the output data */ if (key_info->exponent) { memcpy(key_info->exponent, part, len); } /* Always set output for exponent_len */ key_info->exponent_len = len*8; /* Remember the exponent to calculate fingerprint later */ exponent = part; } /* Go to next part to parse */ /* 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, modulus, exponent, 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, modulus, key_info->modulus_len, exponent, key_info->exponent_len, key_info->keytype); 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->keytype; LOG_FUNC_CALLED(card->ctx); if (id > card->algorithm_count) { sc_log(card->ctx, "This key ID %d is out of the card's algorithm list."); 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 length attribute */ algo->key_length = key_info->modulus_len; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /** * Generate key. * Set key_info->modulus_len to zero if want to use old key size. * Similarly for exponent length. * key_info->modulus_len and key_info->exponent_len will be returned with new values. **/ static int pgp_gen_key(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info) { struct pgp_priv_data *priv = DRVDATA(card); struct blob *algo_blob; sc_apdu_t apdu; unsigned int modulus_bitlen; /* Temporary variables to hold APDU params */ u8 apdu_case; u8 *apdu_data; size_t apdu_le; int r = SC_SUCCESS; LOG_FUNC_CALLED(card->ctx); /* Set Control Reference Template for key */ if (key_info->keytype == SC_OPENPGP_KEY_SIGN) apdu_data = "\xb6"; /* As a string, apdu_data will end with '\0' (B6 00) */ else if (key_info->keytype == SC_OPENPGP_KEY_ENCR) apdu_data = "\xb8"; else if (key_info->keytype == SC_OPENPGP_KEY_AUTH) apdu_data = "\xa4"; else { sc_log(card->ctx, "Unknown key type %X.", key_info->keytype); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } /* 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->modulus_len > 1900) { /* 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_le = 256; apdu_case = SC_APDU_CASE_4_SHORT; } /* Prepare APDU */ sc_format_apdu(card, &apdu, apdu_case, 0x47, 0x80, 0); apdu.data = apdu_data; apdu.datalen = 2; /* Data = B600 */ apdu.lc = 2; apdu.le = apdu_le; /* Buffer to receive response */ apdu.resp = calloc(apdu.le, 1); if (apdu.resp == NULL) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY); } apdu.resplen = apdu.le; /* 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."); if (r < 0) { sc_log(card->ctx, "APDU transmit failed. Error %s.", sc_strerror(r)); goto finish; } /* Check response */ r = sc_check_sw(card, apdu.sw1, apdu.sw2); /* Instruct more in case of error */ if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Please verify PIN first."); goto finish; } /* 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); finish: free(apdu.resp); LOG_FUNC_RETURN(card->ctx, r); } /** * Internal: Build TLV. * @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; u8 cla; 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 left most byte of tag, which contains class bits */ while (tag >> 8*highest_order) { highest_order++; } highest_order--; cla = tag >> 8*highest_order; /* Restore class bits */ *out[0] |= cla; return SC_SUCCESS; } /** * Internal: Set Tag & Length components for TLV, store them in buffer. * 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++ = length; else if (length < 256) { *p++ = 0x81; *p++ = 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.7 - OpenPGP card spec v.2) **/ static int pgp_build_extended_header_list(sc_card_t *card, sc_cardctl_openpgp_keystore_info_t *key_info, u8 **result, size_t *resultlen) { struct pgp_priv_data *priv = DRVDATA(card); sc_context_t *ctx = card->ctx; /* The 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[] = {key_info->e, key_info->p, key_info->q, key_info->n}; size_t componentlens[] = {key_info->e_len, key_info->p_len, key_info->q_len, key_info->n_len}; unsigned int componenttags[] = {0x91, 0x92, 0x93, 0x95}; char *componentnames[] = { "public exponent", "prime p", "prime q", "modulus" }; size_t comp_to_add = 3; size_t req_e_len = 0; /* The exponent length specified in Algorithm Attributes */ struct blob *alat_blob; u8 i; int r; LOG_FUNC_CALLED(ctx); if (key_info->keyformat == SC_OPENPGP_KEYFORMAT_STDN || key_info->keyformat == SC_OPENPGP_KEYFORMAT_CRTN) comp_to_add = 4; /* Validate */ if (comp_to_add == 4 && (key_info->n == NULL || key_info->n_len == 0)){ sc_log(ctx, "Error: Modulus required!"); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); } /* Cardholder private key template's data part */ memset(pritemplate, 0, max_prtem_len); /* Get required exponent length */ alat_blob = pgp_find_blob(card, 0x00C0 | key_info->keytype); if (!alat_blob) { sc_log(ctx, "Cannot read Algorithm Attributes."); LOG_FUNC_RETURN(ctx, SC_ERROR_OBJECT_NOT_FOUND); } req_e_len = bebytes2ushort(alat_blob->data + 3) >> 3; /* 1/8 */ assert(key_info->e_len <= req_e_len); /* We need to right justify the exponent with required length, for example, * from 01 00 01 to 00 01 00 01 */ if (key_info->e_len < req_e_len) { /* Create new buffer */ p = calloc(req_e_len, 1); memcpy(p + req_e_len - key_info->e_len, key_info->e, key_info->e_len); key_info->e_len = req_e_len; /* Set key_info->e to new buffer */ free(key_info->e); key_info->e = p; components[0] = p; componentlens[0] = req_e_len; } /* 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_RET(ctx, r, "Failed to build TLV for 5F48."); /* Data part's length for Extended Header list */ len = 2 + tlvlen_7f48 + tlvlen_5f48; /* Set data part content */ data = calloc(len, 1); if (data == NULL) { sc_log(ctx, "Not enough memory."); r = SC_ERROR_NOT_ENOUGH_MEMORY; goto out2; } switch (key_info->keytype) { case SC_OPENPGP_KEY_SIGN: data[0] = 0xB6; break; case SC_OPENPGP_KEY_ENCR: data[0] = 0xB8; break; case SC_OPENPGP_KEY_AUTH: data[0] = 0xA4; break; default: sc_log(ctx, "Unknown key type %d.", key_info->keytype); r = SC_ERROR_INVALID_ARGUMENTS; goto out1; } memcpy(data + 2, tlv_7f48, tlvlen_7f48); memcpy(data + 2 + tlvlen_7f48, tlv_5f48, tlvlen_5f48); r = pgp_build_tlv(ctx, 0x4D, data, len, &tlvblock, &tlvlen); if (r < 0) { sc_log(ctx, "Cannot build TLV for Extended Header list."); goto out1; } /* Set output */ if (result != NULL) { *result = tlvblock; *resultlen = tlvlen; } else { free(tlvblock); } out1: free(data); out2: free(tlv_7f48); free(tlv_5f48); LOG_FUNC_RETURN(ctx, r); } /** * Store key. **/ static int pgp_store_key(sc_card_t *card, sc_cardctl_openpgp_keystore_info_t *key_info) { struct pgp_priv_data *priv = DRVDATA(card); sc_context_t *ctx = card->ctx; sc_cardctl_openpgp_keygen_info_t pubkey; u8 *data; size_t len; int r; LOG_FUNC_CALLED(ctx); /* Validate */ if (key_info->keytype < 1 || key_info->keytype > 3) { sc_log(ctx, "Unknown key type %d.", key_info->keytype); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); } /* We just support standard key format */ switch (key_info->keyformat) { case SC_OPENPGP_KEYFORMAT_STD: case SC_OPENPGP_KEYFORMAT_STDN: break; case SC_OPENPGP_KEYFORMAT_CRT: case SC_OPENPGP_KEYFORMAT_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->e_len > 4) { sc_log(card->ctx, "Exponent %bit (>32) is not supported.", key_info->e_len*8); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } /* Set algorithm attributes */ memset(&pubkey, 0, sizeof(pubkey)); pubkey.keytype = key_info->keytype; if (key_info->n && key_info->n_len) { pubkey.modulus = key_info->n; pubkey.modulus_len = 8*key_info->n_len; /* We won't update exponent length, because smaller exponent length * will be padded later */ } r = pgp_update_new_algo_attr(card, &pubkey); LOG_TEST_RET(card->ctx, r, "Failed to update new algorithm attributes"); /* Build Extended Header list */ r = pgp_build_extended_header_list(card, key_info, &data, &len); if (r < 0) { sc_log(ctx, "Failed to build Extended Header list."); goto out; } /* Write to DO */ r = pgp_put_data(card, 0x4D, data, len); if (r < 0) { sc_log(ctx, "Failed to write to DO."); goto out; } free(data); data = NULL; /* Store creation time */ r = pgp_store_creationtime(card, key_info->keytype, &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, key_info->n, key_info->e, &pubkey); LOG_TEST_RET(card->ctx, r, "Cannot store fingerprint."); /* Update pubkey blobs (B601,B801, A401) */ sc_log(card->ctx, "Update blobs holding pubkey info."); r = pgp_update_pubkey_blob(card, key_info->n, 8*key_info->n_len, key_info->e, 8*key_info->e_len, key_info->keytype); sc_log(ctx, "Update card algorithms."); pgp_update_card_algorithms(card, &pubkey); out: if (data) { free(data); data = NULL; } LOG_FUNC_RETURN(ctx, r); } #endif /* ENABLE_OPENSSL */ /* ABI: 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; #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 */ } LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } /* ABI: DELETE FILE */ static int pgp_delete_file(sc_card_t *card, const sc_path_t *path) { struct pgp_priv_data *priv = DRVDATA(card); struct blob *blob; sc_file_t *file; int r; LOG_FUNC_CALLED(card->ctx); /* In sc_pkcs15init_delete_by_path(), the path type was set 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; /* do try to delete MF */ if (blob == priv->mf) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); if (file->id == 0xB601 || file->id == 0xB801 || file->id == 0xA401) { /* These tags are just symbolic. We don't really delete it. */ r = SC_SUCCESS; } else { /* call pgp_put_data() with zero-sized NULL-buffer to zap the DO contents */ r = pgp_put_data(card, file->id, NULL, 0); } /* set "current" blob to parent */ priv->current = blob->parent; LOG_FUNC_RETURN(card->ctx, r); } /* ABI: UPDATE BINARY */ 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); struct blob *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: driver binding stuff */ static struct sc_card_driver * sc_get_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.read_binary = pgp_read_binary; pgp_ops.write_binary = pgp_write_binary; pgp_ops.pin_cmd = pgp_pin_cmd; 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; return &pgp_drv; } struct sc_card_driver * sc_get_openpgp_driver(void) { return sc_get_driver(); } opensc-0.13.0/src/libopensc/card-starcos.c0000644000015201777760000012271312057406034015331 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" static 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 }, { NULL, NULL, NULL, 0, 0, NULL } }; static struct sc_card_operations starcos_ops; static struct sc_card_operations *iso_ops = NULL; static struct sc_card_driver starcos_drv = { "STARCOS SPK 2.3/2.4", "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 int fix_digestInfo; } starcos_ex_data; /* 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; } 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 SPK 2.3"; card->cla = 0x00; card->drv_data = (void *)ex_data; 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; _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); card->caps = SC_CARD_CAP_RNG; /* 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 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, " bytes in file: %d\n", bytes); file->size = bytes; } tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen); if (tag != NULL) { 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_debug(ctx, SC_LOG_DEBUG_NORMAL, " type: %s\n", type); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " EF structure: %s\n", structure); } file->magic = SC_FILE_MAGIC; return SC_SUCCESS; } static int starcos_select_aid(sc_card_t *card, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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) { 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.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; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); file->id = (id_hi << 8) + id_lo; file->path = card->cache.current_path; 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 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; size_t i, pathlen; char pbuf[SC_MAX_PATH_STRING_SIZE]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); r = sc_path_print(pbuf, sizeof(pbuf), &card->cache.current_path); if (r != SC_SUCCESS) pbuf[0] = '\0'; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "current path (%s, %s): %s (len: %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 starcos_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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "cache hit\n"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } else return starcos_select_aid(card, pathbuf, pathlen, file_out); } else if (in_path->type == SC_PATH_TYPE_PATH) { u8 n_pathbuf[SC_MAX_PATH_SIZE]; int bMatch = -1; /* Select with path (sequence of File-IDs) */ /* 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); /* unify path (the first FID should be MF) */ if (path[0] != 0x3f || path[1] != 0x00) { n_pathbuf[0] = 0x3f; n_pathbuf[1] = 0x00; for (i=0; i< pathlen; i++) n_pathbuf[i+2] = pathbuf[i]; path = n_pathbuf; pathlen += 2; } /* check current working directory */ if (card->cache.valid && card->cache.current_path.type == SC_PATH_TYPE_PATH && card->cache.current_path.len >= 2 && card->cache.current_path.len <= pathlen ) { bMatch = 0; for (i=0; i < card->cache.current_path.len; i+=2) if (card->cache.current_path.value[i] == path[i] && card->cache.current_path.value[i+1] == path[i+1] ) bMatch += 2; } if ( card->cache.valid && bMatch >= 0 ) { if ( pathlen - bMatch == 2 ) /* we are in the rigth directory */ return starcos_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 = starcos_select_fid(card, path[bMatch], path[bMatch+1], NULL); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "SELECT FILE (DF-ID) failed"); memset(&new_path, 0, sizeof(sc_path_t)); new_path.type = SC_PATH_TYPE_PATH; new_path.len = pathlen - bMatch-2; memcpy(new_path.value, &(path[bMatch+2]), new_path.len); /* final step: select file */ return starcos_select_file(card, &new_path, file_out); } else /* if (bMatch - pathlen == 0) */ { /* done: we are already in the * requested directory */ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "cache hit\n"); /* copy file info (if necessary) */ if (file_out) { sc_file_t *file = sc_file_new(); if (!file) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); file->id = (path[pathlen-2] << 8) + path[pathlen-1]; file->path = card->cache.current_path; file->type = SC_FILE_TYPE_DF; file->ef_structure = SC_FILE_EF_UNKNOWN; file->size = 0; file->namelen = 0; file->magic = SC_FILE_MAGIC; *file_out = file; } /* nothing left to do */ return SC_SUCCESS; } } else { /* no usable cache */ for ( i=0; ictx, SC_LOG_DEBUG_NORMAL, r, "SELECT FILE (DF-ID) failed"); } return starcos_select_fid(card, path[pathlen-2], path[pathlen-1], file_out); } } else SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); } #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 occured otherwise error code * * This function tries to create a somewhat useable 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; /* combinde 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 { /* (mis)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; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "creating DF\n"); /* first step: REGISTER DF */ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); /* second step: CREATE DF */ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, 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; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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 finishs 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; 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; 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; 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); /* invalidate cache */ card->cache.valid = 0; 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; 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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 clen = tlen < STARCOS_WKEY_CSIZE ? tlen : STARCOS_WKEY_CSIZE; sbuf[0] = 0xc2; sbuf[1] = 3 + clen; sbuf[2] = data->kid; sbuf[3] = (offset >> 8) & 0xff; sbuf[4] = offset & 0xff; memcpy(sbuf+5, p, clen); len = 5 + clen; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xf4, data->mode, 0x00); apdu.cla |= 0x80; apdu.lc = len; apdu.datalen = len; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return sc_check_sw(card, apdu.sw1, apdu.sw2); offset += clen; p += clen; tlen -= clen; } 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; /* 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; /* copy key reference, if present */ if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) *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) { *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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); return SC_SUCCESS; } /* try COMPUTE SIGNATURE */ if (operation == SC_SEC_OPERATION_SIGN && ( env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1 || env->algorithm_flags & SC_ALGORITHM_RSA_PAD_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) { 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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 */ /* 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 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)); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 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_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) { const int err_count = sizeof(starcos_errors)/sizeof(starcos_errors[0]); int i; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "sw1 = 0x%02x, sw2 = 0x%02x\n", sw1, sw2); if (sw1 == 0x90) return SC_SUCCESS; if (sw1 == 0x63 && (sw2 & ~0x0fU) == 0xc0 ) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Verification failed (remaining tries: %d)\n", (sw2 & 0x0f)); return SC_ERROR_PIN_CODE_INCORRECT; } /* check starcos error messages */ for (i = 0; i < err_count; i++) if (starcos_errors[i].SWs == ((sw1 << 8) | sw2)) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s\n", starcos_errors[i].errorstr); 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; } /* get serial number via GET CARD DATA */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xf6, 0x00, 0x00); apdu.cla |= 0x80; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; apdu.lc = 0; apdu.datalen = 0; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return SC_ERROR_INTERNAL; /* cache serial number */ memcpy(card->serialnr.value, apdu.resp, MIN(apdu.resplen, SC_MAX_SERIALNR)); card->serialnr.len = MIN(apdu.resplen, SC_MAX_SERIALNR); /* 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(sc_card_t *card) { int r; 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; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; 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.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.card_ctl = starcos_card_ctl; starcos_ops.logout = starcos_logout; return &starcos_drv; } struct sc_card_driver * sc_get_starcos_driver(void) { return sc_get_driver(); } opensc-0.13.0/src/libopensc/pkcs15-cert.c0000644000015201777760000003000212057406034014772 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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, const u8 *buf, size_t buflen, struct sc_pkcs15_cert *cert) { int r; struct sc_algorithm_id sig_alg; struct sc_pkcs15_pubkey * pubkey = NULL; u8 *serial = NULL; size_t serial_len = 0; 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_x509v3[] = { { "certificatePolicies", SC_ASN1_OCTET_STRING, SC_ASN1_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "subjectKeyIdentifier", SC_ASN1_OCTET_STRING, SC_ASN1_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "crlDistributionPoints", SC_ASN1_OCTET_STRING, SC_ASN1_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, &cert->crl, &cert->crl_len }, { "authorityKeyIdentifier", SC_ASN1_OCTET_STRING, SC_ASN1_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "keyUsage", SC_ASN1_BOOLEAN, SC_ASN1_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_extensions[] = { { "x509v3", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, asn1_x509v3, NULL }, { 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, &cert->issuer, &cert->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, &cert->subject, &cert->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, &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 } }; const u8 *obj; size_t objlen; 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"); cert->data.len = objlen + (obj - buf); r = sc_asn1_decode(ctx, asn1_cert, obj, objlen, NULL, NULL); LOG_TEST_RET(ctx, r, "ASN.1 parsing of certificate failed"); cert->version++; if (pubkey) { cert->key = pubkey; pubkey = NULL; } else { LOG_TEST_RET(ctx, SC_ERROR_INVALID_ASN1_OBJECT, "Unable to decode subjectPublicKeyInfo from cert"); } sc_asn1_clear_algorithm_id(&sig_alg); if (r < 0) return r; if (serial && serial_len) { sc_format_asn1_entry(asn1_serial_number + 0, serial, &serial_len, 1); r = sc_asn1_encode(ctx, asn1_serial_number, &cert->serial, &cert->serial_len); free(serial); } return r; } 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->value, cert_blob->len, 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, struct sc_pkcs15_cert **cert_out) { struct sc_pkcs15_cert *cert; struct sc_pkcs15_der der; int r; assert(p15card != NULL && info != NULL && cert_out != NULL); LOG_FUNC_CALLED(p15card->card->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); if (r) return r; } else { return SC_ERROR_OBJECT_NOT_FOUND; } cert = malloc(sizeof(struct sc_pkcs15_cert)); if (cert == NULL) { free(der.value); return SC_ERROR_OUT_OF_MEMORY; } memset(cert, 0, sizeof(struct sc_pkcs15_cert)); if (parse_x509_cert(p15card->card->ctx, der.value, der.len, cert)) { free(der.value); sc_pkcs15_free_certificate(cert); return SC_ERROR_INVALID_ASN1_OBJECT; } cert->data = der; *cert_out = cert; return 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) { 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)); 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; } void sc_pkcs15_free_certificate(struct sc_pkcs15_cert *cert) { assert(cert != NULL); if (cert->key) sc_pkcs15_free_pubkey(cert->key); free(cert->subject); free(cert->issuer); free(cert->serial); free(cert->data.value); free(cert->crl); free(cert); } void sc_pkcs15_free_cert_info(sc_pkcs15_cert_info_t *cert) { if (!cert) return; if (cert->value.value) free(cert->value.value); free(cert); } opensc-0.13.0/src/libopensc/pkcs15-westcos.c0000644000015201777760000001653112057406034015537 00000000000000/* * pkcs15-westcos.c: pkcs15 emulation for westcos card * * Copyright (C) 2009 francois.leblanc@cev-sa.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include "internal.h" #include "pkcs15.h" #include "cardctl.h" #include "common/compat_strlcpy.h" int sc_pkcs15emu_westcos_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); static int sc_pkcs15emu_westcos_init(sc_pkcs15_card_t * p15card) { int i, r; int modulus_length = 0; char buf[256]; sc_card_t *card = p15card->card; sc_serial_number_t serial; sc_path_t path; sc_file_t *file = NULL; sc_format_path("3F00", &path); r = sc_select_file(card, &path, &file); if (r) goto out; if (file) sc_file_free(file); file = NULL; if (p15card->tokeninfo->label != NULL) free(p15card->tokeninfo->label); p15card->tokeninfo->label = strdup("westcos"); if (p15card->tokeninfo->manufacturer_id != NULL) free(p15card->tokeninfo->manufacturer_id); p15card->tokeninfo->manufacturer_id = strdup("CEV"); /* get serial number */ r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); r = sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0); if (r) goto out; if (p15card->tokeninfo->serial_number != NULL) free(p15card->tokeninfo->serial_number); p15card->tokeninfo->serial_number = strdup(buf); sc_format_path("AAAA", &path); r = sc_select_file(card, &path, &file); if (r) { goto out; } else { for (i = 0; i < 1; i++) { unsigned int flags; struct sc_pkcs15_auth_info pin_info; struct sc_pkcs15_object pin_obj; memset(&pin_info, 0, sizeof(pin_info)); memset(&pin_obj, 0, sizeof(pin_obj)); flags = SC_PKCS15_PIN_FLAG_INITIALIZED; if (i == 1) { flags |= SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED | SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN; } pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; pin_info.auth_id.len = 1; pin_info.auth_id.value[0] = i + 1; pin_info.attrs.pin.reference = i; pin_info.attrs.pin.flags = flags; pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_BCD; pin_info.attrs.pin.min_length = 4; pin_info.attrs.pin.stored_length = 8; pin_info.attrs.pin.max_length = 8; pin_info.attrs.pin.pad_char = 0xff; pin_info.path = path; pin_info.tries_left = -1; if (i == 1) strlcpy(pin_obj.label, "Unblock", sizeof(pin_obj.label)); else strlcpy(pin_obj.label, "User", sizeof(pin_obj.label)); pin_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE; r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); if (r) goto out; } } if (file) sc_file_free(file); file = NULL; sc_format_path("0002", &path); r = sc_select_file(card, &path, &file); if (r) { goto out; } else { /* certificate file */ struct sc_pkcs15_cert_info cert_info; struct sc_pkcs15_object cert_obj; struct sc_pkcs15_pubkey_info pubkey_info; struct sc_pkcs15_object pubkey_obj; struct sc_pkcs15_pubkey *pkey = NULL; memset(&cert_info, 0, sizeof(cert_info)); memset(&cert_obj, 0, sizeof(cert_obj)); cert_info.id.len = 1; cert_info.id.value[0] = 0x45; cert_info.authority = 0; cert_info.path = path; r = sc_pkcs15_read_certificate(p15card, &cert_info, (sc_pkcs15_cert_t **) (&cert_obj.data)); if (!r) { sc_pkcs15_cert_t *cert = (sc_pkcs15_cert_t *) (cert_obj.data); strlcpy(cert_obj.label, "User certificat", sizeof(cert_obj.label)); cert_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE; r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); if (r) goto out; pkey = cert->key; if (pkey->algorithm == SC_ALGORITHM_RSA) { modulus_length = (int)(pkey->u.rsa.modulus.len * 8); } } else { /* or public key */ memset(&pubkey_info, 0, sizeof(pubkey_info)); memset(&pubkey_obj, 0, sizeof(pubkey_obj)); pubkey_info.id.len = 1; pubkey_info.id.value[0] = 0x45; pubkey_info.modulus_length = modulus_length; pubkey_info.key_reference = 1; pubkey_info.native = 1; pubkey_info.usage = SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER | SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP; pubkey_info.path = path; strlcpy(pubkey_obj.label, "Public Key", sizeof(pubkey_obj.label)); pubkey_obj.auth_id.len = 1; pubkey_obj.auth_id.value[0] = 1; pubkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; pubkey_obj.type = SC_PKCS15_TYPE_PUBKEY_RSA; if (pkey == NULL) { pubkey_obj.data = &pubkey_info; r = sc_pkcs15_read_pubkey(p15card, &pubkey_obj, &pkey); if (r) goto out; /* not sure if necessary */ pubkey_obj.flags = 0; } if (pkey->algorithm == SC_ALGORITHM_RSA) { modulus_length = (int)(pkey->u.rsa.modulus.len * 8); } pubkey_info.modulus_length = modulus_length; pubkey_obj.data = pkey; r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info); if (r < 0) goto out; } } if (file) sc_file_free(file); file = NULL; sc_format_path("0001", &path); r = sc_select_file(card, &path, &file); if (r) { goto out; } else { struct sc_pkcs15_prkey_info prkey_info; struct sc_pkcs15_object prkey_obj; memset(&prkey_info, 0, sizeof(prkey_info)); memset(&prkey_obj, 0, sizeof(prkey_obj)); prkey_info.id.len = 1; prkey_info.id.value[0] = 0x45; prkey_info.usage = SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; prkey_info.native = 1; prkey_info.key_reference = 1; prkey_info.modulus_length = modulus_length; prkey_info.path = path; strlcpy(prkey_obj.label, "Private Key", sizeof(prkey_obj.label)); prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; prkey_obj.auth_id.len = 1; prkey_obj.auth_id.value[0] = 1; r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); if (r < 0) goto out; } r = 0; out: if (file) sc_file_free(file); return r; } static int westcos_detect_card(sc_pkcs15_card_t * p15card) { sc_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; const char *name = "WESTCOS"; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "westcos_detect_card (%s)", card->name); if (strncmp(card->name, name, strlen(name))) return SC_ERROR_WRONG_CARD; return SC_SUCCESS; } int sc_pkcs15emu_westcos_init_ex(sc_pkcs15_card_t * p15card, sc_pkcs15emu_opt_t * opts) { int r; sc_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "sc_pkcs15_init_func_ex westcos\n"); if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) return sc_pkcs15emu_westcos_init(p15card); r = westcos_detect_card(p15card); if (r) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_westcos_init(p15card); } opensc-0.13.0/src/libopensc/card-myeid.c0000644000015201777760000007577412057406034015000 00000000000000/* * card-myeid.c * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" #include "types.h" #define LOAD_KEY_MODULUS 0x80 #define LOAD_KEY_PUBLIC_EXPONENT 0x81 #define LOAD_KEY_PRIME_P 0x83 #define LOAD_KEY_PRIME_Q 0x84 #define LOAD_KEY_DP1 0x85 #define LOAD_KEY_DQ1 0x86 #define LOAD_KEY_INVQ 0x87 #define LOAD_KEY_MODE_EC_PRIV 0x87 #define LOAD_KEY_MODE_EC_PUB 0x86 #define LOAD_KEY_EC_PRIVATE 0x97 #define LOAD_KEY_EC_PUBLIC 0x96 #define MYEID_STATE_CREATION 0x01 #define MYEID_STATE_ACTIVATED 0x07 #define MYEID_ECC_SUPPORT 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 }; static const char *myeid_atrs[] = { "3B:F5:18:00:FF:81:31:FE:45:4D:79:45:49:44:65", "3B:F5:18:00:00:81:31:FE:45:4D:79:45:49:44:9A", "3B:85:80:01:4D:79:45:49:44:78", "3B:89:80:01:09:38:33:B1:4D:79:45:49:44:4C", NULL }; typedef struct myeid_private_data { int card_state; } myeid_private_data_t; static int myeid_match_card(struct sc_card *card) { int i, match = -1; for (i = 0; myeid_atrs[i] != NULL; i++) { u8 defatr[SC_MAX_ATR_SIZE]; size_t len = sizeof(defatr); const char *atrp = myeid_atrs[i]; if (sc_hex_to_bin(atrp, defatr, &len)) continue; if (len != card->atr.len) continue; if (memcmp(card->atr.value, defatr, len) != 0) continue; match = i; break; } if (match == -1) return 0; return 1; } static int myeid_init(struct sc_card *card) { unsigned long flags = 0, ext_flags = 0; myeid_private_data_t *priv; LOG_FUNC_CALLED(card->ctx); priv = calloc(1, sizeof(myeid_private_data_t)); if (!priv) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); priv->card_state = SC_FILE_STATUS_CREATION; card->drv_data = priv; flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_ONBOARD_KEY_GEN; flags |= SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_RSA_HASH_SHA1; _sc_card_add_rsa_alg(card, 512, flags, 0); _sc_card_add_rsa_alg(card, 768, flags, 0); _sc_card_add_rsa_alg(card, 1024, flags, 0); _sc_card_add_rsa_alg(card, 1536, flags, 0); _sc_card_add_rsa_alg(card, 2048, flags, 0); #ifdef MYEID_ECC_SUPPORT flags |= SC_ALGORITHM_ECDSA_RAW; ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE | SC_ALGORITHM_EXT_EC_UNCOMPRESES; _sc_card_add_ec_alg(card, 192, flags, ext_flags); _sc_card_add_ec_alg(card, 224, flags, ext_flags); _sc_card_add_ec_alg(card, 256, flags, ext_flags); #endif /* State that we have an RNG */ card->caps |= SC_CARD_CAP_RNG; card->max_recv_size = 255; card->max_send_size = 255; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static const struct sc_card_operations *iso_ops = NULL; static int acl_to_byte(const struct sc_acl_entry *e) { switch (e->method) { case SC_AC_NONE: return 0x00; case SC_AC_CHV: case SC_AC_TERM: case SC_AC_AUT: if (e->key_ref == SC_AC_KEY_REF_NONE) return 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) { struct sc_file *dummy_file = NULL; int r; LOG_FUNC_CALLED(card->ctx); r = iso_ops->select_file(card, in_path, &dummy_file); if (file) *file = dummy_file; else if (dummy_file) sc_file_free(dummy_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_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 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]); } tag = sc_asn1_find_tag(NULL, buf, buflen, 0x8A, &taglen); if (tag != NULL && taglen > 0) { if(tag[0] == MYEID_STATE_CREATION) { file->status = SC_FILE_STATUS_CREATION; sc_log(card->ctx, "File id (%X) status SC_FILE_STATUS_CREATION (0x%X)", file->id, tag[0]); } else if(tag[0] == MYEID_STATE_ACTIVATED) { file->status = SC_FILE_STATUS_ACTIVATED; sc_log(card->ctx, "File id (%X) status SC_FILE_STATUS_ACTIVATED (0x%X)", file->id, tag[0]); } priv->card_state = file->status; } LOG_FUNC_RETURN(card->ctx, 0); } static int encode_file_structure(sc_card_t *card, const sc_file_t *file, u8 *out, size_t *outlen) { const sc_acl_entry_t *read, *update, *delete, *generate; u8 buf[40]; int i; LOG_FUNC_CALLED(card->ctx); /* PrivateKey * 0E0000019 6217 81020400 820111 83024B01 8603000000 85028000 8A0100 RESULT 6984 * 6217 81020400 820111 83024B01 8603000000 85021000 8A0100 */ memset(buf, 0x0, sizeof(buf)); 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); 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; /* AC right to clear default 0 */ /* TODO: Implement this */ buf[20] = 0x00; /*(SC_FILE_TYPE_INTERNAL_EF == file->type ? 0x00 : 0x80);*/ 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 < (int)file->namelen;i++) buf[i + 26] = file->name[i]; buf[1] = 0x19 + file->namelen + 2; } break; default: sc_log(card->ctx, "Unknown file type\n"); return SC_ERROR_INVALID_ARGUMENTS; } *outlen = buf[1]+2; memcpy(out, buf, *outlen); LOG_FUNC_RETURN(card->ctx, 0); } static int myeid_create_file(struct sc_card *card, struct sc_file *file) { sc_apdu_t apdu; u8 sbuf[32]; size_t buflen; 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(%d), pin2 len (%d)\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)); } 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, locked = 0; assert(card != NULL && env != NULL); LOG_FUNC_CALLED(card->ctx); if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) { sc_log(card->ctx, "asymmetric 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; 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; } if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { *p++ = 0x84; *p++ = 1; *p++ = 0; } r = 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); 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; int r, locked = 0; assert(card != NULL && env != NULL); LOG_FUNC_CALLED(card->ctx); if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) { sc_log(card->ctx, "asymmetric 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: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Decipher operation is not supported with EC keys.\n"); return SC_ERROR_NOT_SUPPORTED; break; case SC_SEC_OPERATION_SIGN: apdu.p1 = 0x41; 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++ = 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; } r = 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); 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) { LOG_FUNC_CALLED(card->ctx); 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) { tmp.algorithm_ref = 0x00; /* potential FIXME: return an error, if an unsupported * pad or hash was requested, although this shouldn't happen */ if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) tmp.algorithm_ref = 0x02; if (tmp.algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) tmp.algorithm_ref |= 0x10; return myeid_set_security_env_rsa(card, &tmp, se_num); } else if (tmp.algorithm == SC_ALGORITHM_EC) { #ifdef MYEID_ECC_SUPPORT /* TODO: Update the algorithm_ref */ tmp.algorithm_ref = 0xAA; tmp.algorithm_flags = 0; return myeid_set_security_env_ec(card, &tmp, se_num); #else sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Elliptic curves are not supported in this version.\n"); return SC_ERROR_NOT_SUPPORTED; #endif } else { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported algorithm.\n"); return SC_ERROR_NOT_SUPPORTED; } } return myeid_set_security_env_rsa(card, env, se_num); } static int myeid_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]; LOG_FUNC_CALLED(card->ctx); assert(card != NULL && data != NULL && out != NULL); if (datalen > 256) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); /* INS: 0x2A PERFORM SECURITY OPERATION * P1: 0x9E Resp: Digital Signature * P2: 0x9A Cmd: Input for Digital Signature */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; if (datalen == 256) { apdu.p2 = data[0]; memcpy(sbuf, data+1, datalen-1); apdu.lc = datalen - 1; apdu.datalen = datalen - 1; } else { memcpy(sbuf, data, datalen); apdu.lc = datalen; apdu.datalen = datalen; } apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { int len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); LOG_FUNC_RETURN(card->ctx, len); } LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); } static int myeid_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]; LOG_FUNC_CALLED(card->ctx); assert(card != NULL && crgram != NULL && out != NULL); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); if (crgram_len > 256) LOG_FUNC_RETURN(card->ctx, 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, (crgram_len < 256) ? SC_APDU_CASE_4_SHORT : SC_APDU_CASE_3_SHORT, 0x2A, 0x80, 0x86); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = crgram_len; if (crgram_len == 256) { apdu.le = 0; /* padding indicator byte, 0x81 = first half of 2048 bit cryptogram */ sbuf[0] = 0x81; memcpy(sbuf + 1, crgram, crgram_len / 2); apdu.lc = crgram_len / 2 + 1; } else { sbuf[0] = 0; /* padding indicator byte, 0x00 = No further indication */ memcpy(sbuf + 1, crgram, crgram_len); apdu.lc = crgram_len + 1; } 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) { if (crgram_len == 256) { sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = crgram_len; /* padding indicator byte, * 0x82 = Second half of 2048 bit cryptogram */ sbuf[0] = 0x82; memcpy(sbuf + 1, crgram + crgram_len / 2, crgram_len / 2); apdu.lc = crgram_len / 2 + 1; apdu.datalen = apdu.lc; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { int len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); LOG_FUNC_RETURN(card->ctx, len); } } else { int len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); LOG_FUNC_RETURN(card->ctx, len); } } LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); } /* 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, int mode, u8* value, int value_len) { sc_apdu_t apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; int r, len; LOG_FUNC_CALLED(card->ctx); len = 0; if(value_len == 0 || value == NULL) return 0; if(value != NULL && value[0] != 0x0 && mode != LOAD_KEY_PUBLIC_EXPONENT) sbuf[len++] = 0x0; if(mode == LOAD_KEY_MODULUS && value_len >= 256) { r=0; if((value_len % 2) > 0 && value[0] == 0x00) { value_len--; memmove(value, value + 1, value_len); } mode = 0x88; len = 128; memcpy(sbuf,value, 128); memset(&apdu, 0, sizeof(apdu)); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDA, 0x01, mode); apdu.cla = 0x00; apdu.data = sbuf; apdu.datalen = len; apdu.lc = len; 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; mode = 0x89; len = value_len - 128; memset(&sbuf, 0, SC_MAX_APDU_BUFFER_SIZE); memcpy(sbuf,value + 128, value_len - 128); } else if(mode == LOAD_KEY_EC_PRIVATE) { memcpy(sbuf, value, value_len); len = value_len; mode = LOAD_KEY_MODE_EC_PRIV; } else if(mode == LOAD_KEY_EC_PUBLIC) { memcpy(sbuf, value, value_len); len = value_len; mode = LOAD_KEY_MODE_EC_PUB; } else { memcpy(sbuf + len, value, value_len); len += value_len; } memset(&apdu, 0, sizeof(apdu)); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDA, 0x01, mode); apdu.cla = 0x00; apdu.data = sbuf; apdu.datalen = len; apdu.lc = len; 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); } } 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; u8 nameBuf[100]; 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)\n"); 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 */ sprintf(nameBuf, "%s %d.%d.%d", card->name, rbuf[5], rbuf[6], rbuf[7]); card->name = nameBuf; //card->driver->name 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_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_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 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.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; return &myeid_drv; } struct sc_card_driver * sc_get_myeid_driver(void) { return sc_get_driver(); } opensc-0.13.0/src/libopensc/internal.h0000644000015201777760000002251612057406034014565 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 #endif #include "common/simclist.h" #include "libopensc/opensc.h" #include "libopensc/log.h" #include "libopensc/cards.h" #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_delete_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); /* 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, 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_rsa_alg(struct sc_card *card, unsigned int key_length, unsigned long flags, unsigned long exponent); int _sc_card_add_ec_alg(struct sc_card *card, unsigned int key_length, unsigned long flags, unsigned long ext_flags); /********************************************************************/ /* pkcs1 padding/encoding functions */ /********************************************************************/ int sc_pkcs1_strip_01_padding(const u8 *in_dat, size_t in_len, u8 *out_dat, size_t *out_len); int sc_pkcs1_strip_02_padding(const u8 *data, size_t len, u8 *out_dat, size_t *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); /** * 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 modlen IN length of the modulus in bytes * @return SC_SUCCESS on success and an error code otherwise */ int sc_pkcs1_encode(sc_context_t *ctx, unsigned long flags, const u8 *in, size_t inlen, u8 *out, size_t *outlen, size_t modlen); /** * 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 level log if ctx->debug >= level * @param buf buffer with the APDU data * @param len length of the APDU * @param is_outgoing != 0 if the data is send to the card */ void sc_apdu_log(sc_context_t *ctx, int level, const u8 *data, size_t len, int is_outgoing); 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_cardmod_driver(void); #ifdef __cplusplus } #endif #endif opensc-0.13.0/src/libopensc/pkcs15-starcert.c0000644000015201777760000002023012057406034015666 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include "common/compat_strlcpy.h" #include "pkcs15.h" #include "cardctl.h" #define MANU_ID "Giesecke & Devrient GmbH" #define STARCERT "StarCertV2201" int sc_pkcs15emu_starcert_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); 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 = ((((size_t) 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 SPK 2.3")) 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[] = { { "1", "DS key", 1024, USAGE_NONREP, "3F00DF01", 0x84, "99", SC_PKCS15_CO_FLAG_PRIVATE}, { "3", "KE key", 1024, USAGE_KE, "3F00DF01", 0x85, NULL, SC_PKCS15_CO_FLAG_PRIVATE}, { "4", "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); r = sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0); if (r != SC_SUCCESS) return SC_ERROR_INTERNAL; if (p15card->tokeninfo->serial_number) free(p15card->tokeninfo->serial_number); p15card->tokeninfo->serial_number = malloc(strlen(buf) + 1); if (!p15card->tokeninfo->serial_number) return SC_ERROR_INTERNAL; strcpy(p15card->tokeninfo->serial_number, buf); /* the manufacturer ID, in this case Giesecke & Devrient GmbH */ if (p15card->tokeninfo->manufacturer_id) free(p15card->tokeninfo->manufacturer_id); p15card->tokeninfo->manufacturer_id = malloc(strlen(MANU_ID) + 1); if (!p15card->tokeninfo->manufacturer_id) return SC_ERROR_INTERNAL; strcpy(p15card->tokeninfo->manufacturer_id, MANU_ID); /* 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) return SC_ERROR_INTERNAL; } /* 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; strlcpy(pin_obj.label, pins[i].label, sizeof(pin_obj.label)); pin_obj.flags = pins[i].obj_flags; r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); if (r < 0) return SC_ERROR_INTERNAL; } /* 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) return SC_ERROR_INTERNAL; } /* select the application DF */ sc_format_path("3F00DF01", &path); r = sc_select_file(card, &path, &file); if (r != SC_SUCCESS || !file) return SC_ERROR_INTERNAL; /* set the application DF */ if (p15card->file_app) free(p15card->file_app); p15card->file_app = file; return SC_SUCCESS; } int sc_pkcs15emu_starcert_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts) { if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) return sc_pkcs15emu_starcert_init(p15card); else { int r = starcert_detect_card(p15card); if (r) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_starcert_init(p15card); } } opensc-0.13.0/src/libopensc/pkcs15-pin.c0000644000015201777760000006142712057406034014642 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include "internal.h" #include "asn1.h" #include "pkcs15.h" 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; r = sc_asn1_decode(ctx, asn1_auth_type, *buf, *buflen, buf, buflen); if (r == SC_ERROR_ASN1_END_OF_CONTENTS) return r; SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "ASN.1 decoding failed"); if (asn1_auth_type_choice[0].flags & SC_ASN1_PRESENT) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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->path.len) info.path = p15card->file_app->path; } } sc_debug(ctx, SC_LOG_DEBUG_ASN1, "decoded PIN(ref:%X,path:%s)", info.attrs.pin.reference, sc_print_path(&info.path)); } else if (asn1_auth_type_choice[1].flags & SC_ASN1_PRESENT) { SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "BIO authentication object not yet supported"); } else if (asn1_auth_type_choice[2].flags & SC_ASN1_PRESENT) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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 { SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "unknown authentication type"); } obj->data = malloc(sizeof(info)); if (obj->data == NULL) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); 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; assert(p15card != NULL); /* 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) 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 = (struct sc_pkcs15_auth_info *)pin_obj->data; int r; sc_card_t *card; struct sc_pin_cmd_data data; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "PIN(%p;len:%i)", pincode, pinlen); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Auth(type:%X;method:%X)", auth_info->auth_type, auth_info->auth_method); r = _validate_pin(p15card, auth_info, pinlen); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "PIN value do not conforms the PIN policy"); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "PIN value validated"); card = p15card->card; /* Initialize arguments */ memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_VERIFY; 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "cannot find secret key with id:%s", sc_pkcs15_print_id(skey_id)); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); } skey_info = (struct sc_pkcs15_skey_info *)skey_obj->data; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "found secret key '%s'", skey_obj->label); data.pin_reference = skey_info->key_reference; } if(p15card->card->reader->capabilities & SC_READER_CAP_PIN_PAD) { 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"; } r = sc_lock(card); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed"); /* the path in the pin object is optional */ if (auth_info->path.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_debug(ctx, SC_LOG_DEBUG_NORMAL, "PIN cmd result %i", r); if (r == SC_SUCCESS) sc_pkcs15_pincache_add(p15card, pin_obj, pincode, pinlen); out: sc_unlock(card); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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) { int r; sc_card_t *card; struct sc_pin_cmd_data data; struct sc_pkcs15_auth_info *auth_info = (struct sc_pkcs15_auth_info *)pin_obj->data; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_NOT_SUPPORTED; /* make sure the pins are in valid range */ if ((r = _validate_pin(p15card, auth_info, oldpinlen)) != SC_SUCCESS) return r; if ((r = _validate_pin(p15card, auth_info, newpinlen)) != SC_SUCCESS) return r; card = p15card->card; r = sc_lock(card); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed"); /* the path in the pin object is optional */ if (auth_info->path.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) { 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) { int r; sc_card_t *card; struct sc_pin_cmd_data data; struct sc_pkcs15_object *puk_obj; struct sc_pkcs15_auth_info *puk_info = NULL; struct sc_pkcs15_auth_info *auth_info = (struct sc_pkcs15_auth_info *)pin_obj->data; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_NOT_SUPPORTED; /* make sure the pins are in valid range */ if ((r = _validate_pin(p15card, auth_info, newpinlen)) != SC_SUCCESS) return r; card = p15card->card; /* 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; } if (!puk_info) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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); if (r != SC_SUCCESS) return r; } else { r = sc_pkcs15_verify_pin(p15card, puk_obj, puk, puklen); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "cannot verify PUK"); } r = sc_lock(card); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed"); /* the path in the pin object is optional */ if (auth_info->path.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.pin1.data = puk; data.pin1.len = puklen; data.pin1.pad_char = auth_info->attrs.pin.pad_char; data.pin1.min_length = auth_info->attrs.pin.min_length; data.pin1.max_length = auth_info->attrs.pin.max_length; data.pin1.pad_length = auth_info->attrs.pin.stored_length; data.pin2.data = newpin; data.pin2.len = newpinlen; data.pin2.pad_char = puk_info->attrs.pin.pad_char; data.pin2.min_length = puk_info->attrs.pin.min_length; data.pin2.max_length = puk_info->attrs.pin.max_length; data.pin2.pad_length = puk_info->attrs.pin.stored_length; 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) { 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); return 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); if (!p15card->opts.use_pin_cache) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "PIN caching not enabled"); return; } else if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "caching refused (user consent)"); return; } } } obj = obj->next; } r = sc_pkcs15_allocate_object_content(ctx, pin_obj, pin, pinlen); if (r != SC_SUCCESS) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Failed to allocate object content"); return; } pin_obj->usage_counter = 0; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); 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) 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, "Verify PIN error %i", r); return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } void sc_pkcs15_pincache_clear(struct sc_pkcs15_card *p15card) { struct sc_pkcs15_object *objs[32]; int i, r; SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_NORMAL); 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.13.0/src/libopensc/dir.c0000644000015201777760000002462112057406034013521 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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" } }; 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]; sc_app_info_t *app = NULL; struct sc_aid aid; u8 label[128], path[128], ddo[128]; size_t label_len = sizeof(label), path_len = sizeof(path), ddo_len = sizeof(ddo); int r; LOG_FUNC_CALLED(ctx); aid.len = sizeof(aid.value); 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"); 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) { /* application path present: ignore AID */ if (path_len > SC_MAX_PATH_SIZE) { 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); 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; LOG_FUNC_CALLED(ctx); if (card->app_count < 0) card->app_count = 0; sc_format_path("3F002F00", &path); if (card->ef_dir != NULL) { sc_file_free(card->ef_dir); card->ef_dir = NULL; } r = sc_select_file(card, &path, &card->ef_dir); LOG_TEST_RET(ctx, r, "Cannot select EF.DIR file"); if (card->ef_dir->type != SC_FILE_TYPE_WORKING_EF) { sc_file_free(card->ef_dir); card->ef_dir = NULL; LOG_TEST_RET(ctx, SC_ERROR_INVALID_CARD, "EF(DIR) is not a working EF."); } ef_structure = card->ef_dir->ef_structure; if (ef_structure == SC_FILE_EF_TRANSPARENT) { u8 *buf = NULL, *p; size_t bufsize; file_size = card->ef_dir->size; if (file_size == 0) LOG_FUNC_RETURN(ctx, 0); 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 uncomplete cards/drivers */ for (rec_nr = 1; rec_nr < 16; rec_nr++) { r = sc_read_record(card, rec_nr, buf, sizeof(buf), SC_RECORD_BY_REC_NR); 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++) { if (card->app[i]->label) free(card->app[i]->label); if (card->app[i]->ddo.value) free(card->app[i]->ddo.value); free(card->app[i]); } 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; } 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, 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, 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.13.0/src/libopensc/pkcs15-pteid.c0000644000015201777760000002202712057406034015152 00000000000000/* * PKCS15 emulation layer for Portugal eID card. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. * */ #include "config.h" #include #include #include #include "common/compat_strlcpy.h" #include "internal.h" #include "pkcs15.h" #define IAS_CARD 0 #define GEMSAFE_CARD 1 int sc_pkcs15emu_pteid_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); static int sc_pkcs15emu_pteid_init(sc_pkcs15_card_t * p15card) { int r, i, type; unsigned char *buf = NULL; size_t len; sc_pkcs15_tokeninfo_t tokeninfo; sc_path_t tmppath; sc_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; /* Parse the TokenInfo EF */ sc_format_path("3f004f005032", &tmppath); r = sc_select_file(card, &tmppath, &p15card->file_tokeninfo); if (r) goto end; if ( (len = p15card->file_tokeninfo->size) == 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "EF(TokenInfo) is empty\n"); goto end; } buf = malloc(len); if (buf == NULL) return SC_ERROR_OUT_OF_MEMORY; r = sc_read_binary(card, 0, buf, len, 0); if (r < 0) goto end; if (r <= 2) { r = SC_ERROR_PKCS15_APP_NOT_FOUND; goto end; } memset(&tokeninfo, 0, sizeof(tokeninfo)); r = sc_pkcs15_parse_tokeninfo(ctx, &tokeninfo, buf, (size_t) r); if (r != SC_SUCCESS) goto end; *(p15card->tokeninfo) = tokeninfo; /* Card type detection */ if (card->type == SC_CARD_TYPE_IAS_PTEID) type = IAS_CARD; else if (card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID) type = GEMSAFE_CARD; else { r = SC_ERROR_INTERNAL; goto end; } p15card->tokeninfo->flags = SC_PKCS15_TOKEN_PRN_GENERATION | SC_PKCS15_TOKEN_EID_COMPLIANT | SC_PKCS15_TOKEN_READONLY; /* TODO: Use the cardholder's name? */ /* TODO: Use Portuguese descriptions? */ /* Add X.509 Certificates */ for (i = 0; i < 4; i++) { static const char *pteid_cert_names[4] = { "AUTHENTICATION CERTIFICATE", "SIGNATURE CERTIFICATE", "SIGNATURE SUB CA", "AUTHENTICATION SUB CA" }; /* X.509 Certificate Paths */ static const char *pteid_cert_paths[4] = { "3f005f00ef09", /* Authentication Certificate path */ "3f005f00ef08", /* Digital Signature Certificate path */ "3f005f00ef0f", /* Signature sub CA path */ "3f005f00ef10" /* Authentication sub CA path */ }; /* X.509 Certificate IDs */ static const int pteid_cert_ids[4] = {0x45, 0x46, 0x51, 0x52}; 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] = pteid_cert_ids[i]; cert_info.id.len = 1; sc_format_path(pteid_cert_paths[i], &cert_info.path); strlcpy(cert_obj.label, pteid_cert_names[i], sizeof(cert_obj.label)); r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); if (r < 0) { r = SC_ERROR_INTERNAL; goto end; } } /* Add PINs */ for (i = 0; i < 3; i++) { static const char *pteid_pin_names[3] = { "Auth PIN", "Sign PIN", "Address PIN" }; /* PIN References */ static const int pteid_pin_ref[2][3] = { {1, 130, 131}, {129, 130, 131} }; /* PIN Authentication IDs */ static const int pteid_pin_authid[3] = {1, 2, 3}; /* PIN Paths */ static const char *pteid_pin_paths[2][3] = { {NULL, "3f005f00", "3f005f00"}, {NULL, NULL, NULL} }; struct sc_pkcs15_auth_info pin_info; struct sc_pkcs15_object pin_obj; memset(&pin_info, 0, sizeof(pin_info)); memset(&pin_obj, 0, sizeof(pin_obj)); pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; pin_info.auth_id.len = 1; pin_info.auth_id.value[0] = pteid_pin_authid[i]; pin_info.attrs.pin.reference = pteid_pin_ref[type][i]; pin_info.attrs.pin.flags = SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_CASE_SENSITIVE; pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; pin_info.attrs.pin.min_length = 4; pin_info.attrs.pin.stored_length = 8; pin_info.attrs.pin.max_length = 8; pin_info.attrs.pin.pad_char = type == IAS_CARD ? 0x2F : 0xFF; pin_info.tries_left = -1; if (pteid_pin_paths[type][i] != NULL) sc_format_path(pteid_pin_paths[type][i], &pin_info.path); strlcpy(pin_obj.label, pteid_pin_names[i], sizeof(pin_obj.label)); pin_obj.flags = 0; r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); if (r < 0) { r = SC_ERROR_INTERNAL; goto end; } } /* Add Private Keys */ for (i = 0; i < 2; i++) { /* Key reference */ static const int pteid_prkey_keyref[2][2] = { {1, 130}, {2, 1} }; /* RSA Private Key usage */ static int pteid_prkey_usage[2] = { SC_PKCS15_PRKEY_USAGE_SIGN, SC_PKCS15_PRKEY_USAGE_NONREPUDIATION}; /* RSA Private Key IDs */ static const int pteid_prkey_ids[2] = {0x45, 0x46}; static const char *pteid_prkey_names[2] = { "CITIZEN AUTHENTICATION KEY", "CITIZEN SIGNATURE KEY"}; /* RSA Private Key Paths */ static const char *pteid_prkey_paths[2][2] = { {NULL, "3f005f00"}, {NULL, NULL} }; 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] = pteid_prkey_ids[i]; prkey_info.usage = pteid_prkey_usage[i]; prkey_info.native = 1; prkey_info.key_reference = pteid_prkey_keyref[type][i]; prkey_info.modulus_length = 1024; if (pteid_prkey_paths[type][i] != NULL) sc_format_path(pteid_prkey_paths[type][i], &prkey_info.path); strlcpy(prkey_obj.label, pteid_prkey_names[i], sizeof(prkey_obj.label)); prkey_obj.auth_id.len = 1; prkey_obj.auth_id.value[0] = i + 1; prkey_obj.user_consent = (i == 1) ? 1 : 0; prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); if (r < 0) { r = SC_ERROR_INTERNAL; goto end; } } /* Add objects */ for (i = 0; i < 3; i++) { static const char *object_ids[3] = {"1", "2", "3"}; static const char *object_oids[3] = {"-1", "-1", "-1"}; static const char *object_labels[3] = {"Citizen Data", "Citizen Address Data", "Citizen Notepad"}; static const char *object_authids[3] = {NULL, "3", "1"}; static const char *object_paths[3] = {"3f005f00ef02", "3f005f00ef05", "3f005f00ef07"}; static const int object_flags[3] = {0, SC_PKCS15_CO_FLAG_PRIVATE, SC_PKCS15_CO_FLAG_MODIFIABLE}; 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(object_ids[i], &obj_info.id); sc_format_path(object_paths[i], &obj_info.path); r = sc_format_oid(&obj_info.app_oid, object_oids[i]); if (r != SC_SUCCESS) goto end; 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]; r = sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_DATA_OBJECT, &obj_obj, &obj_info); if (r < 0) goto end; } end: if (buf != NULL) { free(buf); buf = NULL; } if (r) return r; return SC_SUCCESS; } static int pteid_detect_card(sc_pkcs15_card_t *p15card) { if (p15card->card->type == SC_CARD_TYPE_IAS_PTEID || p15card->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, sc_pkcs15emu_opt_t *opts) { if (opts != NULL && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) return sc_pkcs15emu_pteid_init(p15card); else { int r = pteid_detect_card(p15card); if (r) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_pteid_init(p15card); } } opensc-0.13.0/src/libopensc/card-acos5.c0000644000015201777760000001265012057406034014663 00000000000000/* * card-acos5.c: Support for ACS ACOS5 cards. * * Copyright (C) 2007 Ian A. Young * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include "internal.h" #include "cardctl.h" static struct sc_atr_table acos5_atrs[] = { {"3b:be:18:00:00:41:05:10:00:00:00:00:00:00:00:00:00:90:00", NULL, NULL, SC_CARD_TYPE_ACOS5_GENERIC, 0, NULL}, {NULL, NULL, NULL, 0, 0, NULL} }; static struct sc_card_operations *iso_ops; static struct sc_card_operations acos5_ops; static struct sc_card_driver acos5_drv = { "ACS ACOS5 card", "acos5", &acos5_ops, NULL, 0, NULL }; static int acos5_match_card(sc_card_t * card) { int i; i = _sc_match_atr(card, acos5_atrs, &card->type); if (i < 0) return 0; return 1; } static int acos5_init(sc_card_t * card) { card->max_recv_size = 128; card->max_send_size = 128; return SC_SUCCESS; } static int acos5_select_file_by_path(sc_card_t * card, const sc_path_t * in_path, sc_file_t ** file_out) { int in_len = in_path->len; const u8 *in_pos = in_path->value; sc_path_t path; memset(&path, 0, sizeof(sc_path_t)); path.len = 2; /* one component at a time */ path.type = SC_PATH_TYPE_FILE_ID; /* * Check parameters. */ if (in_len % 2 != 0) return SC_ERROR_INVALID_ARGUMENTS; /* * File ID by file ID... */ while (in_len) { int result; memcpy(path.value, in_pos, 2); result = iso_ops->select_file(card, &path, file_out); if (result != SC_SUCCESS) return result; in_len -= 2; in_pos += 2; } return SC_SUCCESS; } static int acos5_select_file(sc_card_t * card, const sc_path_t * in_path, sc_file_t ** file_out) { switch (in_path->type) { case SC_PATH_TYPE_PATH: return acos5_select_file_by_path(card, in_path, file_out); default: return iso_ops->select_file(card, in_path, file_out); } } static int acos5_get_serialnr(sc_card_t * card, sc_serial_number_t * serial) { int r; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_apdu_t apdu; /* * Check arguments. */ if (!serial) return SC_ERROR_INVALID_ARGUMENTS; /* * Return a cached serial number, if we have one. */ if (card->serialnr.len) { memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } /* * Fetch serial number using GET CARD INFO. */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x14, 0, 0); apdu.cla |= 0x80; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 6; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return SC_ERROR_INTERNAL; /* * Cache serial number. */ memcpy(card->serialnr.value, apdu.resp, MIN(apdu.resplen, SC_MAX_SERIALNR)); card->serialnr.len = MIN(apdu.resplen, SC_MAX_SERIALNR); /* * Copy and return serial number. */ memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } static int acos5_card_ctl(sc_card_t * card, unsigned long cmd, void *ptr) { switch (cmd) { case SC_CARDCTL_GET_SERIALNR: return acos5_get_serialnr(card, (sc_serial_number_t *) ptr); default: return SC_ERROR_NOT_SUPPORTED; } } static int acos5_list_files(sc_card_t * card, u8 * buf, size_t buflen) { sc_apdu_t apdu; int r; size_t count; u8 *bufp = buf; /* pointer into buf */ int fno = 0; /* current file index */ /* * Check parameters. */ if (!buf || (buflen & 1)) return SC_ERROR_INVALID_ARGUMENTS; /* * Use CARD GET INFO to fetch the number of files under the * curently selected DF. */ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x14, 0x01, 0x00); apdu.cla |= 0x80; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90) return SC_ERROR_INTERNAL; count = apdu.sw2; while (count--) { u8 info[8]; /* * Truncate the scan if no more room left in output buffer. */ if (buflen == 0) break; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x14, 0x02, fno++); apdu.cla |= 0x80; apdu.resp = info; apdu.resplen = sizeof(info); apdu.le = sizeof(info); r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return SC_ERROR_INTERNAL; *bufp++ = info[2]; *bufp++ = info[3]; buflen -= 2; } return (bufp - buf); } static struct sc_card_driver *sc_get_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); iso_ops = iso_drv->ops; acos5_ops = *iso_ops; acos5_ops.match_card = acos5_match_card; acos5_ops.init = acos5_init; acos5_ops.select_file = acos5_select_file; acos5_ops.card_ctl = acos5_card_ctl; acos5_ops.list_files = acos5_list_files; return &acos5_drv; } struct sc_card_driver *sc_get_acos5_driver(void) { return sc_get_driver(); } opensc-0.13.0/src/libopensc/card-iasecc.c0000644000015201777760000032160112057406034015077 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "sm.h" */ #include "pkcs15.h" /* #include "hash-strings.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) /* 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 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:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF", "IAS/ECC Gemalto", SC_CARD_TYPE_IASECC_GEMALTO, 0, NULL }, { "3B:DD:18:00:81:31:FE:45:80:F9:A0:00:00:00:77:01:08:00:07:90:00:FE", NULL, "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: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 }, { NULL, NULL, NULL, 0, 0, NULL } }; static struct sc_aid GlobalPlatform_CardManager_AID = { { 0xA0,0x00,0x00,0x00,0x03,0x00,0x00}, 7 }; static struct sc_aid GlobalPlatform_ISD_Default_RID = { { 0xA0,0x00,0x00,0x01,0x51,0x00,0x00}, 7 }; static struct sc_aid OberthurIASECC_AID = { {0xA0,0x00,0x00,0x00,0x77,0x01,0x08,0x00,0x07,0x00,0x00,0xFE,0x00,0x00,0x01,0x00}, 16 }; 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; static int iasecc_select_file(struct sc_card *card, const struct sc_path *path, struct sc_file **file_out); static int iasecc_process_fci(struct sc_card *card, struct sc_file *file, const unsigned char *buf, size_t buflen); static int iasecc_get_serialnr(struct sc_card *card, struct sc_serial_number *serial); static int iasecc_sdo_get_data(struct sc_card *card, struct iasecc_sdo *sdo); static int iasecc_pin_get_policy (struct sc_card *card, struct sc_pin_cmd_data *data); static int iasecc_pin_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, int *tries_left); static int iasecc_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); 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); 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(ctx, "iasecc_chv_cache_verified() sha1(PIN): %s", sc_dump_hex(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(ctx, "data_sha1: %s", sc_dump_hex(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 failes when not FCP data returned */ sc_format_path("3F00", &path); path.type = SC_PATH_TYPE_FILE_ID; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x00, 0x00); apdu.lc = path.len; apdu.data = path.value; apdu.datalen = path.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 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 invisaged. */ 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 && card->cache.current_df) sc_file_free(card->cache.current_df); card->cache.current_df = NULL; if (card->cache.valid && card->cache.current_ef) 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; /* Select application (deselect previously selected application) */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x04, 0x00); apdu.lc = aid->len; apdu.data = aid->value; apdu.datalen = aid->len; apdu.resplen = sizeof(apdu_resp); apdu.resp = apdu_resp; rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, rv, "Cannot select AID"); if (*out_len < apdu.resplen) LOG_TEST_RET(card->ctx, SC_ERROR_BUFFER_TOO_SMALL, "Cannot select AID"); memcpy(out, apdu.resp, apdu.resplen); return SC_SUCCESS; } static int iasecc_match_card(struct sc_card *card) { struct sc_context *ctx = card->ctx; int i; sc_log(ctx, "iasecc_match_card(%s) called", sc_dump_hex(card->atr.value, card->atr.len)); i = _sc_match_atr(card, iasecc_known_atrs, &card->type); if (i < 0) { sc_log(ctx, "card not matched"); 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]; 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 %X/%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; _sc_card_add_rsa_alg(card, 1024, flags, 0x10001); _sc_card_add_rsa_alg(card, 2048, flags, 0x10001); card->caps = SC_CARD_CAP_RNG; card->caps |= SC_CARD_CAP_APDU_EXT; card->caps |= SC_CARD_CAP_USE_FCI_AC; sc_format_path("3F00", &path); sc_select_file(card, &path, NULL); 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"); 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(ctx, "AID in historical_bytes '%s'", sc_dump_hex(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 char resp[0x100]; size_t resp_len; 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 = SC_CARD_CAP_RNG; card->caps |= SC_CARD_CAP_APDU_EXT; card->caps |= SC_CARD_CAP_USE_FCI_AC; iasecc_parse_ef_atr(card); resp_len = sizeof(resp); if (iasecc_select_aid(card, &GlobalPlatform_CardManager_AID, resp, &resp_len)) { resp_len = sizeof(resp); iasecc_select_aid(card, &GlobalPlatform_ISD_Default_RID, resp, &resp_len); } 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_init_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 = SC_CARD_CAP_RNG; card->caps |= SC_CARD_CAP_APDU_EXT; card->caps |= SC_CARD_CAP_USE_FCI_AC; rv = iasecc_parse_ef_atr(card); if (rv == SC_ERROR_FILE_NOT_FOUND) { rv = iasecc_select_mf(card, NULL); LOG_TEST_RET(ctx, rv, "MF selection error"); rv = iasecc_parse_ef_atr(card); } LOG_TEST_RET(ctx, rv, "IASECC: ATR parse failed"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_init_amos(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 = SC_CARD_CAP_RNG; card->caps |= SC_CARD_CAP_APDU_EXT; card->caps |= SC_CARD_CAP_USE_FCI_AC; rv = iasecc_parse_ef_atr(card); if (rv == SC_ERROR_FILE_NOT_FOUND) { rv = iasecc_select_mf(card, NULL); LOG_TEST_RET(ctx, rv, "MF selection error"); rv = iasecc_parse_ef_atr(card); } LOG_TEST_RET(ctx, rv, "IASECC: ATR parse failed"); 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; 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_sagem(card); else if (card->type == SC_CARD_TYPE_IASECC_AMOS) rv = iasecc_init_amos(card); else LOG_FUNC_RETURN(ctx, SC_ERROR_NO_CARD_SUPPORT); 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_RET(ctx, rv, "Select EF.ATR AID failed"); } rv = 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) 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_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 %i", 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 %i", apdu.resplen); if (apdu.resplen == IASECC_READ_BINARY_LENGTH_MAX && apdu.resplen < count) { rv = iasecc_read_binary(card, offs + apdu.resplen, buf + apdu.resplen, count - apdu.resplen, flags); if (rv != SC_ERROR_WRONG_LENGTH) { LOG_TEST_RET(ctx, rv, "iasecc_read_binary() read tail failed"); apdu.resplen += rv; } } LOG_FUNC_RETURN(ctx, apdu.resplen); } 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 %i", 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_TEST_RET(ctx, rv, "iasecc_erase_binary() update binary error"); LOG_FUNC_RETURN(ctx, rv); } 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:%i ", 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->method == SC_AC_SCB) ? (entry->key_ref & IASECC_SCB_METHOD_MASK_REF) : 0; 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:%i ", 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->method == SC_AC_SCB ? entry->key_ref & IASECC_SCB_METHOD_MASK_REF : 0; rv = iasecc_sm_update_binary(card, se_num, offs, buff, count); LOG_FUNC_RETURN(ctx, rv); } } LOG_FUNC_RETURN(ctx, 0); } 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 inermediate 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 %i; path.type %i; aid_len %i", card, path->len, path->type, path->aid.len); sc_log(ctx, "iasecc_select_file() path:%s", sc_print_path(path)); sc_print_cache(card); if (lpath.len >= 2 && lpath.value[0] == 0x3F && lpath.value[1] == 0x00) { sc_log(ctx, "EF.ATR(aid:'%s')", card->ef_atr ? sc_dump_hex(card->ef_atr->aid.value, card->ef_atr->aid.len) : ""); rv = iasecc_select_mf(card, file_out); LOG_TEST_RET(ctx, rv, "MF selection error"); if (lpath.len >= 2 && lpath.value[0] == 0x3F && lpath.value[1] == 0x00) { memcpy(&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/%i", 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_RET(ctx, rv, "select AID path failed"); if (file_out) *file_out = file; else if (file) 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) LOG_FUNC_RETURN(ctx, SC_SUCCESS); sc_print_cache(card); if (card->cache.valid && card->cache.current_df && lpath.type == SC_PATH_TYPE_DF_NAME && card->cache.current_df->path.len == lpath.len && !memcmp(card->cache.current_df->path.value, lpath.value, lpath.len)) { sc_log(ctx, "returns current DF path %s", sc_print_path(&card->cache.current_df->path)); if (file_out) { if (*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]; int 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) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported card"); if (lpath.type == SC_PATH_TYPE_FILE_ID) { apdu.p1 = 0x02; if (card->type == SC_CARD_TYPE_IASECC_OBERTHUR) { apdu.p1 = 0x01; apdu.p2 = 0x04; } if (card->type == SC_CARD_TYPE_IASECC_AMOS) apdu.p2 = 0x04; } else if (lpath.type == SC_PATH_TYPE_FROM_CURRENT) { apdu.p1 = 0x09; if (card->type == SC_CARD_TYPE_IASECC_OBERTHUR) apdu.p2 = 0x04; if (card->type == SC_CARD_TYPE_IASECC_AMOS) apdu.p2 = 0x04; } 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) apdu.p2 = 0x04; } else { sc_log(ctx, "Invalid PATH type: 0x%X", lpath.type); LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "iasecc_select_file() invalid PATH type"); } 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_RET(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) { 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_RET(ctx, rv, "Failed to emulate DF FCP"); } break; } /* * Using of the cached DF and EF can cause problems in the multi-thread environment. * FIXME: introduce config. option that invalidates this cache outside the locked card session, * (or invent something else) */ if (rv == SC_ERROR_FILE_NOT_FOUND && cache_valid && df_from_cache) { card->cache.valid = 0; sc_log(ctx, "iasecc_select_file() file not found, retry without cached DF"); if (file_out && *file_out) { sc_file_free(*file_out); *file_out = NULL; } rv = iasecc_select_file(card, path, file_out); LOG_FUNC_RETURN(ctx, rv); } LOG_TEST_RET(ctx, rv, "iasecc_select_file() check SW failed"); sc_log(ctx, "iasecc_select_file() apdu.resp %i", 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) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); file->path = lpath; rv = iasecc_process_fci(card, file, apdu.resp, apdu.resplen); if (rv) LOG_FUNC_RETURN(ctx, rv); break; default: 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 && card->cache.current_df) sc_file_free(card->cache.current_df); card->cache.current_df = NULL; if (card->cache.valid && card->cache.current_ef) sc_file_free(card->cache.current_ef); card->cache.current_ef = NULL; sc_file_dup(&card->cache.current_df, file); card->cache.valid = 1; } else { if (card->cache.valid && card->cache.current_ef) sc_file_free(card->cache.current_ef); card->cache.current_ef = NULL; sc_file_dup(&card->cache.current_ef, file); } if (file_out) { 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) { if (card->cache.current_df) sc_file_free(card->cache.current_df); card->cache.current_df = NULL; if (card->cache.current_ef) 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); } 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; int rv, ii, offs; 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 %i", 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 %i", 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(%i) %s", buflen, sc_dump_hex(buf, buflen)); LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "ACLs tag missing"); } sc_log(ctx, "ACLs(%i) '%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]; if (!(mask & acls[0])) continue; sc_log(ctx, "ACLs mask 0x%X, offs %i, op 0x%X, acls[offs] 0x%X", mask, offs, op, acls[offs]); 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, offs = 0, amb, 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, "%i: AMB %X; nn_smb %i", 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, 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"); sbuf_len = iasecc_fcp_encode(card, file, sbuf + 2, sizeof(sbuf)-2); LOG_TEST_RET(ctx, sbuf_len, "FCP encode error"); 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_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) { if (se_info->df) 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) LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "Cannot delete file: no 'DELETE' acl"); sc_log(ctx, "DELETE method/reference %X/%X", entry->method, entry->key_ref); if (entry->method == SC_AC_SCB && (entry->key_ref & IASECC_SCB_METHOD_SM)) { unsigned char se_num = (entry->method == SC_AC_SCB) ? (entry->key_ref & IASECC_SCB_METHOD_MASK_REF) : 0; rv = iasecc_sm_delete_file(card, se_num, file->id); } else { 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 && card->cache.current_ef) sc_file_free(card->cache.current_ef); card->cache.current_ef = NULL; } sc_file_free(file); 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) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate current DF file"); } rv = iasecc_docp_copy(ctx, &se->docp, &se_info->docp); 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 (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, 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%X, senv.algorithm_ref 0x%X", 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%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)) 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%X, senv.algorithm_ref 0x%X", env->algorithm, env->algorithm_ref); sc_log(ctx, "se_num %i, operation 0x%X, algorithm 0x%X, algorithm_ref 0x%X, flags 0x%X; key size %i", 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)) 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 DECHIPHER: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_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]; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "CHV PINPAD PIN reference %i", pin_cmd->pin_reference); rv = iasecc_pin_is_verified(card, pin_cmd, tries_left); if (!rv) LOG_FUNC_RETURN(ctx, rv); if (!card->reader || !card->reader->ops || !card->reader->ops->perform_verify) { sc_log(ctx, "Reader not ready for PIN PAD"); LOG_FUNC_RETURN(ctx, SC_ERROR_READER); } if (pin_cmd->pin1.min_length != pin_cmd->pin1.max_length) { sc_log(ctx, "Different values for PIN min and max lengths is not actually compatible with PinPAD."); LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Different values for PIN min and max lengths is not actually compatible with PinPAD."); } pin_cmd->pin1.len = pin_cmd->pin1.min_length; memset(buffer, 0xFF, sizeof(buffer)); pin_cmd->pin1.data = buffer; pin_cmd->cmd = SC_PIN_CMD_VERIFY; pin_cmd->flags |= SC_PIN_CMD_USE_PINPAD; /* if (card->reader && card->reader->ops && card->reader->ops->load_message) { rv = card->reader->ops->load_message(card->reader, card->slot, 0, "Here we are!"); sc_log(ctx, "Load message returned %i", rv); } */ rv = iso_ops->pin_cmd(card, pin_cmd, tries_left); sc_log(ctx, "rv %i", rv); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_chv_verify(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, int *tries_left) { struct sc_context *ctx = card->ctx; struct sc_acl_entry acl = pin_cmd->pin1.acls[IASECC_ACLS_CHV_VERIFY]; struct sc_apdu apdu; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "Verify CHV PIN(ref:%i,len:%i,acl:%X:%X)", pin_cmd->pin_reference, pin_cmd->pin1.len, acl.method, acl.key_ref); if (acl.method & IASECC_SCB_METHOD_SM) { rv = iasecc_sm_pin_verify(card, acl.key_ref, pin_cmd, tries_left); LOG_FUNC_RETURN(ctx, rv); } if (pin_cmd->pin1.data && !pin_cmd->pin1.len) { sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0, pin_cmd->pin_reference); } else if (pin_cmd->pin1.data && pin_cmd->pin1.len) { sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0, pin_cmd->pin_reference); apdu.data = pin_cmd->pin1.data; apdu.datalen = pin_cmd->pin1.len; apdu.lc = pin_cmd->pin1.len; } else if ((card->reader->capabilities & SC_READER_CAP_PIN_PAD) && !pin_cmd->pin1.data && !pin_cmd->pin1.len) { rv = iasecc_chv_verify_pinpad(card, pin_cmd, tries_left); sc_log(ctx, "Result of verifying CHV with PIN pad %i", rv); LOG_FUNC_RETURN(ctx, rv); } else { LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, rv, "APDU transmit failed"); if (tries_left && apdu.sw1 == 0x63 && (apdu.sw2 & 0xF0) == 0xC0) *tries_left = apdu.sw2 & 0x0F; rv = sc_check_sw(card, apdu.sw1, apdu.sw2); 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]; if (se.df) sc_file_free(se.df); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_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; struct sc_acl_entry acl = pin_cmd_data->pin1.acls[IASECC_ACLS_CHV_VERIFY]; int rv = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; 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"); sc_log(ctx, "Verify ACL(method:%X;ref:%X)", acl.method, acl.key_ref); if (acl.method != IASECC_SCB_ALWAYS) LOG_FUNC_RETURN(ctx, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED); pin_cmd = *pin_cmd_data; pin_cmd.pin1.data = (unsigned char *)""; pin_cmd.pin1.len = 0; rv = iasecc_chv_verify(card, &pin_cmd, tries_left); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_pin_verify(struct sc_card *card, unsigned type, unsigned reference, const unsigned char *data, size_t data_len, int *tries_left) { struct sc_context *ctx = card->ctx; struct sc_pin_cmd_data pin_cmd; unsigned chv_ref = reference; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "Verify PIN(type:%X,ref:%i,data(len:%i,%p)", type, reference, data_len, data); if (type == SC_AC_AUT) { rv = iasecc_sm_external_authentication(card, reference, tries_left); LOG_FUNC_RETURN(ctx, rv); } else if (type == SC_AC_SCB) { if (reference & IASECC_SCB_METHOD_USER_AUTH) { type = SC_AC_SEN; reference = reference & IASECC_SCB_METHOD_MASK_REF; } else { sc_log(ctx, "Do not try to verify non CHV PINs"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } } if (type == SC_AC_SEN) { rv = iasecc_se_at_to_chv_reference(card, reference, &chv_ref); LOG_TEST_RET(ctx, rv, "SE AT to CHV reference error"); } memset(&pin_cmd, 0, sizeof(pin_cmd)); pin_cmd.pin_type = SC_AC_CHV; pin_cmd.pin_reference = chv_ref; pin_cmd.cmd = SC_PIN_CMD_VERIFY; rv = iasecc_pin_get_policy(card, &pin_cmd); LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); pin_cmd.pin1.data = data; pin_cmd.pin1.len = data_len; rv = iasecc_pin_is_verified(card, &pin_cmd, tries_left); if (data && !data_len) LOG_FUNC_RETURN(ctx, rv); if (!rv) { if (iasecc_chv_cache_is_verified(card, &pin_cmd)) LOG_FUNC_RETURN(ctx, SC_SUCCESS); } else if (rv != SC_ERROR_PIN_CODE_INCORRECT && rv != SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { LOG_FUNC_RETURN(ctx, rv); } iasecc_chv_cache_clean(card, &pin_cmd); rv = iasecc_chv_verify(card, &pin_cmd, tries_left); LOG_TEST_RET(ctx, rv, "PIN CHV verification error"); rv = iasecc_chv_cache_verified(card, &pin_cmd); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_chv_change_pinpad(struct sc_card *card, unsigned reference, int *tries_left) { struct sc_context *ctx = card->ctx; struct sc_pin_cmd_data pin_cmd; unsigned char pin1_data[0x100], pin2_data[0x100]; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "CHV PINPAD PIN reference %i", reference); memset(pin1_data, 0xFF, sizeof(pin1_data)); memset(pin2_data, 0xFF, sizeof(pin2_data)); 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; rv = iasecc_pin_get_policy(card, &pin_cmd); LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); if (pin_cmd.pin1.min_length != pin_cmd.pin1.max_length) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Different values for PIN min and max lengths is not allowed with PinPAD."); if (pin_cmd.pin1.min_length < 4) pin_cmd.pin1.min_length = 4; pin_cmd.pin1.len = pin_cmd.pin1.min_length; pin_cmd.pin1.data = pin1_data; memcpy(&pin_cmd.pin2, &pin_cmd.pin1, sizeof(pin_cmd.pin1)); pin_cmd.pin2.data = pin2_data; sc_log(ctx, "PIN1 max/min: %i/%i", pin_cmd.pin1.max_length, pin_cmd.pin1.min_length); sc_log(ctx, "PIN2 max/min: %i/%i", pin_cmd.pin2.max_length, pin_cmd.pin2.min_length); rv = iso_ops->pin_cmd(card, &pin_cmd, tries_left); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_chv_set_pinpad(struct sc_card *card, unsigned char reference) { struct sc_context *ctx = card->ctx; struct sc_pin_cmd_data pin_cmd; unsigned char pin_data[0x100]; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "Set CHV PINPAD PIN reference %i", reference); memset(pin_data, 0xFF, sizeof(pin_data)); if (!card->reader || !card->reader->ops || !card->reader->ops->perform_verify) { sc_log(ctx, "Reader not ready for PIN PAD"); LOG_FUNC_RETURN(ctx, SC_ERROR_READER); } memset(&pin_cmd, 0, sizeof(pin_cmd)); pin_cmd.pin_type = SC_AC_CHV; pin_cmd.pin_reference = reference; pin_cmd.cmd = SC_PIN_CMD_UNBLOCK; pin_cmd.flags |= SC_PIN_CMD_USE_PINPAD; rv = iasecc_pin_get_policy(card, &pin_cmd); LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); if (pin_cmd.pin1.min_length != pin_cmd.pin1.max_length) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Different values for PIN min and max lengths is not allowed with PinPAD."); if (pin_cmd.pin1.min_length < 4) pin_cmd.pin1.min_length = 4; pin_cmd.pin1.len = pin_cmd.pin1.min_length; pin_cmd.pin1.data = pin_data; memcpy(&pin_cmd.pin2, &pin_cmd.pin1, sizeof(pin_cmd.pin1)); memset(&pin_cmd.pin1, 0, sizeof(pin_cmd.pin1)); sc_log(ctx, "PIN1(max:%i,min:%i)", pin_cmd.pin1.max_length, pin_cmd.pin1.min_length); sc_log(ctx, "PIN2(max:%i,min:%i)", pin_cmd.pin2.max_length, pin_cmd.pin2.min_length); rv = iso_ops->pin_cmd(card, &pin_cmd, NULL); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_pin_get_policy (struct sc_card *card, struct sc_pin_cmd_data *data) { struct sc_context *ctx = card->ctx; struct sc_file *save_current_df = NULL, *save_current_ef = NULL; struct iasecc_sdo sdo; struct sc_path path; unsigned ii; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_pin_get_policy(card:%p)", card); if (data->pin_type != SC_AC_CHV) { sc_log(ctx, "To unblock PIN it's CHV reference should be presented"); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); } if (card->cache.valid && card->cache.current_df) { sc_file_dup(&save_current_df, card->cache.current_df); if (save_current_df == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate current DF file"); } if (card->cache.valid && card->cache.current_ef) { sc_file_dup(&save_current_ef, card->cache.current_ef); if (save_current_ef == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate current EF file"); } if (!(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_RET(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_RET(ctx, rv, "Cannot get SDO PIN data"); if (sdo.docp.acls_contact.size == 0) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Extremely strange ... there is no ACLs"); for (ii=0; iipin1.acls[ii]; int crt_num = 0; memset(&se, 0, sizeof(se)); memset(&acl->crts, 0, sizeof(acl->crts)); sc_log(ctx, "iasecc_pin_get_policy() set info acls: SCB 0x%X", scb); /* acl->raw_value = scb; */ acl->method = scb & IASECC_SCB_METHOD_MASK; acl->key_ref = scb & IASECC_SCB_METHOD_MASK_REF; if (scb==0 || scb==0xFF) continue; if (se.reference != (int)acl->key_ref) { memset(&se, 0, sizeof(se)); se.reference = acl->key_ref; rv = iasecc_se_get_info(card, &se); LOG_TEST_RET(ctx, rv, "SDO get data error"); } if (scb & IASECC_SCB_METHOD_USER_AUTH) { rv = iasecc_se_get_crt_by_usage(card, &se, IASECC_CRT_TAG_AT, IASECC_UQB_AT_USER_PASSWORD, &acl->crts[crt_num]); LOG_TEST_RET(ctx, rv, "no authentication template for 'USER PASSWORD'"); sc_log(ctx, "iasecc_pin_get_policy() scb:0x%X; sdo_ref:[%i,%i,...]", scb, acl->crts[crt_num].refs[0], acl->crts[crt_num].refs[1]); crt_num++; } if (scb & (IASECC_SCB_METHOD_SM | IASECC_SCB_METHOD_EXT_AUTH)) { sc_log(ctx, "'SM' and 'EXTERNAL AUTHENTICATION' protection methods are not supported: SCB:0x%X", scb); /* Set to 'NEVER' if all conditions are needed or * there is no user authentication method allowed */ if (!crt_num || (scb & IASECC_SCB_METHOD_NEED_ALL)) acl->method = SC_AC_NEVER; continue; } if (se.df) sc_file_free(se.df); } if (sdo.data.chv.size_max.value) data->pin1.max_length = *sdo.data.chv.size_max.value; if (sdo.data.chv.size_min.value) data->pin1.min_length = *sdo.data.chv.size_min.value; if (sdo.docp.tries_maximum.value) data->pin1.max_tries = *sdo.docp.tries_maximum.value; if (sdo.docp.tries_remaining.value) data->pin1.tries_left = *sdo.docp.tries_remaining.value; data->pin1.encoding = SC_PIN_ENCODING_ASCII; data->pin1.offset = 5; sc_log(ctx, "PIN policy: size max/min %i/%i, tries max/left %i/%i", data->pin1.max_length, data->pin1.min_length, data->pin1.max_tries, data->pin1.tries_left); iasecc_sdo_free_fields(card, &sdo); if (save_current_df) { struct sc_file *dummy_file = NULL; sc_log(ctx, "iasecc_pin_get_policy() restore current DF"); rv = iasecc_select_file(card, &save_current_df->path, &dummy_file); LOG_TEST_RET(ctx, rv, "Cannot return to saved DF"); sc_file_free(dummy_file); sc_file_free(save_current_df); } if (save_current_ef) { struct sc_file *dummy_file = NULL; sc_log(ctx, "iasecc_pin_get_policy() restore current EF"); rv = iasecc_select_file(card, &save_current_ef->path, &dummy_file); LOG_TEST_RET(ctx, rv, "Cannot return to saved EF"); sc_file_free(dummy_file); sc_file_free(save_current_ef); } 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:%i)", 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]; iasecc_sdo_free_fields(card, &sdo); sc_log(ctx, "SCB:0x%X", scb); if (!(scb & IASECC_SCB_METHOD_SM)) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Other then protected by SM, the keyset change is not supported"); memset(&update, 0, sizeof(update)); update.magic = SC_CARDCTL_IASECC_SDO_MAGIC_PUT_DATA; update.sdo_class = sdo.sdo_class; update.sdo_ref = sdo.sdo_ref; update.fields[0].parent_tag = IASECC_SDO_KEYSET_TAG; update.fields[0].tag = IASECC_SDO_KEYSET_TAG_MAC; update.fields[0].value = 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; update.fields[1].value = 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); } 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_apdu apdu; unsigned reference = data->pin_reference; unsigned char pin_data[0x100]; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "Change PIN(ref:%i,type:0x%X,lengths:%i/%i)", reference, data->pin_type, data->pin1.len, data->pin2.len); if ((card->reader->capabilities & SC_READER_CAP_PIN_PAD)) { if (!data->pin1.data && !data->pin1.len && &data->pin2.data && !data->pin2.len) { rv = iasecc_chv_change_pinpad(card, reference, tries_left); sc_log(ctx, "iasecc_pin_cmd(SC_PIN_CMD_CHANGE) chv_change_pinpad returned %i", rv); LOG_FUNC_RETURN(ctx, rv); } } if (!data->pin1.data && data->pin1.len) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid PIN1 arguments"); if (!data->pin2.data && data->pin2.len) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid PIN2 arguments"); rv = iasecc_pin_verify(card, data->pin_type, reference, data->pin1.data, data->pin1.len, tries_left); sc_log(ctx, "iasecc_pin_cmd(SC_PIN_CMD_CHANGE) pin_verify returned %i", rv); LOG_TEST_RET(ctx, rv, "PIN verification error"); if ((unsigned)(data->pin1.len + data->pin2.len) > sizeof(pin_data)) LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Buffer too small for the 'Change PIN' data"); if (data->pin1.data) memcpy(pin_data, data->pin1.data, data->pin1.len); if (data->pin2.data) memcpy(pin_data + data->pin1.len, data->pin2.data, data->pin2.len); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0, reference); apdu.data = pin_data; apdu.datalen = data->pin1.len + data->pin2.len; apdu.lc = apdu.datalen; 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"); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_pin_reset(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { struct sc_context *ctx = card->ctx; struct sc_file *save_current = NULL; struct iasecc_sdo sdo; struct sc_apdu apdu; unsigned reference, scb; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "Reset PIN(ref:%i,lengths:%i/%i)", data->pin_reference, data->pin1.len, data->pin2.len); if (data->pin_type != SC_AC_CHV) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Unblock procedure can be used only with the PINs of type CHV"); reference = data->pin_reference; if (!(data->pin_reference & IASECC_OBJECT_REF_LOCAL) && card->cache.valid && card->cache.current_df) { struct sc_path path; sc_file_dup(&save_current, card->cache.current_df); if (save_current == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate current DF file"); sc_format_path("3F00", &path); path.type = SC_PATH_TYPE_FILE_ID; rv = iasecc_select_file(card, &path, NULL); LOG_TEST_RET(ctx, rv, "Unable to select MF"); } memset(&sdo, 0, sizeof(sdo)); sdo.sdo_class = IASECC_SDO_CLASS_CHV; sdo.sdo_ref = reference & ~IASECC_OBJECT_REF_LOCAL; rv = iasecc_sdo_get_data(card, &sdo); LOG_TEST_RET(ctx, rv, "Cannot get PIN data"); if (sdo.docp.acls_contact.size == 0) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Extremely strange ... there are no ACLs"); scb = sdo.docp.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) { rv = iasecc_pin_verify(card, SC_AC_SEN, se_num, data->pin1.data, data->pin1.len, 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 (!need_all) break; } if (scb & IASECC_SCB_METHOD_EXT_AUTH) { rv = iasecc_sm_external_authentication(card, reference, tries_left); LOG_TEST_RET(ctx, rv, "iasecc_pin_reset() external authentication error"); } } while(0); iasecc_sdo_free_fields(card, &sdo); if (data->pin2.len) { sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2C, 0x02, reference); apdu.data = data->pin2.data; apdu.datalen = data->pin2.len; apdu.lc = apdu.datalen; rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, rv, "PIN cmd failed"); } else if (data->pin2.data) { sc_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 = iasecc_chv_set_pinpad(card, reference); sc_log(ctx, "Set CHV with PIN pad returned %i", rv); } if (save_current) { struct sc_file *dummy_file = NULL; rv = iasecc_select_file(card, &save_current->path, &dummy_file); LOG_TEST_RET(ctx, rv, "Cannot return to saved PATH"); } 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; struct sc_apdu apdu; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_pin_cmd() cmd 0x%X, PIN type 0x%X, PIN reference %i, PIN-1 %p:%i, PIN-2 %p:%i", data->cmd, data->pin_type, data->pin_reference, data->pin1.data, data->pin1.len, data->pin2.data, data->pin2.len); switch (data->cmd) { case SC_PIN_CMD_VERIFY: rv = iasecc_pin_verify(card, data->pin_type, data->pin_reference, data->pin1.data, data->pin1.len, tries_left); LOG_FUNC_RETURN(ctx, rv); 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); LOG_FUNC_RETURN(ctx, rv); case SC_PIN_CMD_UNBLOCK: rv = iasecc_pin_reset(card, data, tries_left); LOG_FUNC_RETURN(ctx, rv); case SC_PIN_CMD_GET_INFO: rv = iasecc_pin_get_policy(card, data); LOG_FUNC_RETURN(ctx, rv); default: sc_log(ctx, "Other pin commands not supported yet: 0x%X", data->cmd); LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Non-supported PIN command"); } 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"); 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; 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 (rbuf[0] != ISO7812_PAN_SN_TAG) LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "serial number parse error"); 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; } offs = rbuf[1] > 8 ? rbuf[1] - 8 : 0; if (card->type == SC_CARD_TYPE_IASECC_SAGEM) { /* 5A 0A 92 50 00 20 10 10 25 00 01 3F */ /* 00 02 01 01 02 50 00 13 */ for (ii=0; ii < rbuf[1] - offs; ii++) *(card->serialnr.value + ii) = ((rbuf[ii + offs + 1] & 0x0F) << 4) + ((rbuf[ii + offs + 2] & 0xF0) >> 4) ; card->serialnr.len = ii; } else { for (ii=0; ii < rbuf[1] - offs; ii++) *(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"); 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); 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"); if (se.df) 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 %i; outlen %i", 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 = 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) { SHA_CTX sha; SHA_LONG pre_hash_Nl, *hh[5] = { &sha.h0, &sha.h1, &sha.h2, &sha.h3, &sha.h4 }; 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 %i", in_len); memset(out, 0, sizeof(struct iasecc_qsign_data)); SHA1_Init(&sha); SHA1_Update(&sha, in, in_len); 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 = sha.Nl - (sha.Nl % (sizeof(sha.data) * 8)); for (ii=0; iicounter[ii] = (sha.Nh >> 8*(hh_size-1-ii)) &0xFF; out->counter[hh_size+ii] = (pre_hash_Nl >> 8*(hh_size-1-ii)) &0xFF; } for (ii=0, out->counter_long=0; ii<(int)sizeof(out->counter); ii++) out->counter_long = out->counter_long*0x100 + out->counter[ii]; sc_log(ctx, "Pre counter(%li):%s", out->counter_long, sc_dump_hex(out->counter, sizeof(out->counter))); if (sha.num) { memcpy(out->last_block, in + in_len - sha.num, sha.num); out->last_block_size = sha.num; sc_log(ctx, "Last block(%i):%s", out->last_block_size, sc_dump_hex(out->last_block, out->last_block_size)); } SHA1_Final(out->hash, &sha); out->hash_size = SHA_DIGEST_LENGTH; sc_log(ctx, "Expected digest %s\n", sc_dump_hex(out->hash, out->hash_size)); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } #if OPENSSL_VERSION_NUMBER >= 0x00908000L static int iasecc_qsign_data_sha256(struct sc_context *ctx, const unsigned char *in, size_t in_len, struct iasecc_qsign_data *out) { SHA256_CTX sha256; 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 %i", in_len); memset(out, 0, sizeof(struct iasecc_qsign_data)); SHA256_Init(&sha256); SHA256_Update(&sha256, in, in_len); for (jj=0; jjpre_hash[jj*hh_size + ii] = ((sha256.h[jj] >> 8*(hh_size-1-ii)) & 0xFF); out->pre_hash_size = SHA256_DIGEST_LENGTH; sc_log(ctx, "Pre hash:%s", sc_dump_hex(out->pre_hash, out->pre_hash_size)); pre_hash_Nl = sha256.Nl - (sha256.Nl % (sizeof(sha256.data) * 8)); for (ii=0; iicounter[ii] = (sha256.Nh >> 8*(hh_size-1-ii)) &0xFF; out->counter[hh_size+ii] = (pre_hash_Nl >> 8*(hh_size-1-ii)) &0xFF; } for (ii=0, out->counter_long=0; ii<(int)sizeof(out->counter); ii++) out->counter_long = out->counter_long*0x100 + out->counter[ii]; sc_log(ctx, "Pre counter(%li):%s", out->counter_long, sc_dump_hex(out->counter, sizeof(out->counter))); if (sha256.num) { memcpy(out->last_block, in + in_len - sha256.num, sha256.num); out->last_block_size = sha256.num; sc_log(ctx, "Last block(%i):%s", out->last_block_size, sc_dump_hex(out->last_block, out->last_block_size)); } SHA256_Final(out->hash, &sha256); out->hash_size = SHA256_DIGEST_LENGTH; sc_log(ctx, "Expected digest %s\n", sc_dump_hex(out->hash, out->hash_size)); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } #endif 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; LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_compute_signature_dst() input length %i", 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) { #if OPENSSL_VERSION_NUMBER >= 0x00908000L rv = iasecc_qsign_data_sha256(card->ctx, in, in_len, &qsign_data); #else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "SHA256 is not supported by OpenSSL previous to v0.9.8"); #endif } else LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Need RSA_HASH_SHA1 or RSA_HASH_SHA256 algorithm"); LOG_TEST_RET(ctx, rv, "Cannot get QSign data"); sc_log(ctx, "iasecc_compute_signature_dst() hash_len %i; key_size %i", 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 %i; 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 %i", 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, 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, 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 = card->ctx; struct iasecc_private_data *prv = (struct iasecc_private_data *) card->drv_data; struct sc_security_env *env = &prv->security_env; LOG_FUNC_CALLED(ctx); sc_log(ctx, "inlen %i, outlen %i", in_len, out_len); if (!card || !in || !out) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid compute signature arguments"); 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, size_t 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(&sdo, 0, sizeof(sdo)); sdo.sdo_class = IASECC_SDO_CLASS_RSA_PUBLIC; sdo.sdo_ref = ref & ~IASECC_OBJECT_REF_LOCAL; rv = iasecc_sdo_get_data(card, &sdo); LOG_TEST_RET(ctx, rv, "failed to read public key: cannot get RSA SDO data"); 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_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "failed to read public key: cannot allocate modulus"); bn[0].len = sdo.data.pub_key.n.size; memcpy(bn[0].data, sdo.data.pub_key.n.value, sdo.data.pub_key.n.size); bn[1].data = (unsigned char *) malloc(sdo.data.pub_key.e.size); if (!bn[1].data) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "failed to read public key: cannot allocate exponent"); 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_RET(ctx, rv, "failed to read public key: cannot encode RSA public key"); sc_log(ctx, "encoded public key: %s", sc_dump_hex(*out, *out_len)); if (bn[0].data) free(bn[0].data); if (bn[1].data) free(bn[1].data); iasecc_sdo_free_fields(card, &sdo); SC_FUNC_RETURN(ctx, SC_SUCCESS, rv); } 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 LOG_TEST_RET(ctx, rv, "get new key reference failed"); sz = *(sdo->docp.size.value + 0) * 0x100 + *(sdo->docp.size.value + 1); sc_log(ctx, "SDO(idx:%i) size %i; key_size %i", idx, sz, ctl_data->key_size); if (sz != ctl_data->key_size / 8) { sc_log(ctx, "key index %i ignored: different key sizes %i/%i", 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 */ /* get_challenge: ISO7816 implementation works */ 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(); } #endif /* ENABLE_OPENSSL */ opensc-0.13.0/src/libopensc/card-setcos.c0000644000015201777760000010256312057406034015154 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" #define _FINEID_BROKEN_SELECT_FLAG 1 static struct sc_atr_table setcos_atrs[] = { /* some Nokia branded SC */ { "3B:1F:11:00:67:80:42:46:49:53:45:10:52:66:FF:81:90:00", NULL, NULL, SC_CARD_TYPE_SETCOS_GENERIC, 0, NULL }, /* RSA SecurID 3100 */ { "3B:9F:94:40:1E:00:67:16:43:46:49:53:45:10:52:66:FF:81:90:00", NULL, NULL, SC_CARD_TYPE_SETCOS_PKI, 0, NULL }, /* FINEID 1016 (SetCOS 4.3.1B3/PKCS#15, VRK) */ { "3b:9f:94:40:1e:00:67:00:43:46:49:53:45:10:52:66:ff:81:90:00", "ff:ff:ff:ff:ff:ff:ff:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff", NULL, SC_CARD_TYPE_SETCOS_FINEID, SC_CARD_FLAG_RNG, NULL }, /* FINEID 2032 (EIDApplet/7816-15, VRK test) */ { "3b:6b:00:ff:80:62:00:a2:56:46:69:6e:45:49:44", "ff:ff:00:ff:ff:ff:00:ff:ff:ff:ff:ff:ff:ff:ff", NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, 0, NULL }, /* FINEID 2132 (EIDApplet/7816-15, 3rdparty test) */ { "3b:64:00:ff:80:62:00:a2", "ff:ff:00:ff:ff:ff:00:ff", NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, 0, NULL }, /* FINEID 2064 (EIDApplet/7816-15, VRK) */ { "3b:7b:00:00:00:80:62:00:51:56:46:69:6e:45:49:44", "ff:ff:00:ff:ff:ff:ff:f0:ff:ff:ff:ff:ff:ff:ff:ff", NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, 0, NULL }, /* FINEID 2164 (EIDApplet/7816-15, 3rdparty) */ { "3b:64:00:00:80:62:00:51", "ff:ff:ff:ff:ff:ff:f0:ff", NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, 0, NULL }, /* FINEID 2264 (EIDApplet/7816-15, OPK/EMV/AVANT) */ { "3b:6e:00:00:00:62:00:00:57:41:56:41:4e:54:10:81:90:00", NULL, NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, 0, NULL }, { "3b:7b:94:00:00:80:62:11:51:56:46:69:6e:45:49:44", NULL, NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, 0, NULL }, /* FINEID cards 1.3.2011 with Samsung chips (round connector) that supports 2048 bit keys. */ { "3b:7b:94:00:00:80:62:12:51:56:46:69:6e:45:49:44", NULL, NULL, SC_CARD_TYPE_SETCOS_FINEID_V2_2048, 0, NULL }, /* FINEID card for organisations, chip unknown. */ { "3b:7b:18:00:00:80:62:01:54:56:46:69:6e:45:49:44", NULL, NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, _FINEID_BROKEN_SELECT_FLAG, NULL }, /* Swedish NIDEL card */ { "3b:9f:94:80:1f:c3:00:68:10:44:05:01:46:49:53:45:31:c8:07:90:00:18", NULL, NULL, SC_CARD_TYPE_SETCOS_NIDEL, 0, NULL }, /* Setcos 4.4.1 */ { "3b:9f:94:80:1f:c3:00:68:11:44:05:01:46:49:53:45:31:c8:00:00:00:00", "ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00", NULL, SC_CARD_TYPE_SETCOS_44, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } }; #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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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; #if 0 /* Hmm. For now, assume it's a bank card with FinEID application */ if (match_hist_bytes(card, "AVANT", 0)) card->type = SC_CARD_TYPE_SETCOS_FINEID_V2; #endif } 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); } 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 allways 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 ACI 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[32]; /* 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SetCOS 4.4 PIN refs can only be 1..7\n"); return SC_ERROR_INVALID_ARGUMENTS; } bCommands_pin[setcos_pin_index_44(pins, sizeof(pins), (int) bNumber)] |= 1 << i; break; case SC_AC_TERM: /* key */ bKeyNumber = bNumber; /* There should be only 1 key */ bCommands_key |= 1; break; } } /* Add the commands that are allways 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 ommands 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_ASYMMETRIC) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "asymmetric keyref not supported.\n"); return SC_ERROR_NOT_SUPPORTED; } if (se_num > 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "restore security environment not supported.\n"); 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) { if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) *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 = p - sbuf; apdu.lc = r; apdu.datalen = r; apdu.data = sbuf; apdu.resplen = 0; if (se_num > 0) { r = sc_lock(card); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed"); locked = 1; } if (apdu.datalen != 0) { r = sc_transmit_apdu(card, &apdu); if (r) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s: APDU transmit failed", sc_strerror(r)); goto err; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Only RSA algorithm supported.\n"); return SC_ERROR_NOT_SUPPORTED; } switch (card->type) { case SC_CARD_TYPE_SETCOS_PKI: case SC_CARD_TYPE_SETCOS_FINEID: case SC_CARD_TYPE_SETCOS_FINEID_V2_2048: case SC_CARD_TYPE_SETCOS_NIDEL: case SC_CARD_TYPE_SETCOS_44: case SC_CARD_TYPE_SETCOS_EID_V2_0: case SC_CARD_TYPE_SETCOS_EID_V2_1: break; default: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Card does not support RSA.\n"); 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->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) tmp.algorithm_ref = 0x02; if (tmp.algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) tmp.algorithm_ref |= 0x10; return 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 whitin the total AC */ while (len > 1) { /* minimum length = 2 */ int iACLen = buf[iOffset] & 0x0F; iPinCount = -1; /* default no pin required */ iMethod = SC_AC_NONE; /* default no authentication required */ if (buf[iOffset] & 0X80) { /* AC in adaptive coding */ /* Evaluates only the command-byte, not the optional P1/P2/Option bytes */ int iParmLen = 1; /* command-byte is always present */ int iKeyLen = 0; /* Encryption key is optional */ 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 = 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 */ iKeyRef = buf[iOffset+1+1+iParmLen]; /* PTL + AM-header + parameter-bytes */ iMethod = SC_AC_CHV; } /* Convert SETCOS command to OpenSC command group */ 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 - 1; if (buf[iOffset] & 0x20) { int 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 ) { 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (card->type == SC_CARD_TYPE_SETCOS_44 && apdu.sw1 == 0x6A && apdu.sw2 == 0x82) return 0; /* no files found */ if (apdu.resplen == 0) return sc_check_sw(card, apdu.sw1, apdu.sw2); return apdu.resplen; } 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "PUT_DATA returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "GET_DATA returned error"); if (apdu.resplen > data_obj->DataLen) r = SC_ERROR_WRONG_LENGTH; else data_obj->DataLen = apdu.resplen; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } /* 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 paramters */ 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "STORE/GENERATE_KEY returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "ACTIVATE_FILE returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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.13.0/src/libopensc/card-javacard.c0000644000015201777760000000421012057406034015415 00000000000000/* * card-javacard.c: Recognize known blank JavaCards * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "internal.h" static struct sc_atr_table javacard_atrs[] = { {"3b:db:18:00:80:b1:fe:45:1f:83:00:31:c0:64:c7:fc:10:00:01:90:00:fa", NULL, "Cosmo v7 64K dual/128K", SC_CARD_TYPE_JAVACARD, 0, NULL}, {"3b:75:94:00:00:62:02:02:02:01", NULL, "Cyberflex 32K", SC_CARD_TYPE_JAVACARD, 0, NULL}, {"3b:95:95:40:ff:ae:01:03:00:00", NULL, "Cyberflex v2 64K", SC_CARD_TYPE_JAVACARD, 0, NULL}, {NULL, NULL, NULL, 0, 0, NULL} }; static struct sc_card_operations javacard_ops; static struct sc_card_driver javacard_drv = { "JavaCard (without supported applet)", "javacard", &javacard_ops, NULL, 0, NULL }; static int javacard_finish(sc_card_t * card) { return SC_SUCCESS; } static int javacard_match_card(sc_card_t * card) { if (_sc_match_atr(card, javacard_atrs, &card->type) < 0) return 0; return 1; } static int javacard_init(sc_card_t * card) { card->drv_data = NULL; return SC_SUCCESS; } static struct sc_card_driver *sc_get_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); javacard_ops = *iso_drv->ops; javacard_ops.match_card = javacard_match_card; javacard_ops.select_file = NULL; javacard_ops.init = javacard_init; javacard_ops.finish = javacard_finish; return &javacard_drv; } struct sc_card_driver *sc_get_javacard_driver(void) { return sc_get_driver(); } opensc-0.13.0/src/libopensc/esteid.h0000644000015201777760000000126212057406034014221 00000000000000#ifndef _OPENSC_ESTEID_H #define _OPENSC_ESTEID_H /* personal data file record numbers */ enum { SC_ESTEID_PD_SURNAME = 1, SC_ESTEID_PD_GIVEN_NAMES1, SC_ESTEID_PD_GIVEN_NAMES2, SC_ESTEID_PD_SEX, SC_ESTEID_PD_CITIZENSHIP, SC_ESTEID_PD_DATE_OF_BIRTH, SC_ESTEID_PD_PERSONAL_ID, SC_ESTEID_PD_DOCUMENT_NR, SC_ESTEID_PD_EXPIRY_DATE, SC_ESTEID_PD_PLACE_OF_BIRTH, SC_ESTEID_PD_ISSUING_DATE, SC_ESTEID_PD_PERMIT_TYPE, SC_ESTEID_PD_REMARK1, SC_ESTEID_PD_REMARK2, SC_ESTEID_PD_REMARK3, SC_ESTEID_PD_REMARK4 }; enum { SC_ESTEID_AUTH = 1, SC_ESTEID_SIGN }; #define SC_ESTEID_KEYREF_FILE_RECLEN 21 int select_esteid_df(sc_card_t * card); int is_esteid_card(sc_card_t *card); #endif opensc-0.13.0/src/libopensc/sm.h0000644000015201777760000002027712057406034013372 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _SM_H #define _SM_H #include #ifdef HAVE_UNISTD_H #include #endif #ifdef __cplusplus extern "C" { #endif #include #include #include #ifndef SHA_DIGEST_LENGTH #define SHA_DIGEST_LENGTH 20 #define SHA1_DIGEST_LENGTH 20 #define SHA256_DIGEST_LENGTH 32 #endif #define SM_TYPE_GP_SCP01 0x100 #define SM_TYPE_CWA14890 0x400 #define SM_MODE_NONE 0x0 #define SM_MODE_ACL 0x100 #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 /* 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 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; } 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: * - 'initiliaze' - 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 iasecc_sm_external_authentication(struct sc_card *, unsigned, int *); #ifdef __cplusplus } #endif #endif opensc-0.13.0/src/libopensc/card-default.c0000644000015201777760000000675512057406034015306 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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(sc_card_t *card) { return 1; /* always match */ } static int autodetect_class(sc_card_t *card) { int classes[] = { 0x00, 0xC0, 0xB0, 0xA0 }; int class_count = sizeof(classes)/sizeof(int); u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_apdu_t apdu; int i, r; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "autodetecting CLA byte\n"); for (i = 0; i < class_count; i++) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "trying with 0x%02X\n", classes[i]); memset(&apdu, 0, sizeof(apdu)); apdu.cla = classes[i]; apdu.cse = SC_APDU_CASE_2_SHORT; apdu.ins = 0xC0; apdu.p1 = apdu.p2 = 0; apdu.datalen = 0; apdu.lc = 0; apdu.le = 256; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 == 0x6E) continue; if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) break; if (apdu.sw1 == 0x61) break; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); break; } if (i == class_count) return -1; card->cla = classes[i]; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "detected CLA byte as 0x%02X\n", card->cla); if (apdu.resplen < 2) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SELECT FILE returned %d bytes\n", apdu.resplen); return SC_SUCCESS; } if (rbuf[0] == 0x6F) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SELECT FILE seems to behave according to ISO 7816-4\n"); return SC_SUCCESS; } if (rbuf[0] == 0x00 && rbuf[1] == 0x00) { struct sc_card_driver *drv; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SELECT FILE seems to return Schlumberger 'flex stuff\n"); drv = sc_get_cryptoflex_driver(); card->ops->select_file = drv->ops->select_file; return SC_SUCCESS; } return SC_SUCCESS; } static int default_init(sc_card_t *card) { int r; card->name = "Unsupported card"; card->drv_data = NULL; r = autodetect_class(card); if (r) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to determine the right class byte\n"); return SC_ERROR_INVALID_CARD; } return 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.13.0/src/libopensc/internal-winscard.h0000644000015201777760000003234112057406034016372 00000000000000#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 #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_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_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 */ #if defined(_WIN32) #define PCSC_API WINAPI #elif defined(USE_CYGWIN) #define PCSC_API __stdcall #else #define PCSC_API #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); /* 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 /* structures used (but not defined) in PCSC Part 10: * "IFDs with Secure Pin Entry Capabilities" */ /* Set structure elements aligment 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[1]; /**< 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[1]; /**< 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.13.0/src/libopensc/card.c0000644000015201777760000010041512057406034013650 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include "internal.h" #include "asn1.h" /* #define INVALIDATE_CARD_CACHE_IN_UNLOCK */ #ifdef ENABLE_SM static int sc_card_sm_load(sc_card_t *card, const char *path, const char *module); static int sc_card_sm_unload(sc_card_t *card); 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) { assert(card != NULL && apdu != NULL); 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; } 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); if (card->ef_dir != NULL) sc_file_free(card->ef_dir); free(card->ops); if (card->algorithms != NULL) free(card->algorithms); if (card->cache.current_ef) sc_file_free(card->cache.current_ef); if (card->cache.current_df) 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); } 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; memcpy(&card->atr, &reader->atr, sizeof(card->atr)); _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->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_debug(ctx, SC_LOG_DEBUG_MATCH, "matching built-in ATRs"); for (i = 0; ctx->card_drivers[i] != NULL; i++) { 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; /* 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; *card_out = card; /* Override card limitations with reader limitations. * Note that zero means no limitations at all. */ if ((card->max_recv_size == 0) || ((reader->driver->max_recv_size != 0) && (reader->driver->max_recv_size < card->max_recv_size))) card->max_recv_size = reader->driver->max_recv_size; if ((card->max_send_size == 0) || ((reader->driver->max_send_size != 0) && (reader->driver->max_send_size < card->max_send_size))) card->max_send_size = reader->driver->max_send_size; sc_log(ctx, "card info name:'%s', type:%i, flags:0x%X, max_send/recv_size:%i/%i", 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 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); assert(card->lock_count == 0); 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); /* invalidate cache */ memset(&card->cache, 0, sizeof(card->cache)); card->cache.valid = 0; r2 = sc_mutex_unlock(card->ctx, card->mutex); if (r2 != SC_SUCCESS) { 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; LOG_FUNC_CALLED(card->ctx); if (card == NULL) return SC_ERROR_INVALID_ARGUMENTS; 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); if (r == SC_ERROR_CARD_RESET || r == SC_ERROR_READER_REATTACHED) { /* invalidate cache */ memset(&card->cache, 0, sizeof(card->cache)); card->cache.valid = 0; r = card->reader->ops->lock(card->reader); } } 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 lock"); r = r != SC_SUCCESS ? r : r2; } return 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) return r; assert(card->lock_count >= 1); if (--card->lock_count == 0) { #ifdef INVALIDATE_CARD_CACHE_IN_UNLOCK /* invalidate cache */ memset(&card->cache, 0, sizeof(card->cache)); card->cache.valid = 0; sc_log(card->ctx, "cache invalidated"); #endif /* release reader lock */ if (card->reader->ops->unlock != NULL) r = card->reader->ops->unlock(card->reader); } 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; assert(card != NULL); 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 = &file->path; assert(card != NULL); 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, size=%u", in_path->type, pbuf, 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]; assert(card != NULL); 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 = card->max_recv_size > 0 ? card->max_recv_size : 256; int r; assert(card != NULL && card->ops != NULL && buf != NULL); sc_log(card->ctx, "called; %d bytes at index %d", count, idx); if (count == 0) return 0; #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); if (count > max_le) { int bytes_read = 0; unsigned char *p = buf; r = sc_lock(card); LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); while (count > 0) { size_t n = count > max_le ? max_le : count; r = sc_read_binary(card, idx, p, n, flags); if (r < 0) { sc_unlock(card); LOG_TEST_RET(card->ctx, r, "sc_read_binary() failed"); } p += r; idx += r; bytes_read += r; count -= r; if (r == 0) { sc_unlock(card); LOG_FUNC_RETURN(card->ctx, bytes_read); } } sc_unlock(card); LOG_FUNC_RETURN(card->ctx, bytes_read); } r = card->ops->read_binary(card, idx, buf, count, flags); LOG_FUNC_RETURN(card->ctx, r); } int sc_write_binary(sc_card_t *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) { size_t max_lc = card->max_send_size > 0 ? card->max_send_size : 255; int r; assert(card != NULL && card->ops != NULL && buf != NULL); sc_log(card->ctx, "called; %d bytes at index %d", count, idx); if (count == 0) LOG_FUNC_RETURN(card->ctx, 0); if (card->ops->write_binary == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); if (count > max_lc) { int bytes_written = 0; const u8 *p = buf; r = sc_lock(card); LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); while (count > 0) { size_t n = count > max_lc? max_lc : count; r = sc_write_binary(card, idx, p, n, flags); if (r < 0) { sc_unlock(card); LOG_TEST_RET(card->ctx, r, "sc_write_binary() failed"); } p += r; idx += r; bytes_written += r; count -= r; if (r == 0) { sc_unlock(card); LOG_FUNC_RETURN(card->ctx, bytes_written); } } sc_unlock(card); LOG_FUNC_RETURN(card->ctx, bytes_written); } r = card->ops->write_binary(card, idx, buf, count, flags); LOG_FUNC_RETURN(card->ctx, r); } int sc_update_binary(sc_card_t *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) { size_t max_lc = card->max_send_size > 0 ? card->max_send_size : 255; int r; assert(card != NULL && card->ops != NULL && buf != NULL); sc_log(card->ctx, "called; %d bytes at index %d", count, idx); if (count == 0) return 0; #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); if (count > max_lc) { int bytes_written = 0; const u8 *p = buf; r = sc_lock(card); LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); while (count > 0) { size_t n = count > max_lc? max_lc : count; r = sc_update_binary(card, idx, p, n, flags); if (r < 0) { sc_unlock(card); LOG_TEST_RET(card->ctx, r, "sc_update_binary() failed"); } p += r; idx += r; bytes_written += r; count -= r; if (r == 0) { sc_unlock(card); LOG_FUNC_RETURN(card->ctx, bytes_written); } } sc_unlock(card); LOG_FUNC_RETURN(card->ctx, bytes_written); } r = card->ops->update_binary(card, idx, buf, count, flags); LOG_FUNC_RETURN(card->ctx, r); } int sc_erase_binary(struct sc_card *card, unsigned int offs, size_t count, unsigned long flags) { int r; assert(card != NULL && card->ops != NULL); sc_log(card->ctx, "called; erase %d bytes from offset %d", count, offs); if (card->ops->erase_binary == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); r = card->ops->erase_binary(card, offs, count, flags); LOG_FUNC_RETURN(card->ctx, r); } 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]; assert(card != NULL && in_path != NULL); 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", 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"); /* Remember file path */ if (file && *file) (*file)->path = *in_path; 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; assert(card != NULL); LOG_FUNC_CALLED(card->ctx); if (card->ops->get_challenge == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); r = card->ops->get_challenge(card, rnd, len); LOG_FUNC_RETURN(card->ctx, r); } int sc_read_record(sc_card_t *card, unsigned int rec_nr, u8 *buf, size_t count, unsigned long flags) { int r; assert(card != NULL); LOG_FUNC_CALLED(card->ctx); if (card->ops->read_record == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); r = card->ops->read_record(card, rec_nr, buf, count, flags); LOG_FUNC_RETURN(card->ctx, r); } int sc_write_record(sc_card_t *card, unsigned int rec_nr, const u8 * buf, size_t count, unsigned long flags) { int r; assert(card != NULL); 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); 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; assert(card != NULL); 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); LOG_FUNC_RETURN(card->ctx, r); } int sc_update_record(sc_card_t *card, unsigned int rec_nr, const u8 * buf, size_t count, unsigned long flags) { int r; assert(card != NULL); LOG_FUNC_CALLED(card->ctx); if (card->ops->update_record == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); r = card->ops->update_record(card, rec_nr, buf, count, flags); LOG_FUNC_RETURN(card->ctx, r); } int sc_delete_record(sc_card_t *card, unsigned int rec_nr) { int r; assert(card != NULL); 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; assert(card != NULL); LOG_FUNC_CALLED(card->ctx); 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; assert(info != NULL); p = (sc_algorithm_info_t *) realloc(card->algorithms, (card->algorithm_count + 1) * sizeof(*info)); if (!p) { if (card->algorithms) free(card->algorithms); card->algorithms = NULL; card->algorithm_count = 0; return SC_ERROR_OUT_OF_MEMORY; } card->algorithms = p; p += card->algorithm_count; card->algorithm_count++; *p = *info; return SC_SUCCESS; } int _sc_card_add_ec_alg(sc_card_t *card, unsigned int key_length, unsigned long flags, unsigned long ext_flags) { sc_algorithm_info_t info; memset(&info, 0, sizeof(info)); info.algorithm = SC_ALGORITHM_EC; info.key_length = key_length; info.flags = flags; info.u._ec.ext_flags = ext_flags; return _sc_card_add_algorithm(card, &info); } static sc_algorithm_info_t * sc_card_find_alg(sc_card_t *card, unsigned int algorithm, unsigned int key_length) { int i; for (i = 0; i < card->algorithm_count; i++) { sc_algorithm_info_t *info = &card->algorithms[i]; if (info->algorithm != algorithm) continue; if (info->key_length != key_length) continue; return info; } return NULL; } sc_algorithm_info_t * sc_card_find_ec_alg(sc_card_t *card, unsigned int key_length) { return sc_card_find_alg(card, SC_ALGORITHM_EC, key_length); } int _sc_card_add_rsa_alg(sc_card_t *card, unsigned int 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; info.u._rsa.exponent = exponent; return _sc_card_add_algorithm(card, &info); } sc_algorithm_info_t * sc_card_find_rsa_alg(sc_card_t *card, unsigned int key_length) { return sc_card_find_alg(card, SC_ALGORITHM_RSA, key_length); } sc_algorithm_info_t * sc_card_find_gostr3410_alg(sc_card_t *card, unsigned int key_length) { return sc_card_find_alg(card, SC_ALGORITHM_GOSTR3410, key_length); } static int match_atr_table(sc_context_t *ctx, struct sc_atr_table *table, struct sc_atr *atr) { u8 *card_atr_bin = atr->value; size_t card_atr_bin_len = atr->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; sc_bin_to_hex(card_atr_bin, card_atr_bin_len, card_atr_hex, sizeof(card_atr_hex), ':'); card_atr_hex_len = strlen(card_atr_hex); sc_log(ctx, "ATR : %s", card_atr_hex); 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_log(ctx, "ATR try : %s", tatr); if (tatr_len != fix_hex_len) { sc_log(ctx, "ignored - wrong length"); continue; } if (matr != NULL) { sc_log(ctx, "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_log(ctx, "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, 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_print_cache(struct sc_card *card) { struct sc_context *ctx = NULL; assert(card != NULL); 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=%i) %s", card->cache.current_df->path.type, card->cache.current_df->path.aid.len, sc_print_path(&card->cache.current_df->path)); } #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]; int temp_len; long rc; HKEY hKey; const char path_delim = '\\'; #else const char path_delim = '/'; #endif assert(card != NULL); ctx = card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); if (!in_module) return sc_card_sm_unload(card); #ifdef _WIN32 if (!module_path) { rc = RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\OpenSC Project\\OpenSC", 0, KEY_QUERY_VALUE, &hKey ); if( rc == ERROR_SUCCESS ) { temp_len = PATH_MAX; rc = RegQueryValueEx( hKey, "SmDir", NULL, NULL, (LPBYTE) temp_path, &temp_len); if( (rc == ERROR_SUCCESS) && (temp_len < PATH_MAX) ) module_path = temp_path; RegCloseKey( hKey ); } } if (!module_path) { rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\OpenSC Project\\OpenSC", 0, KEY_QUERY_VALUE, &hKey ); if( rc == ERROR_SUCCESS ) { temp_len = PATH_MAX; rc = RegQueryValueEx( hKey, "SmDir", NULL, NULL, (LPBYTE) temp_path, &temp_len); if(rc == ERROR_SUCCESS && temp_len < PATH_MAX) module_path = temp_path; RegCloseKey( hKey ); } } #endif sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "SM module '%s' located in '%s'", in_module, module_path); if (module_path) { int sz = strlen(in_module) + strlen(module_path) + 3; 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_MEMORY_FAILURE; 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; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); /* 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", NULL); module_name = scconf_get_str(sm_conf_block, "module_name", NULL); 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"); strncpy(card->sm_ctx.module.filename, module_name, sizeof(card->sm_ctx.module.filename)); strncpy(card->sm_ctx.config_section, sm, sizeof(card->sm_ctx.config_section)); /* allocate resources for the external SM module */ sc_log(ctx, "'module_init' handler %p", card->sm_ctx.module.ops.module_init); if (card->sm_ctx.module.ops.module_init) { module_data = scconf_get_str(sm_conf_block, "module_data", NULL); sc_log(ctx, "module_data '%s'", module_data); rv = card->sm_ctx.module.ops.module_init(ctx, module_data); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot initialize SM module"); } /* initialize SM session in the case of 'APDU TRANSMIT' SM mode */ sm_mode = scconf_get_str(sm_conf_block, "mode", NULL); sc_log(ctx, "SM mode '%s'; 'open' handler %p", sm_mode, card->sm_ctx.ops.open); if (sm_mode && !strcasecmp("Transmit", sm_mode)) { if (!card->sm_ctx.ops.open || !card->sm_ctx.ops.get_sm_apdu || !card->sm_ctx.ops.free_sm_apdu) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "'Transmit' SM asked but not supported by card driver"); 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.13.0/src/libopensc/card-sc-hsm.c0000644000015201777760000006412012057406034015042 00000000000000/* * 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 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" #include "types.h" #include "card-sc-hsm.h" /* 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 */ static 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}, {NULL, NULL, NULL, 0, 0, NULL} }; /* Known ATRs for JavaCards that qualify for SmartCard-HSMs */ static struct sc_atr_table sc_hsm_jc_atrs[] = { /* standard version */ {"3b:f8:13:00:00:81:31:fe:45:4a:43:4f:50:76:32:34:31:b7", NULL, NULL, SC_CARD_TYPE_SC_HSM, 0, NULL}, // JCOP 2.4.1 Default ATR contact based {"3b:88:80:01:4a:43:4f:50:76:32:34:31:5e", NULL, NULL, SC_CARD_TYPE_SC_HSM, 0, NULL}, // JCOP 2.4.1 Default ATR contactless {NULL, NULL, NULL, 0, 0, NULL} }; static int sc_hsm_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { int rv; sc_file_t *file = NULL; if (file_out == NULL) { // Versions before 0.16 of the SmartCard-HSM do not support P2='0C' if (!in_path->len && in_path->aid.len) { sc_log(card->ctx, "Preventing reselection of applet which would clear the security state"); return SC_SUCCESS; } rv = sc_hsm_select_file(card, in_path, &file); if (file != NULL) { sc_file_free(file); } 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 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; } return (*iso_ops->select_file)(card, in_path, file_out); } static int sc_hsm_match_card(struct sc_card *card) { sc_path_t path; int i, r; i = _sc_match_atr(card, sc_hsm_atrs, &card->type); if (i >= 0) return 1; i = _sc_match_atr(card, sc_hsm_jc_atrs, &card->type); if (i < 0) return 0; sc_path_set(&path, SC_PATH_TYPE_DF_NAME, sc_hsm_aid.value, sc_hsm_aid.len, 0, 0); r = sc_hsm_select_file(card, &path, NULL); LOG_TEST_RET(card->ctx, r, "Could not select SmartCard-HSM application"); // Select Applet to be sure return 1; } static int sc_hsm_pin_info(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; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, data->pin_reference); 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) { data->pin1.tries_left = apdu.sw2 & 0xF; r = SC_SUCCESS; } else if (r == SC_ERROR_AUTH_METHOD_BLOCKED) { data->pin1.tries_left = 0; r = SC_SUCCESS; } LOG_TEST_RET(card->ctx, r, "Check SW error"); if (tries_left != NULL) { *tries_left = data->pin1.tries_left; } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } 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; if (data->cmd == SC_PIN_CMD_GET_INFO) { return sc_hsm_pin_info(card, data, tries_left); } if (data->pin_reference == 0x88) { // Save SO PIN for later use in init pin memcpy(priv->initpw, data->pin1.data, sizeof(priv->initpw)); return SC_SUCCESS; } return (*iso_ops->pin_cmd)(card, data, tries_left); } 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 recvbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 cmdbuff[4]; int r; if (idx > 0xffff) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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 <= (card->max_recv_size > 0 ? card->max_recv_size : 256)); sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xB1, 0x00, 0x00); apdu.data = cmdbuff; apdu.datalen = 4; apdu.lc = 4; apdu.le = count; apdu.resplen = count; apdu.resp = recvbuf; 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"); } memcpy(buf, recvbuf, apdu.resplen); LOG_FUNC_RETURN(ctx, apdu.resplen); } static int sc_hsm_update_binary(sc_card_t *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) { sc_context_t *ctx = card->ctx; sc_apdu_t apdu; u8 recvbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 *cmdbuff, *p; size_t len; int r; if (idx > 0xffff) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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); } p = cmdbuff; *p++ = 0x54; *p++ = 0x02; *p++ = (idx >> 8) & 0xFF; *p++ = idx & 0xFF; *p++ = 0x53; if (count < 128) { *p++ = count; len = 6; } else if (count < 256) { *p++ = 0x81; *p++ = count; len = 7; } else { *p++ = 0x82; *p++ = count >> 8; *p++ = count & 0xFF; len = 8; } memcpy(p, buf, count); len += count; sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xD7, 0x00, 0x00); apdu.data = cmdbuff; apdu.datalen = len; apdu.lc = len; r = sc_transmit_apdu(card, &apdu); free(cmdbuff); LOG_TEST_RET(ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, r, "Check SW error"); LOG_FUNC_RETURN(ctx, 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"); memcpy(buf, recvbuf, buflen); LOG_FUNC_RETURN(card->ctx, apdu.resplen); } static int sc_hsm_create_file(sc_card_t *card, sc_file_t *file) { sc_context_t *ctx = card->ctx; sc_apdu_t apdu; u8 cmdbuff[] = { 0x54, 0x02, 0x00, 0x00, 0x53, 0x00 }; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD7, file->id >> 8, file->id & 0xFF); apdu.data = cmdbuff; apdu.datalen = sizeof(cmdbuff); apdu.lc = sizeof(cmdbuff); 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_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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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 { priv->algorithm = ALGO_RSA_PKCS1; } } 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->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_RAW) { if (env->operation == SC_SEC_OPERATION_DERIVE) { priv->algorithm = ALGO_EC_DH; } else { 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) { int fieldsizebytes, i, r; const u8 *body, *tag; size_t bodylen, taglen; // Determine field size from length of signature if (datalen <= 58) { // 192 bit curve = 24 * 2 + 10 byte maximum DER signature fieldsizebytes = 24; } else if (datalen <= 66) { // 224 bit curve = 28 * 2 + 10 byte maximum DER signature fieldsizebytes = 28; } else if (datalen <= 74) { // 256 bit curve = 32 * 2 + 10 byte maximum DER signature fieldsizebytes = 32; } else if (datalen <= 90) { // 320 bit curve = 40 * 2 + 10 byte maximum DER signature fieldsizebytes = 40; } else { fieldsizebytes = 64; } sc_log(card->ctx, "Field size %d, signature buffer size %d", fieldsizebytes, outlen); if (outlen < (fieldsizebytes * 2)) { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "output too small for EC signature"); } memset(out, 0, outlen); // Copied from card-piv.c. Thanks body = sc_asn1_find_tag(card->ctx, data, datalen, 0x30, &bodylen); for (i = 0; i<2; i++) { if (body) { tag = sc_asn1_find_tag(card->ctx, body, bodylen, 0x02, &taglen); if (tag) { bodylen -= taglen - (tag - body); body = tag + taglen; if (taglen > fieldsizebytes) { /* drop leading 00 if present */ if (*tag != 0x00) { r = SC_ERROR_INVALID_DATA; goto err; } tag++; taglen--; } memcpy(out + fieldsizebytes*i + fieldsizebytes - taglen , tag, taglen); } else { r = SC_ERROR_INVALID_DATA; goto err; } } else { r = SC_ERROR_INVALID_DATA; goto err; } } r = 2 * fieldsizebytes; err: 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[SC_MAX_APDU_BUFFER_SIZE]; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; assert(card != NULL && data != NULL && out != NULL); if (priv->env == NULL) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_FOUND); } sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x68, priv->env->key_ref[0], priv->algorithm); apdu.cla = 0x80; 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(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); if (len < 0) { LOG_FUNC_RETURN(card->ctx, len); } } else { len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); } LOG_FUNC_RETURN(card->ctx, len); } LOG_FUNC_RETURN(card->ctx, 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[SC_MAX_APDU_BUFFER_SIZE]; sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; assert(card != NULL && crgram != NULL && out != NULL); LOG_FUNC_CALLED(card->ctx); sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x62, priv->env->key_ref[0], priv->algorithm); apdu.cla = 0x80; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; 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) { 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, len); } else { len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); LOG_FUNC_RETURN(card->ctx, 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) { return SC_ERROR_OBJECT_NOT_FOUND; } serial->len = strlen(priv->serialno); strncpy(serial->value, priv->serialno, sizeof(serial->value)); 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; int r, i; sc_apdu_t apdu; u8 ibuff[50], *p; LOG_FUNC_CALLED(card->ctx); p = ibuff; *p++ = 0x80; // Options *p++ = 0x02; memcpy(p, params->options, 2); p += 2; *p++ = 0x81; // User PIN *p++ = 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; } 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"); 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[SC_MAX_EXT_APDU_BUFFER_SIZE]; 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"); 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); } 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; u8 status[MAX_EXT_APDU_LENGTH]; int r; LOG_FUNC_CALLED(card->ctx); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_EXT, 0x74, params->key_id, 0x93); apdu.cla = 0x80; apdu.lc = params->wrapped_key_length; apdu.data = params->wrapped_key; apdu.datalen = params->wrapped_key_length; r = sc_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 sc_hsm_init_token(sc_card_t *card, sc_cardctl_pkcs11_init_token_t *params) { sc_context_t *ctx = card->ctx; int r, i; sc_apdu_t apdu; u8 ibuff[50], *p; LOG_FUNC_CALLED(card->ctx); if (params->so_pin_len != 16) { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "SO PIN wrong length (!=16)"); } p = ibuff; *p++ = 0x80; // Options *p++ = 0x02; *p++ = 0x00; *p++ = 0x01; *p++ = 0x81; // User PIN *p++ = 0x06; // Default value, later changed with C_InitPIN // We use only 6 of the 16 bytes init password for the initial user PIN memcpy(p, params->so_pin, 6); p += 6; *p++ = 0x82; // Initialization code *p++ = 0x08; memset(p, 0, 8); for (i = 0; i < 16; i++) { *p <<= 4; *p |= params->so_pin[i] & 0xf; if (i & 1) p++; } *p++ = 0x91; // User PIN retry counter *p++ = 0x01; *p++ = 0x03; 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"); LOG_FUNC_RETURN(card->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; // We use only 6 of the 8 bytes init password for the initial user PIN memcpy(p, priv->initpw, 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->initpw, 0, sizeof(priv->initpw)); 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) { sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; u8 rbuf[1024]; 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); } return SC_ERROR_NOT_SUPPORTED; } static int sc_hsm_init(struct sc_card *card) { sc_hsm_private_data_t *priv; int flags,ext_flags; LOG_FUNC_CALLED(card->ctx); priv = calloc(1, sizeof(sc_hsm_private_data_t)); if (!priv) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); card->drv_data = priv; flags = SC_ALGORITHM_RSA_RAW|SC_ALGORITHM_ONBOARD_KEY_GEN; _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); flags = SC_ALGORITHM_ECDSA_RAW| SC_ALGORITHM_ECDSA_HASH_NONE| SC_ALGORITHM_ECDSA_HASH_SHA1| SC_ALGORITHM_ECDSA_HASH_SHA224| SC_ALGORITHM_ECDSA_HASH_SHA256| SC_ALGORITHM_ONBOARD_KEY_GEN; ext_flags = SC_ALGORITHM_EXT_EC_F_P| SC_ALGORITHM_EXT_EC_ECPARAMETERS| SC_ALGORITHM_EXT_EC_UNCOMPRESES| SC_ALGORITHM_ONBOARD_KEY_GEN; _sc_card_add_ec_alg(card, 192, flags, ext_flags); _sc_card_add_ec_alg(card, 224, flags, ext_flags); _sc_card_add_ec_alg(card, 256, flags, ext_flags); _sc_card_add_ec_alg(card, 320, flags, ext_flags); card->caps |= SC_CARD_CAP_RNG|SC_CARD_CAP_APDU_EXT; card->max_send_size = 1431; // 1439 buffer size - 8 byte TLV because of odd ins in UPDATE BINARY 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; if (priv->serialno) { free(priv->serialno); } 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.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; /* 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.13.0/src/libopensc/pkcs15-itacns.c0000644000015201777760000005451012057406034015330 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "pkcs15.h" #include "log.h" #include "cards.h" #include "itacns.h" #include #include #include #include "common/compat_strlcpy.h" #include "common/compat_strlcat.h" #ifdef ENABLE_OPENSSL #include #endif int sc_pkcs15emu_itacns_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); static const char path_serial[] = "10001003"; /* Manufacturers */ const char * itacns_mask_manufacturers[] = { "Unknown", "Kaitech", "Gemplus", "Ghirlanda", "Giesecke & Devrient", "Oberthur Card Systems", "Orga", "Axalto", "Siemens", "STIncard", "GEP", "EPS Corp", "Athena" }; 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 void set_string(char **strp, const char *value) { if (*strp) free(*strp); *strp = value ? strdup(value) : NULL; } static int loadFile(const sc_pkcs15_card_t *p15card, const sc_path_t *path, u8 *buf, const size_t buflen) { int sc_res; SC_FUNC_CALLED(p15card->card->ctx, 1); sc_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) { int r; /* const char *label = "Certificate"; */ sc_pkcs15_cert_info_t info; sc_pkcs15_object_t obj; #ifdef ENABLE_OPENSSL X509 *x509; #endif sc_pkcs15_cert_t *cert; SC_FUNC_CALLED(p15card->card->ctx, 1); if(type != SC_PKCS15_TYPE_CERT_X509) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "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)); obj.flags = obj_flags; r = sc_pkcs15emu_add_x509_cert(p15card, &obj, &info); SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, "Could not add X.509 certificate"); /* If we have OpenSSL, read keyUsage */ #ifdef ENABLE_OPENSSL r = sc_pkcs15_read_certificate(p15card, &info, &cert); SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, "Could not read X.509 certificate"); { const u8 *throwaway = cert->data.value; x509 = d2i_X509(NULL, &throwaway, cert->data.len); } sc_pkcs15_free_certificate(cert); if (!x509) return SC_SUCCESS; X509_check_purpose(x509, -1, 0); if(x509->ex_flags & EXFLAG_KUSAGE) { *ext_info_ok = 1; *key_usage = x509->ex_kusage; *x_key_usage = x509->ex_xkusage; } OPENSSL_free(x509); return SC_SUCCESS; #else /* ENABLE_OPENSSL */ return SC_SUCCESS; #endif /* ENABLE_OPENSSL */ } static int itacns_add_pubkey(sc_pkcs15_card_t *p15card, const sc_path_t *path, const sc_pkcs15_id_t *id, const char *label, int usage, int ref, int obj_flags, int *modulus_len_out) { int r; sc_pkcs15_pubkey_info_t info; sc_pkcs15_object_t obj; SC_FUNC_CALLED(p15card->card->ctx, 1); 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; /* * This is hard-coded, unless unforeseen versions of the CNS * turn up sometime. */ info.modulus_length = 1024; *modulus_len_out = info.modulus_length; r = sc_pkcs15emu_add_rsa_pubkey(p15card, &obj, &info); SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, "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, 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; SC_FUNC_CALLED(p15card->card->ctx, 1); if(type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "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; info.access_flags = SC_PKCS15_PRKEY_ACCESS_SENSITIVE | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE | SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE | SC_PKCS15_PRKEY_ACCESS_LOCAL; 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, 1); 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; 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+1); hex[len] = '\0'; res = strtol(hex, &end, 0x10); if(end != (char*)&hex[len]) return -1; return res; } static int get_name_from_EF_DatiPersonali(unsigned char *EFdata, char name[], int name_len) { /* * Bytes 0-5 contain the ASCII encoding of the following TLV * strcture's total size, in base 16. */ const unsigned int EF_personaldata_maxlen = 400; const unsigned int tlv_length_size = 6; char *file = (char*)&EFdata[tlv_length_size]; int file_size = hextoint((char*)EFdata, tlv_length_size); 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); if((field_size < 0) || (field_size+i > file_size)) return -1; i += 2; if(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; snprintf(name, name_len, "%s %s", fields[f_first_name].value, 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; 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); } /* * 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)); strcpy(dinfo.app_label, "EF_DatiPersonali"); /* Find EF_DatiPersonali */ rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_DATA_OBJECT, objs, 32); if(rv < 0) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Could not find EF_DatiPersonali: " "keeping generic card name"); return SC_SUCCESS; } rv = sc_pkcs15_read_data_object(p15card, cinfo, &p15_personaldata); if (rv) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Could not read EF_DatiPersonali: " "keeping generic card name"); } { char fullname[160]; if(get_name_from_EF_DatiPersonali(p15_personaldata->data, fullname, sizeof(fullname))) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Could not parse EF_DatiPersonali: " "keeping generic card name"); sc_pkcs15_free_data_object(p15_personaldata); return SC_SUCCESS; } set_string(&p15card->tokeninfo->label, fullname); } 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) { int r; sc_path_t path; sc_path_t *private_path = NULL; char pinlabel[16]; int fake_puk_authid, pin_flags; /* This is hard-coded, for the time being. */ int modulus_length = 1024; /* Public key; not really needed */ /* FIXME: set usage according to the certificate. */ if (pubkey_path) { sc_format_path(pubkey_path, &path); r = itacns_add_pubkey(p15card, &path, cert_id, label, pubkey_usage_flags, sec_env, 0, &modulus_length); SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, "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_length, prkey_usage_flags, private_path, sec_env, cert_id, SC_PKCS15_CO_FLAG_PRIVATE); SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, "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); SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, 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, xku; 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_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "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]; r = loadFile(p15card, &path, certlen, sizeof(certlen)); SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, "Could not read certificate file"); path.index = cert_offset; path.count = (certlen[1] << 8) + certlen[2]; /* If those bytes are 00, then we are probably dealign 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); if (r == SC_ERROR_INVALID_ASN1_OBJECT) return 0; SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, 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, 1); 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); SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, "Could not add CNS1"); certificate_count += found_certs; /* Did we find anything? */ if (certificate_count == 0) sc_debug(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE, "Warning: no certificates found!"); /* Back to Master File */ sc_format_path("3F00", &path); r = sc_select_file(p15card->card, &path, NULL); SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, "Could not select master file again"); return r; } int sc_pkcs15emu_itacns_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts) { sc_card_t *card = p15card->card; SC_FUNC_CALLED(card->ctx, 1); /* Check card */ if (!(opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)) { if (! ( (card->type > SC_CARD_TYPE_ITACNS_BASE && card->type < SC_CARD_TYPE_ITACNS_BASE + 1000) || card->type == SC_CARD_TYPE_CARDOS_CIE_V1) ) return SC_ERROR_WRONG_CARD; } /* Init card */ return itacns_init(p15card); } opensc-0.13.0/src/libopensc/pkcs15-esteid.c0000644000015201777760000001770012057406034015324 00000000000000/* * PKCS15 emulation layer for EstEID card. * * Copyright (C) 2004, Martin Paljak * Copyright (C) 2004, Bud P. Bruegger * Copyright (C) 2004, Antonino Iacono * Copyright (C) 2003, Olaf Kirch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #ifdef ENABLE_OPENSSL #include #endif #include "common/compat_strlcpy.h" #include "common/compat_strlcat.h" #include "internal.h" #include "pkcs15.h" #include "esteid.h" int sc_pkcs15emu_esteid_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); static void set_string (char **strp, const char *value) { if (*strp) free (*strp); *strp = value ? strdup (value) : NULL; } int select_esteid_df (sc_card_t * card) { int r; sc_path_t tmppath; sc_format_path ("3F00EEEE", &tmppath); r = sc_select_file (card, &tmppath, NULL); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "esteid select DF failed"); return r; } static int sc_pkcs15emu_esteid_init (sc_pkcs15_card_t * p15card) { sc_card_t *card = p15card->card; unsigned char buff[128]; int r, i; sc_path_t tmppath; set_string (&p15card->tokeninfo->label, "ID-kaart"); set_string (&p15card->tokeninfo->manufacturer_id, "AS Sertifitseerimiskeskus"); /* Select application directory */ sc_format_path ("3f00eeee5044", &tmppath); r = sc_select_file (card, &tmppath, NULL); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "select esteid PD failed"); /* read the serial (document number) */ r = sc_read_record (card, SC_ESTEID_PD_DOCUMENT_NR, buff, sizeof(buff), SC_RECORD_BY_REC_NR); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "read document number failed"); buff[r] = '\0'; set_string (&p15card->tokeninfo->serial_number, (const char *) buff); p15card->tokeninfo->flags = SC_PKCS15_TOKEN_PRN_GENERATION | SC_PKCS15_TOKEN_EID_COMPLIANT | SC_PKCS15_TOKEN_READONLY; /* add certificates */ for (i = 0; i < 2; i++) { static const char *esteid_cert_names[2] = { "Isikutuvastus", "Allkirjastamine"}; static char const *esteid_cert_paths[2] = { "3f00eeeeaace", "3f00eeeeddce"}; static int esteid_cert_ids[2] = {1, 2}; struct sc_pkcs15_cert_info cert_info; struct sc_pkcs15_object cert_obj; memset(&cert_info, 0, sizeof(cert_info)); memset(&cert_obj, 0, sizeof(cert_obj)); cert_info.id.value[0] = esteid_cert_ids[i]; cert_info.id.len = 1; sc_format_path(esteid_cert_paths[i], &cert_info.path); strlcpy(cert_obj.label, esteid_cert_names[i], sizeof(cert_obj.label)); r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); if (r < 0) return SC_ERROR_INTERNAL; #ifdef ENABLE_OPENSSL if (i == 0) { BIO *mem = NULL; X509 *x509 = NULL; sc_pkcs15_cert_t *cert; char cardholder_name[64]; unsigned char *tmp = NULL; r = sc_pkcs15_read_certificate(p15card, &cert_info, &cert); if (r == SC_SUCCESS) { mem = BIO_new_mem_buf(cert->data.value, cert->data.len); if (!mem) return SC_ERROR_INTERNAL; x509 = d2i_X509_bio(mem, NULL); BIO_free(mem); if (!x509) return SC_ERROR_INTERNAL; r = X509_NAME_get_index_by_NID(X509_get_subject_name(x509), NID_commonName, -1); if (r >= 0) { X509_NAME_ENTRY *ne; ASN1_STRING *a_str; ne = X509_NAME_get_entry(X509_get_subject_name(x509), r); if (!ne) { X509_free(x509); return SC_ERROR_INTERNAL; } a_str = X509_NAME_ENTRY_get_data(ne); if (!a_str) { X509_free(x509); return SC_ERROR_INTERNAL; } r = ASN1_STRING_to_UTF8(&tmp, a_str); if (r > 0) { if ((unsigned)r > sizeof(cardholder_name) - 1) r = sizeof(cardholder_name) -1; memcpy(cardholder_name, tmp, r); cardholder_name[r] = '\0'; set_string(&p15card->tokeninfo->label, cardholder_name); OPENSSL_free(tmp); } } X509_free(x509); } } #endif } /* the file with key pin info (tries left) */ sc_format_path ("3f000016", &tmppath); r = sc_select_file (card, &tmppath, NULL); if (r < 0) return SC_ERROR_INTERNAL; /* add pins */ for (i = 0; i < 3; i++) { unsigned char tries_left; static const char *esteid_pin_names[3] = { "PIN1", "PIN2", "PUK" }; static const int esteid_pin_min[3] = {4, 5, 8}; static const int esteid_pin_ref[3] = {1, 2, 0}; static const int esteid_pin_authid[3] = {1, 2, 3}; static const int esteid_pin_flags[3] = {0, 0, SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN}; struct sc_pkcs15_auth_info pin_info; struct sc_pkcs15_object pin_obj; memset(&pin_info, 0, sizeof(pin_info)); memset(&pin_obj, 0, sizeof(pin_obj)); /* read the number of tries left for the PIN */ r = sc_read_record (card, i + 1, buff, sizeof(buff), SC_RECORD_BY_REC_NR); if (r < 0) return SC_ERROR_INTERNAL; tries_left = buff[5]; pin_info.auth_id.len = 1; pin_info.auth_id.value[0] = esteid_pin_authid[i]; pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; pin_info.attrs.pin.reference = esteid_pin_ref[i]; pin_info.attrs.pin.flags = esteid_pin_flags[i]; pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; pin_info.attrs.pin.min_length = esteid_pin_min[i]; pin_info.attrs.pin.stored_length = 12; pin_info.attrs.pin.max_length = 12; pin_info.attrs.pin.pad_char = '\0'; pin_info.tries_left = (int)tries_left; pin_info.max_tries = 3; strlcpy(pin_obj.label, esteid_pin_names[i], sizeof(pin_obj.label)); pin_obj.flags = esteid_pin_flags[i]; /* Link normal PINs with PUK */ if (i < 2) { pin_obj.auth_id.len = 1; pin_obj.auth_id.value[0] = 3; } r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); if (r < 0) return SC_ERROR_INTERNAL; } /* add private keys */ for (i = 0; i < 2; i++) { static int prkey_pin[2] = {1, 2}; static int prkey_usage[2] = { SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_SIGN, SC_PKCS15_PRKEY_USAGE_NONREPUDIATION}; static const char *prkey_name[2] = { "Isikutuvastus", "Allkirjastamine"}; struct sc_pkcs15_prkey_info prkey_info; struct sc_pkcs15_object prkey_obj; memset(&prkey_info, 0, sizeof(prkey_info)); memset(&prkey_obj, 0, sizeof(prkey_obj)); prkey_info.id.len = 1; prkey_info.id.value[0] = prkey_pin[i]; prkey_info.usage = prkey_usage[i]; prkey_info.native = 1; prkey_info.key_reference = i + 1; if (card->type == SC_CARD_TYPE_MCRD_ESTEID_V30) prkey_info.modulus_length = 2048; else prkey_info.modulus_length = 1024; strlcpy(prkey_obj.label, prkey_name[i], sizeof(prkey_obj.label)); prkey_obj.auth_id.len = 1; prkey_obj.auth_id.value[0] = prkey_pin[i]; prkey_obj.user_consent = 0; prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); if (r < 0) return SC_ERROR_INTERNAL; } return SC_SUCCESS; } static int esteid_detect_card(sc_pkcs15_card_t *p15card) { if (is_esteid_card(p15card->card)) return SC_SUCCESS; else return SC_ERROR_WRONG_CARD; } int sc_pkcs15emu_esteid_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts) { if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) return sc_pkcs15emu_esteid_init(p15card); else { int r = esteid_detect_card(p15card); if (r) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_esteid_init(p15card); } } opensc-0.13.0/src/libopensc/pace.h0000644000015201777760000000530612057406034013657 00000000000000/* * opensc.h: PACE library header file * * Copyright (C) ???? 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.13.0/src/libopensc/errors.h0000644000015201777760000001146412057406034014265 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 /* 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 /* 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.13.0/src/libopensc/pkcs15-pubkey.c0000644000015201777760000013132212057406034015343 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #include "internal.h" #include "asn1.h" #include "pkcs15.h" #ifdef ENABLE_OPENSSL #include #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x10000000L #ifndef OPENSSL_NO_EC #include #endif #endif #endif #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 4 static const struct sc_asn1_entry c_asn1_eckey_attr[C_ASN1_ECKEY_ATTR_SIZE] = { { "value", SC_ASN1_CHOICE, 0, 0, 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 }, { 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_DSAKEY_ATTR_SIZE 2 static const struct sc_asn1_entry c_asn1_dsakey_attr[C_ASN1_DSAKEY_ATTR_SIZE] = { { "value", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_DSA_TYPE_ATTR_SIZE 2 static const struct sc_asn1_entry c_asn1_dsa_type_attr[C_ASN1_DSA_TYPE_ATTR_SIZE] = { { "publicDSAKeyAttributes", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_GOST3410KEY_ATTR_SIZE 5 static const struct sc_asn1_entry c_asn1_gostr3410key_attr[C_ASN1_GOST3410KEY_ATTR_SIZE] = { { "value", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { "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 5 static const struct sc_asn1_entry c_asn1_pubkey_choice[C_ASN1_PUBKEY_CHOICE_SIZE] = { { "publicRSAKey", SC_ASN1_PKCS15_OBJECT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { "publicDSAKey", SC_ASN1_PKCS15_OBJECT, 2 | SC_ASN1_CTX | SC_ASN1_CONS, 0, NULL, NULL }, { "publicGOSTR3410Key", SC_ASN1_PKCS15_OBJECT, 4 | SC_ASN1_CTX | SC_ASN1_CONS, 0, NULL, NULL }, { "publicECKey", SC_ASN1_PKCS15_OBJECT, 0 | SC_ASN1_CTX | SC_ASN1_CONS, 0, NULL, NULL }, /*TODO: -DEE not clear EC is needed here as look like it is for pukdf */ { 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_decode_pukdf_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_pubkey_info info; int r, gostr3410_params[3]; struct sc_pkcs15_keyinfo_gostparams *keyinfo_gostparams; size_t usage_len = sizeof(info.usage); size_t af_len = sizeof(info.access_flags); 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_dsakey_attr[C_ASN1_DSAKEY_ATTR_SIZE]; struct sc_asn1_entry asn1_dsa_type_attr[C_ASN1_DSA_TYPE_ATTR_SIZE]; struct sc_asn1_entry asn1_gostr3410key_attr[C_ASN1_GOST3410KEY_ATTR_SIZE]; struct sc_asn1_entry asn1_gostr3410_type_attr[C_ASN1_GOST3410_TYPE_ATTR_SIZE]; struct sc_asn1_entry asn1_pubkey_choice[C_ASN1_PUBKEY_CHOICE_SIZE]; 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 dsakey_obj = { obj, asn1_com_key_attr, asn1_com_pubkey_attr, asn1_dsa_type_attr }; struct sc_asn1_pkcs15_object gostr3410key_obj = { obj, asn1_com_key_attr, asn1_com_pubkey_attr, asn1_gostr3410_type_attr }; 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_dsa_type_attr, asn1_dsa_type_attr); sc_copy_asn1_entry(c_asn1_dsakey_attr, asn1_dsakey_attr); sc_copy_asn1_entry(c_asn1_gostr3410_type_attr, asn1_gostr3410_type_attr); sc_copy_asn1_entry(c_asn1_gostr3410key_attr, asn1_gostr3410key_attr); sc_copy_asn1_entry(c_asn1_com_pubkey_attr, asn1_com_pubkey_attr); sc_copy_asn1_entry(c_asn1_com_key_attr, asn1_com_key_attr); sc_format_asn1_entry(asn1_com_pubkey_attr + 0, &info.subject.value, &info.subject.len, 0); sc_format_asn1_entry(asn1_pubkey_choice + 0, &rsakey_obj, NULL, 0); sc_format_asn1_entry(asn1_pubkey_choice + 1, &dsakey_obj, NULL, 0); sc_format_asn1_entry(asn1_pubkey_choice + 2, &gostr3410key_obj, NULL, 0); sc_format_asn1_entry(asn1_pubkey_choice + 3, &eckey_obj, NULL, 0); sc_format_asn1_entry(asn1_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_eckey_attr + 1, &info.field_length, NULL, 0); sc_format_asn1_entry(asn1_dsa_type_attr + 0, asn1_dsakey_attr, NULL, 0); sc_format_asn1_entry(asn1_dsakey_attr + 0, &info.path, NULL, 0); sc_format_asn1_entry(asn1_gostr3410_type_attr + 0, asn1_gostr3410key_attr, NULL, 0); sc_format_asn1_entry(asn1_gostr3410key_attr + 0, &info.path, NULL, 0); sc_format_asn1_entry(asn1_gostr3410key_attr + 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 */ memset(&info, 0, sizeof(info)); info.key_reference = -1; info.native = 1; memset(gostr3410_params, 0, sizeof(gostr3410_params)); r = sc_asn1_decode(ctx, asn1_pubkey, *buf, *buflen, buf, buflen); if (r == SC_ERROR_ASN1_END_OF_CONTENTS) return r; LOG_TEST_RET(ctx, r, "ASN.1 decoding failed"); if (asn1_pubkey_choice[0].flags & SC_ASN1_PRESENT) { obj->type = SC_PKCS15_TYPE_PUBKEY_RSA; } else if (asn1_pubkey_choice[2].flags & SC_ASN1_PRESENT) { obj->type = SC_PKCS15_TYPE_PUBKEY_GOSTR3410; assert(info.modulus_length == 0); info.modulus_length = SC_PKCS15_GOSTR3410_KEYSIZE; assert(info.params.len == 0); info.params.len = sizeof(struct sc_pkcs15_keyinfo_gostparams); info.params.data = malloc(info.params.len); if (info.params.data == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); assert(sizeof(*keyinfo_gostparams) == info.params.len); keyinfo_gostparams = info.params.data; keyinfo_gostparams->gostr3410 = (unsigned int)gostr3410_params[0]; keyinfo_gostparams->gostr3411 = (unsigned int)gostr3410_params[1]; keyinfo_gostparams->gost28147 = (unsigned int)gostr3410_params[2]; } else if (asn1_pubkey_choice[3].flags & SC_ASN1_PRESENT) { obj->type = SC_PKCS15_TYPE_PUBKEY_EC; } else { obj->type = SC_PKCS15_TYPE_PUBKEY_DSA; } if (!p15card->app || !p15card->app->ddo.aid.len) { r = sc_pkcs15_make_absolute_path(&p15card->file_app->path, &info.path); if (r < 0) { sc_pkcs15_free_key_params(&info.params); return r; } } 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 = malloc(sizeof(info)); if (obj->data == NULL) { sc_pkcs15_free_key_params(&info.params); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(obj->data, &info, sizeof(info)); return 0; } int sc_pkcs15_encode_pukdf_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_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_dsakey_attr[C_ASN1_DSAKEY_ATTR_SIZE]; struct sc_asn1_entry asn1_dsa_type_attr[C_ASN1_DSA_TYPE_ATTR_SIZE]; struct sc_asn1_entry asn1_gostr3410key_attr[C_ASN1_GOST3410KEY_ATTR_SIZE]; struct sc_asn1_entry asn1_gostr3410_type_attr[C_ASN1_GOST3410_TYPE_ATTR_SIZE]; struct sc_asn1_entry asn1_pubkey_choice[C_ASN1_PUBKEY_CHOICE_SIZE]; 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 dsakey_obj = { (struct sc_pkcs15_object *) obj, asn1_com_key_attr, asn1_com_pubkey_attr, asn1_dsa_type_attr }; struct sc_asn1_pkcs15_object gostr3410key_obj = { (struct sc_pkcs15_object *) obj, asn1_com_key_attr, asn1_com_pubkey_attr, asn1_gostr3410_type_attr }; struct sc_pkcs15_keyinfo_gostparams *keyinfo_gostparams; int r; size_t af_len, usage_len; 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_dsa_type_attr, asn1_dsa_type_attr); sc_copy_asn1_entry(c_asn1_dsakey_attr, asn1_dsakey_attr); sc_copy_asn1_entry(c_asn1_gostr3410_type_attr, asn1_gostr3410_type_attr); sc_copy_asn1_entry(c_asn1_gostr3410key_attr, asn1_gostr3410key_attr); sc_copy_asn1_entry(c_asn1_com_pubkey_attr, asn1_com_pubkey_attr); sc_copy_asn1_entry(c_asn1_com_key_attr, asn1_com_key_attr); 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 || !obj->content.value) sc_format_asn1_entry(asn1_rsakey_value_choice + 0, &pubkey->path, NULL, 1); else sc_format_asn1_entry(asn1_rsakey_value_choice + 1, obj->content.value, (void *)&obj->content.len, 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_DSA: sc_format_asn1_entry(asn1_pubkey_choice + 1, &dsakey_obj, NULL, 1); sc_format_asn1_entry(asn1_dsa_type_attr + 0, asn1_dsakey_attr, NULL, 1); sc_format_asn1_entry(asn1_dsakey_attr + 0, &pubkey->path, NULL, 1); break; case SC_PKCS15_TYPE_PUBKEY_GOSTR3410: sc_format_asn1_entry(asn1_pubkey_choice + 2, &gostr3410key_obj, NULL, 1); sc_format_asn1_entry(asn1_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: /* MyEID is a PKCS15 card with ECC */ sc_format_asn1_entry(asn1_pubkey_choice + 3, &eckey_obj, NULL, 1); sc_format_asn1_entry(asn1_ec_type_attr + 0, asn1_eckey_attr, NULL, 1); if (pubkey->path.len || !obj->content.value) sc_format_asn1_entry(asn1_eckey_value_choice + 0, &pubkey->path, NULL, 1); else sc_format_asn1_entry(asn1_eckey_value_choice + 1, obj->content.value, (void *)&obj->content.len, 1); sc_format_asn1_entry(asn1_eckey_attr + 0, asn1_eckey_value_choice, NULL, 1); sc_format_asn1_entry(asn1_eckey_attr + 1, &pubkey->field_length, 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)); 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_DSA_PUB_COEFFICIENTS_SIZE 5 static struct sc_asn1_entry c_asn1_dsa_pub_coefficients[C_ASN1_DSA_PUB_COEFFICIENTS_SIZE] = { { "publicKey",SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC|SC_ASN1_UNSIGNED, NULL, NULL }, { "paramP", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC|SC_ASN1_UNSIGNED, NULL, NULL }, { "paramQ", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC|SC_ASN1_UNSIGNED, NULL, NULL }, { "paramG", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC|SC_ASN1_UNSIGNED, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL }, }; #define C_ASN1_GOSTR3410_PUB_COEFFICIENTS_SIZE 2 static struct sc_asn1_entry c_asn1_gostr3410_pub_coefficients[C_ASN1_GOSTR3410_PUB_COEFFICIENTS_SIZE] = { { "xy", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_EC_POINTQ_SIZE 2 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 } }; 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; 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"); return 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; 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"); return 0; } int sc_pkcs15_decode_pubkey_dsa(sc_context_t *ctx, struct sc_pkcs15_pubkey_dsa *key, const u8 *buf, size_t buflen) { struct sc_asn1_entry asn1_public_key[C_ASN1_PUBLIC_KEY_SIZE]; struct sc_asn1_entry asn1_dsa_pub_coefficients[C_ASN1_DSA_PUB_COEFFICIENTS_SIZE]; int r; sc_copy_asn1_entry(c_asn1_public_key, asn1_public_key); sc_copy_asn1_entry(c_asn1_dsa_pub_coefficients, asn1_dsa_pub_coefficients); sc_format_asn1_entry(asn1_public_key + 0, asn1_dsa_pub_coefficients, NULL, 1); sc_format_asn1_entry(asn1_dsa_pub_coefficients + 0, &key->pub.data, &key->pub.len, 0); sc_format_asn1_entry(asn1_dsa_pub_coefficients + 1, &key->g.data, &key->g.len, 0); sc_format_asn1_entry(asn1_dsa_pub_coefficients + 2, &key->p.data, &key->p.len, 0); sc_format_asn1_entry(asn1_dsa_pub_coefficients + 3, &key->q.data, &key->q.len, 0); r = sc_asn1_decode(ctx, asn1_public_key, buf, buflen, NULL, NULL); LOG_TEST_RET(ctx, r, "ASN.1 decoding failed"); return 0; } int sc_pkcs15_encode_pubkey_dsa(sc_context_t *ctx, struct sc_pkcs15_pubkey_dsa *key, u8 **buf, size_t *buflen) { struct sc_asn1_entry asn1_public_key[C_ASN1_PUBLIC_KEY_SIZE]; struct sc_asn1_entry asn1_dsa_pub_coefficients[C_ASN1_DSA_PUB_COEFFICIENTS_SIZE]; int r; sc_copy_asn1_entry(c_asn1_public_key, asn1_public_key); sc_copy_asn1_entry(c_asn1_dsa_pub_coefficients, asn1_dsa_pub_coefficients); sc_format_asn1_entry(asn1_public_key + 0, asn1_dsa_pub_coefficients, NULL, 1); sc_format_asn1_entry(asn1_dsa_pub_coefficients + 0, key->pub.data, &key->pub.len, 1); sc_format_asn1_entry(asn1_dsa_pub_coefficients + 1, key->g.data, &key->g.len, 1); sc_format_asn1_entry(asn1_dsa_pub_coefficients + 2, key->p.data, &key->p.len, 1); sc_format_asn1_entry(asn1_dsa_pub_coefficients + 3, key->q.data, &key->q.len, 1); r = sc_asn1_encode(ctx, asn1_public_key, buf, buflen); LOG_TEST_RET(ctx, r, "ASN.1 encoding failed"); return 0; } 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}}; 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; return 0; } 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 a octet string. * Thus we will just copy the string. * But to get the field length we decode it. */ 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; size_t ecpoint_len; struct sc_asn1_entry asn1_ec_pointQ[C_ASN1_EC_POINTQ_SIZE]; sc_copy_asn1_entry(c_asn1_ec_pointQ, asn1_ec_pointQ); sc_format_asn1_entry(asn1_ec_pointQ + 0, &ecpoint_data, &ecpoint_len, 1); r = sc_asn1_decode(ctx, asn1_ec_pointQ, buf, buflen, NULL, NULL); if (r < 0) LOG_TEST_RET(ctx, r, "ASN.1 encoding failed"); sc_log(ctx, "DEE-EC key=%p, buf=%p, buflen=%d", key, buf, buflen); key->ecpointQ.value = malloc(buflen); if (key->ecpointQ.value == NULL) return SC_ERROR_OUT_OF_MEMORY; key->ecpointQ.len = buflen; memcpy(key->ecpointQ.value, buf, buflen); /* An uncompressed ecpoint is of the form 04||x||y * The 04 indicates uncompressed * x and y are same size, and field_length = sizeof(x) in bits. */ /* TODO: -DEE support more then uncompressed */ key->params.field_length = (ecpoint_len - 1)/2 * 8; if (ecpoint_data) free (ecpoint_data); return r; } 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]; int r; 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); r = sc_asn1_encode(ctx, asn1_ec_pointQ, buf, buflen); LOG_TEST_RET(ctx, r, "ASN.1 encoding failed"); sc_log(ctx, "EC key->ecpointQ=%p:%d *buf=%p:%d", key->ecpointQ.value, key->ecpointQ.len, *buf, *buflen); return 0; } 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_DSA) return sc_pkcs15_encode_pubkey_dsa(ctx, &key->u.dsa, buf, len); if (key->algorithm == SC_ALGORITHM_GOSTR3410) return sc_pkcs15_encode_pubkey_gostr3410(ctx, &key->u.gostr3410, buf, len); if (key->algorithm == SC_ALGORITHM_EC) return sc_pkcs15_encode_pubkey_ec(ctx, &key->u.ec, buf, len); sc_log(ctx, "Encoding of public key type %u not supported", key->algorithm); return SC_ERROR_NOT_SUPPORTED; } 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_DSA) return sc_pkcs15_decode_pubkey_dsa(ctx, &key->u.dsa, buf, len); if (key->algorithm == SC_ALGORITHM_GOSTR3410) return sc_pkcs15_decode_pubkey_gostr3410(ctx, &key->u.gostr3410, buf, len); if (key->algorithm == SC_ALGORITHM_EC) return sc_pkcs15_decode_pubkey_ec(ctx, &key->u.ec, buf, len); sc_log(ctx, "Decoding of public key type %u 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 = p15card->card->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; assert(p15card != NULL && obj != NULL && out != NULL); LOG_FUNC_CALLED(ctx); switch (obj->type) { case SC_PKCS15_TYPE_PUBKEY_RSA: algorithm = SC_ALGORITHM_RSA; break; case SC_PKCS15_TYPE_PUBKEY_DSA: algorithm = SC_ALGORITHM_DSA; break; case SC_PKCS15_TYPE_PUBKEY_GOSTR3410: algorithm = SC_ALGORITHM_GOSTR3410; break; case SC_PKCS15_TYPE_PUBKEY_EC: algorithm = SC_ALGORITHM_EC; break; default: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported public key type."); } info = (const struct sc_pkcs15_pubkey_info *) obj->data; sc_log(ctx, "Content (%p, %i)", obj->content.value, obj->content.len); if (obj->content.value && obj->content.len) { /* public key data is present as 'direct' value of pkcs#15 object */ data = calloc(1, obj->content.len); if (!data) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(data, obj->content.value, obj->content.len); len = obj->content.len; } else if (p15card->card->ops->read_public_key) { r = p15card->card->ops->read_public_key(p15card->card, algorithm, &info->path, info->key_reference, info->modulus_length, &data, &len); LOG_TEST_RET(ctx, r, "Card specific 'read-public' procedure failed."); } else if (info->path.len) { r = sc_pkcs15_read_file(p15card, &info->path, &data, &len); LOG_TEST_RET(ctx, r, "Failed to read public key file."); } else { LOG_TEST_RET(ctx, SC_ERROR_NOT_IMPLEMENTED, "No way to get public key"); } if (!data || !len) LOG_FUNC_RETURN(ctx, SC_ERROR_OBJECT_NOT_VALID); pubkey = calloc(1, sizeof(struct sc_pkcs15_pubkey)); if (pubkey == NULL) { free(data); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } pubkey->algorithm = algorithm; pubkey->data.value = data; pubkey->data.len = len; if (sc_pkcs15_decode_pubkey(ctx, pubkey, data, len)) { free(data); free(pubkey); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ASN1_OBJECT); } *out = pubkey; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int sc_pkcs15_dup_bignum (struct sc_pkcs15_bignum *dst, struct sc_pkcs15_bignum *src) { assert(dst && src); 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; int rv = SC_SUCCESS; assert(prvkey && out); *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_DSA: rv = sc_pkcs15_dup_bignum(&pubkey->u.dsa.pub, &prvkey->u.dsa.pub); if (!rv) rv = sc_pkcs15_dup_bignum(&pubkey->u.dsa.p, &prvkey->u.dsa.p); if (!rv) rv = sc_pkcs15_dup_bignum(&pubkey->u.dsa.q, &prvkey->u.dsa.q); if (!rv) rv = sc_pkcs15_dup_bignum(&pubkey->u.dsa.g, &prvkey->u.dsa.g); break; case SC_ALGORITHM_GOSTR3410: break; case SC_ALGORITHM_EC: pubkey->u.ec.ecpointQ.value = malloc(prvkey->u.ec.ecpointQ.len); 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; default: sc_log(ctx, "Unsupported private key algorithm"); return SC_ERROR_NOT_SUPPORTED; } if (rv) sc_pkcs15_free_pubkey(pubkey); else *out = pubkey; return rv; } void sc_pkcs15_erase_pubkey(struct sc_pkcs15_pubkey *key) { assert(key != NULL); 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_DSA: if (key->u.dsa.pub.data) free(key->u.dsa.pub.data); if (key->u.dsa.g.data) free(key->u.dsa.g.data); if (key->u.dsa.p.data) free(key->u.dsa.p.data); if (key->u.dsa.q.data) free(key->u.dsa.q.data); 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; } if (key->data.value) free(key->data.value); 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 *key) { if (key->subject.value) free(key->subject.value); sc_pkcs15_free_key_params(&key->params); free(key); } 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; u8 tagbuf[16]; /* enough to read in the tag and length */ u8 * rbuf = NULL; size_t rbuflen; const u8 * body; size_t bodylen; unsigned int cla_out, tag_out; *buf = NULL; LOG_FUNC_CALLED(ctx); f = open(filename, O_RDONLY); if (f < 0) { r = SC_ERROR_FILE_NOT_FOUND; goto out; } r = read(f, tagbuf, sizeof(tagbuf)); /* get tag and length */ if (r < 2) { sc_log(ctx, "Problem with '%s'", filename); r = SC_ERROR_DATA_OBJECT_NOT_FOUND; goto out; } len = r; body = tagbuf; if (sc_asn1_read_tag(&body, 0xfffff, &cla_out, &tag_out, &bodylen) != SC_SUCCESS) { sc_log(ctx, "DER problem"); r = SC_ERROR_INVALID_ASN1_OBJECT; goto out; } rbuflen = body - tagbuf + 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 */ r = read(f, rbuf + len, rbuflen - len); if (r < (int)(rbuflen - len)) { r = SC_ERROR_INVALID_ASN1_OBJECT; free (rbuf); rbuf = NULL; goto out; } } *buflen = rbuflen; *buf = rbuf; rbuf = NULL; r = rbuflen; out: if (rbuf) free(rbuf); 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_filename */ int sc_pkcs15_pubkey_from_spki(sc_context_t *ctx, sc_pkcs15_pubkey_t ** outpubkey, u8 *buf, size_t buflen, int depth) { int r; sc_pkcs15_pubkey_t * pubkey = NULL; sc_pkcs15_der_t pk = { NULL, 0 }; struct sc_algorithm_id pk_alg; struct sc_asn1_entry asn1_pkinfo[C_ASN1_PKINFO_ATTR_SIZE]; struct sc_asn1_entry asn1_ec_pointQ[2]; sc_log(ctx, "sc_pkcs15_pubkey_from_spki %p:%d", buf, buflen); memset(&pk_alg, 0, sizeof(pk_alg)); pubkey = calloc(1, sizeof(sc_pkcs15_pubkey_t)); if (pubkey == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } 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, buf, buflen, NULL, NULL); if (r < 0) goto err; pubkey->alg_id = calloc(1, sizeof(struct sc_algorithm_id)); if (pubkey->alg_id == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } memcpy(pubkey->alg_id, &pk_alg, sizeof(struct sc_algorithm_id)); pubkey->algorithm = pk_alg.algorithm; sc_log(ctx, "DEE pk_alg.algorithm=%d", pk_alg.algorithm); /* pk.len is in bits at this point */ switch (pk_alg.algorithm) { case SC_ALGORITHM_EC: /* * For most keys, the above ASN.1 parsing of a key works, but for EC keys, * the ec_pointQ in a certificate is stored in a bitstring, but * in PKCS#11 it is an octet string and we just decoded its * contents from the bitstring in the certificate. So we need to encode it * back to an octet string so we can store it as an octet string. */ pk.len >>= 3; /* Assume it is multiple of 8 */ /* pubkey->u.ec.field_length = (pk.len - 1)/2 * 8; */ sc_copy_asn1_entry(c_asn1_ec_pointQ, asn1_ec_pointQ); sc_format_asn1_entry(&asn1_ec_pointQ[0], pk.value, &pk.len, 1); r = sc_asn1_encode(ctx, asn1_ec_pointQ, &pubkey->data.value, &pubkey->data.len); sc_log(ctx, "DEE r=%d data=%p:%d", r, pubkey->data.value, pubkey->data.len); break; default: pk.len >>= 3; /* convert number of bits to bytes */ pubkey->data = pk; /* save in publey */ pk.value = NULL; break; } /* Now decode what every is in pk as it depends on the key algorthim */ r = sc_pkcs15_decode_pubkey(ctx, pubkey, pubkey->data.value, pubkey->data.len); if (r < 0) goto err; *outpubkey = pubkey; pubkey = NULL; return 0; err: if (pubkey) free(pubkey); if (pk.value) free(pk.value); LOG_TEST_RET(ctx, r, "ASN.1 parsing of subjectPubkeyInfo failed"); LOG_FUNC_RETURN(ctx, r); } int sc_pkcs15_pubkey_from_spki_filename(sc_context_t *ctx, char * filename, sc_pkcs15_pubkey_t ** outpubkey) { int r; u8 * buf = NULL; size_t buflen = 0; sc_pkcs15_pubkey_t * pubkey = NULL; struct sc_asn1_entry asn1_spki[] = { { "PublicKeyInfo",SC_ASN1_CALLBACK, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, sc_pkcs15_pubkey_from_spki, &pubkey}, { NULL, 0, 0, 0, NULL, NULL } }; *outpubkey = NULL; r = sc_pkcs15_read_der_file(ctx, filename, &buf, &buflen); if (r < 0) return r; r = sc_asn1_decode(ctx, asn1_spki, buf, buflen, NULL, NULL); if (buf) free(buf); *outpubkey = pubkey; return 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}, {"prime192r1", "1.2.840.10045.3.1.1", "06082A8648CE3D030101", 192}, {"ansiX9p192r1", "1.2.840.10045.3.1.1", "06082A8648CE3D030101", 192}, {"prime256v1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256}, {"secp256r1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256}, {"ansiX9p256r1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256}, {"secp384r1", "1.3.132.0.34", "06052B81040022", 384}, {"prime384v1", "1.3.132.0.34", "06052B81040022", 384}, {"ansiX9p384r1", "1.3.132.0.34", "06052B81040022", 384}, {"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}, {NULL, NULL, NULL, 0}, }; int sc_pkcs15_fix_ec_parameters(struct sc_context *ctx, struct sc_pkcs15_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 %i", 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) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported named curve"); 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 if (sc_valid_oid(&ecparams->id)) { 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; switch (pk->type) { case EVP_PKEY_RSA: { struct sc_pkcs15_pubkey_rsa *dst = &pkcs15_key->u.rsa; RSA *src = EVP_PKEY_get1_RSA(pk); pkcs15_key->algorithm = SC_ALGORITHM_RSA; if (!sc_pkcs15_convert_bignum(&dst->modulus, src->n) || !sc_pkcs15_convert_bignum(&dst->exponent, src->e)) return SC_ERROR_INVALID_DATA; RSA_free(src); break; } case EVP_PKEY_DSA: { struct sc_pkcs15_pubkey_dsa *dst = &pkcs15_key->u.dsa; DSA *src = EVP_PKEY_get1_DSA(pk); pkcs15_key->algorithm = SC_ALGORITHM_DSA; sc_pkcs15_convert_bignum(&dst->pub, src->pub_key); sc_pkcs15_convert_bignum(&dst->p, src->p); sc_pkcs15_convert_bignum(&dst->q, src->q); sc_pkcs15_convert_bignum(&dst->g, src->g); DSA_free(src); break; } #if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) case NID_id_GostR3410_2001: { struct sc_pkcs15_pubkey_gostr3410 *dst = &pkcs15_key->u.gostr3410; EC_KEY *eckey = EVP_PKEY_get0(pk); const EC_POINT *point; BIGNUM *X, *Y; int r = 0; assert(eckey); point = EC_KEY_get0_public_key(eckey); if (!point) return SC_ERROR_INTERNAL; X = BN_new(); Y = BN_new(); if (X && Y && EC_KEY_get0_group(eckey)) r = EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(eckey), point, X, Y, NULL); if (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 (r != 1) return SC_ERROR_INTERNAL; break; } case EVP_PKEY_EC: { struct sc_pkcs15_pubkey_ec *dst = &pkcs15_key->u.ec; EC_KEY *src = NULL; const EC_GROUP *grp = NULL; unsigned char buf[255]; size_t buflen = 255; int nid; src = EVP_PKEY_get0(pk); assert(src); assert(EC_KEY_get0_public_key(src)); pkcs15_key->algorithm = SC_ALGORITHM_EC; grp = EC_KEY_get0_group(src); if(grp == 0) return SC_ERROR_INCOMPATIBLE_KEY; /* Decode EC_POINT from a octet string */ buflen = EC_POINT_point2oct(grp, (const EC_POINT *) EC_KEY_get0_public_key(src), POINT_CONVERSION_UNCOMPRESSED, buf, buflen, NULL); /* get curve name */ nid = EC_GROUP_get_curve_name(grp); if(nid != 0) { const char *name = OBJ_nid2sn(nid); if(sizeof(name) > 0) dst->params.named_curve = strdup(name); } /* copy the public key */ if (buflen > 0) { dst->ecpointQ.value = malloc(buflen); 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 /* OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) */ default: return SC_ERROR_NOT_SUPPORTED; } return SC_SUCCESS; #else return SC_ERROR_NOT_IMPLEMENTED; #endif } opensc-0.13.0/src/libopensc/pkcs15-infocamere.c0000644000015201777760000005455512057406034016170 00000000000000/* * PKCS15 emulation layer for 1202, 1203 and 1400 Infocamere card. * To see how this works, run p15dump on your Infocamere card. * * Copyright (C) 2005, Sirio Capizzi * Copyright (C) 2004, Antonino Iacono * Copyright (C) 2003, Olaf Kirch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #ifdef ENABLE_ZLIB #include #endif #include "common/compat_strlcpy.h" #include "pkcs15.h" #include "log.h" int sc_pkcs15emu_infocamere_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); static int (*set_security_env) (sc_card_t *, const sc_security_env_t *, int); static int set_sec_env(sc_card_t * card, const sc_security_env_t * env, int se_num) { sc_security_env_t tenv = *env; if (tenv.operation == SC_SEC_OPERATION_SIGN) tenv.operation = SC_SEC_OPERATION_DECIPHER; return set_security_env(card, &tenv, se_num); } static int do_sign(sc_card_t * card, const u8 * in, size_t inlen, u8 * out, size_t outlen) { return card->ops->decipher(card, in, inlen, out, outlen); } static void set_string(char **strp, const char *value) { if (*strp) free(*strp); *strp = value ? strdup(value) : NULL; } #if 1 /* XXX: temporary copy of the old pkcs15emu functions, * to be removed */ static int sc_pkcs15emu_add_pin(sc_pkcs15_card_t *p15card, const sc_pkcs15_id_t *id, const char *label, const sc_path_t *path, int ref, int type, unsigned int min_length, unsigned int max_length, int flags, int tries_left, const char pad_char, int obj_flags) { sc_pkcs15_auth_info_t info; sc_pkcs15_object_t obj; memset(&info, 0, sizeof(info)); memset(&obj, 0, sizeof(obj)); info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; info.auth_id = *id; info.attrs.pin.min_length = min_length; info.attrs.pin.max_length = max_length; info.attrs.pin.stored_length = max_length; info.attrs.pin.type = type; info.attrs.pin.reference = ref; info.attrs.pin.flags = flags; info.attrs.pin.pad_char = pad_char; info.tries_left = tries_left; 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.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; obj.flags = obj_flags; strlcpy(obj.label, label, sizeof(obj.label)); if (auth_id != NULL) obj.auth_id = *auth_id; return sc_pkcs15emu_add_rsa_prkey(p15card, &obj, &info); } static int sc_pkcs15emu_add_cert(sc_pkcs15_card_t *p15card, int type, int authority, const sc_path_t *path, const sc_pkcs15_id_t *id, const char *label, int obj_flags) { /* const char *label = "Certificate"; */ sc_pkcs15_cert_info_t info; sc_pkcs15_object_t obj; memset(&info, 0, sizeof(info)); memset(&obj, 0, sizeof(obj)); info.id = *id; info.authority = authority; if (path) info.path = *path; strlcpy(obj.label, label, sizeof(obj.label)); obj.flags = obj_flags; return sc_pkcs15emu_add_x509_cert(p15card, &obj, &info); } #endif static int infocamere_1200_init(sc_pkcs15_card_t * p15card) { const int prkey_usage = SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; const int authprkey_usage = SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER | SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT; sc_card_t *card = p15card->card; sc_path_t path; sc_file_t *file; sc_pkcs15_id_t id, auth_id; unsigned char buffer[256]; unsigned char ef_gdo[256]; char serial[256]; unsigned char certlen[2]; int authority, change_sign = 0; struct sc_pkcs15_cert_info cert_info; struct sc_pkcs15_object cert_obj; const char *label = "User Non-repudiation Certificate"; const char *calabel = "CA Certificate"; const char *authlabel = "User Authentication Certificate"; const char *infocamere_cert_path[2] = { "DF01C000", "3F00000011111A02" }; const char *infocamere_auth_certpath[2] = { "11111A02", "000011111B02" }; const char *infocamere_cacert_path[2] = { "DF01C008", "000011114101" }; const char *infocamere_auth_path[2] = { "3F001111", "3F0000001111" }; const char *infocamere_nrepud_path[2] = { "3F00DF01", "3F0000001111" }; const int infocamere_idpin_auth_obj[2] = { 0x95, 0x81 }; const int infocamere_idpin_nrepud_obj[2] = { 0x99, 0x81 }; const int infocamere_idprkey_auth_obj[2] = { 0x9B, 0x01 }; const int infocamere_idprkey_nrepud_obj[2] = { 0x84, 0x01 }; const char *authPIN = "Authentication PIN"; const char *nonrepPIN = "Non-repudiation PIN"; const char *authPRKEY = "Authentication Key"; const char *nonrepPRKEY = "Non repudiation Key"; const int flags = SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_NEEDS_PADDING; int r, len_iccsn, len_chn; sc_format_path("3F002F02", &path); r = sc_select_file(card, &path, &file); if (r != SC_SUCCESS || file->size > 255) { /* Not EF.GDO */ return SC_ERROR_WRONG_CARD; } sc_read_binary(card, 0, ef_gdo, file->size, 0); if (ef_gdo[0] != 0x5A || file->size < 3) { /* Not EF.GDO */ return SC_ERROR_WRONG_CARD; } len_iccsn = ef_gdo[1]; memcpy(buffer, ef_gdo + 2, len_iccsn); sc_bin_to_hex(buffer, len_iccsn, serial, sizeof(serial), 0); if (file->size < (size_t) (len_iccsn + 5)) { /* Not CHN */ return SC_ERROR_WRONG_CARD; } if (! (ef_gdo[len_iccsn + 2] == 0x5F && ef_gdo[len_iccsn + 3] == 0x20)) { /* Not CHN */ return SC_ERROR_WRONG_CARD; } len_chn = ef_gdo[len_iccsn + 4]; if (len_chn < 2 || len_chn > 8) { /* Length CHN incorrect */ return SC_ERROR_WRONG_CARD; } if (! (ef_gdo[len_iccsn + 5] == 0x12 && (ef_gdo[len_iccsn + 6] == 0x02 || ef_gdo[len_iccsn + 6] == 0x03))) { /* Not Infocamere Card */ return SC_ERROR_WRONG_CARD; } set_string(&p15card->tokeninfo->serial_number, serial); if (ef_gdo[len_iccsn + 6] == 0x02) set_string(&p15card->tokeninfo->label, "Infocamere 1202 Card"); else { set_string(&p15card->tokeninfo->label, "Infocamere 1203 Card"); change_sign = 1; } set_string(&p15card->tokeninfo->manufacturer_id, "Infocamere"); authority = 0; /* Get the authentication certificate length */ sc_format_path(infocamere_auth_certpath[ef_gdo[len_iccsn+6]-2], &path); r = sc_select_file(card, &path, NULL); if (r >= 0) { sc_read_binary(card, 0, certlen, 2, 0); /* Now set the certificate offset/len */ path.index = 2; path.count = (certlen[1] << 8) + certlen[0]; memset(&cert_info, 0, sizeof(cert_info)); memset(&cert_obj, 0, sizeof(cert_obj)); sc_pkcs15_format_id("1", &cert_info.id); cert_info.authority = authority; cert_info.path = path; strlcpy(cert_obj.label, authlabel, sizeof(cert_obj.label)); cert_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE; r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); if (r < 0) return SC_ERROR_INTERNAL; /* XXX: the IDs for the key/pin in case of the 1203 type * are wrong, therefore I disable them for now -- Nils */ if (!change_sign) { /* add authentication PIN */ sc_format_path(infocamere_auth_path[ef_gdo[len_iccsn+6]-2], &path); sc_pkcs15_format_id("1", &id); sc_pkcs15emu_add_pin(p15card, &id, authPIN, &path, infocamere_idpin_auth_obj[ef_gdo[len_iccsn+6]-2], SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 5, 8, flags, 3, 0, SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE); /* add authentication private key */ auth_id.value[0] = 1; auth_id.len = 1; sc_pkcs15emu_add_prkey(p15card, &id, authPRKEY, SC_PKCS15_TYPE_PRKEY_RSA, 1024, authprkey_usage, &path, infocamere_idprkey_auth_obj[ef_gdo[len_iccsn+6]-2], &auth_id, SC_PKCS15_CO_FLAG_PRIVATE); } } /* Get the non-repudiation certificate length */ sc_format_path(infocamere_cert_path[ef_gdo[len_iccsn+6]-2], &path); if (sc_select_file(card, &path, NULL) < 0) { return SC_ERROR_INTERNAL; } sc_read_binary(card, 0, certlen, 2, 0); /* Now set the certificate offset/len */ path.index = 2; path.count = (certlen[1] << 8) + certlen[0]; memset(&cert_info, 0, sizeof(cert_info)); memset(&cert_obj, 0, sizeof(cert_obj)); sc_pkcs15_format_id("2", &cert_info.id); cert_info.authority = authority; cert_info.path = path; strlcpy(cert_obj.label, label, sizeof(cert_obj.label)); cert_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE; r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); if (r < 0) return SC_ERROR_INTERNAL; /* Get the CA certificate length */ authority = 1; sc_format_path(infocamere_cacert_path[ef_gdo[len_iccsn+6]-2], &path); r = sc_select_file(card, &path, NULL); if (r >= 0) { size_t len; sc_read_binary(card, 0, certlen, 2, 0); len = (certlen[1] << 8) + certlen[0]; if (len != 0) { /* Now set the certificate offset/len */ path.index = 2; path.count = len; memset(&cert_info, 0, sizeof(cert_info)); memset(&cert_obj, 0, sizeof(cert_obj)); sc_pkcs15_format_id("3", &cert_info.id); cert_info.authority = authority; cert_info.path = path; strlcpy(cert_obj.label, calabel, sizeof(cert_obj.label)); cert_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE; r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); if (r < 0) return SC_ERROR_INTERNAL; } } /* add non repudiation PIN */ sc_format_path(infocamere_nrepud_path[ef_gdo[len_iccsn+6]-2], &path); sc_pkcs15_format_id("2", &id); sc_pkcs15emu_add_pin(p15card, &id, nonrepPIN, &path, infocamere_idpin_nrepud_obj[ef_gdo[len_iccsn+6]-2], SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 5, 8, flags, 3, 0, SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE); /* add non repudiation private key */ auth_id.value[0] = 2; auth_id.len = 1; sc_pkcs15emu_add_prkey(p15card, &id, nonrepPRKEY, SC_PKCS15_TYPE_PRKEY_RSA, 1024, prkey_usage, &path, infocamere_idprkey_nrepud_obj[ef_gdo[len_iccsn+6]-2], &auth_id, SC_PKCS15_CO_FLAG_PRIVATE); /* return to MF */ sc_format_path("3F00", &path); r = sc_select_file(card, &path, NULL); if (r != SC_SUCCESS) return r; if (change_sign) { /* save old signature funcs */ set_security_env = card->ops->set_security_env; /* set new one */ card->ops->set_security_env = set_sec_env; card->ops->compute_signature = do_sign; } return SC_SUCCESS; } static int infocamere_1400_set_sec_env(struct sc_card *card, const struct sc_security_env *env, int se_num) { int r; struct sc_security_env tenv = *env; if (tenv.operation == SC_SEC_OPERATION_SIGN) tenv.operation = SC_SEC_OPERATION_DECIPHER; if ((r = card->ops->restore_security_env(card, 0x40)) == SC_SUCCESS) return set_security_env(card, &tenv, se_num); else return r; } #ifdef ENABLE_ZLIB static const u8 ATR_1400[] = { 0x3b, 0xfc, 0x98, 0x00, 0xff, 0xc1, 0x10, 0x31, 0xfe, 0x55, 0xc8, 0x03, 0x49, 0x6e, 0x66, 0x6f, 0x63, 0x61, 0x6d, 0x65, 0x72, 0x65, 0x28 }; /* Loads certificates. * Certificates are stored in a ZLib compressed form with * a 4 byte header, so we extract, decompress and cache * them. */ static int loadCertificate(sc_pkcs15_card_t * p15card, int i, const char *certPath, const char *certLabel) { unsigned char *compCert = NULL, *cert = NULL, size[2]; unsigned long int compLen, len; sc_pkcs15_cert_info_t cert_info; sc_pkcs15_object_t cert_obj; sc_path_t cpath; sc_card_t *card = p15card->card; sc_pkcs15_id_t id; int r; memset(&cert_info, 0, sizeof(cert_info)); memset(&cert_obj, 0, sizeof(cert_obj)); sc_format_path(certPath, &cpath); if (sc_select_file(card, &cpath, NULL) != SC_SUCCESS) return SC_ERROR_WRONG_CARD; sc_read_binary(card, 2, size, 2, 0); compLen = (size[0] << 8) + size[1]; compCert = malloc(compLen * sizeof(unsigned char)); len = 4 * compLen; /*Approximation of the uncompressed size */ cert = malloc(len * sizeof(unsigned char)); sc_read_binary(card, 4, compCert, compLen, 0); if ((r = uncompress(cert, &len, compCert, compLen)) != Z_OK) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Zlib error: %d", r); return SC_ERROR_INTERNAL; } cpath.index = 0; cpath.count = len; sc_pkcs15_cache_file(p15card, &cpath, cert, len); id.len=1; id.value[0] = i + 1; cert_info.id = id; cert_info.path = cpath; cert_info.authority = (i == 2); strlcpy(cert_obj.label, certLabel, sizeof(cert_obj.label)); cert_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE; sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); return SC_SUCCESS; } static int infocamere_1400_init(sc_pkcs15_card_t * p15card) { sc_card_t *card = p15card->card; sc_path_t path; sc_pkcs15_id_t id, auth_id; unsigned char serial[16]; int flags; int r; int hasAuthCert = 0; const char *certLabel[] = { "User Non-repudiation Certificate", "User Authentication Certificate", "CA Certificate" }; const char *certPath[] = { "300060000000", "300060000001", "300060000002" }; const char *pinLabel[] = { "Non-repudiation PIN", "Authentication PIN" }; int retries[] = { 3, -1 }; const char *keyPath[] = { "30004000001", "30004000002" }; const char *keyLabel[] = { "Non repudiation Key", "Authentication Key" }; static int usage[] = { SC_PKCS15_PRKEY_USAGE_NONREPUDIATION, SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER | SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT }; auth_id.len = 1; id.len = 1; /* OpenSC doesn't define constants to identify BSOs for * restoring security environment, so we overload * the set_security_env function to support restore_sec_env */ set_security_env = card->ops->set_security_env; card->ops->set_security_env = infocamere_1400_set_sec_env; card->ops->compute_signature = do_sign; p15card->opts.use_file_cache = 1; sc_format_path("30000001", &path); r = sc_select_file(card, &path, NULL); if (r != SC_SUCCESS) return SC_ERROR_WRONG_CARD; sc_read_binary(card, 15, serial, 15, 0); serial[15] = '\0'; set_string(&p15card->tokeninfo->serial_number, (char *)serial); set_string(&p15card->tokeninfo->label, "Infocamere 1400 Card"); set_string(&p15card->tokeninfo->manufacturer_id, "Infocamere"); if ((r = loadCertificate(p15card, 0, certPath[0], certLabel[0])) != SC_SUCCESS) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "%s", sc_strerror(r)); return SC_ERROR_WRONG_CARD; } hasAuthCert = loadCertificate(p15card, 1, certPath[1], certLabel[1]) == SC_SUCCESS; loadCertificate(p15card, 2, certPath[2], certLabel[2]); flags = SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_NEEDS_PADDING; /* adding PINs & private keys */ sc_format_path("30004000", &path); id.value[0] = 1; sc_pkcs15emu_add_pin(p15card, &id, pinLabel[0], &path, 1, SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 5, 8, flags, retries[0], 0, SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE); sc_format_path(keyPath[0], &path); auth_id.value[0] = 1; sc_pkcs15emu_add_prkey(p15card, &id, keyLabel[0], SC_PKCS15_TYPE_PRKEY_RSA, 1024, usage[0], &path, 1, &auth_id, SC_PKCS15_CO_FLAG_PRIVATE); if (hasAuthCert) { sc_format_path("30004000", &path); id.value[0] = 2; sc_pkcs15emu_add_pin(p15card, &id, pinLabel[1], &path, 2, SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 5, 8, flags, retries[1], 0, SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE); sc_format_path(keyPath[1], &path); auth_id.value[0] = 2; sc_pkcs15emu_add_prkey(p15card, &id, keyLabel[1], SC_PKCS15_TYPE_PRKEY_RSA, 1024, usage[1], &path, 2, &auth_id, SC_PKCS15_CO_FLAG_PRIVATE); } /* return to MF */ sc_format_path("3F00", &path); r = sc_select_file(card, &path, NULL); return r; } #endif static const u8 ATR_1600[] = { 0x3B, 0xF4, 0x98, 0x00, 0xFF, 0xC1, 0x10, 0x31, 0xFE, 0x55, 0x4D, 0x34, 0x63, 0x76, 0xB4 }; static int infocamere_1600_init(sc_pkcs15_card_t * p15card) { sc_card_t *card = p15card->card; sc_path_t path; sc_pkcs15_id_t id, auth_id; unsigned char serial[17]; int flags; int r; int hasAuthCert = 0; const char *certLabel[] = { "User Non-repudiation Certificate", "User Authentication Certificate" }; const char *certPath[] = { "200020010008", "20002001000E" }; const char *pinLabel[] = { "Non-repudiation PIN", "Authentication PIN" }; int retries[] = { 3, -1 }; const char *keyPath[] = { "200020010004", "20002001000A" }; const char *keyLabel[] = { "Non repudiation Key", "Authentication Key" }; static int usage[] = { SC_PKCS15_PRKEY_USAGE_NONREPUDIATION, SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER | SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT }; auth_id.len = 1; id.len = 1; /* OpenSC doesn't define constants to identify BSOs for * restoring security environment, so we overload * the set_security_env function to support restore_sec_env */ set_security_env = card->ops->set_security_env; card->ops->set_security_env = infocamere_1400_set_sec_env; card->ops->compute_signature = do_sign; sc_format_path("200020012002", &path); r = sc_select_file(card, &path, NULL); if (r != SC_SUCCESS) return SC_ERROR_WRONG_CARD; sc_read_binary(card, 30, serial, 16, 0); serial[16] = '\0'; set_string(&p15card->tokeninfo->serial_number, (char *) serial); set_string(&p15card->tokeninfo->label, "Infocamere 1600 Card"); set_string(&p15card->tokeninfo->manufacturer_id, "Infocamere"); /* Adding certificates. * Certificates are stored in a ZLib compressed form with * a 4 byte header, so we extract, decompress and cache * them. */ sc_format_path(certPath[0], &path); if (sc_select_file(card, &path, NULL) != SC_SUCCESS) return SC_ERROR_WRONG_CARD; id.value[0] = 1; sc_pkcs15emu_add_cert(p15card, SC_PKCS15_TYPE_CERT_X509, 0, &path, &id, certLabel[0], SC_PKCS15_CO_FLAG_MODIFIABLE); sc_format_path(certPath[1], &path); if (sc_select_file(card, &path, NULL) == SC_SUCCESS) { hasAuthCert = 1; id.value[0] = 2; sc_pkcs15emu_add_cert(p15card, SC_PKCS15_TYPE_CERT_X509, 1, &path, &id, certLabel[1], SC_PKCS15_CO_FLAG_MODIFIABLE); } flags = SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_NEEDS_PADDING; /* adding PINs & private keys */ sc_format_path("2000", &path); id.value[0] = 1; sc_pkcs15emu_add_pin(p15card, &id, pinLabel[0], &path, 1, SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 5, 8, flags, retries[0], 0, SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE); sc_format_path(keyPath[0], &path); auth_id.value[0] = 1; sc_pkcs15emu_add_prkey(p15card, &id, keyLabel[0], SC_PKCS15_TYPE_PRKEY_RSA, 1024, usage[0], &path, 1, &auth_id, SC_PKCS15_CO_FLAG_PRIVATE); if (hasAuthCert) { id.value[0] = 2; sc_pkcs15emu_add_pin(p15card, &id, pinLabel[1], &path, 2, SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 5, 8, flags, retries[1], 0, SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE); sc_format_path(keyPath[1], &path); auth_id.value[0] = 2; sc_pkcs15emu_add_prkey(p15card, &id, keyLabel[1], SC_PKCS15_TYPE_PRKEY_RSA, 1024, usage[1], &path, 2, &auth_id, SC_PKCS15_CO_FLAG_PRIVATE); } /* return to MF */ sc_format_path("3F00", &path); r = sc_select_file(card, &path, NULL); return SC_SUCCESS; } static int infocamere_detect_card(sc_pkcs15_card_t * p15card) { sc_card_t *card = p15card->card; /* check if we have the correct card OS */ if (strcmp(card->name, "STARCOS SPK 2.3") && strcmp(card->name, "CardOS M4")) return SC_ERROR_WRONG_CARD; return SC_SUCCESS; } int sc_pkcs15emu_infocamere_init_ex(sc_pkcs15_card_t * p15card, sc_pkcs15emu_opt_t * opts) { if (!(opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)) { if (infocamere_detect_card(p15card)) return SC_ERROR_WRONG_CARD; } if (memcmp(p15card->card->atr.value, ATR_1600, sizeof(ATR_1600)) == 0) return infocamere_1600_init(p15card); #ifdef ENABLE_ZLIB else if (memcmp(p15card->card->atr.value, ATR_1400, sizeof(ATR_1400)) == 0) return infocamere_1400_init(p15card); #endif else return infocamere_1200_init(p15card); } opensc-0.13.0/src/libopensc/pkcs15-esinit.c0000644000015201777760000000535412057406034015344 00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Initially written by Weitao Sun (weitao@ftsafe.com) 2008*/ #include "config.h" #include #include #include #include "internal.h" #include "pkcs15.h" #include "cardctl.h" #define MANU_ID "entersafe" int sc_pkcs15emu_entersafe_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); 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); r = sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0); if (r != SC_SUCCESS) return SC_ERROR_INTERNAL; if (p15card->tokeninfo->serial_number) free(p15card->tokeninfo->serial_number); p15card->tokeninfo->serial_number = malloc(strlen(buf) + 1); if (!p15card->tokeninfo->serial_number) return SC_ERROR_INTERNAL; strcpy(p15card->tokeninfo->serial_number, buf); /* the manufacturer ID, in this case Giesecke & Devrient GmbH */ if (p15card->tokeninfo->manufacturer_id) free(p15card->tokeninfo->manufacturer_id); p15card->tokeninfo->manufacturer_id = malloc(strlen(MANU_ID) + 1); if (!p15card->tokeninfo->manufacturer_id) return SC_ERROR_INTERNAL; strcpy(p15card->tokeninfo->manufacturer_id, MANU_ID); return SC_SUCCESS; } int sc_pkcs15emu_entersafe_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts) { SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) return sc_pkcs15emu_entersafe_init(p15card); else { int r = entersafe_detect_card(p15card); if (r) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_entersafe_init(p15card); } } opensc-0.13.0/src/libopensc/card-gpk.c0000644000015201777760000014114112057406034014430 00000000000000/* * card-gpk: Driver for GPK 4000 cards * * Copyright (C) 2002 Olaf Kirch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #ifdef ENABLE_OPENSSL /* empty file without openssl */ #include #include #include #include #include "internal.h" #include "cardctl.h" #include "pkcs15.h" #define GPK_SEL_MF 0x00 #define GPK_SEL_DF 0x01 #define GPK_SEL_EF 0x02 #define GPK_SEL_AID 0x04 #define GPK_FID_MF 0x3F00 #define GPK_FTYPE_SC 0x21 #define GPK_SIGN_RSA_MD5 0x11 #define GPK_SIGN_RSA_SHA 0x12 #define GPK_SIGN_RSA_SSL 0x18 #define GPK_VERIFY_RSA_MD5 0x21 #define GPK_VERIFY_RSA_SHA 0x22 #define GPK_AUTH_RSA_MD5 0x31 #define GPK_AUTH_RSA_SHA 0x32 #define GPK_AUTH_RSA_SSL 0x38 #define GPK_UNWRAP_RSA 0x77 #define GPK_MAX_PINS 8 #define GPK_HASH_CHUNK 62 /* * GPK4000 private data */ struct gpk_private_data { /* The GPK usually do file offsets in multiples of * 4 bytes. This can be customized however. We * should really query for this during gpk_init */ unsigned int offset_shift; unsigned int offset_mask; unsigned int locked : 1, sample_card : 1; /* access control bits of file most recently selected */ unsigned short int ac[3]; /* is non-zero if we should use secure messaging */ unsigned key_set : 1; unsigned int key_reference; u8 key[16]; /* crypto related data from set_security_env */ unsigned int sec_algorithm; unsigned int sec_hash_len; unsigned int sec_mod_len; unsigned int sec_padding; }; #define DRVDATA(card) ((struct gpk_private_data *) ((card)->drv_data)) static int gpk_get_info(sc_card_t *, int, int, u8 *, size_t); /* * ATRs of GPK4000 cards courtesy of libscez */ static struct sc_atr_table gpk_atrs[] = { { "3B:27:00:80:65:A2:04:01:01:37", NULL, "GPK 4K", SC_CARD_TYPE_GPK_GPK4000_s, 0, NULL }, { "3B:27:00:80:65:A2:05:01:01:37", NULL, "GPK 4K", SC_CARD_TYPE_GPK_GPK4000_sp, 0, NULL }, { "3B:27:00:80:65:A2:0C:01:01:37", NULL, "GPK 4K", SC_CARD_TYPE_GPK_GPK4000_su256, 0, NULL }, { "3B:A7:00:40:14:80:65:A2:14:01:01:37", NULL, "GPK 4K", SC_CARD_TYPE_GPK_GPK4000_sdo, 0, NULL }, { "3B:A7:00:40:18:80:65:A2:08:01:01:52", NULL, "GPK 8K", SC_CARD_TYPE_GPK_GPK8000_8K, 0, NULL }, { "3B:A7:00:40:18:80:65:A2:09:01:01:52", NULL, "GPK 8K", SC_CARD_TYPE_GPK_GPK8000_16K, 0, NULL }, { "3B:A7:00:40:18:80:65:A2:09:01:02:52", NULL, "GPK 16K", SC_CARD_TYPE_GPK_GPK16000, 0, NULL }, { "3B:A7:00:40:18:80:65:A2:09:01:03:52", NULL, "GPK 16K", SC_CARD_TYPE_GPK_GPK16000, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } }; /* * Driver and card ops structures */ static struct sc_card_operations gpk_ops, *iso_ops; static struct sc_card_driver gpk_drv = { "Gemplus GPK", "gpk", &gpk_ops, NULL, 0, NULL }; /* * return 1 if this driver can handle the card */ static int gpk_match_card(sc_card_t *card) { int i; i = _sc_match_atr(card, gpk_atrs, &card->type); if (i < 0) { const u8 *hist_bytes = card->reader->atr_info.hist_bytes; /* Gemplus GPK docs say we can use just the * FMN and PRN fields of the historical bytes * to recognize a GPK card * See Table 43, pp. 188 * We'll use the first 2 bytes as well */ if ((card->reader->atr_info.hist_bytes_len >= 7) && (hist_bytes[0] == 0x80) && (hist_bytes[1] == 0x65) && (hist_bytes[2] == 0xa2)) { /* FMN */ if (hist_bytes[3] == 0x08) { /* PRN? */ card->type = SC_CARD_TYPE_GPK_GPK8000; return 1; } if (hist_bytes[3] == 0x09) { /* PRN? */ card->type = SC_CARD_TYPE_GPK_GPK16000; return 1; } } return 0; } return 1; } /* * Initialize the card struct */ static int gpk_init(sc_card_t *card) { struct gpk_private_data *priv; unsigned long exponent, flags, kg; unsigned char info[13]; card->drv_data = priv = calloc(1, sizeof(*priv)); if (card->drv_data == NULL) return SC_ERROR_OUT_OF_MEMORY; /* read/write/update binary expect offset to be the * number of 32 bit words. * offset_shift is the shift value. * offset_mask is the corresponding mask. */ priv->offset_shift = 2; priv->offset_mask = 3; card->cla = 0x00; /* Set up algorithm info. GPK 16000 will do any RSA * exponent, earlier ones are restricted to 0x10001 */ flags = SC_ALGORITHM_RSA_HASH_MD5 | SC_ALGORITHM_RSA_HASH_SHA1 | SC_ALGORITHM_RSA_HASH_MD5_SHA1; flags |= SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_PAD_ANSI | SC_ALGORITHM_RSA_PAD_ISO9796; exponent = (card->type < SC_CARD_TYPE_GPK_GPK16000) ? 0x10001 : 0; kg = (card->type >= SC_CARD_TYPE_GPK_GPK8000) ? SC_ALGORITHM_ONBOARD_KEY_GEN : 0; _sc_card_add_rsa_alg(card, 512, flags|kg, exponent); _sc_card_add_rsa_alg(card, 768, flags, exponent); _sc_card_add_rsa_alg(card, 1024, flags|kg, exponent); /* Inspect the LOCK byte */ if (gpk_get_info(card, 0x02, 0xA4, info, sizeof(info)) >= 0) { if (info[12] & 0x40) { priv->offset_shift = 0; priv->offset_mask = 0; } if (info[12] & 0x10) { /* DSA supported - add algo information. * It's highly unlikely we'll ever see this. */ } if (info[12] & 0x08) { priv->locked = 1; } /* Sample cards use a transport key of "TEST KEYTEST KEY" */ if (!memcmp(info+5, "\x00\xff\x00", 3)) { priv->sample_card = 1; } } /* State that we have an RNG */ card->caps |= SC_CARD_CAP_RNG; /* Make sure max send/receive size is 4 byte aligned and <256. */ card->max_recv_size = 252; return 0; } /* * Card is being closed; discard any private data etc */ static int gpk_finish(sc_card_t *card) { if (card->drv_data) free(card->drv_data); card->drv_data = NULL; return 0; } /* * Error code handling for the GPK4000. * sc_check_sw doesn't seem to handle all of the * status words the GPK is capable of returning */ #if 0 static int gpk_check_sw(sc_card_t *card, u8 sw1, u8 sw2) { unsigned short int sw = (sw1 << 8) | sw2; if ((sw & 0xFFF0) == 0x63C0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "wrong PIN, %u tries left\n", sw&0xf); return SC_ERROR_PIN_CODE_INCORRECT; } switch (sw) { case 0x6400: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "wrong crypto context\n"); return SC_ERROR_OBJECT_NOT_VALID; /* XXX ??? */ /* The following are handled by iso7816_check_sw * but all return SC_ERROR_UNKNOWN_DATA_RECEIVED * XXX: fix in the iso driver? */ case 0x6983: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "PIN is blocked\n"); return SC_ERROR_PIN_CODE_INCORRECT; case 0x6581: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "out of space on card or file\n"); return SC_ERROR_OUT_OF_MEMORY; case 0x6981: return SC_ERROR_FILE_NOT_FOUND; case 0x6A80: case 0x6b00: return SC_ERROR_INVALID_ARGUMENTS; } return iso7816_check_sw(card, sw1, sw2); } #endif /* * Select a DF/EF */ static int match_path(sc_card_t *card, unsigned short int **pathptr, size_t *pathlen, int need_info) { unsigned short int *curptr, *ptr; size_t curlen, len; size_t i; curptr = (unsigned short int *) card->cache.current_path.value; curlen = card->cache.current_path.len; ptr = *pathptr; len = *pathlen; if (curlen < 1 || len < 1) return 0; /* Make sure path starts with MF. * Note the cached path should always begin with MF. */ if (ptr[0] != GPK_FID_MF || curptr[0] != GPK_FID_MF) return 0; for (i = 1; i < len && i < curlen; i++) { if (ptr[i] != curptr[i]) break; } if (len < curlen) { /* Caller asked us to select the DF, but the * current file is some EF within the DF we're * interested in. Say ACK */ if (len == 2) goto okay; /* Anything else won't work */ return 0; } /* In the case of an exact match: * If the caller needs info on the file to be selected, * make sure we at least select the file itself. * If the DF matches the current DF, just return the * FID */ if (i == len && need_info) { if (i > 1) { *pathptr = ptr + len - 1; *pathlen = len - 1; return 1; } /* bummer */ return 0; } okay: *pathptr = ptr + i; *pathlen = len - i; return 1; } static void ac_to_acl(unsigned int ac, sc_file_t *file, unsigned int op) { unsigned int npins, pin; npins = (ac >> 14) & 3; if (npins == 3) { sc_file_add_acl_entry(file, op, SC_AC_NEVER, SC_AC_KEY_REF_NONE); return; } sc_file_add_acl_entry(file, op, SC_AC_NONE, SC_AC_KEY_REF_NONE); pin = ac & 0xFF; if (npins >= 1) sc_file_add_acl_entry(file, op, SC_AC_CHV, (pin >> 4) & 0xF); if (npins == 2) sc_file_add_acl_entry(file, op, SC_AC_CHV, pin & 0xF); /* Check whether secure messaging key is specified */ if (ac & 0x3F00) sc_file_add_acl_entry(file, op, SC_AC_PRO, (ac & 0x3F00) >> 8); } /* * Convert ACLs requested by the application to access condition * bits supported by the GPK. Since these do not map 1:1 there's * some fuzz involved. */ static void acl_to_ac(sc_file_t *file, unsigned int op, u8 *ac) { const sc_acl_entry_t *acl; unsigned int npins = 0; ac[0] = ac[1] = 0; if ((acl = sc_file_get_acl_entry(file, op)) == NULL) return; assert(acl->method != SC_AC_UNKNOWN); switch (acl->method) { case SC_AC_NEVER: ac[0] = 0xC0; return; case SC_AC_NONE: return; } while (acl) { if (acl->method == SC_AC_CHV) { /* Support up to 2 PINS only */ if (++npins >= 2) continue; ac[1] >>= 4; ac[1] |= acl->key_ref << 4; ac[0] += 0x40; } if (acl->method == SC_AC_PRO) { ac[0] |= acl->key_ref & 0x1f; } acl = acl->next; } } static int gpk_parse_fci(sc_card_t *card, const u8 *buf, size_t buflen, sc_file_t *file) { const u8 *end, *next; unsigned int tag, len; end = buf + buflen; for (; buf + 2 < end; buf = next) { next = buf + 2 + buf[1]; if (next > end) break; tag = *buf++; len = *buf++; if (tag == 0x84) { /* unknown purpose - usually the name, but * the content looks weird, such as * 84 0D A0 00 00 00 18 0F 00 00 01 63 00 01 04 */ } else if (tag == 0xC1 && len >= 2) { /* Seems to be the file id, followed by something * C1 04 02 00 00 00 */ file->id = (buf[0] << 8) | buf[1]; } else if (tag == 0xC2) { /* unknown purpose * C2 01 01 */ } } return 0; } static int gpk_parse_fileinfo(sc_card_t *card, const u8 *buf, size_t buflen, sc_file_t *file) { const u8 *sp, *end, *next; int i, rc; memset(file, 0, sizeof(*file)); for (i = 0; i < SC_MAX_AC_OPS; i++) sc_file_add_acl_entry(file, i, SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE); end = buf + buflen; for (sp = buf; sp + 2 < end; sp = next) { next = sp + 2 + sp[1]; if (next > end) break; if (sp[0] == 0x84) { /* ignore if name is longer than what it should be */ if (sp[1] > sizeof(file->name)) continue; memset(file->name, 0, sizeof(file->name)); memcpy(file->name, sp+2, sp[1]); } else if (sp[0] == 0x85) { unsigned int ac[3], n; file->id = (sp[4] << 8) | sp[5]; file->size = (sp[8] << 8) | sp[9]; file->record_length = sp[7]; /* Map ACLs. Note the third AC byte is * valid of EFs only */ for (n = 0; n < 3; n++) ac[n] = (sp[10+2*n] << 8) | sp[11+2*n]; /* Examine file type */ switch (sp[6] & 7) { case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = sp[6] & 7; ac_to_acl(ac[0], file, SC_AC_OP_UPDATE); ac_to_acl(ac[1], file, SC_AC_OP_WRITE); ac_to_acl(ac[2], file, SC_AC_OP_READ); break; case 0x00: /* 0x38 is DF */ file->type = SC_FILE_TYPE_DF; /* Icky: the GPK uses different ACLs * for creating data files and * 'sensitive' i.e. key files */ ac_to_acl(ac[0], file, SC_AC_OP_LOCK); ac_to_acl(ac[1], file, SC_AC_OP_CREATE); sc_file_add_acl_entry(file, SC_AC_OP_SELECT, SC_AC_NONE, SC_AC_KEY_REF_NONE); sc_file_add_acl_entry(file, SC_AC_OP_DELETE, SC_AC_NEVER, SC_AC_KEY_REF_NONE); sc_file_add_acl_entry(file, SC_AC_OP_REHABILITATE, SC_AC_NEVER, SC_AC_KEY_REF_NONE); sc_file_add_acl_entry(file, SC_AC_OP_INVALIDATE, SC_AC_NEVER, SC_AC_KEY_REF_NONE); sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES, SC_AC_NEVER, SC_AC_KEY_REF_NONE); break; } } else if (sp[0] == 0x6f) { /* oops - this is a directory with an IADF. * This happens with the personalized GemSafe cards * for instance. */ file->type = SC_FILE_TYPE_DF; rc = gpk_parse_fci(card, sp + 2, sp[1], file); if (rc < 0) return rc; } } if (file->record_length) file->record_count = file->size / file->record_length; file->magic = SC_FILE_MAGIC; return 0; } static int gpk_select(sc_card_t *card, int kind, const u8 *buf, size_t buflen, sc_file_t **file) { struct gpk_private_data *priv = DRVDATA(card); sc_apdu_t apdu; u8 resbuf[256]; int r; /* If we're about to select a DF, invalidate secure messaging keys */ if (kind == GPK_SEL_MF || kind == GPK_SEL_DF) { memset(priv->key, 0, sizeof(priv->key)); priv->key_set = 0; } /* do the apdu thing */ memset(&apdu, 0, sizeof(apdu)); apdu.cla = 0x00; apdu.cse = SC_APDU_CASE_3_SHORT; apdu.ins = 0xA4; apdu.p1 = kind; apdu.p2 = 0; apdu.data = buf; apdu.datalen = buflen; apdu.lc = apdu.datalen; if (file) { apdu.cse = SC_APDU_CASE_4_SHORT; apdu.resp = resbuf; apdu.resplen = sizeof(resbuf); apdu.le = sizeof(resbuf); } r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); /* Nothing we can say about it... invalidate * path cache */ if (kind == GPK_SEL_AID) { card->cache.current_path.len = 0; } if (file == NULL) return 0; *file = sc_file_new(); r = gpk_parse_fileinfo(card, apdu.resp, apdu.resplen, *file); if (r < 0) { sc_file_free(*file); *file = NULL; } return r; } static int gpk_select_id(sc_card_t *card, int kind, unsigned int fid, sc_file_t **file) { sc_path_t *cp = &card->cache.current_path; u8 fbuf[2]; int r; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "gpk_select_id(0x%04X, kind=%u)\n", fid, kind); fbuf[0] = fid >> 8; fbuf[1] = fid & 0xff; r = gpk_select(card, kind, fbuf, 2, file); /* Fix up the path cache. * NB we never cache the ID of an EF, just the DF path */ if (r == 0) { unsigned short int *path; switch (kind) { case GPK_SEL_MF: cp->len = 0; /* fallthru */ case GPK_SEL_DF: assert(cp->len + 1 <= SC_MAX_PATH_SIZE / 2); path = (unsigned short int *) cp->value; path[cp->len++] = fid; } } else { cp->len = 0; } return r; } static int gpk_select_file(sc_card_t *card, const sc_path_t *path, sc_file_t **file) { unsigned short int pathtmp[SC_MAX_PATH_SIZE/2]; unsigned short int *pathptr; size_t pathlen, n; int locked = 0, r = 0, use_relative = 0, retry = 1; u8 leaf_type; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* Handle the AID case first */ if (path->type == SC_PATH_TYPE_DF_NAME) { if (path->len > 16) return SC_ERROR_INVALID_ARGUMENTS; r = gpk_select(card, GPK_SEL_AID, path->value, path->len, file); goto done; } /* Now we know we're dealing with 16bit FIDs, either as * an absolute path name (SC_PATH_TYPE_PATH) or a relative * FID (SC_PATH_TYPE_FILE_ID) * * The API should really tell us whether this is a DF or EF * we're selecting. All we can do is read tea leaves... */ leaf_type = GPK_SEL_EF; try_again: if ((path->len & 1) || path->len > sizeof(pathtmp)) return SC_ERROR_INVALID_ARGUMENTS; pathptr = pathtmp; for (n = 0; n < path->len; n += 2) pathptr[n>>1] = (path->value[n] << 8)|path->value[n+1]; pathlen = path->len >> 1; /* See whether we can skip an initial portion of the * (absolute) path */ if (path->type == SC_PATH_TYPE_PATH) { /* Do not retry selecting if this cannot be a DF */ if ((pathptr[0] == GPK_FID_MF && pathlen > 2) || (pathptr[0] != GPK_FID_MF && pathlen > 1)) retry = 0; use_relative = match_path(card, &pathptr, &pathlen, file != 0); if (pathlen == 0) goto done; } else { /* SC_PATH_TYPE_FILEID */ if (pathlen > 1) return SC_ERROR_INVALID_ARGUMENTS; use_relative = 1; } if (pathlen == 1 && pathptr[0] == GPK_FID_MF) { /* Select just the MF */ leaf_type = GPK_SEL_MF; } else { if (!locked++) { r = sc_lock(card); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed"); } /* Do we need to select the MF first? */ if (!use_relative) { r = gpk_select_id(card, GPK_SEL_MF, GPK_FID_MF, NULL); if (r) sc_unlock(card); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to select MF"); /* Consume the MF FID if it's there */ if (pathptr[0] == GPK_FID_MF) { pathptr++; pathlen--; } if (pathlen == 0) goto done; } /* Next comes a DF, if at all. * This loop can deal with nesting levels > 1 even * though the GPK4000 doesn't support it. */ while (pathlen > 1) { r = gpk_select_id(card, GPK_SEL_DF, pathptr[0], NULL); if (r) sc_unlock(card); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to select DF"); pathptr++; pathlen--; } } /* Remaining component will be a DF or EF. How do we find out? * All we can do is try */ r = gpk_select_id(card, leaf_type, pathptr[0], file); if (r) { /* Did we guess EF, and were wrong? If so, invalidate * path cache and try again; this time aiming for a DF */ if (leaf_type == GPK_SEL_EF && retry) { card->cache.current_path.len = 0; leaf_type = GPK_SEL_DF; goto try_again; } } done: if (locked) sc_unlock(card); return r; } /* * GPK versions of {read,write,update}_binary functions. * Required because by default the GPKs do word offsets */ static int gpk_read_binary(sc_card_t *card, unsigned int offset, u8 *buf, size_t count, unsigned long flags) { struct gpk_private_data *priv = DRVDATA(card); if (offset & priv->offset_mask) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid file offset (not a multiple of %d)", priv->offset_mask + 1); return SC_ERROR_INVALID_ARGUMENTS; } return iso_ops->read_binary(card, offset >> priv->offset_shift, buf, count, flags); } static int gpk_write_binary(sc_card_t *card, unsigned int offset, const u8 *buf, size_t count, unsigned long flags) { struct gpk_private_data *priv = DRVDATA(card); if (offset & priv->offset_mask) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid file offset (not a multiple of %d)", priv->offset_mask + 1); return SC_ERROR_INVALID_ARGUMENTS; } return iso_ops->write_binary(card, offset >> priv->offset_shift, buf, count, flags); } static int gpk_update_binary(sc_card_t *card, unsigned int offset, const u8 *buf, size_t count, unsigned long flags) { struct gpk_private_data *priv = DRVDATA(card); if (offset & priv->offset_mask) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid file offset (not a multiple of %d)", priv->offset_mask + 1); return SC_ERROR_INVALID_ARGUMENTS; } return iso_ops->update_binary(card, offset >> priv->offset_shift, buf, count, flags); } /* * Secure messaging */ static int gpk_compute_crycks(sc_card_t *card, sc_apdu_t *apdu, u8 *crycks1) { struct gpk_private_data *priv = DRVDATA(card); u8 in[8], out[8], block[64]; unsigned int len = 0, i; int r = SC_SUCCESS, outl; EVP_CIPHER_CTX ctx; /* Fill block with 0x00 and then with the data. */ memset(block, 0x00, sizeof(block)); block[len++] = apdu->cla; block[len++] = apdu->ins; block[len++] = apdu->p1; block[len++] = apdu->p2; block[len++] = apdu->lc + 3; if ((i = apdu->datalen) + len > sizeof(block)) i = sizeof(block) - len; memcpy(block+len, apdu->data, i); len += i; /* Set IV */ memset(in, 0x00, 8); EVP_CIPHER_CTX_init(&ctx); EVP_EncryptInit_ex(&ctx, EVP_des_ede_cbc(), NULL, priv->key, in); for (i = 0; i < len; i += 8) { if (!EVP_EncryptUpdate(&ctx, out, &outl, &block[i], 8)) { r = SC_ERROR_INTERNAL; break; } } if (!EVP_CIPHER_CTX_cleanup(&ctx)) r = SC_ERROR_INTERNAL; memcpy((u8 *) (apdu->data + apdu->datalen), out + 5, 3); apdu->datalen += 3; apdu->lc += 3; apdu->le += 3; if (crycks1) memcpy(crycks1, out, 3); sc_mem_clear(in, sizeof(in)); sc_mem_clear(out, sizeof(out)); sc_mem_clear(block, sizeof(block)); return r; } /* * Verify secure messaging response */ static int gpk_verify_crycks(sc_card_t *card, sc_apdu_t *apdu, u8 *crycks) { if (apdu->resplen < 3 || memcmp(apdu->resp + apdu->resplen - 3, crycks, 3)) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid secure messaging reply\n"); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } apdu->resplen -= 3; return 0; } /* * Create a file or directory. * This is a bit tricky because we abuse the ef_structure * field to transport file types that are non-standard * (the GPK4000 has lots of bizarre file types). */ static int gpk_create_file(sc_card_t *card, sc_file_t *file) { struct gpk_private_data *priv = DRVDATA(card); sc_apdu_t apdu; u8 data[28+3], crycks[3], resp[3]; size_t datalen, namelen; int r; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "gpk_create_file(0x%04X)\n", file->id); /* Prepare APDU */ memset(&apdu, 0, sizeof(apdu)); apdu.cla = 0x80; /* assume no secure messaging */ apdu.cse = SC_APDU_CASE_3_SHORT; apdu.ins = 0xE0; apdu.p2 = 0x00; /* clear data */ memset(data, 0, sizeof(data)); datalen = 12; /* FID */ data[0] = file->id >> 8; data[1] = file->id & 0xFF; /* encode ACLs */ if (file->type == SC_FILE_TYPE_DF) { /* The GPK4000 has separate AC bits for * creating sensitive files and creating * data files. Since OpenSC has just the notion * of "file" we use the same ACL for both AC words */ apdu.p1 = 0x01; /* create DF */ data[2] = 0x38; acl_to_ac(file, SC_AC_OP_CREATE, data + 6); acl_to_ac(file, SC_AC_OP_CREATE, data + 8); if ((namelen = file->namelen) != 0) { if (namelen > 16) return SC_ERROR_INVALID_ARGUMENTS; memcpy(data+datalen, file->name, namelen); data[5] = namelen; datalen += namelen; } } else { apdu.p1 = 0x02; /* create EF */ data[2] = file->ef_structure; data[3] = file->record_length; data[4] = file->size >> 8; data[5] = file->size & 0xff; acl_to_ac(file, SC_AC_OP_UPDATE, data + 6); acl_to_ac(file, SC_AC_OP_WRITE, data + 8); acl_to_ac(file, SC_AC_OP_READ, data + 10); } apdu.data = data; apdu.datalen = datalen; apdu.lc = datalen; if (priv->key_set) { apdu.cla = 0x84; apdu.cse = SC_APDU_CASE_4_SHORT; r = gpk_compute_crycks(card, &apdu, crycks); if (r) return r; apdu.resp = resp; apdu.resplen = sizeof(resp); /* XXX? */ } r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); /* verify secure messaging response */ if (priv->key_set) r = gpk_verify_crycks(card, &apdu, crycks); return r; } /* * Set the secure messaging key following a Select FileKey */ static int gpk_set_filekey(const u8 *key, const u8 *challenge, const u8 *r_rn, u8 *kats) { int r = SC_SUCCESS, outl; EVP_CIPHER_CTX ctx; u8 out[16]; memcpy(out, key+8, 8); memcpy(out+8, key, 8); EVP_CIPHER_CTX_init(&ctx); EVP_EncryptInit_ex(&ctx, EVP_des_ede(), NULL, key, NULL); if (!EVP_EncryptUpdate(&ctx, kats, &outl, r_rn+4, 8)) r = SC_ERROR_INTERNAL; if (!EVP_CIPHER_CTX_cleanup(&ctx)) r = SC_ERROR_INTERNAL; if (r == SC_SUCCESS) { EVP_CIPHER_CTX_init(&ctx); EVP_EncryptInit_ex(&ctx, EVP_des_ede(), NULL, out, NULL); if (!EVP_EncryptUpdate(&ctx, kats+8, &outl, r_rn+4, 8)) r = SC_ERROR_INTERNAL; if (!EVP_CIPHER_CTX_cleanup(&ctx)) r = SC_ERROR_INTERNAL; } memset(out, 0, sizeof(out)); /* Verify Cryptogram presented by the card terminal * XXX: what is the appropriate error code to return * here? INVALID_ARGS doesn't seem quite right */ if (r == SC_SUCCESS) { EVP_CIPHER_CTX_init(&ctx); EVP_EncryptInit_ex(&ctx, EVP_des_ede(), NULL, kats, NULL); if (!EVP_EncryptUpdate(&ctx, out, &outl, challenge, 8)) r = SC_ERROR_INTERNAL; if (!EVP_CIPHER_CTX_cleanup(&ctx)) r = SC_ERROR_INTERNAL; if (memcmp(r_rn, out+4, 4) != 0) r = SC_ERROR_INVALID_ARGUMENTS; } sc_mem_clear(out, sizeof(out)); return r; } /* * Verify a key presented by the user for secure messaging */ static int gpk_select_key(sc_card_t *card, int key_sfi, const u8 *buf, size_t buflen) { struct gpk_private_data *priv = DRVDATA(card); sc_apdu_t apdu; u8 rnd[8], resp[258]; int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (buflen != 16) return SC_ERROR_INVALID_ARGUMENTS; /* now do the SelFk */ RAND_pseudo_bytes(rnd, sizeof(rnd)); memset(&apdu, 0, sizeof(apdu)); apdu.cla = 0x80; apdu.cse = SC_APDU_CASE_4_SHORT; apdu.ins = 0x28; apdu.p1 = 0; apdu.p2 = key_sfi; apdu.data = rnd; apdu.datalen = sizeof(rnd); apdu.lc = apdu.datalen; apdu.resp = resp; apdu.resplen = sizeof(resp); apdu.le = 12; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); if (apdu.resplen != 12) { r = SC_ERROR_UNKNOWN_DATA_RECEIVED; } else if ((r = gpk_set_filekey(buf, rnd, resp, priv->key)) == 0) { priv->key_set = 1; priv->key_reference = key_sfi; } sc_mem_clear(resp, sizeof(resp)); return r; } /* * Select a security environment (Set Crypto Context in GPK docs). * When we get here, the PK file has already been selected. * * Issue: the GPK distinguishes between "signing" and * "card internal authentication". I don't know whether this * makes any difference in practice... * * Issue: it seems that sc_compute_signature() does not hash * the data for the caller. So what is the purpose of HASH_SHA * and other flags? */ static int gpk_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { struct gpk_private_data *priv = DRVDATA(card); sc_apdu_t apdu; unsigned int context, algorithm; unsigned int file_id; u8 sysrec[7]; int r; /* According to several sources from GemPlus, they don't * have off the shelf cards that do DSA. So I won't bother * with implementing this stuff here. */ algorithm = SC_ALGORITHM_RSA; if (env->flags & SC_SEC_ENV_ALG_PRESENT) algorithm = env->algorithm; if (algorithm != SC_ALGORITHM_RSA) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Algorithm not supported.\n"); return SC_ERROR_NOT_SUPPORTED; } priv->sec_algorithm = algorithm; /* If there's a key reference, it must be 0 */ if ((env->flags & SC_SEC_ENV_KEY_REF_PRESENT) && (env->key_ref_len != 1 || env->key_ref[0] != 0)) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown key referenced.\n"); return SC_ERROR_NOT_SUPPORTED; } /* Right now, the OpenSC flags do not support any padding * other than PKCS#1. */ if (env->flags & SC_ALGORITHM_RSA_PAD_PKCS1) priv->sec_padding = 0; else if (env->flags & SC_ALGORITHM_RSA_PAD_ANSI) priv->sec_padding = 1; else if (env->flags & SC_ALGORITHM_RSA_PAD_ISO9796) priv->sec_padding = 2; else { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Padding algorithm not supported.\n"); return SC_ERROR_NOT_SUPPORTED; } switch (env->operation) { case SC_SEC_OPERATION_SIGN: /* Again, the following may not make any difference * because we don't do any hashing on-card. But * what the hell, we have all those nice macros, * so why not use them :) */ if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) { context = GPK_SIGN_RSA_SHA; priv->sec_hash_len = 20; } else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5_SHA1) { context = GPK_SIGN_RSA_SSL; priv->sec_hash_len = 36; } else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5) { context = GPK_SIGN_RSA_MD5; priv->sec_hash_len = 16; } else { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported signature algorithm"); return SC_ERROR_NOT_SUPPORTED; } break; case SC_SEC_OPERATION_DECIPHER: context = GPK_UNWRAP_RSA; break; default: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Crypto operation not supported.\n"); return SC_ERROR_NOT_SUPPORTED; } /* Get the file ID */ if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) { if (env->file_ref.len != 2) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File reference: invalid length.\n"); return SC_ERROR_INVALID_ARGUMENTS; } file_id = (env->file_ref.value[0] << 8) | env->file_ref.value[1]; } else { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File reference missing.\n"); return SC_ERROR_INVALID_ARGUMENTS; } /* Select the PK file. The caller has already selected * the DF. */ r = gpk_select_id(card, GPK_SEL_EF, file_id, NULL); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to select PK file"); /* Read the sys record of the PK file to find out the key length */ r = sc_read_record(card, 1, sysrec, sizeof(sysrec), SC_RECORD_BY_REC_NR); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to read PK sysrec"); if (r != 7 || sysrec[0] != 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "First record of file is not the sysrec"); return SC_ERROR_OBJECT_NOT_VALID; } if (sysrec[5] != 0x00) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Public key is not an RSA key"); return SC_ERROR_OBJECT_NOT_VALID; } switch (sysrec[1]) { case 0x00: priv->sec_mod_len = 512 / 8; break; case 0x10: priv->sec_mod_len = 768 / 8; break; case 0x11: priv->sec_mod_len = 1024 / 8; break; default: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported modulus length"); return SC_ERROR_OBJECT_NOT_VALID; } /* Now do SelectCryptoContext */ memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_1; apdu.cla = 0x80; apdu.ins = 0xA6; apdu.p1 = file_id & 0x1f; apdu.p2 = context; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); return r; } /* * Restore security environment * Not sure what this is supposed to do. */ static int gpk_restore_security_env(sc_card_t *card, int se_num) { return 0; } /* * Revert buffer (needed for all GPK crypto operations because * they want LSB byte order internally */ static int reverse(u8 *out, size_t outlen, const u8 *in, size_t inlen) { if (inlen > outlen) return SC_ERROR_BUFFER_TOO_SMALL; outlen = inlen; while (inlen--) *out++ = in[inlen]; return outlen; } /* * Use the card's on-board hashing functions to hash some data */ #ifdef dontuse static int gpk_hash(sc_card_t *card, const u8 *data, size_t datalen) { sc_apdu_t apdu; unsigned int count, chain, len; int r; chain = 0x01; for (count = 0; count < datalen; count += len) { unsigned char buffer[GPK_HASH_CHUNK+2]; if ((len = datalen - count) > GPK_HASH_CHUNK) len = GPK_HASH_CHUNK; else chain |= 0x10; buffer[0] = 0x55; buffer[1] = len; memcpy(buffer+2, data + count, len); memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_3_SHORT; apdu.cla = 0x80; apdu.ins = 0xDA; apdu.p1 = chain; apdu.p2 = len; apdu.lc = len + 2; apdu.data= buffer; apdu.datalen = len + 2; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); chain = 0; } return 0; } #endif /* * Send the hashed data to the card. */ static int gpk_init_hashed(sc_card_t *card, const u8 *digest, unsigned int len) { sc_apdu_t apdu; u8 tsegid[64]; int r; r = reverse(tsegid, sizeof(tsegid), digest, len); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to reverse buffer"); memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_3_SHORT; apdu.cla = 0x80; apdu.ins = 0xEA; apdu.lc = len; apdu.data= tsegid; apdu.datalen = len; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); return r; } /* * Compute a signature. * Note we hash everything manually and send it to the card. */ static int gpk_compute_signature(sc_card_t *card, const u8 *data, size_t data_len, u8 * out, size_t outlen) { struct gpk_private_data *priv = DRVDATA(card); sc_apdu_t apdu; u8 cardsig[1024/8]; int r; if (data_len > priv->sec_mod_len) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Data length (%u) does not match key modulus %u.\n", data_len, priv->sec_mod_len); return SC_ERROR_INTERNAL; } if (sizeof(cardsig) < priv->sec_mod_len) return SC_ERROR_BUFFER_TOO_SMALL; r = gpk_init_hashed(card, data, data_len); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to send hash to card"); /* Now sign the hash. * The GPK has Internal Authenticate and PK_Sign. I am not * sure what the difference between the two is. */ memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_2_SHORT; apdu.cla = 0x80; apdu.ins = 0x86; #if 0 /* Don't know why I did this. It conflicts with the spec * (but it worked with the gpk4k, strange). --okir */ apdu.p1 = priv->sec_padding; apdu.p2 = priv->sec_mod_len; #else apdu.p2 = priv->sec_padding; #endif apdu.resp= cardsig; apdu.resplen = sizeof(cardsig); apdu.le = priv->sec_mod_len; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); /* The GPK returns the signature as little endian numbers. * Need to revert these */ r = reverse(out, outlen, cardsig, apdu.resplen); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to reverse signature"); return r; } /* * Decrypt some RSA encrypted piece of data. * Due to legal restrictions, the GPK will not let you see the * full cleartext block, just the last N bytes. * The GPK documentation refers to N as the MaxSessionKey size, * probably because this feature limits the maximum size of an * SSL session key you will be able to decrypt using this card. */ static int gpk_decipher(sc_card_t *card, const u8 *in, size_t inlen, u8 *out, size_t outlen) { struct gpk_private_data *priv = DRVDATA(card); sc_apdu_t apdu; u8 buffer[256]; int r; if (inlen != priv->sec_mod_len) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Data length (%u) does not match key modulus %u.\n", inlen, priv->sec_mod_len); return SC_ERROR_INVALID_ARGUMENTS; } /* First revert the cryptogram */ r = reverse(buffer, sizeof(buffer), in, inlen); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Cryptogram too large"); in = buffer; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x1C, 0x00, 0x00); apdu.cla |= 0x80; apdu.lc = inlen; apdu.data = in; apdu.datalen = inlen; apdu.le = 256; /* give me all you got :) */ apdu.resp = buffer; apdu.resplen = sizeof(buffer); r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); /* Reverse the data we got back */ r = reverse(out, outlen, buffer, apdu.resplen); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to reverse buffer"); return r; } /* * Erase card */ static int gpk_erase_card(sc_card_t *card) { struct gpk_private_data *priv = DRVDATA(card); sc_apdu_t apdu; u8 offset; int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); switch (card->type) { case SC_CARD_TYPE_GPK_GPK4000_su256: case SC_CARD_TYPE_GPK_GPK4000_sdo: offset = 0x6B; /* courtesy gemplus hotline */ break; case SC_CARD_TYPE_GPK_GPK4000_s: offset = 7; break; case SC_CARD_TYPE_GPK_GPK8000: case SC_CARD_TYPE_GPK_GPK8000_8K: case SC_CARD_TYPE_GPK_GPK8000_16K: case SC_CARD_TYPE_GPK_GPK16000: offset = 0; break; default: return SC_ERROR_NOT_SUPPORTED; } memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_1; apdu.cla = 0xDB; apdu.ins = 0xDE; apdu.p2 = offset; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); priv->key_set = 0; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } /* * Lock a file Access Condition. * * File must be selected, and we assume that any authentication * that needs to be presented in order to allow this operation * have been presented (ACs from the DF; AC1 for sensitive files, * AC2 for normal files). */ static int gpk_lock(sc_card_t *card, struct sc_cardctl_gpk_lock *args) { struct gpk_private_data *priv = DRVDATA(card); sc_file_t *file = args->file; sc_apdu_t apdu; u8 data[8], crycks[3], resp[3]; int r; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "gpk_lock(0x%04X, %u)\n", file->id, args->operation); memset(data, 0, sizeof(data)); data[0] = file->id >> 8; data[1] = file->id; switch (args->operation) { case SC_AC_OP_UPDATE: data[2] = 0x40; break; case SC_AC_OP_WRITE: data[3] = 0x40; break; case SC_AC_OP_READ: data[4] = 0x40; break; default: return SC_ERROR_INVALID_ARGUMENTS; } memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_3_SHORT; apdu.cla = 0x80; apdu.ins = 0x16; apdu.p1 = (file->type == SC_FILE_TYPE_DF)? 1 : 2; apdu.p2 = 0; apdu.lc = 5; apdu.datalen = 5; apdu.data = data; if (priv->key_set) { apdu.cla = 0x84; apdu.cse = SC_APDU_CASE_4_SHORT; r = gpk_compute_crycks(card, &apdu, crycks); if (r) return r; apdu.resp = resp; apdu.resplen = sizeof(resp); /* XXX? */ } r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); if (priv->key_set) r = gpk_verify_crycks(card, &apdu, crycks); return r; } /* * Initialize the private portion of a public key file */ static int gpk_pkfile_init(sc_card_t *card, struct sc_cardctl_gpk_pkinit *args) { sc_apdu_t apdu; int r; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "gpk_pkfile_init(%u)\n", args->privlen); memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_1; apdu.cla = 0x80; apdu.ins = 0x12; apdu.p1 = args->file->id & 0x1F; apdu.p2 = args->privlen / 4; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); return r; } /* * Initialize the private portion of a public key file */ static int gpk_generate_key(sc_card_t *card, struct sc_cardctl_gpk_genkey *args) { sc_apdu_t apdu; int r; u8 buffer[256]; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "gpk_generate_key(%u)\n", args->privlen); if (args->privlen != 512 && args->privlen != 1024) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Key generation not supported for key length %d", args->privlen); return SC_ERROR_NOT_SUPPORTED; } memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_2_SHORT; apdu.cla = 0x80; apdu.ins = 0xD2; apdu.p1 = 0x80 | (args->fid & 0x1F); apdu.p2 = (args->privlen == 1024) ? 0x11 : 0; apdu.le = args->privlen / 8 + 2; apdu.resp = buffer; apdu.resplen = 256; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); /* Return the public key, inverted. * The first two bytes must be stripped off. */ if (args->pubkey_len && apdu.resplen > 2) { r = reverse(args->pubkey, args->pubkey_len, buffer + 2, apdu.resplen - 2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to reverse buffer"); args->pubkey_len = r; } return r; } /* * Store a privat key component */ static int gpk_pkfile_load(sc_card_t *card, struct sc_cardctl_gpk_pkload *args) { struct gpk_private_data *priv = DRVDATA(card); sc_apdu_t apdu; unsigned int n; u8 temp[256]; int r = SC_SUCCESS, outl; EVP_CIPHER_CTX ctx; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "gpk_pkfile_load(fid=%04x, len=%d, datalen=%d)\n", args->file->id, args->len, args->datalen); if (0) { char buf[2048]; sc_hex_dump(card->ctx, SC_LOG_DEBUG_NORMAL, args->data, args->datalen, buf, sizeof(buf)); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Sending %d bytes (cleartext):\n%s", args->datalen, buf); } memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_3_SHORT; apdu.cla = 0x80; apdu.ins = 0x18; apdu.p1 = args->file->id & 0x1F; apdu.p2 = args->len; apdu.lc = args->datalen; /* encrypt the private key material */ assert(args->datalen <= sizeof(temp)); if (!priv->key_set) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No secure messaging key set!\n"); return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; } EVP_CIPHER_CTX_init(&ctx); EVP_EncryptInit_ex(&ctx, EVP_des_ede(), NULL, priv->key, NULL); for (n = 0; n < args->datalen; n += 8) { if (!EVP_EncryptUpdate(&ctx, temp+n, &outl, args->data + n, 8)) { r = SC_ERROR_INTERNAL; break; } } if (!EVP_CIPHER_CTX_cleanup(&ctx) || r != SC_SUCCESS) return SC_ERROR_INTERNAL; apdu.data = temp; apdu.datalen = args->datalen; /* Forget the key. The card seems to forget it, too :) */ priv->key_set = 0; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } /* * This function lets pkcs15init query for the transport key */ static int gpk_get_default_key(sc_card_t *card, struct sc_cardctl_default_key *data) { if (data->method == SC_AC_PRO && data->key_ref == 1) { if (data->len < 16) return SC_ERROR_BUFFER_TOO_SMALL; memcpy(data->key_data, "TEST KEYTEST KEY", 16); data->len = 16; return 0; } return SC_ERROR_NO_DEFAULT_KEY; } /* * Get the maximum size of a session key the card is * willing to decrypt */ #if 0 static int gpk_max_session_key(sc_card_t *card) { struct gpk_private_data *priv = DRVDATA(card); sc_path_t path; u8 value; int r; if (priv->max_session_key) return priv->max_session_key; /* GPK cards limit the amount of data they're willing * to RSA decrypt. This data is stored in EFMaxSessionKey */ sc_format_path("01000001", &path); if ((r = sc_select_file(card, &path, NULL)) < 0 || (r = sc_read_binary(card, 0, &value, 1, 0)) < 0) return r; priv->max_session_key = value; return value; } #endif /* * GetInfo call */ static int gpk_get_info(sc_card_t *card, int p1, int p2, u8 *buf, size_t buflen) { sc_apdu_t apdu; int r, retry = 0; /* We may have to retry the get info command. It * returns 6B00 if a previous command returned a 61xx response, * but the host failed to collect the results. * * Note the additional sc_lock/sc_unlock pair, which * is required to prevent sc_transmit_apdu from * calling logout(), which in turn does a SELECT MF * without collecting the response :) */ r = sc_lock(card); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed"); do { memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_2_SHORT; apdu.cla = 0x80; apdu.ins = 0xC0; apdu.p1 = p1; apdu.p2 = p2; apdu.le = buflen; apdu.resp = buf; apdu.resplen = buflen; if ((r = sc_transmit_apdu(card, &apdu)) < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed: %s", sc_strerror(r)); sc_unlock(card); return r; } } while (apdu.sw1 == 0x6B && apdu.sw2 == 0x00 && retry++ < 1); sc_unlock(card); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); return r; } static int gpk_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) { int r; u8 rbuf[10]; sc_apdu_t apdu; if (card->type != SC_CARD_TYPE_GPK_GPK16000) return SC_ERROR_NOT_SUPPORTED; if (!serial) return SC_ERROR_INVALID_ARGUMENTS; /* see if we have cached serial number */ if (card->serialnr.len) { memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } /* get serial number via Get CSN */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xb8, 0x00, 0x00); apdu.cla |= 0x80; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 8; apdu.lc = 0; apdu.datalen = 0; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return SC_ERROR_INTERNAL; /* cache serial number */ memcpy(card->serialnr.value, apdu.resp, apdu.resplen); card->serialnr.len = apdu.resplen; /* copy and return serial number */ memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } /* * Dispatch card_ctl calls */ static int gpk_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { switch (cmd) { case SC_CARDCTL_ERASE_CARD: return gpk_erase_card(card); case SC_CARDCTL_GET_DEFAULT_KEY: return gpk_get_default_key(card, (struct sc_cardctl_default_key *) ptr); case SC_CARDCTL_GPK_VARIANT: *(int *) ptr = card->type; return 0; case SC_CARDCTL_GPK_LOCK: return gpk_lock(card, (struct sc_cardctl_gpk_lock *) ptr); case SC_CARDCTL_GPK_PKINIT: return gpk_pkfile_init(card, (struct sc_cardctl_gpk_pkinit *) ptr); case SC_CARDCTL_GPK_PKLOAD: return gpk_pkfile_load(card, (struct sc_cardctl_gpk_pkload *) ptr); case SC_CARDCTL_GPK_IS_LOCKED: *(int *) ptr = DRVDATA(card)->locked; return 0; case SC_CARDCTL_GPK_GENERATE_KEY: return gpk_generate_key(card, (struct sc_cardctl_gpk_genkey *) ptr); case SC_CARDCTL_GET_SERIALNR: return gpk_get_serialnr(card, (sc_serial_number_t *) ptr); } return SC_ERROR_NOT_SUPPORTED; } static int gpk_build_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, struct sc_pin_cmd_data *data) { static u8 sbuf[8]; int r; if (data->pin_type != SC_AC_CHV) return SC_ERROR_INVALID_ARGUMENTS; /* XXX deal with secure messaging here */ memset(apdu, 0, sizeof(*apdu)); apdu->cse = SC_APDU_CASE_3_SHORT; data->flags |= SC_PIN_CMD_NEED_PADDING; switch (data->cmd) { case SC_PIN_CMD_VERIFY: /* Copy PIN to buffer and pad */ data->pin1.encoding = SC_PIN_ENCODING_ASCII; data->pin1.pad_length = 8; data->pin1.pad_char = 0x00; data->pin1.offset = 5; r = sc_build_pin(sbuf, 8, &data->pin1, 1); if (r < 0) return r; apdu->cla = 0x00; apdu->ins = 0x20; apdu->p1 = 0x00; break; case SC_PIN_CMD_CHANGE: case SC_PIN_CMD_UNBLOCK: /* Copy PINs to buffer, BCD-encoded, and pad */ data->pin1.encoding = SC_PIN_ENCODING_BCD; data->pin1.pad_length = 8; data->pin1.pad_char = 0x00; data->pin1.offset = 5; data->pin2.encoding = SC_PIN_ENCODING_BCD; data->pin2.pad_length = 8; data->pin2.pad_char = 0x00; data->pin2.offset = 5 + 4; if ((r = sc_build_pin(sbuf, 4, &data->pin1, 1)) < 0 || (r = sc_build_pin(sbuf + 4, 4, &data->pin2, 1)) < 0) return r; apdu->cla = 0x80; apdu->ins = 0x24; apdu->p1 = (data->cmd == SC_PIN_CMD_CHANGE)? 0x00 : 0x01; break; default: return SC_ERROR_NOT_SUPPORTED; } apdu->p2 = data->pin_reference & 7; apdu->lc = 8; apdu->datalen = 8; apdu->data = sbuf; return 0; } static int gpk_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { sc_apdu_t apdu; int r; /* Special case - External Authenticate */ if (data->cmd == SC_PIN_CMD_VERIFY && data->pin_type == SC_AC_PRO) return gpk_select_key(card, data->pin_reference, data->pin1.data, data->pin1.len); r = gpk_build_pin_apdu(card, &apdu, data); if (r < 0) return r; data->apdu = &apdu; return iso_ops->pin_cmd(card, data, tries_left); } /* * Initialize the driver struct */ static struct sc_card_driver * sc_get_driver(void) { struct sc_card_driver *iso_drv; iso_drv = sc_get_iso7816_driver(); iso_ops = iso_drv->ops; gpk_ops = *iso_ops; gpk_ops.match_card = gpk_match_card; gpk_ops.init = gpk_init; gpk_ops.finish = gpk_finish; gpk_ops.select_file = gpk_select_file; gpk_ops.read_binary = gpk_read_binary; gpk_ops.write_binary = gpk_write_binary; gpk_ops.update_binary = gpk_update_binary; gpk_ops.create_file = gpk_create_file; /* gpk_ops.check_sw = gpk_check_sw; */ gpk_ops.card_ctl = gpk_card_ctl; gpk_ops.set_security_env= gpk_set_security_env; gpk_ops.restore_security_env= gpk_restore_security_env; gpk_ops.compute_signature= gpk_compute_signature; gpk_ops.decipher = gpk_decipher; gpk_ops.pin_cmd = gpk_pin_cmd; return &gpk_drv; } struct sc_card_driver * sc_get_gpk_driver(void) { return sc_get_driver(); } #endif /* ENABLE_OPENSSL */ opensc-0.13.0/src/libopensc/ctx.c0000644000015201777760000006056412057406034013547 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #include #include #ifdef _WIN32 #include #include #endif #include "common/libscdl.h" #include "internal.h" int _sc_add_reader(sc_context_t *ctx, sc_reader_t *reader) { assert(reader != NULL); reader->ctx = ctx; list_append(&ctx->readers, reader); return SC_SUCCESS; } int _sc_delete_reader(sc_context_t *ctx, sc_reader_t *reader) { assert(reader != NULL); if (reader->ops->release) reader->ops->release(reader); if (reader->name) free(reader->name); list_delete(&ctx->readers, reader); free(reader); return SC_SUCCESS; } struct _sc_driver_entry { const char *name; void *(*func)(void); }; static const struct _sc_driver_entry internal_card_drivers[] = { { "cardos", (void *(*)(void)) sc_get_cardos_driver }, { "flex", (void *(*)(void)) sc_get_cryptoflex_driver }, { "cyberflex", (void *(*)(void)) sc_get_cyberflex_driver }, #ifdef ENABLE_OPENSSL { "gpk", (void *(*)(void)) sc_get_gpk_driver }, #endif { "gemsafeV1", (void *(*)(void)) sc_get_gemsafeV1_driver }, { "miocos", (void *(*)(void)) sc_get_miocos_driver }, { "mcrd", (void *(*)(void)) sc_get_mcrd_driver }, { "asepcos", (void *(*)(void)) sc_get_asepcos_driver }, { "starcos", (void *(*)(void)) sc_get_starcos_driver }, { "tcos", (void *(*)(void)) sc_get_tcos_driver }, { "openpgp", (void *(*)(void)) sc_get_openpgp_driver }, { "jcop", (void *(*)(void)) sc_get_jcop_driver }, #ifdef ENABLE_OPENSSL { "oberthur", (void *(*)(void)) sc_get_oberthur_driver }, { "authentic", (void *(*)(void)) sc_get_authentic_driver }, { "iasecc", (void *(*)(void)) sc_get_iasecc_driver }, #endif { "belpic", (void *(*)(void)) sc_get_belpic_driver }, { "ias", (void *(*)(void)) sc_get_ias_driver }, { "incrypto34", (void *(*)(void)) sc_get_incrypto34_driver }, { "acos5", (void *(*)(void)) sc_get_acos5_driver }, { "akis", (void *(*)(void)) sc_get_akis_driver }, #ifdef ENABLE_OPENSSL { "entersafe",(void *(*)(void)) sc_get_entersafe_driver }, #ifdef ENABLE_SM { "epass2003",(void *(*)(void)) sc_get_epass2003_driver }, #endif #endif { "rutoken", (void *(*)(void)) sc_get_rutoken_driver }, { "rutoken_ecp",(void *(*)(void)) sc_get_rtecp_driver }, { "westcos", (void *(*)(void)) sc_get_westcos_driver }, { "myeid", (void *(*)(void)) sc_get_myeid_driver }, { "sc-hsm", (void *(*)(void)) sc_get_sc_hsm_driver }, /* Here should be placed drivers that need some APDU transactions to * recognise its cards. */ { "setcos", (void *(*)(void)) sc_get_setcos_driver }, { "muscle", (void *(*)(void)) sc_get_muscle_driver }, { "atrust-acos",(void *(*)(void)) sc_get_atrust_acos_driver }, { "PIV-II", (void *(*)(void)) sc_get_piv_driver }, { "itacns", (void *(*)(void)) sc_get_itacns_driver }, /* javacard without supported applet - last before default */ { "javacard", (void *(*)(void)) sc_get_javacard_driver }, /* The default driver should be last, as it handles all the * unrecognized cards. */ { "default", (void *(*)(void)) sc_get_default_driver }, { NULL, NULL } }; struct _sc_ctx_options { struct _sc_driver_entry cdrv[SC_MAX_CARD_DRIVERS]; int ccount; char *forced_card_driver; }; /* 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 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->paranoid_memory = 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); /* 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 int load_parameters(sc_context_t *ctx, scconf_block *block, struct _sc_ctx_options *opts) { int err = 0; const scconf_list *list; const char *val, *s_internal = "internal"; int debug; int reopen; #ifdef _WIN32 char expanded_val[PATH_MAX]; DWORD expanded_len; #endif reopen = scconf_get_bool(block, "reopen_debug_file", 1); 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 = ExpandEnvironmentStrings(val, expanded_val, expanded_len); if (expanded_len > 0) val = expanded_val; #endif if (reopen) ctx->debug_filename = strdup(val); sc_ctx_log_to_file(ctx, val); } ctx->paranoid_memory = scconf_get_bool (block, "paranoid-memory", ctx->paranoid_memory); val = scconf_get_str(block, "force_card_driver", NULL); if (val) { if (opts->forced_card_driver) free(opts->forced_card_driver); opts->forced_card_driver = strdup(val); } list = scconf_find_list(block, "card_drivers"); if (list != NULL) del_drvs(opts); while (list != NULL) { if (strcmp(list->data, s_internal) == 0) add_internal_drvs(opts); else add_drv(opts, list->data); list = list->next; } return err; } static void load_reader_driver_options(sc_context_t *ctx) { struct sc_reader_driver *driver = ctx->reader_driver; scconf_block *conf_block = NULL; driver->max_send_size = 0; driver->max_recv_size = 0; conf_block = sc_get_conf_block(ctx, "reader_driver", driver->short_name, 1); if (conf_block != NULL) { driver->max_send_size = scconf_get_int(conf_block, "max_send_size", driver->max_send_size); driver->max_recv_size = scconf_get_int(conf_block, "max_recv_size", driver->max_recv_size); } } /** * 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; 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 if (libname && libname[0] != '\\' ) #else if (libname && libname[0] != '/' ) #endif 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 (name == NULL) { /* should not occurr, but... */ sc_log(ctx, "No module specified", name); 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; } if (dll) *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 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(); 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 (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]; DWORD temp_len; long rc; HKEY hKey; #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 conf_path = getenv("OPENSC_CONF"); if (!conf_path) { rc = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\OpenSC Project\\OpenSC", 0, KEY_QUERY_VALUE, &hKey); if (rc == ERROR_SUCCESS) { temp_len = PATH_MAX; rc = RegQueryValueEx( hKey, "ConfigFile", NULL, NULL, (LPBYTE) temp_path, &temp_len); if ((rc == ERROR_SUCCESS) && (temp_len < PATH_MAX)) conf_path = temp_path; RegCloseKey(hKey); } } if (!conf_path) { rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\OpenSC Project\\OpenSC", 0, KEY_QUERY_VALUE, &hKey ); if (rc == ERROR_SUCCESS) { temp_len = PATH_MAX; rc = RegQueryValueEx( hKey, "ConfigFile", NULL, NULL, (LPBYTE) temp_path, &temp_len); if ((rc == ERROR_SUCCESS) && (temp_len < PATH_MAX)) conf_path = temp_path; RegCloseKey(hKey); } } if (!conf_path) { sc_log(ctx, "process_config_file doesn't find opensc config file. Please set the registry key."); return; } #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; } blocks = scconf_find_blocks(ctx->conf, NULL, "app", ctx->app_name); if (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[0]) ctx->conf_blocks[count] = blocks[0]; free(blocks); } /* Above we add 2 blocks at most, but conf_blocks has 3 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; } 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; 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; } set_defaults(ctx, &opts); list_init(&ctx->readers); 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) { sc_release_context(ctx); return r; } process_config_file(ctx, &opts); sc_log(ctx, "==================================="); /* first thing in the log */ sc_log(ctx, "opensc version: %s", sc_get_version()); #ifdef ENABLE_PCSC ctx->reader_driver = sc_get_pcsc_driver(); /* XXX: remove cardmod pseudoreader driver */ #ifdef ENABLE_MINIDRIVER if(strcmp(ctx->app_name, "cardmod") == 0) ctx->reader_driver = sc_get_cardmod_driver(); #endif #elif defined(ENABLE_CTAPI) ctx->reader_driver = sc_get_ctapi_driver(); #elif defined(ENABLE_OPENCT) ctx->reader_driver = sc_get_openct_driver(); #endif load_reader_driver_options(ctx); r = ctx->reader_driver->ops->init(ctx); if (r != SC_SUCCESS) { sc_release_context(ctx); return r; } load_card_drivers(ctx, &opts); load_card_atrs(ctx); if (opts.forced_card_driver) { /* FIXME: check return value? */ sc_set_card_driver(ctx, opts.forced_card_driver); free(opts.forced_card_driver); } 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) { SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); 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) { SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); 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) { SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); 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; assert(ctx != NULL); 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); } 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); if (ctx->debug_filename != NULL) free(ctx->debug_filename); if (ctx->app_name != NULL) free(ctx->app_name); 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 (ctx->card_drivers[i] != NULL && i < SC_MAX_CARD_DRIVERS) { 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; #ifdef _WIN32 char temp_path[PATH_MAX]; #endif #ifndef _WIN32 cache_dir = ".eid/cache"; 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') { GetWindowsDirectory(temp_path, sizeof(temp_path)); homedir = temp_path; } #endif if (homedir == NULL) 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; size_t j, namelen; if ((r = sc_get_cache_dir(ctx, dirname, sizeof(dirname))) < 0) return r; namelen = strlen(dirname); while (1) { #ifdef _WIN32 if (mkdir(dirname) >= 0) #else if (mkdir(dirname, 0700) >= 0) #endif 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 if (mkdir(dirname) < 0) #else if (mkdir(dirname, 0700) < 0) #endif 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.13.0/src/libopensc/ctbcs.c0000644000015201777760000001633212057406034014041 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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; } #if 0 static int ctbcs_build_input_apdu(sc_apdu_t *apdu, int echo, const char *prompt, u8 *rbuf, size_t rbuflen) { ctbcs_init_apdu(apdu, SC_APDU_CASE_2_SHORT, CTBCS_INS_INPUT, CTBCS_P1_KEYPAD, echo? CTBCS_P2_INPUT_ECHO : CTBCS_P2_INPUT_ASTERISKS); if (prompt && *prompt) { apdu->cse = SC_APDU_CASE_4_SHORT; apdu->data = (u8 *) prompt; apdu->lc = apdu->datalen = strlen(prompt); } apdu->le = apdu->resplen = rbuflen; apdu->resp = rbuf; return 0; } static int ctbcs_build_output_apdu(sc_apdu_t *apdu, const char *message) { ctbcs_init_apdu(apdu, SC_APDU_CASE_3_SHORT, CTBCS_INS_INPUT, CTBCS_P1_DISPLAY, 0); if (!message || !*message) message = " "; apdu->lc = apdu->datalen = strlen(message); return 0; } #endif 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[254]; 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 (count + len + 2 > buflen || len > 255) 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 + 7 > 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 (j + len > buflen || len > 256) 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[254]; 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 (count + len + 2 > buflen || len > 255) 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 + 8 > 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 (j + len > buflen || len > 256) 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_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "unable to destroy mutex\n"); return s; } SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; } SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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.13.0/src/libopensc/card-rtecp.c0000644000015201777760000005440312057406034014770 00000000000000/* * card-rtecp.c: Support for Rutoken ECP 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #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 driver", "rutoken_ecp", &rtecp_ops, NULL, 0, NULL }; static struct sc_atr_table rtecp_atrs[] = { /* Rutoken ECP */ { "3B:8B:01:52:75:74:6F:6B:65:6E:20:45:43:50:A0", NULL, "Rutoken ECP", SC_CARD_TYPE_GENERIC_BASE, 0, NULL }, /* Rutoken ECP (DS) */ { "3B:8B:01:52:75:74:6F:6B:65:6E:20:44:53:20:C1", NULL, "Rutoken ECP (DS)", SC_CARD_TYPE_GENERIC_BASE, 0, NULL }, { NULL, 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; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 1); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 0); } static int rtecp_init(sc_card_t *card) { sc_algorithm_info_t info; unsigned long flags; assert(card && card->ctx); card->caps |= SC_CARD_CAP_RNG; card->cla = 0; 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); 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, 0); } static void reverse(unsigned char *buf, size_t len) { unsigned char tmp; size_t i; assert(buf || len == 0); 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; assert(card && card->ctx && file); assert(file->sec_attr && file->sec_attr_len == SC_RTECP_SEC_ATTR_SIZE); assert(1 + 6 < SC_RTECP_SEC_ATTR_SIZE); sc_file_add_acl_entry(file, SC_AC_OP_SELECT, SC_AC_NONE, SC_AC_KEY_REF_NONE); if (file->sec_attr[0] & 0x40) /* if AccessMode.6 */ { method = sec_attr_to_method(file->sec_attr[1 + 6]); key_ref = sec_attr_to_key_ref(file->sec_attr[1 + 6]); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SC_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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, (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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SC_AC_OP_UPDATE %i %lu\n", (int)method, key_ref); sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, method, key_ref); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SC_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; assert(card && card->ctx && file); assert(!file->sec_attr && file->sec_attr_len == 0); assert(1 + 6 < sizeof(sec_attr)); 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_out_copy, *file; int r; assert(card && card->ctx && in_path); switch (in_path->type) { case SC_PATH_TYPE_DF_NAME: case SC_PATH_TYPE_FROM_CURRENT: case SC_PATH_TYPE_PARENT: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); } assert(iso_ops && iso_ops->select_file); file_out_copy = file_out; r = iso_ops->select_file(card, in_path, file_out_copy); if (r || file_out_copy == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); assert(file_out_copy); file = *file_out_copy; assert(file); if (file->sec_attr && file->sec_attr_len == SC_RTECP_SEC_ATTR_SIZE) set_acl_from_sec_attr(card, file); else r = SC_ERROR_UNKNOWN_DATA_RECEIVED; if (r) sc_file_free(file); else { assert(file_out); *file_out = file; } SC_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 */ assert(card && card->ctx && data); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (send_logout++ == 0 && apdu.sw1 == 0x6F && apdu.sw2 == 0x86) { r = sc_logout(card); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Logout failed"); } 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; assert(card && card->ctx); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x40, 0, 0); apdu.cla = 0x80; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } 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; assert(card && card->ctx && data && out); buf_out = malloc(out_len + 2); buf = malloc(data_len); if (!buf || !buf_out) { free(buf); free(buf_out); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); } 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) { assert(buf); sc_mem_clear(buf, data_len); } assert(buf); free(buf); if (r) sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed: %s\n", sc_strerror(r)); else { if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { assert(buf_out); 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) { assert(buf_out); sc_mem_clear(buf_out, out_len + 2); } assert(buf_out); free(buf_out); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int rtecp_decipher(sc_card_t *card, const u8 *data, size_t data_len, u8 *out, size_t out_len) { int r; assert(card && card->ctx && data && out); /* 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; assert(card && card->ctx && data && out); /* 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 tmp[2], buf[0x1000], *p = buf; size_t taglen; int r; assert(card && card->ctx && newref); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "newlen = %u\n", newlen); if (newlen > sizeof(buf) - 2 - sizeof(tmp) - 2 * (sizeof(buf) / 0xFF + 1)) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); if (type == SC_AC_CHV && old && oldlen != 0) { r = sc_verify(card, type, ref_qualifier, old, oldlen, tries_left); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Verify old pin failed"); } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, ref_qualifier); 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); if (r == SC_ERROR_INVALID_ARGUMENTS) { while (newlen) { assert(sizeof(buf) - (p - buf) >= newlen + 2); assert((p - buf) % 0xFF < 0x80); if ((p - buf) % 0xFF % 0x80 + newlen + 2 > 0xFF) taglen = 0xFF - 2 - (p - buf) % 0xFF % 0x80; else taglen = newlen; *p++ = 0xA5; *p++ = (u8)taglen; assert(taglen <= newlen); memcpy(p, newref, taglen); p += taglen; newref += taglen; newlen -= taglen; 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, sizeof(buf)); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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 */ assert(card && card->ctx); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2C, 0x03, ref_qualifier); r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "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; assert(card && card->ctx && file); if (file->sec_attr_len == 0) { r = set_sec_attr_from_acl(card, file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Set sec_attr from ACL failed"); } assert(iso_ops && iso_ops->create_file); 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_BUFFER_SIZE], previd[2]; const u8 *tag; size_t taglen, len = 0; int r; assert(card && card->ctx && 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; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 == 0x6A && apdu.sw2 == 0x82) break; /* Next file not found */ r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, ""); if (apdu.resplen <= 2) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_WRONG_LENGTH); /* save first file(dir) ID */ tag = sc_asn1_find_tag(card->ctx, apdu.resp + 2, apdu.resplen - 2, 0x83, &taglen); if (!tag || taglen != sizeof(previd)) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); if (tag[0] == 0x38) { /* Select parent DF of the current DF */ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xA4, 0x03, 0); r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, ""); } sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0x02); apdu.lc = sizeof(previd); apdu.data = previd; apdu.datalen = sizeof(previd); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); } static int rtecp_card_ctl(sc_card_t *card, unsigned long request, void *data) { sc_apdu_t apdu; u8 buf[SC_MAX_APDU_BUFFER_SIZE]; sc_rtecp_genkey_data_t *genkey_data = data; sc_serial_number_t *serial = data; int r; assert(card && card->ctx); 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; break; case SC_CARDCTL_LIFECYCLE_SET: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s\n", "SC_CARDCTL_LIFECYCLE_SET not supported"); /* no call sc_debug (SC_FUNC_RETURN) */ return SC_ERROR_NOT_SUPPORTED; default: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "request = 0x%lx\n", request); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); } r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); 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 (serial->len >= apdu.resplen) { 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; assert(card && card->ctx && file && out && outlen); assert(*outlen >= (size_t)(p - out) + 2); *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) { 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: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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) { 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); } 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; rtecp_ops.get_response = NULL; /* get_challenge */ rtecp_ops.verify = rtecp_verify; rtecp_ops.logout = rtecp_logout; /* restore_security_env */ /* 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.13.0/src/libopensc/muscle.c0000644000015201777760000007505612057406034014243 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include "internal.h" #include "muscle.h" #define MSC_RSA_PUBLIC 0x01 #define MSC_RSA_PRIVATE 0x02 #define MSC_RSA_PRIVATE_CRT 0x03 #define MSC_DSA_PUBLIC 0x04 #define MSC_DSA_PRIVATE 0x05 static msc_id inputId = { { 0xFF, 0xFF, 0xFF, 0xFF } }; static msc_id outputId = { { 0xFF, 0xFF, 0xFF, 0xFE } }; 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "expected 14 bytes, got %d.\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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "READ: Offset: %x\tLength: %i\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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) return dataLength; if(apdu.sw1 == 0x9C) { if(apdu.sw2 == 0x07) { 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); return dataLength; } int msc_read_object(sc_card_t *card, msc_id objectId, int offset, u8 *data, size_t dataLength) { int r; size_t i; size_t max_read_unit = MSC_MAX_READ; for(i = 0; i < dataLength; i += max_read_unit) { r = msc_partial_read_object(card, objectId, offset + i, data + i, MIN(dataLength - i, max_read_unit)); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Error in partial object read"); } return dataLength; } int msc_zero_object(sc_card_t *card, msc_id objectId, size_t dataLength) { u8 zeroBuffer[MSC_MAX_APDU]; size_t i; size_t max_write_unit = MSC_MAX_SEND - 9; /* - 9 for object ID+length */ memset(zeroBuffer, 0, max_write_unit); for(i = 0; i < dataLength; i += max_write_unit) { int r = msc_partial_update_object(card, objectId, i, zeroBuffer, MIN(dataLength - i, max_write_unit)); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Error in zeroing file update"); } 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) return 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } msc_zero_object(card, objectId, objectSize); return objectSize; } /* Update up to MSC_MAX_READ - 9 bytes */ int msc_partial_update_object(sc_card_t *card, msc_id objectId, int offset, const u8 *data, size_t dataLength) { u8 buffer[MSC_MAX_APDU]; sc_apdu_t apdu; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x54, 0x00, 0x00); apdu.lc = dataLength + 9; if (card->ctx->debug >= 2) sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "WRITE: Offset: %x\tLength: %i\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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) return dataLength; if(apdu.sw1 == 0x9C) { if(apdu.sw2 == 0x07) { 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } return 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)); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Error in partial object update"); } return 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) return 1; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_CARD_CMD_FAILED); } /* Truncate the nulls at the end of a PIN, useful in padding is unnecessarily added */ static void truncatePinNulls(const u8* pin, int *pinLength) { 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]; assert(pinLength <= MSC_MAX_PIN_LENGTH); msc_verify_pin_apdu(card, &apdu, buffer, bufferLength, pinNumber, pinValue, pinLength); if(tries) *tries = -1; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { return 0; } else if(apdu.sw1 == 0x63) { /* Invalid auth */ if(tries) *tries = apdu.sw2 & 0x0F; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PIN_CODE_INCORRECT); } else if(apdu.sw1 == 0x9C && apdu.sw2 == 0x02) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PIN_CODE_INCORRECT); } else if(apdu.sw1 == 0x69 && apdu.sw2 == 0x83) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_AUTH_METHOD_BLOCKED); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_PIN_CODE_INCORRECT); } /* USE ISO_VERIFY due to tries return */ void msc_verify_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bufferLength, int pinNumber, const u8 *pinValue, int pinLength) { assert(buffer); assert(bufferLength >= (size_t)pinLength); assert(pinLength <= MSC_MAX_PIN_LENGTH); 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; } 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]; assert(pukLength <= MSC_MAX_PIN_LENGTH); msc_unblock_pin_apdu(card, &apdu, buffer, bufferLength, pinNumber, pukValue, pukLength); if(tries) *tries = -1; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { return 0; } else if(apdu.sw1 == 0x63) { /* Invalid auth */ if(tries) *tries = apdu.sw2 & 0x0F; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PIN_CODE_INCORRECT); } else if(apdu.sw1 == 0x9C && apdu.sw2 == 0x02) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PIN_CODE_INCORRECT); } else if(apdu.sw1 == 0x69 && apdu.sw2 == 0x83) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_AUTH_METHOD_BLOCKED); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_PIN_CODE_INCORRECT); } void msc_unblock_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bufferLength, int pinNumber, const u8 *pukValue, int pukLength) { assert(buffer); assert(bufferLength >= (size_t)pukLength); assert(pukLength <= MSC_MAX_PIN_LENGTH); 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; } 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]; msc_change_pin_apdu(card, &apdu, buffer, bufferLength, pinNumber, pinValue, pinLength, newPin, newPinLength); if(tries) *tries = -1; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { return 0; } else if(apdu.sw1 == 0x63) { /* Invalid auth */ if(tries) *tries = apdu.sw2 & 0x0F; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PIN_CODE_INCORRECT); } else if(apdu.sw1 == 0x9C && apdu.sw2 == 0x02) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PIN_CODE_INCORRECT); } else if(apdu.sw1 == 0x69 && apdu.sw2 == 0x83) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_AUTH_METHOD_BLOCKED); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_PIN_CODE_INCORRECT); } /* USE ISO_VERIFY due to tries return */ void msc_change_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bufferLength, int pinNumber, const u8 *pinValue, int pinLength, const u8 *newPin, int newPinLength) { u8 *ptr; assert(pinLength <= MSC_MAX_PIN_LENGTH); assert(newPinLength <= MSC_MAX_PIN_LENGTH); assert(buffer); assert(bufferLength >= pinLength + newPinLength + 2UL); 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; } 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; assert(seedLength < MSC_MAX_SEND - 4); assert(dataLength < MSC_MAX_READ - 9); /* Output buffer doesn't seem to operate as desired.... nobody can read/delete */ buffer = malloc(len); if(!buffer) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_CARD_CMD_FAILED); } } else { if(apdu.sw1 != 0x90 || apdu.sw2 != 0x00) { r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_CARD_CMD_FAILED); } r = msc_read_object(card, inputId, 2, outputData, dataLength); if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); msc_delete_object(card, inputId,0); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } } int msc_generate_keypair(sc_card_t *card, int privateKey, int publicKey, int algorithm, int keySize, int options) { sc_apdu_t apdu; u8 buffer[16]; /* Keypair payload length */ u8 *ptr = buffer; int r; unsigned short prRead = 0xFFFF, prWrite = 0x0002, prCompute = 0x0002, puRead = 0x0000, puWrite = 0x0002, puCompute = 0x0000; assert(privateKey <= 0x0F && publicKey <= 0x0F); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x30, privateKey, publicKey); *ptr = algorithm; ptr++; ushort2bebytes(ptr, keySize); ptr+=2; ushort2bebytes(ptr, prRead); ptr+=2; ushort2bebytes(ptr, prWrite); ptr+=2; ushort2bebytes(ptr, prCompute); ptr+=2; ushort2bebytes(ptr, puRead); ptr+=2; ushort2bebytes(ptr, puWrite); ptr+=2; ushort2bebytes(ptr, puCompute); ptr+=2; *ptr = 0; /* options; -- no options for now, they need extra data */ apdu.data = buffer; apdu.datalen = 16; apdu.lc = 16; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { return 0; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { return 0; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_CARD_CMD_FAILED); } int msc_extract_rsa_public_key(sc_card_t *card, int keyLocation, int* modLength, u8** modulus, int* expLength, u8** exponent) { int r; u8 buffer[1024]; /* Should be plenty... */ int fileLocation = 1; r = msc_extract_key(card, keyLocation); if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); /* Read keyType, keySize, and what should be the modulus size */ r = msc_read_object(card, inputId, fileLocation, buffer, 5); fileLocation += 5; if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); if(buffer[0] != MSC_RSA_PUBLIC) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); *modLength = (buffer[3] << 8) | buffer[4]; /* Read the modulus and the exponent length */ r = msc_read_object(card, inputId, fileLocation, buffer, *modLength + 2); fileLocation += *modLength + 2; if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); *modulus = malloc(*modLength); if(!*modulus) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); memcpy(*modulus, buffer, *modLength); *expLength = (buffer[*modLength] << 8) | buffer[*modLength + 1]; r = msc_read_object(card, inputId, fileLocation, buffer, *expLength); if(r < 0) { free(*modulus); *modulus = NULL; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } *exponent = malloc(*expLength); if(!*exponent) { free(*modulus); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { short receivedData = outputBuffer[0] << 8 | outputBuffer[1]; *outputDataLength = receivedData; *outputDataLength = 0; assert(receivedData <= MSC_MAX_APDU); memcpy(outputData, outputBuffer + 2, receivedData); return 0; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "init: got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_CARD_CMD_FAILED); } #if 0 int msc_compute_crypt_process( 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]; u8 *ptr; int r; if(dataLength > MSC_MAX_SEND - 3) return SC_ERROR_INVALID_ARGUMENTS; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x36, keyLocation, 0x02); /* Process */ apdu.data = buffer; apdu.datalen = dataLength + 3; apdu.lc = dataLength + 3; /* Specs say crypt returns data all the time??? But... its not implemented that way */ memset(outputBuffer, 0, sizeof(outputBuffer)); apdu.resp = outputBuffer; apdu.resplen = MSC_MAX_READ; apdu.le = dataLength; 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { short receivedData = outputBuffer[0] << 8 | outputBuffer[1]; *outputDataLength = receivedData; *outputDataLength = 0; assert(receivedData <= MSC_MAX_APDU); memcpy(outputData, outputBuffer + 2, receivedData); return 0; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "process: got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_CARD_CMD_FAILED); } #endif 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { short receivedData = outputBuffer[0] << 8 | outputBuffer[1]; *outputDataLength = receivedData; assert(receivedData <= MSC_MAX_APDU); memcpy(outputData, outputBuffer + 2, receivedData); return 0; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "final: got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { r = msc_read_object(card, inputId, 2, outputData, dataLength); *outputDataLength = dataLength; msc_delete_object(card, outputId, 0); msc_delete_object(card, inputId, 0); return 0; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "final: got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } } else { r = SC_ERROR_CARD_CMD_FAILED; } /* this is last ditch cleanup */ msc_delete_object(card, outputId, 0); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; assert(outputDataLength >= dataLength); /* 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); left -= toSend; inPtr += toSend; outPtr += received; toSend = MIN(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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } else { /* Data is too big: use objects */ r = msc_compute_crypt_final_object(card, keyLocation, inPtr, outPtr, toSend, &received); if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } outPtr += received; return 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; int bufferSize = 0; u8 *buffer, *p; u8 apduBuffer[6]; sc_apdu_t apdu; int r; assert(data->keyType == 0x02 || data->keyType == 0x03); if(data->keyType == 0x02) { 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "keyimport: got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } /* this is last ditch cleanup */ msc_delete_object(card, outputId, 0); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } /* this is last ditch cleanup */ msc_delete_object(card, outputId, 0); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_CARD_CMD_FAILED); } #undef CPYVAL opensc-0.13.0/src/libopensc/base64.c0000644000015201777760000000774712057406034014041 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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, unsigned int fillers) { unsigned int s = 18, c; for (c = 0; c < 4; c++) { if (fillers >= 4 - c) *out = base64_table[64]; else *out = base64_table[(i >> s) & 0x3f]; out++; s -= 6; } } static int from_base64(const char *in, unsigned int *out, int *skip) { 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) 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; size_t 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 |= *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, skip; unsigned int i; 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.13.0/src/libopensc/muscle.h0000644000015201777760000001047112057406034014236 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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, int 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); void msc_verify_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bufferLength, int pinNumber, const u8 *pinValue, int pinLength); int msc_unblock_pin(sc_card_t *card, int pinNumber, const u8 *pukValue, int pukLength, int *tries); void msc_unblock_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bufferLength, int pinNumber, const u8 *pukValue, int pukLength); int msc_change_pin(sc_card_t *card, int pinNumber, const u8 *pinValue, int pinLength, const u8 *newPin, int newPinLength, int *tries); void msc_change_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bufferLength, int pinNumber, const u8 *pinValue, int pinLength, const u8 *newPin, int newPinLength); int msc_get_challenge(sc_card_t *card, unsigned short dataLength, unsigned short seedLength, u8 *seedData, u8 *outputData); int msc_generate_keypair(sc_card_t *card, int privateKey, int publicKey, int algorithm, int keySize, int options); int msc_extract_rsa_public_key(sc_card_t *card, int keyLocation, int* modLength, u8** modulus, int* 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.13.0/src/libopensc/log.c0000644000015201777760000001173012057406034013521 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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 #include "internal.h" static void sc_do_log_va(sc_context_t *ctx, int level, const char *file, int line, const char *func, const char *format, va_list args); void sc_do_log(sc_context_t *ctx, int level, const char *file, int line, const char *func, const char *format, ...) { va_list ap; va_start(ap, format); sc_do_log_va(ctx, level, file, line, func, format, ap); va_end(ap); } void sc_do_log_noframe(sc_context_t *ctx, int level, const char *format, va_list args) { sc_do_log_va(ctx, level, NULL, 0, NULL, format, args); } static void sc_do_log_va(sc_context_t *ctx, int level, const char *file, int line, const char *func, const char *format, va_list args) { char buf[1836], *p; int r; size_t left; #ifdef _WIN32 SYSTEMTIME st; #else struct tm *tm; struct timeval tv; char time_string[40]; #endif FILE *outf = NULL; int n; assert(ctx != NULL); if (ctx->debug < level) return; p = buf; left = sizeof(buf); #ifdef _WIN32 GetLocalTime(&st); r = snprintf(p, left, "%i-%02i-%02i %02i:%02i:%02i.%03i ", 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), "%H:%M:%S", tm); r = snprintf(p, left, "0x%lx %s.%03ld ", (unsigned long)pthread_self(), time_string, tv.tv_usec / 1000); #endif p += r; left -= r; if (file != NULL) { r = snprintf(p, left, "[%s] %s:%d:%s: ", ctx->app_name, file, line, func ? func : ""); if (r < 0 || (unsigned int)r > sizeof(buf)) return; } else { r = 0; } p += r; left -= r; r = vsnprintf(p, left, format, args); if (r < 0) return; #ifdef _WIN32 if (ctx->debug_filename) sc_ctx_log_to_file(ctx, ctx->debug_filename); #endif outf = ctx->debug_file; if (outf == NULL) return; fprintf(outf, "%s", buf); n = strlen(buf); if (n == 0 || buf[n-1] != '\n') fprintf(outf, "\n"); fflush(outf); #ifdef _WIN32 if (ctx->debug_filename) { fclose(ctx->debug_file); ctx->debug_file = NULL; } #endif return; } void _sc_debug(struct sc_context *ctx, int level, const char *format, ...) { va_list ap; va_start(ap, format); sc_do_log_va(ctx, level, NULL, 0, NULL, format, ap); 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, format, ap); va_end(ap); } /* Although not used, we need this for consistent exports */ void sc_hex_dump(struct sc_context *ctx, int level, const u8 * in, size_t count, char *buf, size_t len) { char *p = buf; int lines = 0; assert(ctx != NULL); if (ctx->debug < level) return; assert(buf != NULL && in != NULL); 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++; } } 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 (ii, * Copyright (C) 2002 Andreas Jellinghaus * Copyright (C) 2001 Juha Yrjölä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include "internal.h" #include "cardctl.h" /* andreas says: hm, my card only works for small payloads */ /* comment by okir: one of the examples in the developer guide * also talks about copying data in chunks of 128. * Either coincidence, or a known problem. */ #define INCRYPTO34_MAX_PAYLOAD 120 static const struct sc_card_operations *iso_ops = NULL; static struct sc_card_operations incrypto34_ops; static struct sc_card_driver incrypto34_drv = { "Incard Incripto34", "incrypto34", &incrypto34_ops, NULL, 0, NULL }; static struct sc_atr_table incrypto34_atrs[] = { { "3b:ff:18:00:ff:81:31:fe:55:00:6b:02:09:02:00:01:01:01:44:53:44:10:31:80:92", NULL, NULL, SC_CARD_TYPE_INCRYPTO34_GENERIC, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } }; static int incrypto34_match_card(struct sc_card *card) { int i; i = _sc_match_atr(card, incrypto34_atrs, &card->type); if (i < 0) return 0; return 1; } static int incrypto34_init(sc_card_t *card) { unsigned long flags; card->name = "Incrypto34"; card->cla = 0x00; /* Set up algorithm info. */ flags = SC_ALGORITHM_NEED_USAGE | SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_ONBOARD_KEY_GEN ; _sc_card_add_rsa_alg(card, 512, flags, 0); _sc_card_add_rsa_alg(card, 768, flags, 0); _sc_card_add_rsa_alg(card, 1024, flags, 0); return 0; } static const struct sc_card_error incrypto34_errors[] = { /* some error inside the card */ /* i.e. nothing you can do */ { 0x6581, SC_ERROR_MEMORY_FAILURE, "EEPROM error; command aborted"}, { 0x6fff, SC_ERROR_CARD_CMD_FAILED, "internal assertion error"}, { 0x6700, SC_ERROR_WRONG_LENGTH, "LC invalid"}, { 0x6985, SC_ERROR_CARD_CMD_FAILED, "no random number available"}, { 0x6f81, SC_ERROR_CARD_CMD_FAILED, "file invalid, maybe checksum error"}, { 0x6f82, SC_ERROR_CARD_CMD_FAILED, "not enough memory in xram"}, { 0x6f84, SC_ERROR_CARD_CMD_FAILED, "general protection fault"}, /* the card doesn't now thic combination of ins+cla+p1+p2 */ /* i.e. command will never work */ { 0x6881, SC_ERROR_NO_CARD_SUPPORT, "logical channel not supported"}, { 0x6a86, SC_ERROR_INCORRECT_PARAMETERS,"p1/p2 invalid"}, { 0x6d00, SC_ERROR_INS_NOT_SUPPORTED, "ins invalid"}, { 0x6e00, SC_ERROR_CLASS_NOT_SUPPORTED, "class invalid (hi nibble)"}, /* known command, but incorrectly used */ /* i.e. command could work, but you need to change something */ { 0x6981, SC_ERROR_CARD_CMD_FAILED, "command cannot be used for file structure"}, { 0x6a80, SC_ERROR_INCORRECT_PARAMETERS,"invalid parameters in data field"}, { 0x6a81, SC_ERROR_NOT_SUPPORTED, "function/mode not supported"}, { 0x6a85, SC_ERROR_INCORRECT_PARAMETERS,"lc does not fit the tlv structure"}, { 0x6986, SC_ERROR_INCORRECT_PARAMETERS,"no current ef selected"}, { 0x6a87, SC_ERROR_INCORRECT_PARAMETERS,"lc does not fit p1/p2"}, { 0x6c00, SC_ERROR_WRONG_LENGTH, "le does not fit the data to be sent"}, { 0x6f83, SC_ERROR_CARD_CMD_FAILED, "command must not be used in transaction"}, /* (something) not found */ { 0x6987, SC_ERROR_INCORRECT_PARAMETERS,"key object for sm not found"}, { 0x6f86, SC_ERROR_CARD_CMD_FAILED, "key object not found"}, { 0x6a82, SC_ERROR_FILE_NOT_FOUND, "file not found"}, { 0x6a83, SC_ERROR_RECORD_NOT_FOUND, "record not found"}, { 0x6a88, SC_ERROR_CARD_CMD_FAILED, "object not found"}, { 0x6a89, SC_ERROR_FILE_ALREADY_EXISTS, "file/object already exists"}, /* (something) invalid */ { 0x6884, SC_ERROR_CARD_CMD_FAILED, "chaining error"}, { 0x6984, SC_ERROR_CARD_CMD_FAILED, "bs object has invalid format"}, { 0x6988, SC_ERROR_INCORRECT_PARAMETERS,"key object used for sm has invalid format"}, /* (something) deactivated */ { 0x6283, SC_ERROR_CARD_CMD_FAILED, "file is deactivated" }, { 0x6983, SC_ERROR_AUTH_METHOD_BLOCKED, "bs object blocked"}, /* access denied */ { 0x6300, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED,"authentication failed"}, { 0x6982, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED,"required access right not granted"}, /* other errors */ { 0x6a84, SC_ERROR_CARD_CMD_FAILED, "not enough memory"}, /* command ok, execution failed */ { 0x6f00, SC_ERROR_CARD_CMD_FAILED, "technical error (see incrypto34 developers guide)"}, /* no error, maybe a note */ { 0x9000, SC_SUCCESS, NULL}, { 0x9001, SC_SUCCESS, "success, but eeprom weakness detected"}, { 0x9850, SC_SUCCESS, "over/underflow useing in/decrease"} }; static int incrypto34_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) { const int err_count = sizeof(incrypto34_errors)/sizeof(incrypto34_errors[0]); int i; for (i = 0; i < err_count; i++) { if (incrypto34_errors[i].SWs == ((sw1 << 8) | sw2)) { if ( incrypto34_errors[i].errorstr ) sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s\n", incrypto34_errors[i].errorstr); return incrypto34_errors[i].errorno; } } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown SWs; SW1=%02X, SW2=%02X\n", sw1, sw2); return SC_ERROR_CARD_CMD_FAILED; } static int incrypto34_list_files(sc_card_t *card, u8 *buf, size_t buflen) { sc_apdu_t apdu; u8 rbuf[256]; int r; size_t fids; u8 offset; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); fids=0; offset=0; /* INS 0xFC: SCAN DF*/ /* P1 0x00: list both DF and EF */ /* P2 0x00/0x01: first/next element */ /* LE 0x03*/ /* returns 3 bytes: FILE_TYPE + FID_HI_BYTE + FID_LO_BYTE */ get_next_part: sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xFC, 0x00, offset?0x01:0x00); apdu.cla = 0xB0; apdu.le = 3; apdu.resplen = sizeof(rbuf); apdu.resp = rbuf; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 == 0x6a && apdu.sw2 == 0x82) goto end; /* no more files */ r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "DIRECTORY command returned error"); if (apdu.resplen >= 3 && ((rbuf[0] >= 0x01 && rbuf[0] <= 0x07) || 0x38 == rbuf[0]) && fids + 2 >= buflen) { buf[fids++] = rbuf[1]; buf[fids++] = rbuf[2]; } ++offset; goto get_next_part; end: r = fids; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } static void add_acl_entry(sc_file_t *file, int op, u8 byte) { unsigned int method, key_ref = SC_AC_KEY_REF_NONE; switch (byte) { case 0x00: method = SC_AC_NONE; break; case 0xFF: method = SC_AC_NEVER; break; default: if (byte > 0x7F) { method = SC_AC_UNKNOWN; } else { method = SC_AC_CHV; key_ref = byte; } break; } sc_file_add_acl_entry(file, op, method, key_ref); } static int acl_to_byte(const sc_acl_entry_t *e) { if (e != NULL) { switch (e->method) { case SC_AC_NONE: return 0x00; case SC_AC_NEVER: return 0xFF; case SC_AC_CHV: case SC_AC_TERM: case SC_AC_AUT: if (e->key_ref == SC_AC_KEY_REF_NONE) return -1; if (e->key_ref > 0x7F) return -1; return e->key_ref; } } return 0x00; } static const int df_acl[9] = { -1, /* LCYCLE (life cycle change) */ SC_AC_OP_UPDATE, /* UPDATE Objects */ -1, /* APPEND Objects */ SC_AC_OP_INVALIDATE, /* DF */ SC_AC_OP_REHABILITATE, /* DF */ SC_AC_OP_DELETE, /* DF */ -1, /* ADMIN DF */ SC_AC_OP_CREATE, /* Files */ -1 /* Reserved */ }; static const int ef_acl[9] = { SC_AC_OP_READ, /* Data */ SC_AC_OP_UPDATE, /* Data (write file content) */ SC_AC_OP_WRITE, /* */ SC_AC_OP_INVALIDATE, /* EF */ SC_AC_OP_REHABILITATE, /* EF */ SC_AC_OP_ERASE, /* (delete) EF */ /* XXX: ADMIN should be an ACL type of its own, or mapped * to erase */ -1, /* ADMIN EF (modify meta information?) */ -1, /* INC (-> cylic fixed files) */ -1 /* DEC */ }; static void parse_sec_attr(sc_file_t *file, const u8 *buf, size_t len) { size_t i; const int *idx; idx = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl; /* acl defaults to 0xFF if unspecified */ for (i = 0; i < 9; i++) if (idx[i] != -1) add_acl_entry(file, idx[i], (u8)((i < len) ? buf[i] : 0xFF)); } static int incrypto34_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file) { int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); r = iso_ops->select_file(card, in_path, file); if (r >= 0 && file) parse_sec_attr((*file), (*file)->sec_attr, (*file)->sec_attr_len); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } static int incrypto34_create_file(sc_card_t *card, sc_file_t *file) { int r, i, byte; const int *idx; u8 acl[9], type[3], status[3]; char pbuf[128+1]; size_t n; for (n = 0; n < file->path.len; n++) { snprintf(pbuf + 2 * n, sizeof(pbuf) - 2 * n, "%02X", file->path.value[n]); } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "incrypto34_create_file(%s)\n", pbuf); if (file->type_attr_len == 0) { memset(type, 0, sizeof(type)); type[0] = 0x00; switch (file->type) { case SC_FILE_TYPE_WORKING_EF: break; case SC_FILE_TYPE_INTERNAL_EF: type[0] = 0x08; break; case SC_FILE_TYPE_DF: type[0] = 0x38; break; default: r = SC_ERROR_NOT_SUPPORTED; goto out; } if (file->type != SC_FILE_TYPE_DF) { switch (file->ef_structure) { case SC_FILE_EF_LINEAR_FIXED_TLV: case SC_FILE_EF_LINEAR_VARIABLE: case SC_FILE_EF_CYCLIC_TLV: r = SC_ERROR_NOT_SUPPORTED; goto out; /* No idea what this means, but it * seems to be required for key * generation. */ case SC_FILE_EF_LINEAR_VARIABLE_TLV: type[1] = 0xff; default: type[0] |= file->ef_structure & 7; break; } } r = sc_file_set_type_attr(file, type, sizeof(type)); if (r) goto out; } if (file->prop_attr_len == 0) { status[0] = 0x01; if (file->type == SC_FILE_TYPE_DF) { status[1] = file->size >> 8; status[2] = file->size; } else { status[1] = status[2] = 0x00; /* not used */ } r = sc_file_set_prop_attr(file, status, sizeof(status)); if (r) goto out; } if (file->sec_attr_len == 0) { idx = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl; for (i = 0; i < 9; i++) { if (idx[i] < 0) byte = 0x00; else byte = acl_to_byte( sc_file_get_acl_entry(file, idx[i])); if (byte < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid ACL\n"); r = SC_ERROR_INVALID_ARGUMENTS; goto out; } acl[i] = byte; } r = sc_file_set_sec_attr(file, acl, sizeof(acl)); if (r) goto out; } r = iso_ops->create_file(card, file); /* FIXME: if this is a DF and there's an AID, set it here * using PUT_DATA_FCI */ out: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } /* * Restore the indicated SE */ static int incrypto34_restore_security_env(sc_card_t *card, int se_num) { sc_apdu_t apdu; int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 0xF3, se_num); r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } /* * Set the security context * Things get a little messy here. It seems you cannot do any * crypto without a security environment - but there isn't really * a way to specify the security environment in PKCS15. * What I'm doing here (for now) is to assume that for a key * object with ID 0xNN there is always a corresponding SE object * with the same ID. * XXX Need to find out how the Aladdin drivers do it. */ static int incrypto34_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { sc_apdu_t apdu; u8 data[3]; int key_id, r; assert(card != NULL && env != NULL); if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || env->key_ref_len != 1) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No or invalid key reference\n"); return SC_ERROR_INVALID_ARGUMENTS; } key_id = env->key_ref[0]; r = incrypto34_restore_security_env(card, 1); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xF1, 0); switch (env->operation) { case SC_SEC_OPERATION_DECIPHER: apdu.p2 = 0xB8; break; case SC_SEC_OPERATION_SIGN: apdu.p2 = 0xB6; break; default: return SC_ERROR_INVALID_ARGUMENTS; } data[0] = 0x83; data[1] = 0x01; data[2] = key_id; apdu.lc = apdu.datalen = 3; apdu.data = data; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } /* * Compute digital signature */ /* internal function to do the actual signature computation */ static int do_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, u8 *out, size_t outlen) { int r; sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; if (datalen > SC_MAX_APDU_BUFFER_SIZE || outlen > SC_MAX_APDU_BUFFER_SIZE) return SC_ERROR_INTERNAL; /* INS: 0x2A PERFORM SECURITY OPERATION * P1: 0x9E Resp: Digital Signature * P2: 0x9A Cmd: Input for Digital Signature */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A); apdu.resp = rbuf; apdu.le = outlen; apdu.resplen = sizeof(rbuf); memcpy(sbuf, data, datalen); apdu.data = sbuf; apdu.lc = datalen; apdu.datalen = datalen; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { memcpy(out, rbuf, outlen); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } static int incrypto34_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, u8 *out, size_t outlen) { int r; u8 buf[SC_MAX_APDU_BUFFER_SIZE]; size_t buf_len = sizeof(buf), tmp_len = buf_len; sc_context_t *ctx; assert(card != NULL && data != NULL && out != NULL); ctx = card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (datalen > 255) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); if (outlen < datalen) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_BUFFER_TOO_SMALL); outlen = datalen; /* XXX As we don't know what operations are allowed with a * certain key, let's try RSA_PURE etc. and see which operation * succeeds (this is not really beautiful, but currently the * only way I see) -- Nils */ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "trying RSA_PURE_SIG (padded DigestInfo)\n"); r = do_compute_signature(card, data, datalen, out, outlen); if (r >= SC_SUCCESS) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "trying RSA_SIG (just the DigestInfo)\n"); /* remove padding: first try pkcs1 bt01 padding */ r = sc_pkcs1_strip_01_padding(data, datalen, buf, &tmp_len); if (r != SC_SUCCESS) { const u8 *p = data; /* no pkcs1 bt01 padding => let's try zero padding. * This can only work if the data tbs doesn't have a * leading 0 byte. */ tmp_len = buf_len; while (*p == 0 && tmp_len != 0) { ++p; --tmp_len; } memcpy(buf, p, tmp_len); } r = do_compute_signature(card, buf, tmp_len, out, outlen); if (r >= SC_SUCCESS) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "trying to sign raw hash value\n"); r = sc_pkcs1_strip_digest_info_prefix(NULL,buf,tmp_len,buf,&buf_len); if (r != SC_SUCCESS) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); return do_compute_signature(card, buf, buf_len, out, outlen); } static int incrypto34_lifecycle_get(sc_card_t *card, int *mode) { sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 01, 0x83); apdu.cla = 0x00; apdu.le = 256; apdu.resplen = sizeof(rbuf); apdu.resp = rbuf; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); if (apdu.resplen < 1) { SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Lifecycle byte not in response"); } r = SC_SUCCESS; switch (rbuf[0]) { case 0x10: *mode = SC_CARDCTRL_LIFECYCLE_USER; break; case 0x20: *mode = SC_CARDCTRL_LIFECYCLE_ADMIN; break; case 0x34: /* MANUFACTURING */ *mode = SC_CARDCTRL_LIFECYCLE_OTHER; break; default: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown lifecycle byte %d", rbuf[0]); r = SC_ERROR_INTERNAL; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } static int incrypto34_put_data_oci(sc_card_t *card, struct sc_cardctl_incrypto34_obj_info *args) { sc_apdu_t apdu; int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_3_SHORT; apdu.cla = 0x00; apdu.ins = 0xda; apdu.p1 = 0x01; apdu.p2 = 0x6e; apdu.lc = args->len; apdu.data = args->data; apdu.datalen = args->len; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } static int incrypto34_change_key_data(struct sc_card *card, struct sc_cardctl_incrypto34_obj_info *args) { struct sc_apdu apdu; int r; memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_3_SHORT; apdu.cla = 0x90; apdu.ins = 0x24; apdu.p1 = args->key_class; apdu.p2 = args->key_id; apdu.lc = args->len; apdu.data = args->data; apdu.datalen = args->len; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); return r; } static int incrypto34_put_data_seci(sc_card_t *card, struct sc_cardctl_incrypto34_obj_info *args) { sc_apdu_t apdu; int r; memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_3_SHORT; apdu.cla = 0x00; apdu.ins = 0xda; apdu.p1 = 0x01; apdu.p2 = 0x6d; apdu.lc = args->len; apdu.data = args->data; apdu.datalen = args->len; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); return r; } static int incrypto34_generate_key(sc_card_t *card, struct sc_cardctl_incrypto34_genkey_info *args) { sc_apdu_t apdu; u8 data[8]; int r; data[0] = 0x20; /* store as PSO object */ data[1] = args->key_id; data[2] = args->fid >> 8; data[3] = args->fid & 0xff; data[4] = 0; /* additional Rabin Miller tests */ data[5] = 0x10; /* length difference between p, q (bits) */ data[6] = 0; /* default length of exponent, MSB */ data[7] = 0x20; /* default length of exponent, LSB */ memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_3_SHORT; apdu.cla = 0x00; apdu.ins = 0x46; apdu.p1 = 0x00; apdu.p2 = args->key_id;/* doc is not clear, it just says "ID" */ apdu.p2 = 0x00; apdu.data= data; apdu.datalen = apdu.lc = sizeof(data); r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "GENERATE_KEY failed"); return r; } static int incrypto34_erase_files(sc_card_t *card) { sc_apdu_t apdu; int r; static u8 pCreateAtrFile[] = { 0x62, 0x1b, 0x80, 0x02, 0x00, 0x1e, 0x82, 0x03, 0x01, 0xff, 0xff, 0x83, 0x02, 0x2f, 0x01, 0x85, 0x01, 0x01, 0x86, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static u8 pWriteAtr[] = { 0x19, 0x3b, 0xff, 0x18, 0x00, 0xff, 0x81, 0x31, 0xfe, 0x55, 0x00, 0x6b, 0x02, 0x09, 0x02, 0x00, 0x01, 0x01, 0x01, 0x43, 0x4e, 0x53, 0x10, 0x31, 0x80, 0x9f }; static u8 pCreateEF_DIR_ADOFile[] = { 0x6F, 0x1D, 0x83, 0x02, 0xFD, 0x01, 0x85, 0x03, 0x01, 0xff, 0xff, 0x81, 0x02, 0x00, 0x64, 0x82, 0x03, 0x05, 0xff, 0xff, 0x86, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; /* Erasing Filesystem */ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xf5, 0, 0); apdu.cla = 0xb0; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error Erasing Filesystem"); /* Creating ATR file*/ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xe0, 0, 0); apdu.data = pCreateAtrFile; apdu.datalen = apdu.lc = sizeof(pCreateAtrFile); r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error Creating ATR file"); /* Filling ATR file*/ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xd6, 0, 0); apdu.data = pWriteAtr; apdu.datalen = apdu.lc = sizeof(pWriteAtr); r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error Filling ATR file"); /* Creating DIR-ADO file*/ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xe0, 0, 0); apdu.data = pCreateEF_DIR_ADOFile; apdu.datalen = apdu.lc = sizeof(pCreateEF_DIR_ADOFile); r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error Creating DIR-ADO file"); return r; } static int incrypto34_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { switch (cmd) { case SC_CARDCTL_INCRYPTO34_PUT_DATA_FCI: break; case SC_CARDCTL_INCRYPTO34_PUT_DATA_OCI: return incrypto34_put_data_oci(card, (struct sc_cardctl_incrypto34_obj_info *) ptr); break; case SC_CARDCTL_INCRYPTO34_PUT_DATA_SECI: return incrypto34_put_data_seci(card, (struct sc_cardctl_incrypto34_obj_info *) ptr); break; case SC_CARDCTL_INCRYPTO34_GENERATE_KEY: return incrypto34_generate_key(card, (struct sc_cardctl_incrypto34_genkey_info *) ptr); case SC_CARDCTL_LIFECYCLE_GET: return incrypto34_lifecycle_get(card, (int *) ptr); case SC_CARDCTL_LIFECYCLE_SET: return 0; case SC_CARDCTL_INCRYPTO34_CHANGE_KEY_DATA: return incrypto34_change_key_data(card, (struct sc_cardctl_incrypto34_obj_info*) ptr); case SC_CARDCTL_INCRYPTO34_ERASE_FILES: return incrypto34_erase_files(card); } return SC_ERROR_NOT_SUPPORTED; } /* * The 0x80 thing tells the card it's okay to search parent * directories as well for the referenced object. * Unfortunately, it doesn't seem to work without this flag :-/ */ static int incrypto34_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { data->flags |= SC_PIN_CMD_NEED_PADDING; data->pin_reference |= 0x80; /* FIXME: the following values depend on what pin length was * used when creating the BS objects */ if (data->pin1.max_length == 0) data->pin1.max_length = 8; if (data->pin2.max_length == 0) data->pin2.max_length = 8; return iso_ops->pin_cmd(card, data, tries_left); } static struct sc_card_driver * sc_get_driver(void) { if (iso_ops == NULL) iso_ops = sc_get_iso7816_driver()->ops; incrypto34_ops = *iso_ops; incrypto34_ops.match_card = incrypto34_match_card; incrypto34_ops.init = incrypto34_init; incrypto34_ops.select_file = incrypto34_select_file; incrypto34_ops.create_file = incrypto34_create_file; incrypto34_ops.set_security_env = incrypto34_set_security_env; incrypto34_ops.restore_security_env = incrypto34_restore_security_env; incrypto34_ops.compute_signature = incrypto34_compute_signature; incrypto34_ops.list_files = incrypto34_list_files; incrypto34_ops.check_sw = incrypto34_check_sw; incrypto34_ops.card_ctl = incrypto34_card_ctl; incrypto34_ops.pin_cmd = incrypto34_pin_cmd; return &incrypto34_drv; } #if 1 struct sc_card_driver * sc_get_incrypto34_driver(void) { return sc_get_driver(); } #endif opensc-0.13.0/src/libopensc/card-atrust-acos.c0000644000015201777760000006734612057406034016132 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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 int 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 (!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_debug(ctx, SC_LOG_DEBUG_NORMAL, "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_debug(ctx, SC_LOG_DEBUG_NORMAL, " 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_debug(ctx, SC_LOG_DEBUG_NORMAL, " type: %s\n", type); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); file->id = (id_hi << 8) + id_lo; file->path = card->cache.current_path; 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "current path (%s, %s): %s (len: %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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "cache hit\n"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } else return atrust_acos_select_aid(card, pathbuf, pathlen, file_out); } else if (in_path->type == SC_PATH_TYPE_PATH) { u8 n_pathbuf[SC_MAX_PATH_SIZE]; int bMatch = -1; /* 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; for (i=0; i< pathlen; i++) n_pathbuf[i+2] = pathbuf[i]; path = n_pathbuf; pathlen += 2; } /* check current working directory */ if (card->cache.valid && card->cache.current_path.type == SC_PATH_TYPE_PATH && card->cache.current_path.len >= 2 && card->cache.current_path.len <= pathlen ) { bMatch = 0; for (i=0; i < card->cache.current_path.len; i+=2) if (card->cache.current_path.value[i] == path[i] && card->cache.current_path.value[i+1] == path[i+1] ) bMatch += 2; } if ( card->cache.valid && bMatch >= 0 ) { if ( pathlen - bMatch == 2 ) /* we are in the rigth directory */ return atrust_acos_select_fid(card, path[bMatch], path[bMatch+1], file_out); else if ( pathlen - bMatch > 2 ) { /* two more steps to go */ sc_path_t new_path; /* first step: change directory */ r = atrust_acos_select_fid(card, path[bMatch], path[bMatch+1], NULL); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "SELECT FILE (DF-ID) failed"); memset(&new_path, 0, sizeof(sc_path_t)); new_path.type = SC_PATH_TYPE_PATH; new_path.len = pathlen - bMatch-2; memcpy(new_path.value, &(path[bMatch+2]), new_path.len); /* final step: select file */ return atrust_acos_select_file(card, &new_path, file_out); } else /* if (bMatch - pathlen == 0) */ { /* done: we are already in the * requested directory */ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "cache hit\n"); /* copy file info (if necessary) */ if (file_out) { sc_file_t *file = sc_file_new(); if (!file) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); file->id = (path[pathlen-2] << 8) + path[pathlen-1]; file->path = card->cache.current_path; file->type = SC_FILE_TYPE_DF; file->ef_structure = SC_FILE_EF_UNKNOWN; file->size = 0; file->namelen = 0; file->magic = SC_FILE_MAGIC; *file_out = file; } /* nothing left to do */ return SC_SUCCESS; } } else { /* no usable cache */ for ( i=0; ictx, SC_LOG_DEBUG_NORMAL, r, "SELECT FILE (DF-ID) failed"); } return atrust_acos_select_fid(card, path[pathlen-2], path[pathlen-1], file_out); } } 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_ASYMMETRIC) *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) { *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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); return SC_SUCCESS; } /* try COMPUTE SIGNATURE */ if (operation == SC_SEC_OPERATION_SIGN && ( env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1 || env->algorithm_flags & SC_ALGORITHM_RSA_PAD_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) { 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 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)); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); } } 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); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } /*****************************************************************************/ static int atrust_acos_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "sw1 = 0x%02x, sw2 = 0x%02x\n", sw1, sw2); if (sw1 == 0x90) return SC_SUCCESS; if (sw1 == 0x63 && (sw2 & ~0x0fU) == 0xc0 ) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return SC_ERROR_INTERNAL; /* cache serial number */ memcpy(card->serialnr.value, apdu.resp, MIN(apdu.resplen, SC_MAX_SERIALNR)); card->serialnr.len = MIN(apdu.resplen, SC_MAX_SERIALNR); /* copy and return serial number */ memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } /*****************************************************************************/ static int 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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.13.0/src/libopensc/reader-openct.c0000644000015201777760000002561512057406034015477 00000000000000/* * reader-openct.c: backend for OpenCT * * Copyright (C) 2003 Olaf Kirch */ #include "config.h" #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, 0, 0, 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; struct driver_data *data; int rc; if (!(reader = calloc(1, sizeof(*reader))) || !(data = (calloc(1, sizeof(*data))))) { if (reader) 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); 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) 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); 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 (data->h) ct_reader_disconnect(data->h); if (!(data->h = ct_reader_connect(data->num))) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "ct_reader_connect socket failed\n"); return SC_ERROR_CARD_NOT_PRESENT; } rc = ct_card_request(data->h, data->slot, 0, NULL, reader->atr.value, sizeof(reader->atr.value)); if (rc < 0) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "openct_reader_connect read failed: %s\n", ct_strerror(rc)); return SC_ERROR_CARD_NOT_PRESENT; } if (rc == 0) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "openct_reader_connect recved no data\n"); 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) 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; /* 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, SC_LOG_DEBUG_NORMAL, sbuf, ssize, 1); r = openct_reader_internal_transmit(reader, sbuf, ssize, rbuf, &rsize, apdu->control); if (r < 0) { /* unable to transmit ... most likely a reader problem */ sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "unable to transmit"); goto out; } sc_apdu_log(reader->ctx, SC_LOG_DEBUG_NORMAL, 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; /* 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); /* 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); /* 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.13.0/src/libopensc/compression.c0000644000015201777760000001154612057406034015306 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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_BUF_ERROR: /* XXX: something else than OOM ? */ case Z_MEM_ERROR: return SC_ERROR_OUT_OF_MEMORY; case Z_VERSION_ERROR: case Z_DATA_ERROR: case Z_STREAM_ERROR: /* case Z_NEED_DICT: */ default: return SC_ERROR_INTERNAL; } } static int detect_method(const u8* in, size_t inLen) { if(inLen > 2 && in[0] == 0x1f && in[1] == 0x8b) { /* GZIP */ return COMPRESSION_GZIP; } else if(inLen > 1 /*&& (in[0] & 0x10) == Z_DEFLATED*/) { /* REALLY SIMPLE ZLIB TEST -- * Check for the compression method to be set to 8... * many things can spoof this, but this is ok for now * */ return COMPRESSION_ZLIB; } else { return COMPRESSION_UNKNOWN; } } 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 = inLen; gz.next_out = out; gz.avail_out = *outLen; 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); return zerr_to_opensc(err); } int sc_decompress(u8* out, size_t* outLen, const u8* in, size_t inLen, int method) { unsigned long zlib_outlen; int rc; if(method == COMPRESSION_AUTO) { method = detect_method(in, inLen); if(method == COMPRESSION_UNKNOWN) { 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 int startSize = inLen < 1024 ? 2048 : inLen * 2; const int blockSize = inLen < 1024 ? 512 : inLen / 2; int bufferSize = startSize; if(gzip) window_size += 0x20; memset(&gz, 0, sizeof(gz)); gz.next_in = (u8*)in; gz.avail_in = inLen; err = inflateInit2(&gz, window_size); if(err != Z_OK) return zerr_to_opensc(err); *outLen = 0; while(1) { /* Setup buffer... */ int num; u8* buf = realloc(*out, bufferSize); if(!buf) { if(*out) free(*out); *out = NULL; return Z_MEM_ERROR; } *out = buf; gz.next_out = buf + *outLen; gz.avail_out = bufferSize - *outLen; err = inflate(&gz, Z_FULL_FLUSH); if(err != Z_STREAM_END && err != Z_OK) { if(*out) free(*out); *out = NULL; break; } num = bufferSize - *outLen - gz.avail_out; if(num > 0) { *outLen += num; bufferSize += num + blockSize; } if(err == Z_STREAM_END) { buf = realloc(buf, *outLen); /* Shrink it down, if it fails, just use old data */ if(buf) { *out = buf; } 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(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.13.0/src/libopensc/Makefile.am0000644000015201777760000000524212057406034014631 00000000000000include $(top_srcdir)/win32/ltrc.inc MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak lib_LTLIBRARIES = libopensc.la noinst_HEADERS = cards.h ctbcs.h internal.h esteid.h muscle.h muscle-filesystem.h \ internal-winscard.h p15card-helper.h \ opensc.h pkcs15.h \ cardctl.h asn1.h log.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 AM_CPPFLAGS = -DOPENSC_CONF_PATH=\"$(sysconfdir)/opensc.conf\" \ -I$(top_srcdir)/src AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_OPENCT_CFLAGS) \ $(OPTIONAL_PCSC_CFLAGS) $(OPTIONAL_ZLIB_CFLAGS) libopensc_la_SOURCES = \ sc.c ctx.c log.c errors.c \ asn1.c base64.c sec.c card.c iso7816.c dir.c ef-atr.c padding.c apdu.c \ \ 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 \ \ muscle.c muscle-filesystem.c \ \ ctbcs.c reader-ctapi.c reader-pcsc.c reader-openct.c \ \ card-setcos.c card-miocos.c card-flex.c card-gpk.c \ card-cardos.c card-tcos.c card-default.c \ card-mcrd.c card-starcos.c card-openpgp.c card-jcop.c \ card-oberthur.c card-belpic.c card-atrust-acos.c \ card-entersafe.c card-epass2003.c \ card-incrypto34.c card-piv.c card-muscle.c card-acos5.c \ card-asepcos.c card-akis.c card-gemsafeV1.c card-rutoken.c \ card-rtecp.c card-westcos.c card-myeid.c card-ias.c \ card-javacard.c card-itacns.c card-authentic.c \ card-iasecc.c iasecc-sdo.c iasecc-sm.c card-sc-hsm.c \ \ pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \ pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafeGPK.c \ pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \ pkcs15-esinit.c pkcs15-westcos.c pkcs15-pteid.c pkcs15-oberthur.c \ pkcs15-itacns.c pkcs15-gemsafeV1.c pkcs15-sc-hsm.c \ compression.c p15card-helper.c \ libopensc.exports if WIN32 libopensc_la_SOURCES += $(top_builddir)/win32/versioninfo.rc endif libopensc_la_LIBADD = $(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/common/libcompat.la if WIN32 libopensc_la_LIBADD += -lws2_32 endif 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 endif opensc-0.13.0/src/libopensc/card-oberthur.c0000644000015201777760000021502112057406034015500 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * best view with tabstop=4 */ #include "config.h" #ifdef ENABLE_OPENSSL /* empty file without openssl */ #include #include #include #include #include #include #include #include "internal.h" #include "cardctl.h" #include "pkcs15.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 /* keep OpenSSL 0.9.6 users happy ;-) */ #if OPENSSL_VERSION_NUMBER < 0x00907000L #define DES_cblock des_cblock #define DES_key_schedule des_key_schedule #define DES_set_key_unchecked(a,b) des_set_key_unchecked(a,*b) #define DES_ecb_encrypt(a,b,c,d) des_ecb_encrypt(a,b,*c,d) #endif static struct sc_atr_table oberthur_atrs[] = { { "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 int 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; unsigned int 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 int 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; unsigned char cm[7] = {0xA0,0x00,0x00,0x00,0x03,0x00,0x00}; struct sc_path tmp_path; /* Select Card Manager (to deselect previously selected application) */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x04, 0x0C); apdu.lc = sizeof(cm); /* apdu.le = sizeof(cm)+4; */ apdu.data = cm; apdu.datalen = sizeof(cm); apdu.resplen = sizeof(apdu_resp); apdu.resp = apdu_resp; rv = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); card->serialnr.len = 4; memcpy(card->serialnr.value, apdu.resp+15, 4); for (ii=0, data->sn = 0; ii < 4; ii++) data->sn += (int)(*(apdu.resp + 15 + ii)) << (3-ii)*8; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "serial number %li/0x%lX\n", 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); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "rv %i\n", rv); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "select parent failed"); sc_format_path("3F00", &tmp_path); rv = iso_ops->select_file(card, &tmp_path, &auth_current_df); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "rv %i\n", rv); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "select parent failed"); sc_format_path("3F00", &card->cache.current_path); 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; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "return %i\n", rv); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Failed to initialize %s\n", card->name); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_CARD, "Failed to initialize"); } flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_PAD_ISO9796; flags |= SC_ALGORITHM_RSA_HASH_NONE; flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; _sc_card_add_rsa_alg(card, 512, flags, 0); _sc_card_add_rsa_alg(card, 1024, flags, 0); _sc_card_add_rsa_alg(card, 2048, flags, 0); #if 0 flags = SC_ALGORITHM_SKEY_CBC | SC_ALGORITHM_SKEY_ECB; memset(&info, 0, sizeof(info)); info.algorithm = SC_ALGORITHM_DES; info.flags = flags; info.key_length = 64; _sc_card_add_algorithm(card, &info); flags = SC_ALGORITHM_SKEY_CBC | SC_ALGORITHM_SKEY_ECB; info.algorithm = SC_ALGORITHM_3DES; info.flags = flags; info.key_length = 192; _sc_card_add_algorithm(card, &info); #endif sc_format_path("3F00", &path); rv = auth_select_file(card, &path, NULL); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "called; op 0x%X; SC_AC_PRO; ref 0x%X\n", 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 tlv_get(const unsigned char *msg, int len, unsigned char tag, unsigned char *ret, int *ret_len) { int cur = 0; while (cur < len) { if (*(msg+cur)==tag) { int ii, ln = *(msg+cur+1); if (ln > *ret_len) return SC_ERROR_WRONG_LENGTH; for (ii=0; iictx, SC_LOG_DEBUG_VERBOSE); attr_len = sizeof(attr); if (tlv_get(buf, buflen, 0x82, attr, &attr_len)) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); type = attr[0]; attr_len = sizeof(attr); if (tlv_get(buf, buflen, 0x83, attr, &attr_len)) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); file->id = attr[0]*0x100 + attr[1]; attr_len = sizeof(attr); if (tlv_get(buf, buflen, type==0x01 ? 0x80 : 0x85, attr, &attr_len)) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); if (attr_len<2 && type != 0x04) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); switch (type) { case 0x01: file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; file->size = attr[0]*0x100 + attr[1]; break; case 0x04: file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_LINEAR_VARIABLE; file->size = attr[0]; attr_len = sizeof(attr); if (tlv_get(buf, buflen, 0x82, attr, &attr_len)) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); if (attr_len!=5) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); file->record_length = attr[2]*0x100+attr[3]; file->record_count = attr[4]; break; case 0x11: 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: 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Not supported public key size: %i\n", file->size); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); } break; case 0x14: 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: file->type = SC_FILE_TYPE_DF; file->size = attr[0]; sc_file_set_type_attr(file,attr,attr_len); break; default: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); } attr_len = sizeof(attr); if (tlv_get(buf, buflen, 0x86, attr, &attr_len)) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); if (attr_len<8) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SC_FILE_TYPE_DF:CRYPTO %X\n", 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; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); assert(card != NULL && in_path != NULL); memcpy(&path, in_path, sizeof(struct sc_path)); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "in_path; type=%d, path=%s, out %p\n", in_path->type, sc_print_path(in_path), file_out); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "current path; type=%d, path=%s\n", auth_current_df->path.type, sc_print_path(&auth_current_df->path)); if (auth_current_ef) sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "current file; type=%d, path=%s\n", auth_current_ef->path.type, sc_print_path(&auth_current_ef->path)); if (path.type == SC_PATH_TYPE_PARENT || path.type == SC_PATH_TYPE_FILE_ID) { if (auth_current_ef) sc_file_free(auth_current_ef); auth_current_ef = NULL; rv = iso_ops->select_file(card, &path, &tmp_file); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "select file failed"); 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); 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); sc_file_dup(&auth_current_df, tmp_file); } else { if (auth_current_ef) sc_file_free(auth_current_ef); 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_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) { if (auth_current_ef) sc_file_free(auth_current_ef); auth_current_ef = NULL; } SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "offs %i\n", offs); if (offs && offs < auth_current_df->path.len) { size_t deep = auth_current_df->path.len - offs; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "deep %i\n", deep); for (ii=0; iipath, sizeof(struct sc_path)); tmp_path.type = SC_PATH_TYPE_PARENT; rv = auth_select_file (card, &tmp_path, file_out); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "select file failed"); } } if (path.len - offs > 0) { 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); rv = auth_select_file(card, &tmp_path, file_out); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "select file failed"); } } else if (path.len - offs == 0 && file_out) { if (sc_compare_path(&path, &auth_current_df->path)) sc_file_dup(file_out, auth_current_df); else if (auth_current_ef) sc_file_dup(file_out, auth_current_ef); else SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL, "No current EF"); } } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "Card returned error"); if (apdu.resplen == 0x100 && rbuf[0]==0 && rbuf[1]==0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 0); buflen = buflen < apdu.resplen ? buflen : apdu.resplen; memcpy(buf, rbuf, buflen); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); rv = sc_path_print(pbuf, sizeof(pbuf), path); if (rv != SC_SUCCESS) pbuf[0] = '\0'; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "path; type=%d, path=%s\n", path->type, pbuf); if (path->len < 2) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid path length\n"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "select DF failed"); len = auth_list_files(card, lbuf, sizeof(lbuf)); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, len, "list DF failed"); for (ii=0; iictx, SC_LOG_DEBUG_NORMAL, rv, "delete failed"); } tmp_path.type = SC_PATH_TYPE_PARENT; rv = auth_select_file(card, &tmp_path, NULL); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "select parent failed"); apdu.p1 = 1; rv = sc_transmit_apdu(card, &apdu); } SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 0); case SC_AC_CHV: if (key_ref > 0 && key_ref < 6) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, (0x20 | key_ref)); else SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INCORRECT_PARAMETERS); case SC_AC_PRO: if (((key_ref & 0xE0) != 0x60) || ((key_ref & 0x18) == 0)) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INCORRECT_PARAMETERS); else SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, key_ref); case SC_AC_NEVER: return 0xff; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; unsigned char *p = buf; unsigned char ops[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "id %04X; size %i; type 0x%X/0x%X\n", file->id, file->size, file->type, file->ef_structure); if (*buflen < 0x18) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid EF structure 0x%X/0x%X\n", file->type, file->ef_structure); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "ef %s\n","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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "incorrect RSA size %X\n", file->size); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "incorrect DES size %i\n", file->size); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SC_FILE_EF_TRANSPARENT\n"); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SC_FILE_EF_LINEAR_VARIABLE\n"); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "EF_DES\n"); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "EF_RSA_PUBLIC\n"); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "EF_RSA_PRIVATE\n"); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "Invalid ACL"); p[16+ii] = rv; } *buflen = 0x18; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); rv = sc_path_print(pbuf, sizeof(pbuf), &file->path); if (rv != SC_SUCCESS) pbuf[0] = '\0'; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, " create path=%s\n", pbuf); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "id %04X; size %i; type 0x%X; ef 0x%X\n", file->id, file->size, file->type, file->ef_structure); if (file->id==0x0000 || file->id==0xFFFF || file->id==0x3FFF) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Cannot select parent DF.\n"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); } } rv = encode_file_structure_V5(card, file, sbuf, &sendlen); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "File structure encoding failed"); if (file->type != SC_FILE_TYPE_DF && file->ef_structure != SC_FILE_EF_TRANSPARENT) rec_nr = 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "rv %i", rv); } if (auth_current_ef) sc_file_free(auth_current_ef); sc_file_dup(&auth_current_ef, file); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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 }; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "op %i; path %s; key_ref 0x%X; algos 0x%X; flags 0x%X\n", 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)) SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL, "Key file is not selected."); switch (env->algorithm) { case SC_ALGORITHM_DES: case SC_ALGORITHM_3DES: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "algo SC_ALGORITHM_xDES: ref %X, flags %X\n", 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid crypto operation: %X\n", env->operation); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "Invalid crypto operation"); } break; case SC_ALGORITHM_RSA: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "algo SC_ALGORITHM_RSA\n"); if (env->algorithm_flags & SC_ALGORITHM_RSA_HASHES) { SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "No support for hashes."); } if (pads & (~supported_pads)) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No support for PAD %X\n",pads); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid crypto operation: %X\n", env->operation); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); } break; default: SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "Invalid crypto algorithm supplied"); } rv = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "Card returned error"); auth_senv->algorithm = env->algorithm; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "inlen %i, outlen %i\n", ilen, olen); if (!card || !in || !out) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); } else if (ilen > 96) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Illegal input length %d\n", ilen); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "Illegal input length"); } 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "Compute signature failed"); if (apdu.resplen > olen) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Compute signature failed: invalide response length %i\n", apdu.resplen); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_CARD_CMD_FAILED); } memcpy(out, apdu.resp, apdu.resplen); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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, _inlen = inlen; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"crgram_len %i; outlen %i\n", inlen, outlen); if (!out || !outlen || inlen > SC_MAX_APDU_BUFFER_SIZE) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "algorithm SC_ALGORITHM_RSA\n"); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "rv %i", rv); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "Card returned error"); _inlen -= 8; in += 8; apdu.cla &= ~0x10; } #if 0 case SC_ALGORITHM_DES: case SC_ALGORITHM_3DES: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"algorithm SC_ALGORITHM_DES\n"); if (crgram_len == 0 || (crgram_len%8) != 0) { rv = SC_ERROR_INVALID_ARGUMENTS; goto done; } break; #endif 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "rv %i", rv); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "rv %i", rv); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "Card returned error"); if (outlen > apdu.resplen) outlen = apdu.resplen; memcpy(out, apdu.resp, outlen); rv = outlen; done: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (data->key_bits < 512 || data->key_bits > 2048 || (data->key_bits%0x20)!=0) { SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "cannot select public key"); rv = auth_read_component(card, SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC, 1, apdu.resp, data->key_bits/8); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "auth_read_component() returned error"); apdu.resplen = rv; if (data->pubkey) { if (data->pubkey_len < apdu.resplen) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); memcpy(data->pubkey,apdu.resp,apdu.resplen); } data->pubkey_len = apdu.resplen; free(apdu.resp); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "resulted public key len %i\n", apdu.resplen); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); } 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (args->len > sizeof(sbuf) || args->len > 0x100) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "nn %i; len %i\n", 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; if (args->len!=8 && args->len!=24) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); p2 = 0; EVP_CIPHER_CTX_init(&ctx); if (args->len == 24) EVP_EncryptInit_ex(&ctx, EVP_des_ede(), NULL, args->data, NULL); else EVP_EncryptInit_ex(&ctx, EVP_des_ecb(), NULL, args->data, NULL); rv = EVP_EncryptUpdate(&ctx, out, &outl, in, 8); if (!EVP_CIPHER_CTX_cleanup(&ctx) || rv == 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "OpenSSL encryption error."); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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)); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, rv); } static int auth_update_key(struct sc_card *card, struct sc_cardctl_oberthur_updatekey_info *info) { int rv, ii; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (info->data_len != sizeof(void *) || !info->data) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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[SC_MAX_APDU_BUFFER_SIZE]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "num %i, outlen %i, type %i\n", num, outlen, type); if (!outlen || type!=SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "Card returned error"); if (outlen < apdu.resplen) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_WRONG_LENGTH); memcpy(out, apdu.resp, apdu.resplen); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, apdu.resplen); } static int auth_get_pin_reference (struct sc_card *card, int type, int reference, int cmd, int *out_ref) { if (!out_ref) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); switch (type) { case SC_AC_CHV: if (reference != 1 && reference != 2 && reference != 4) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_PIN_REFERENCE); *out_ref = reference; if (reference == 1 || reference == 4) if (cmd == SC_PIN_CMD_VERIFY) *out_ref |= 0x80; break; default: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); memset(ffs1, 0xFF, sizeof(ffs1)); memset(&pin_cmd, 0, sizeof(pin_cmd)); rv = auth_pin_is_verified(card, pin_reference, tries_left); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "auth_pin_is_verified returned rv %i\n", rv); /* Return SUCCESS without verifying if * PIN has been already verified and PIN pad has to be used. */ if (!rv) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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 uppler 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "PIN CMD 'VERIFY' with pinpad failed"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (type != SC_AC_CHV) SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "auth_pin_is_verified returned rv %i\n", rv); /* Return if only PIN status has been asked. */ if (data->pin1.data && !data->pin1.len) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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) SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "PIN CMD 'VERIFY' with pinpad failed"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "'PIN CHANGE' failedi: SOPIN verify with pinpad failed"); } else { SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "'PIN CHANGE' failed"); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); local_pin_reference = data->pin_reference & ~OBERTHUR_PIN_LOCAL; if (data->pin_reference != OBERTHUR_PIN_REFERENCE_USER) SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "select PUK file"); if (tmp_file->size < OBERTHUR_AUTH_MAX_LENGTH_PUK) SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_TOO_SMALL, "Oberthur style 'PIN RESET' failed"); rv = iso_ops->read_binary(card, 0, puk, OBERTHUR_AUTH_MAX_LENGTH_PUK, 0); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "read PUK file error"); if (rv != OBERTHUR_AUTH_MAX_LENGTH_PUK) SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s: PIN CMD 'VERIFY' with pinpad failed", sc_strerror(rvv)); if (auth_current_ef) rv = iso_ops->select_file(card, &auth_current_ef->path, &auth_current_ef); if (rv > 0) rv = 0; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* Oberthur unblock style: PUK value is a SOPIN */ rv = auth_pin_reset_oberthur_style(card, SC_AC_CHV, data, tries_left); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "Oberthur style 'PIN RESET' failed"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, rv); } static int auth_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { int rv; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (data->pin_type != SC_AC_CHV) SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "auth_pin_cmd() unsupported PIN type"); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "PIN CMD:%i; reference:%i; pin1:%p/%i, pin2:%p/%i\n", 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "CMD 'PIN VERIFY' failed"); break; case SC_PIN_CMD_CHANGE: rv = auth_pin_change(card, SC_AC_CHV, data, tries_left); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "CMD 'PIN VERIFY' failed"); break; case SC_PIN_CMD_UNBLOCK: rv = auth_pin_reset(card, SC_AC_CHV, data, tries_left); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "CMD 'PIN VERIFY' failed"); break; default: SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "Unsupported PIN operation"); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "PIN reference %i\n", args->ref); if (args->type != SC_AC_CHV) SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "Unsupported PIN type"); if (args->pin_tries < 1 || !args->pin || !args->pin_len) SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "Invalid PIN options"); if (args->ref != OBERTHUR_PIN_REFERENCE_USER && args->ref != OBERTHUR_PIN_REFERENCE_PUK) SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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)) SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "Invalid PUK options"); len = 0; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "len %i", len); sbuf[len++] = args->pin_tries; sbuf[len++] = pin_info.pad_length; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "len %i", len); if (args->puk && args->puk_len) { sbuf[len++] = args->puk_tries; sbuf[len++] = args->puk_len / puk_info.pad_length; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "len %i", len); memcpy(sbuf + len, args->puk, args->puk_len); len += args->puk_len; } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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)); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; char debug_buf[2048]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); debug_buf[0] = 0; sc_hex_dump(card->ctx, SC_LOG_DEBUG_NORMAL, buf, count, debug_buf, sizeof(debug_buf)); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "write_publickey in %d bytes :\n%s", count, debug_buf); if (offset > sizeof(rsa_der)) SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "der_size %i\n",der_size); if (offset + len < der_size + 2) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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)); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "Update component failed"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "offset %i; count %i\n", offset, count); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "last selected : magic %X; ef %X\n", auth_current_ef->magic, auth_current_ef->ef_structure); if (offset & ~0x7FFF) SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, rv); } static int auth_read_binary(struct sc_card *card, unsigned int offset, unsigned char *buf, size_t count, unsigned long flags) { int rv; char debug_buf[2048]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"offset %i; size %i; flags 0x%lX\n", offset, count, flags); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"last selected : magic %X; ef %X\n", auth_current_ef->magic, auth_current_ef->ef_structure); if (offset & ~0x7FFF) SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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[0x100], *out = NULL; size_t resp_len, out_len; struct sc_pkcs15_bignum bn[2]; 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "read component failed"); for (jj=0; jjctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot read RSA public key component"); bn[1].data = calloc(1, rv); 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_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ASN1_OBJECT, "cannot encode RSA public key"); } else { rv = out_len - offset > count ? count : out_len - offset; memcpy(buf, out + offset, rv); debug_buf[0] = 0; sc_hex_dump(card->ctx, SC_LOG_DEBUG_NORMAL, buf, rv, debug_buf, sizeof(debug_buf)); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "write_publickey in %d bytes :\n%s", count, debug_buf); } if (bn[0].data) free(bn[0].data); if (bn[1].data) free(bn[1].data); if (out) free(out); } else { rv = iso_ops->read_binary(card, offset, buf, count, 0); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, rv); } static int auth_read_record(struct sc_card *card, unsigned int nr_rec, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "auth_read_record(): nr_rec %i; count %i\n", nr_rec, count); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "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); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "Card returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, apdu.resplen); } static int auth_delete_record(struct sc_card *card, unsigned int nr_rec) { struct sc_apdu apdu; int rv = 0; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "auth_delete_record(): nr_rec %i\n", nr_rec); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x32, nr_rec, 0x04); apdu.cla = 0x80; rv = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, rv); } static int auth_get_serialnr(struct sc_card *card, struct sc_serial_number *serial) { if (!serial) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); if (card->serialnr.len==0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); memcpy(serial, &card->serialnr, sizeof(*serial)); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s\n", 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.13.0/src/libopensc/pkcs15-atrust-acos.c0000644000015201777760000002124312057406034016311 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #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" int sc_pkcs15emu_atrust_acos_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); 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 = ((((size_t) 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 */ #if 0 {"C.CH.DS", 0, "DF70C002","2", 0},/* Signature Certificate */ #endif {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 }, #if 0 { "02", "PIN.SIG", "3F00DF70", 0x81, /* Signature PIN */ SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 6, 6, 8, SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_LOCAL, -1, 0x00, SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE }, { "03", "PIN.INF", "3F00DF71", 0x83, /* Infobox 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 }, #endif { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; const prdata prkeys[] = { { "1", "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}, #if 0 { "2", "SK.CH.DS", 192, SC_PKCS15_PRKEY_USAGE_SIGN, "", /* do not specify file here to prevent reset of security state */ 0x88, "02", SC_PKCS15_CO_FLAG_PRIVATE}, #endif { 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; if (p15card->tokeninfo->serial_number) free(p15card->tokeninfo->serial_number); p15card->tokeninfo->serial_number = malloc(strlen(buf2) + 1); if (!p15card->tokeninfo->serial_number) return SC_ERROR_INTERNAL; strcpy(p15card->tokeninfo->serial_number, buf2); /* manufacturer ID */ if (p15card->tokeninfo->manufacturer_id) free(p15card->tokeninfo->manufacturer_id); p15card->tokeninfo->manufacturer_id = malloc(strlen(MANU_ID) + 1); if (!p15card->tokeninfo->manufacturer_id) return SC_ERROR_INTERNAL; strcpy(p15card->tokeninfo->manufacturer_id, MANU_ID); /* card label */ if (p15card->tokeninfo->label) free(p15card->tokeninfo->label); p15card->tokeninfo->label = malloc(strlen(CARD_LABEL) + 1); if (!p15card->tokeninfo->label) return SC_ERROR_INTERNAL; strcpy(p15card->tokeninfo->label, CARD_LABEL); /* 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) return SC_ERROR_INTERNAL; } /* 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; strlcpy(pin_obj.label, pins[i].label, sizeof(pin_obj.label)); pin_obj.flags = pins[i].obj_flags; r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); if (r < 0) return SC_ERROR_INTERNAL; } /* 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) return SC_ERROR_INTERNAL; } /* select the application DF */ sc_format_path("DF71", &path); r = sc_select_file(card, &path, &file); if (r != SC_SUCCESS || !file) return SC_ERROR_INTERNAL; /* set the application DF */ if (p15card->file_app) free(p15card->file_app); p15card->file_app = file; return SC_SUCCESS; } int sc_pkcs15emu_atrust_acos_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts) { if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) return sc_pkcs15emu_atrust_acos_init(p15card); else { int r = acos_detect_card(p15card); if (r) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_atrust_acos_init(p15card); } } opensc-0.13.0/src/libopensc/opensc.h0000644000015201777760000013202512057406034014235 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 #define SC_SEC_OPERATION_DECIPHER 0x0001 #define SC_SEC_OPERATION_SIGN 0x0002 #define SC_SEC_OPERATION_AUTHENTICATE 0x0003 #define SC_SEC_OPERATION_DERIVE 0x0004 /* sc_security_env flags */ #define SC_SEC_ENV_ALG_REF_PRESENT 0x0001 #define SC_SEC_ENV_FILE_REF_PRESENT 0x0002 #define SC_SEC_ENV_KEY_REF_PRESENT 0x0004 /* FIXME: the flag below is misleading */ #define SC_SEC_ENV_KEY_REF_ASYMMETRIC 0x0008 #define SC_SEC_ENV_ALG_PRESENT 0x0010 /* PK algorithms */ #define SC_ALGORITHM_RSA 0 #define SC_ALGORITHM_DSA 1 #define SC_ALGORITHM_EC 2 #define SC_ALGORITHM_GOSTR3410 3 /* Symmetric algorithms */ #define SC_ALGORITHM_DES 64 #define SC_ALGORITHM_3DES 65 #define SC_ALGORITHM_GOST 66 /* 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 algoprithms */ #define SC_ALGORITHM_PBES2 256 #define SC_ALGORITHM_ONBOARD_KEY_GEN 0x80000000 /* need usage = either sign or decrypt. keys with both? decrypt, emulate sign */ #define SC_ALGORITHM_NEED_USAGE 0x40000000 #define SC_ALGORITHM_SPECIFIC_FLAGS 0x0001FFFF #define SC_ALGORITHM_RSA_RAW 0x00000001 /* If the card is willing to produce a cryptogram padded with the following * methods, set these flags accordingly. */ #define SC_ALGORITHM_RSA_PADS 0x0000000E #define SC_ALGORITHM_RSA_PAD_NONE 0x00000000 #define SC_ALGORITHM_RSA_PAD_PKCS1 0x00000002 #define SC_ALGORITHM_RSA_PAD_ANSI 0x00000004 #define SC_ALGORITHM_RSA_PAD_ISO9796 0x00000008 /* If the card is willing to produce a cryptogram with the following * hash values, set these flags accordingly. */ #define SC_ALGORITHM_RSA_HASH_NONE 0x00000010 #define SC_ALGORITHM_RSA_HASH_SHA1 0x00000020 #define SC_ALGORITHM_RSA_HASH_MD5 0x00000040 #define SC_ALGORITHM_RSA_HASH_MD5_SHA1 0x00000080 #define SC_ALGORITHM_RSA_HASH_RIPEMD160 0x00000100 #define SC_ALGORITHM_RSA_HASH_SHA256 0x00000200 #define SC_ALGORITHM_RSA_HASH_SHA384 0x00000400 #define SC_ALGORITHM_RSA_HASH_SHA512 0x00000800 #define SC_ALGORITHM_RSA_HASH_SHA224 0x00001000 #define SC_ALGORITHM_RSA_HASHES 0x00001FE0 #define SC_ALGORITHM_GOSTR3410_RAW 0x00002000 #define SC_ALGORITHM_GOSTR3410_HASH_NONE 0x00004000 #define SC_ALGORITHM_GOSTR3410_HASH_GOSTR3411 0x00008000 #define SC_ALGORITHM_GOSTR3410_HASHES 0x00008000 /*TODO: -DEE Should the above be 0x0000E000 */ /* Or should the HASH_NONE be 0x00000010 and HASHES be 0x00008010 */ /* May need more bits if card can do more hashes */ /* TODO: -DEE Will overload RSA_HASHES with EC_HASHES */ /* Not clear if these need their own bits or not */ /* The PIV card does not support and hashes */ #define SC_ALGORITHM_ECDSA_RAW 0x00010000 #define SC_ALGORITHM_ECDH_CDH_RAW 0x00020000 #define SC_ALGORITHM_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) /* define mask of all algorithms that can do raw */ #define SC_ALGORITHM_RAW_MASK (SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_GOSTR3410_RAW | SC_ALGORITHM_ECDSA_RAW) /* extened 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 /* 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 struct sc_supported_algo_info { unsigned int reference; unsigned int mechanism; unsigned int operations; struct sc_object_id algo_id; unsigned int algo_ref; }; typedef struct sc_security_env { unsigned long flags; int operation; unsigned int algorithm, algorithm_flags; unsigned int algorithm_ref; struct sc_path file_ref; u8 key_ref[8]; size_t key_ref_len; struct sc_supported_algo_info supported_algos[SC_MAX_SUPPORTED_ALGORITHMS]; } sc_security_env_t; struct sc_algorithm_id { unsigned int 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; }; struct sc_ec_params { int type; u8 * der; size_t der_len; }; typedef struct sc_algorithm_info { unsigned int algorithm; unsigned int key_length; unsigned int flags; union { struct sc_rsa_info { unsigned long exponent; } _rsa; struct sc_ec_info { unsigned ext_flags; } _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; 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; size_t max_send_size; /* Max Lc supported by the reader layer */ size_t max_recv_size; /* Mac Le supported by the reader layer */ 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 /* reader capabilities */ #define SC_READER_CAP_DISPLAY 0x00000001 #define SC_READER_CAP_PIN_PAD 0x00000002 #define SC_READER_CAP_PACE_GENERIC 0x00000003 #define SC_READER_CAP_PACE_EID 0x00000004 #define SC_READER_CAP_PACE_ESIGN 0x00000008 #define SC_READER_CAP_PACE_DESTROY_CHANNEL 0x00000010 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; unsigned long flags, capabilities; unsigned int supported_protocols, active_protocol; struct sc_atr atr; 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_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 */ struct sc_pin_cmd_pin { const char *prompt; /* Prompt to display */ const u8 *data; /* PIN, if given by the appliction */ int len; /* set to -1 to get pin from pin pad */ size_t min_length; /* min/max length of PIN */ size_t max_length; unsigned int encoding; /* ASCII-numeric, BCD, etc */ size_t pad_length; /* filled in by the card driver */ u8 pad_char; size_t offset; /* PIN offset in the APDU */ size_t length_offset; /* Effective PIN length offset in the APDU */ int max_tries; /* Used for signaling back from SC_PIN_CMD_GET_INFO */ int tries_left; /* Used for signaling back from SC_PIN_CMD_GET_INFO */ struct sc_acl_entry acls[SC_MAX_SDO_ACLS]; }; struct sc_pin_cmd_data { unsigned int cmd; unsigned int flags; unsigned int pin_type; /* usually SC_AC_CHV */ int pin_reference; struct sc_pin_cmd_pin pin1, pin2; struct sc_apdu *apdu; /* APDU of the PIN command */ }; #if 0 #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; }; #endif 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 /* * 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 /* 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 /* D-TRUST CardOS cards special flags */ #define SC_CARD_CAP_ONLY_RAW_HASH 0x00000040 #define SC_CARD_CAP_ONLY_RAW_HASH_STRIPPED 0x00000080 typedef struct sc_card { struct sc_context *ctx; struct sc_reader *reader; struct sc_atr atr; 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_file *ef_dir; 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 { /* 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. */ int (*match_card)(struct sc_card *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. */ int (*init)(struct sc_card *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 */ int (*read_binary)(struct sc_card *card, unsigned int idx, u8 * buf, size_t count, unsigned long flags); int (*write_binary)(struct sc_card *card, unsigned int idx, const u8 * buf, size_t count, unsigned long flags); int (*update_binary)(struct sc_card *card, unsigned int idx, const u8 * buf, size_t count, unsigned long flags); 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, 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, 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); 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. Similiar * 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 *); }; 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; typedef struct sc_context { scconf_context *conf; scconf_block *conf_blocks[3]; char *app_name; int debug; int paranoid_memory; 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; unsigned int magic; } 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 succcess and an error code otherwise */ int sc_transmit_apdu(struct sc_card *card, sc_apdu_t *apdu); void sc_format_apdu(struct sc_card *card, sc_apdu_t *apdu, int cse, int ins, int p1, int p2); /** 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); 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 * dependend configuration data). If NULL the name "default" * will be used. */ const char *app_name; /** flags, currently unused */ unsigned long flags; /** mutex functions to use (optional) */ sc_thread_context_t *thread_ctx; } 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); /** * 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 on success and an error code otherwise. */ int sc_ctx_use_reader(sc_context_t *ctx, 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); /** * 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 occured, 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. Note: only the event is detected, * there is no update of any card or other info. * NOTE: Only PC/SC backend implements this. * @param ctx pointer to a Context structure * @param event_mask The types of events to wait for; this should * be ORed from one of the following * SC_EVENT_CARD_REMOVED * SC_EVENT_CARD_INSERTED * SC_EVENT_READER_ATTACHED * @param event_reader (OUT) the reader on which the event was detected, or NULL if new reader * @param event (OUT) the events that occurred. This is also ORed * from the SC_EVENT_CARD_* constants listed above. * @param timeout Amount of millisecs to wait; -1 means forever * @retval < 0 if an error occured * @retval = 0 if a an event happened * @retval = 1 if the timeout occured */ 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); /********************************************************************/ /* 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); /** * Read data from a binary EF * @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); /** * Write data to a binary EF * @param card struct sc_card object on which to issue the command * @param idx index within the file for the data to be written * @param buf buffer with the data * @param count number of bytes to write * @param flags flags for the WRITE BINARY command (currently not used) * @return number of bytes writen or an error code */ int sc_write_binary(struct sc_card *card, unsigned int idx, const u8 * buf, size_t count, unsigned long flags); /** * Updates the content of a binary EF * @param card struct sc_card object on which to issue the command * @param idx index within the file for the data to be updated * @param buf buffer with the new data * @param count number of bytes to update * @param flags flags for the UPDATE BINARY command (currently not used) * @return number of bytes writen or an error code */ int sc_update_binary(struct sc_card *card, unsigned int idx, const u8 * buf, size_t count, unsigned long flags); /** * Sets (part of) the content fo an EF to its logical erased state * @param card struct sc_card object on which to issue the command * @param idx index within the file for the data to be erased * @param count number of bytes to erase * @param flags flags for the ERASE BINARY command (currently not used) * @return number of bytes writen or an error code */ 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 buf Pointer to a buffer for storing the data * @param count Number of bytes to read * @param flags flags (may contain a short file id of a file to select) * @retval number of bytes read or an error value */ int sc_read_record(struct sc_card *card, unsigned int rec_nr, u8 * buf, size_t count, unsigned long flags); /** * Writes data to a record from the current (i.e. selected) file. * @param card struct sc_card object on which to issue the command * @param rec_nr SC_READ_RECORD_CURRENT or a record number starting from 1 * @param buf buffer with to the data to be writen * @param count number of bytes to write * @param flags flags (may contain a short file id of a file to select) * @retval number of bytes writen or an error value */ 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 writen 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 buf buffer with to the new data to be writen * @param count number of bytes to update * @param flags flags (may contain a short file id of a file to select) * @retval number of bytes writen or an error value */ int sc_update_record(struct sc_card *card, unsigned int rec_nr, 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 * @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 *buf, size_t buflen, 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); /********************************************************************/ /* 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); /********************************************************************/ /* 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); int sc_bin_to_hex(const u8 *, size_t, char *, size_t, int separator); 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 non-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_alloc_secure(sc_context_t *ctx, size_t len); 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_update_dir(struct sc_card *card, sc_app_info_t *app); void sc_print_cache(struct sc_card *card); struct sc_algorithm_info * sc_card_find_rsa_alg(struct sc_card *card, unsigned int key_length); struct sc_algorithm_info * sc_card_find_ec_alg(struct sc_card *card, unsigned int field_length); struct sc_algorithm_info * sc_card_find_gostr3410_alg(struct sc_card *card, unsigned int key_length); /** * 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(unsigned char *value, size_t len); /** * 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); 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); #ifdef __cplusplus } #endif #endif opensc-0.13.0/src/libopensc/cards.h0000644000015201777760000001546612057406034014053 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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, /* 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, /* gpk driver */ SC_CARD_TYPE_GPK_BASE = 3000, SC_CARD_TYPE_GPK_GENERIC, SC_CARD_TYPE_GPK_GPK4000_su256 = 3040, SC_CARD_TYPE_GPK_GPK4000_s, SC_CARD_TYPE_GPK_GPK4000_sp, SC_CARD_TYPE_GPK_GPK4000_sdo, SC_CARD_TYPE_GPK_GPK8000 = 3080, SC_CARD_TYPE_GPK_GPK8000_8K, SC_CARD_TYPE_GPK_GPK8000_16K, SC_CARD_TYPE_GPK_GPK16000 = 3160, /* miocos driver */ SC_CARD_TYPE_MIOCOS_BASE = 4000, SC_CARD_TYPE_MIOCOS_GENERIC, /* mcrd driver */ SC_CARD_TYPE_MCRD_BASE = 5000, SC_CARD_TYPE_MCRD_GENERIC, SC_CARD_TYPE_MCRD_ESTEID_V10, SC_CARD_TYPE_MCRD_ESTEID_V11, SC_CARD_TYPE_MCRD_ESTEID_V30, SC_CARD_TYPE_MCRD_DTRUST, /* setcos driver */ SC_CARD_TYPE_SETCOS_BASE = 6000, SC_CARD_TYPE_SETCOS_GENERIC, SC_CARD_TYPE_SETCOS_PKI, SC_CARD_TYPE_SETCOS_FINEID, SC_CARD_TYPE_SETCOS_FINEID_V2, SC_CARD_TYPE_SETCOS_NIDEL, SC_CARD_TYPE_SETCOS_FINEID_V2_2048, SC_CARD_TYPE_SETCOS_44 = 6100, SC_CARD_TYPE_SETCOS_EID_V2_0, SC_CARD_TYPE_SETCOS_EID_V2_1, /* starcos driver */ SC_CARD_TYPE_STARCOS_BASE = 7000, SC_CARD_TYPE_STARCOS_GENERIC, /* 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, /* jcop driver */ SC_CARD_TYPE_JCOP_BASE = 10000, SC_CARD_TYPE_JCOP_GENERIC, /* oberthur driver */ SC_CARD_TYPE_OBERTHUR_BASE = 11000, SC_CARD_TYPE_OBERTHUR_GENERIC, 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, /* incrypto34 driver */ SC_CARD_TYPE_INCRYPTO34_BASE = 13000, SC_CARD_TYPE_INCRYPTO34_GENERIC, /* PIV-II type cards */ SC_CARD_TYPE_PIV_II_BASE = 14000, SC_CARD_TYPE_PIV_II_GENERIC, /* 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, /* ACOS5 driver */ SC_CARD_TYPE_ACOS5_BASE = 16000, SC_CARD_TYPE_ACOS5_GENERIC, /* Athena APCOS cards */ SC_CARD_TYPE_ASEPCOS_BASE = 17000, SC_CARD_TYPE_ASEPCOS_GENERIC, SC_CARD_TYPE_ASEPCOS_JAVA, /* TUBITAK UEKAE cards */ SC_CARD_TYPE_AKIS_BASE = 18000, SC_CARD_TYPE_AKIS_GENERIC, /* EnterSafe cards */ SC_CARD_TYPE_ENTERSAFE_BASE = 19000, SC_CARD_TYPE_ENTERSAFE_3K, SC_CARD_TYPE_ENTERSAFE_FTCOS_PK_01C, SC_CARD_TYPE_ENTERSAFE_FTCOS_EPASS2003, /* MyEID cards */ SC_CARD_TYPE_MYEID_BASE = 20000, SC_CARD_TYPE_MYEID_GENERIC, /* GemsafeV1 cards */ SC_CARD_TYPE_GEMSAFEV1_BASE = 21000, SC_CARD_TYPE_GEMSAFEV1_GENERIC, SC_CARD_TYPE_GEMSAFEV1_PTEID, /* IAS cards */ SC_CARD_TYPE_IAS_BASE = 22000, SC_CARD_TYPE_IAS_PTEID, /* Italian CNS cards */ SC_CARD_TYPE_ITACNS_BASE = 23000, SC_CARD_TYPE_ITACNS_GENERIC, SC_CARD_TYPE_ITACNS_CNS, 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, /* SmartCard-HSM */ SC_CARD_TYPE_SC_HSM = 26000, }; extern sc_card_driver_t *sc_get_default_driver(void); extern sc_card_driver_t *sc_get_cardos_driver(void); extern sc_card_driver_t *sc_get_cryptoflex_driver(void); extern sc_card_driver_t *sc_get_cyberflex_driver(void); extern sc_card_driver_t *sc_get_gpk_driver(void); extern sc_card_driver_t *sc_get_gemsafeV1_driver(void); extern sc_card_driver_t *sc_get_miocos_driver(void); extern sc_card_driver_t *sc_get_mcrd_driver(void); extern sc_card_driver_t *sc_get_setcos_driver(void); extern sc_card_driver_t *sc_get_starcos_driver(void); extern sc_card_driver_t *sc_get_tcos_driver(void); extern sc_card_driver_t *sc_get_openpgp_driver(void); extern sc_card_driver_t *sc_get_jcop_driver(void); extern sc_card_driver_t *sc_get_oberthur_driver(void); extern sc_card_driver_t *sc_get_belpic_driver(void); extern sc_card_driver_t *sc_get_atrust_acos_driver(void); extern sc_card_driver_t *sc_get_incrypto34_driver(void); extern sc_card_driver_t *sc_get_piv_driver(void); extern sc_card_driver_t *sc_get_muscle_driver(void); extern sc_card_driver_t *sc_get_acos5_driver(void); extern sc_card_driver_t *sc_get_asepcos_driver(void); extern sc_card_driver_t *sc_get_akis_driver(void); extern sc_card_driver_t *sc_get_entersafe_driver(void); extern sc_card_driver_t *sc_get_rutoken_driver(void); extern sc_card_driver_t *sc_get_rtecp_driver(void); extern sc_card_driver_t *sc_get_westcos_driver(void); extern sc_card_driver_t *sc_get_myeid_driver(void); extern sc_card_driver_t *sc_get_ias_driver(void); extern sc_card_driver_t *sc_get_sc_hsm_driver(void); extern sc_card_driver_t *sc_get_javacard_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); #ifdef __cplusplus } #endif #endif /* _OPENSC_CARDS_H */ opensc-0.13.0/src/libopensc/itacns.h0000644000015201777760000000045712057406034014232 00000000000000#ifndef _OPENSC_ITACNS_H #define _OPENSC_ITACNS_H typedef struct { u8 ic_manufacturer_code; u8 mask_manufacturer_code; u8 os_version_h; u8 os_version_l; u8 cns_version; } itacns_drv_data_t; #define ITACNS_MASKMAN_SIEMENS 0x08 #define ITACNS_MASKMAN_STINCARD 0x09 #endif /* _OPENSC_ITACNS_H */ opensc-0.13.0/src/libopensc/types.h0000644000015201777760000002454512057406034014121 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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_EXT_APDU_BUFFER_SIZE 65538 #define SC_MAX_PIN_SIZE 256 /* OpenPGP card has 254 max */ #define SC_MAX_ATR_SIZE 33 #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 /* 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 */ #define SC_MAX_SUPPORTED_ALGORITHMS 8 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; }; /* 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_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 /* If you add more OPs here, make sure you increase SC_MAX_AC_OPS*/ #define SC_MAX_AC_OPS 27 /* 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_crt crts[SC_MAX_CRTS_IN_SE]; struct sc_acl_entry *next; } sc_acl_entry_t; /* File types */ #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 status flags */ #define SC_FILE_STATUS_ACTIVATED 0x00 #define SC_FILE_STATUS_INVALIDATED 0x01 #define SC_FILE_STATUS_CREATION 0x02 /* Full access in this state, (at least for SetCOS 4.4 */ typedef struct sc_file { struct sc_path path; u8 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; /* Short file id (2 bytes) */ struct sc_acl_entry *acl[SC_MAX_AC_OPS]; /* Access Control List */ int record_length; /* In case of fixed-length or cyclic EF */ int record_count; /* Valid, if not transparent EF or DF */ u8 *sec_attr; size_t sec_attr_len; u8 *prop_attr; size_t prop_attr_len; u8 *type_attr; size_t type_attr_len; 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 typedef struct sc_apdu { int cse; /* APDU case */ u8 cla, ins, p1, p2; /* CLA, INS, P1 and P2 bytes */ size_t lc, le; /* Lc and Le bytes */ const u8 *data; /* C-APDU data */ size_t datalen; /* length of data in C-APDU */ u8 *resp; /* R-APDU data buffer */ size_t resplen; /* in: size of R-APDU buffer, * out: length of data returned in R-APDU */ u8 control; /* Set if APDU should go to the reader */ 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.13.0/src/libopensc/card-asepcos.c0000644000015201777760000010247612057406034015314 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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 r; } /* 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 trasport 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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 >= 4U + p[4]) { /* TODO: support OR expressions */ int r = set_sec_attr(file, amode, p[5], SC_AC_CHV); if (r != SC_SUCCESS) return r; tlen += 2 + p[4]; /* FIXME */ } else if (p[3] == 0xAF && len >= 4U + p[4]) { /* TODO: support AND expressions */ int r = set_sec_attr(file, amode, p[5], SC_AC_CHV); if (r != SC_SUCCESS) return r; tlen += 2 + p[4]; /* FIXME */ } else { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid security condition"); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); 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; 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "error parsing security attributes"); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } static int asepcos_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { #if 0 /* this function doesn't seem to be necessary if RSA ENCRYPT DECRYPT * is used. */ sc_apdu_t apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE], *p = sbuf; int r, locked = 0; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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; default: return SC_ERROR_INVALID_ARGUMENTS; } 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++ = 0x84; *p++ = env->file_ref.len; memcpy(p, env->file_ref.value, env->file_ref.len); p += env->file_ref.len; } apdu.lc = p - sbuf; apdu.datalen = p - sbuf; apdu.data = sbuf; if (se_num > 0) { r = sc_lock(card); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed"); locked = 1; } if (apdu.datalen != 0) { r = sc_transmit_apdu(card, &apdu); if (r) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s: APDU transmit failed", sc_strerror(r)); goto err; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); err: if (locked) sc_unlock(card); return r; #else return SC_SUCCESS; #endif } 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; 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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 wether 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unknow 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); /* 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); return apdu.resplen; } /* 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return sc_check_sw(card, apdu.sw1, apdu.sw2); /* set security attributes */ r = asepcos_set_security_attributes(card, file); if (r != SC_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to set security attributes"); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return sc_check_sw(card, apdu.sw1, apdu.sw2); /* set security attributes */ r = asepcos_set_security_attributes(card, file); if (r != SC_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to set security attributes"); 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 (rv != 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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]; #if 0 /* select the file (note: if the file is already selected we do * not re-select it as we might otherwise lose the necessary * credential */ r = sc_select_file(card, path, NULL); if (r != SC_SUCCESS) return r; #endif /* 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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 { /* presumedly a DF */ atype = SC_APDU_CASE_1; ftype = 0x00; } sc_format_apdu(card, &apdu, atype, 0xe4, ftype, 0x00); if (atype == SC_APDU_CASE_3_SHORT) { apdu.lc = 2; apdu.datalen = 2; apdu.data = buf; } r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return SC_ERROR_INTERNAL; if (apdu.resplen != 8) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unexpected response to GET DATA serial number\n"); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; if (is_puk == 0) { memcpy(p, data->pin1.data, data->pin1.len); p += data->pin1.len; } else { 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed"); break; case SC_PIN_CMD_CHANGE: if (pdata->pin_type != SC_AC_CHV) return SC_ERROR_INVALID_ARGUMENTS; if (pdata->pin2.len < 4 || pdata->pin2.len > 16) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid PIN2 length"); return SC_ERROR_INVALID_PIN_LENGTH; } /* 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed"); break; } if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) { /* unable to verify the old PIN */ r = sc_check_sw(card, apdu.sw1, apdu.sw2); break; } /* 2, step: use CHANGE KEY to update the PIN */ 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); break; case SC_PIN_CMD_UNBLOCK: if (pdata->pin_type != SC_AC_CHV) return SC_ERROR_INVALID_ARGUMENTS; if (pdata->pin2.len < 4 || pdata->pin2.len > 16) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid PIN2 length"); return SC_ERROR_INVALID_PIN_LENGTH; } /* 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed"); break; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); break; default: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "error: unknow cmd type"); return SC_ERROR_INTERNAL; } /* Clear the buffer - it may contain pins */ sc_mem_clear(sbuf, sizeof(sbuf)); /* check for remaining tries if verification failed */ if (apdu.sw1 == 0x63) { if ((apdu.sw2 & 0xF0) == 0xC0 && tries_left != NULL) *tries_left = apdu.sw2 & 0x0F; return SC_ERROR_PIN_CODE_INCORRECT; } return sc_check_sw(card, apdu.sw1, apdu.sw2); } 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; return &asepcos_drv; } struct sc_card_driver * sc_get_asepcos_driver(void) { return sc_get_driver(); } opensc-0.13.0/src/libopensc/card-akis.c0000644000015201777760000003260612057406034014603 00000000000000/* * card-akis.c: Support for AKIS smart cards * * Copyright (C) 2007 TUBITAK / UEKAE * contact: bilgi@pardus.org.tr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" /* generic iso 7816 operations table */ static const struct sc_card_operations *iso_ops = NULL; /* our operations table with overrides */ static struct sc_card_operations akis_ops; static struct sc_card_driver akis_drv = { "TUBITAK UEKAE AKIS", "akis", &akis_ops, NULL, 0, NULL }; static struct sc_atr_table akis_atrs[] = { { "3b:ba:11:00:81:31:fe:4d:55:45:4b:41:45:20:56:31:2e:30:ae", NULL, NULL, SC_CARD_TYPE_AKIS_GENERIC, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } }; static int akis_match_card(sc_card_t *card) { int i; i = _sc_match_atr(card, akis_atrs, &card->type); if (i < 0) return 0; return 1; } static int akis_init(sc_card_t *card) { unsigned long flags; card->name = "AKIS"; card->cla = 0x00; card->max_pin_len = 16; card->max_recv_size = 244; card->max_send_size = 244; flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1; _sc_card_add_rsa_alg(card, 2048, flags, 0); return 0; } static int select_file(sc_card_t *card, sc_apdu_t *apdu, const sc_path_t *path, int mode, sc_file_t **file_out) { int r; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_file_t *file; sc_format_apdu(card, apdu, SC_APDU_CASE_4_SHORT, 0xA4, mode, 0); apdu->resp = rbuf; apdu->resplen = sizeof(rbuf); apdu->datalen = path->len; apdu->data = path->value; apdu->lc = path->len; apdu->le = 256; r = sc_transmit_apdu(card, apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu->sw1, apdu->sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); if (file_out == NULL) return 0; file = sc_file_new(); if (file == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); r = card->ops->process_fci(card, file, apdu->resp + 2, apdu->resp[1]); if (r) { sc_file_free(file); return r; } *file_out = file; return 0; } static int akis_select_file(sc_card_t *card, const sc_path_t *path, sc_file_t **file_out) { int r; sc_apdu_t apdu; if (path->type == SC_PATH_TYPE_PATH) { /* FIXME: iso implementation seems already do that */ r = select_file(card, &apdu, path, path->len == 2 ? 0 : 8, file_out); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to select DF"); return 0; } else if (path->type == SC_PATH_TYPE_FILE_ID) { /* AKIS differentiates between EF and DF files * when selecting with ID, this workaround tries both */ r = select_file(card, &apdu, path, 2, file_out); if (r) r = select_file(card, &apdu, path, 0, file_out); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to select DF"); return 0; } else { return iso_ops->select_file(card, path, file_out); } } static int akis_list_files(sc_card_t *card, u8 *buf, size_t buflen) { /* This AKIS specific command is not provided by generic ISO driver */ sc_apdu_t apdu; u8 rbuf[256]; size_t left, fids = 0; u8 *p; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x18, 0, 0); apdu.cla = 0x80; apdu.le = 256; apdu.resplen = sizeof(rbuf); apdu.resp = rbuf; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "DIRECTORY command returned error"); left = apdu.resplen; p = rbuf; while (left > 19) { if (p[0] != 0x2f && p[0] != 0x3d) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Malformatted list reply %02x", p[0]); return SC_ERROR_INTERNAL; } if (buflen >= 2) { buf[fids++] = p[1]; buf[fids++] = p[2]; buflen -= 2; } else { break; } p += 20; left -= 20; } r = fids; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } static int akis_process_fci(sc_card_t *card, sc_file_t *file, const u8 *buf, size_t buflen) { int r; size_t len; const u8 *p; u8 perms; r = iso_ops->process_fci(card, file, buf, buflen); if (r < 0) return r; /* AKIS uses a different security model than ISO implementation */ p = sc_asn1_find_tag(card->ctx, buf, buflen, 0x90, &len); if (p == NULL) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Security tag missing"); return SC_ERROR_INTERNAL; } perms = p[0]; /* Bit definitions: * 0x01 Encrypted * 0x02 Valid * 0x04 PIN needed * 0x08 New PIN * 0x10 Read * 0x20 Write * 0x40 Wrong PIN entered once * 0x80 Last try for PIN */ if (file->type == SC_FILE_TYPE_DF) { if (perms & 0x04) sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES, SC_AC_CHV, 0x80); } else { if (!(perms & 0x04)) sc_file_add_acl_entry(file, SC_AC_OP_READ, SC_AC_CHV, 0x80); } return 0; } static int akis_create_file(sc_card_t *card, sc_file_t *file) { int r; u8 type; u8 acl; u8 fid[4]; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_apdu_t apdu; /* AKIS uses different create commands for EF and DF. * Parameters are not passed as ASN.1 structs but in * a custom format. */ /* FIXME: hardcoded for now, better get it from file acl params */ acl = 0xb0; fid[0] = (file->id >> 8) & 0xFF; fid[1] = file->id & 0xFF; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x15, 0, acl); apdu.cla = 0x80; apdu.data = fid; apdu.datalen = 2; apdu.lc = 2; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); if (file->type == SC_FILE_TYPE_WORKING_EF) { switch (file->ef_structure) { case SC_FILE_EF_TRANSPARENT: type = 0x80; break; case SC_FILE_EF_LINEAR_FIXED: type = 0x41; break; case SC_FILE_EF_CYCLIC: type = 0x43; break; case SC_FILE_EF_LINEAR_VARIABLE_TLV: type = 0x45; break; default: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "This EF structure is not supported yet"); return SC_ERROR_NOT_SUPPORTED; } apdu.p1 = type; if (type == 0x41 || type == 0x43) { fid[2] = file->record_length; apdu.datalen++; apdu.lc++; } } else if (file->type == SC_FILE_TYPE_DF) { apdu.ins = 0x10; } else { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown file type"); return SC_ERROR_NOT_SUPPORTED; } r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } static int akis_delete_file(sc_card_t *card, const sc_path_t *path) { int r; u8 sbuf[2]; const u8 *buf; size_t buflen; int type; sc_apdu_t apdu; switch (path->type) { case SC_PATH_TYPE_FILE_ID: sbuf[0] = path->value[0]; sbuf[1] = path->value[1]; buf = sbuf; buflen = 2; type = 0x02; break; case SC_PATH_TYPE_PATH: buf = path->value; buflen = path->len; type = 0x08; break; default: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File type has to be FID or PATH"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x16, type, 0x00); apdu.cla = 0x80; apdu.lc = buflen; apdu.datalen = buflen; apdu.data = buf; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } static int akis_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { if (data->cmd == SC_PIN_CMD_VERIFY) { /* ISO7816 implementation works */ return iso_ops->pin_cmd(card, data, tries_left); } if (data->cmd == SC_PIN_CMD_CHANGE) { /* This is AKIS specific */ int r; sc_apdu_t apdu; u8 buf[64]; int p1, p2; p2 = data->pin_reference; if (p2 & 0x80) { p1 = 2; p2 &= 0x7f; } else { p1 = 1; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, p1, p2); buf[0] = data->pin1.len; memcpy(buf+1, data->pin1.data, data->pin1.len); buf[data->pin1.len+1] = data->pin2.len; memcpy(buf+data->pin1.len+2, data->pin2.data, data->pin2.len); apdu.data = buf; apdu.datalen = data->pin1.len + data->pin2.len + 2; apdu.lc = apdu.datalen; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); return r; } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Other pin cmds not supported yet"); return SC_ERROR_NOT_SUPPORTED; } static int akis_get_data(sc_card_t *card, unsigned int dataid, u8 *buf, size_t len) { int r; sc_apdu_t apdu; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, dataid); apdu.resp = buf; apdu.resplen = len; apdu.le = len; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); return r; } static int akis_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) { int r; u8 system_buffer[128]; if (!serial) return SC_ERROR_INVALID_ARGUMENTS; /* see if we have cached serial number */ if (card->serialnr.len) goto end; /* read serial number */ r = akis_get_data(card, 6, system_buffer, 0x4D); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "GET_DATA failed"); card->serialnr.len = 12; memcpy(card->serialnr.value, system_buffer+55, 12); end: memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } static int akis_lifecycle_get(sc_card_t *card, int *mode) { int r; u8 memory[10]; r = akis_get_data(card, 4, memory, 10); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "GET_DATA failed"); switch(memory[6]) { case 0xA0: *mode = SC_CARDCTRL_LIFECYCLE_ADMIN; break; case 0xA5: *mode = SC_CARDCTRL_LIFECYCLE_USER; break; default: *mode = SC_CARDCTRL_LIFECYCLE_OTHER; break; } return SC_SUCCESS; } static int akis_lifecycle_set(sc_card_t *card, int *mode) { int r; u8 stage; sc_apdu_t apdu; switch(*mode) { case SC_CARDCTRL_LIFECYCLE_ADMIN: stage = 0x02; break; case SC_CARDCTRL_LIFECYCLE_USER: stage = 0x01; break; default: return SC_ERROR_INVALID_ARGUMENTS; } sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x09, 0x00, stage); apdu.cla = 0x80; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); return r; } static int akis_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { switch (cmd) { case SC_CARDCTL_GET_SERIALNR: return akis_get_serialnr(card, (sc_serial_number_t *)ptr); case SC_CARDCTL_LIFECYCLE_GET: return akis_lifecycle_get(card, (int *) ptr); case SC_CARDCTL_LIFECYCLE_SET: return akis_lifecycle_set(card, (int *) ptr); } return SC_ERROR_NOT_SUPPORTED; } static int akis_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { int r; u8 ref; sc_apdu_t apdu; /* AKIS uses key references for accessing keys */ if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { ref = env->key_ref[0]; sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 0xC3, ref); r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); return r; } return SC_SUCCESS; } static int akis_logout(sc_card_t *card) { int r; sc_apdu_t apdu; sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x1A, 0, 0); apdu.cla = 0x80; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); return r; } static struct sc_card_driver * sc_get_driver(void) { if (iso_ops == NULL) iso_ops = sc_get_iso7816_driver()->ops; akis_ops = *iso_ops; akis_ops.match_card = akis_match_card; akis_ops.init = akis_init; /* read_binary: ISO7816 implementation works */ /* write_binary: ISO7816 implementation works */ /* update_binary: ISO7816 implementation works */ /* erase_binary: Untested */ /* read_record: Untested */ /* write_record: Untested */ /* append_record: Untested */ /* update_record: Untested */ akis_ops.select_file = akis_select_file; /* get_response: ISO7816 implementation works */ /* get_challenge: ISO7816 implementation works */ akis_ops.logout = akis_logout; /* restore_security_env: Untested */ akis_ops.set_security_env = akis_set_security_env; /* decipher: Untested */ /* compute_signature: ISO7816 implementation works */ akis_ops.create_file = akis_create_file; akis_ops.delete_file = akis_delete_file; akis_ops.list_files = akis_list_files; /* check_sw: ISO7816 implementation works */ akis_ops.card_ctl = akis_card_ctl; akis_ops.process_fci = akis_process_fci; /* construct_fci: Not needed */ akis_ops.pin_cmd = akis_pin_cmd; akis_ops.get_data = akis_get_data; /* put_data: Not implemented */ /* delete_record: Not implemented */ return &akis_drv; } #if 1 struct sc_card_driver * sc_get_akis_driver(void) { return sc_get_driver(); } #endif opensc-0.13.0/src/libopensc/pkcs15-skey.c0000644000015201777760000001715612057406034015027 00000000000000/* * pkcs15-skey.c: PKCS #15 secret key functions * * Copyright (C) 2002 Juha Yrjl * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "internal.h" #include "pkcs15.h" #include "asn1.h" #include #include #include #include #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_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_GENERIC_SKEY_ATTR_SIZE 2 static const struct sc_asn1_entry c_asn1_generic_skey_attr[C_ASN1_COM_GENERIC_SKEY_ATTR_SIZE] = { { "value", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, 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; 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_GENERIC_SKEY_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_pkcs15_object skey_des_obj = { 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_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_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); sc_format_asn1_entry(asn1_com_skey_attr + 0, &info.value_len, NULL, 0); sc_format_asn1_entry(asn1_generic_skey_attr + 0, &info.path, NULL, 0); /* Fill in defaults */ memset(&info, 0, sizeof(info)); 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[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) { #if 0 struct sc_asn1_entry asn1_com_key_attr[6], asn1_com_skey_attr[1]; struct sc_asn1_entry asn1_skey_des_attr[4], asn1_skey_des_type_attr[2]; struct sc_asn1_entry asn1_skey_choice[5]; struct sc_asn1_entry asn1_skey[2]; struct sc_pkcs15_skey_info *skey = (struct sc_pkcs15_skey_info *) obj->data; struct sc_asn1_pkcs15_object skey_des_obj = { (struct sc_pkcs15_object *) obj, asn1_com_key_attr, asn1_com_skey_attr, asn1_skey_des_type_attr }; int r; size_t af_len, usage_len; 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_skey_des_type_attr, asn1_skey_des_type_attr); sc_copy_asn1_entry(c_asn1_skey_des_attr, asn1_skey_des_attr); sc_copy_asn1_entry(c_asn1_com_skey_attr, asn1_com_skey_attr); sc_copy_asn1_entry(c_asn1_com_key_attr, asn1_com_key_attr); switch (obj->type) { case SC_PKCS15_TYPE_SKEY_DES: case SC_PKCS15_TYPE_SKEY_2DES: case SC_PKCS15_TYPE_SKEY_3DES: sc_format_asn1_entry(asn1_skey_choice + 0, &skey_des_obj, NULL, 1); sc_format_asn1_entry(asn1_skey_des_type_attr + 0, asn1_skey_des_attr, NULL, 1); sc_format_asn1_entry(asn1_skey_des_attr + 0, &skey->path, NULL, 1); sc_format_asn1_entry(asn1_skey_des_attr + 1, &skey->type, NULL, 1); sc_format_asn1_entry(asn1_skey_des_attr + 2, &skey->size, NULL, 1); break; default: sc_log(ctx, "Unsupported skey type: %X\n", obj->type); LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); break; } sc_format_asn1_entry(asn1_com_key_attr + 0, &skey->id, NULL, 1); usage_len = sizeof(skey->usage); sc_format_asn1_entry(asn1_com_key_attr + 1, &skey->usage, &usage_len, 1); if (skey->access_flags) { af_len = sizeof(skey->access_flags); sc_format_asn1_entry(asn1_com_key_attr + 3, &skey->access_flags, &af_len, 1); } sc_format_asn1_entry(asn1_skey + 0, asn1_skey_choice, NULL, 1); r = sc_asn1_encode(ctx, asn1_skey, buf, buflen); return r; #else LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); #endif } opensc-0.13.0/src/libopensc/card-miocos.c0000644000015201777760000003135012057406034015140 00000000000000/* * card-miocos.c: Support for PKI cards by Miotec * * Copyright (C) 2002 Juha Yrjölä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" static struct sc_atr_table miocos_atrs[] = { /* Test card with 32 kB memory */ { "3B:9D:94:40:23:00:68:10:11:4D:69:6F:43:4F:53:00:90:00", NULL, NULL, SC_CARD_TYPE_MIOCOS_GENERIC, 0, NULL }, /* Test card with 64 kB memory */ { "3B:9D:94:40:23:00:68:20:01:4D:69:6F:43:4F:53:00:90:00", NULL, NULL, SC_CARD_TYPE_MIOCOS_GENERIC, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } }; static struct sc_card_operations miocos_ops; static struct sc_card_driver miocos_drv = { "MioCOS 1.1", "miocos", &miocos_ops, NULL, 0, NULL }; static int miocos_match_card(sc_card_t *card) { int i; i = _sc_match_atr(card, miocos_atrs, &card->type); if (i < 0) return 0; return 1; } static int miocos_init(sc_card_t *card) { card->name = "MioCOS"; card->cla = 0x00; if (1) { unsigned long flags; flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1; flags |= SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_RSA_HASH_SHA1; _sc_card_add_rsa_alg(card, 1024, flags, 0); } /* read_binary and friends shouldn't do more than 244 bytes * per operation */ card->max_send_size = 244; card->max_recv_size = 244; return 0; } static const struct sc_card_operations *iso_ops = NULL; static int acl_to_byte(const sc_acl_entry_t *e) { switch (e->method) { case SC_AC_NONE: return 0x00; case SC_AC_CHV: case SC_AC_TERM: case SC_AC_AUT: if (e->key_ref == SC_AC_KEY_REF_NONE) return -1; if (e->key_ref < 1 || e->key_ref > 14) return -1; return e->key_ref; case SC_AC_NEVER: return 0x0F; } return 0x00; } static int encode_file_structure(sc_card_t *card, const sc_file_t *file, u8 *buf, size_t *buflen) { u8 *p = buf; const int df_ops[8] = { SC_AC_OP_DELETE, SC_AC_OP_CREATE, /* RFU */ -1, /* CREATE AC */ SC_AC_OP_CREATE, /* UPDATE AC */ SC_AC_OP_CREATE, -1, -1, -1 }; const int ef_ops[8] = { /* DELETE */ SC_AC_OP_UPDATE, -1, SC_AC_OP_READ, SC_AC_OP_UPDATE, -1, -1, SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE }; const int key_ops[8] = { /* DELETE */ SC_AC_OP_UPDATE, -1, -1, SC_AC_OP_UPDATE, SC_AC_OP_CRYPTO, -1, SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE }; const int *ops; int i; *p++ = file->id >> 8; *p++ = file->id & 0xFF; switch (file->type) { case SC_FILE_TYPE_DF: *p++ = 0x20; ops = df_ops; break; case SC_FILE_TYPE_WORKING_EF: switch (file->ef_structure) { case SC_FILE_EF_TRANSPARENT: *p++ = 0x40; break; case SC_FILE_EF_LINEAR_FIXED: *p++ = 0x41; break; case SC_FILE_EF_CYCLIC: *p++ = 0x43; break; default: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid EF structure\n"); return SC_ERROR_INVALID_ARGUMENTS; } ops = ef_ops; break; case SC_FILE_TYPE_INTERNAL_EF: *p++ = 0x44; ops = key_ops; break; default: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown file type\n"); return SC_ERROR_INVALID_ARGUMENTS; } if (file->type == SC_FILE_TYPE_DF) { *p++ = 0; *p++ = 0; } else { *p++ = file->size >> 8; *p++ = file->size & 0xFF; } if (file->sec_attr_len == 4) { memcpy(p, file->sec_attr, 4); p += 4; } else for (i = 0; i < 8; i++) { u8 nibble; if (ops[i] == -1) nibble = 0x00; else { int byte = acl_to_byte(sc_file_get_acl_entry(file, ops[i])); if (byte < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid ACL\n"); return SC_ERROR_INVALID_ARGUMENTS; } nibble = byte; } if ((i & 1) == 0) *p = nibble << 4; else { *p |= nibble & 0x0F; p++; } } if (file->type == SC_FILE_TYPE_WORKING_EF && file->ef_structure != SC_FILE_EF_TRANSPARENT) *p++ = file->record_length; else *p++ = 0; if (file->status & SC_FILE_STATUS_INVALIDATED) *p++ = 0; else *p++ = 0x01; if (file->type == SC_FILE_TYPE_DF && file->namelen) { assert(file->namelen <= 16); memcpy(p, file->name, file->namelen); p += file->namelen; } *buflen = p - buf; return 0; } static int miocos_create_file(sc_card_t *card, sc_file_t *file) { sc_apdu_t apdu; u8 sbuf[32]; size_t buflen; int r; r = encode_file_structure(card, file, sbuf, &buflen); if (r) return r; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00); apdu.data = sbuf; apdu.datalen = buflen; apdu.lc = buflen; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 == 0x6A && apdu.sw2 == 0x89) return SC_ERROR_FILE_ALREADY_EXISTS; r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); return 0; } static int miocos_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { if (env->flags & SC_SEC_ENV_ALG_PRESENT) { sc_security_env_t tmp; tmp = *env; tmp.flags &= ~SC_SEC_ENV_ALG_PRESENT; tmp.flags |= SC_SEC_ENV_ALG_REF_PRESENT; if (tmp.algorithm != SC_ALGORITHM_RSA) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Only RSA algorithm supported.\n"); return SC_ERROR_NOT_SUPPORTED; } tmp.algorithm_ref = 0x00; /* potential FIXME: return an error, if an unsupported * pad or hash was requested, although this shouldn't happen. */ if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) tmp.algorithm_ref = 0x02; if (tmp.algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) tmp.algorithm_ref |= 0x10; return iso_ops->set_security_env(card, &tmp, se_num); } return iso_ops->set_security_env(card, env, se_num); } static void add_acl_entry(sc_file_t *file, int op, u8 byte) { unsigned int method, key_ref = SC_AC_KEY_REF_NONE; switch (byte) { case 0: method = SC_AC_NONE; break; case 15: method = SC_AC_NEVER; break; default: method = SC_AC_CHV; key_ref = byte; break; } sc_file_add_acl_entry(file, op, method, key_ref); } static void parse_sec_attr(sc_file_t *file, const u8 *buf, size_t len) { int i; const int df_ops[8] = { SC_AC_OP_DELETE, SC_AC_OP_CREATE, -1, /* CREATE AC */ -1, /* UPDATE AC */ -1, -1, -1, -1 }; const int ef_ops[8] = { SC_AC_OP_DELETE, -1, SC_AC_OP_READ, SC_AC_OP_UPDATE, -1, -1, SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE }; const int key_ops[8] = { SC_AC_OP_DELETE, -1, -1, SC_AC_OP_UPDATE, SC_AC_OP_CRYPTO, -1, SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE }; const int *ops; if (len < 4) return; switch (file->type) { case SC_FILE_TYPE_WORKING_EF: ops = ef_ops; break; case SC_FILE_TYPE_INTERNAL_EF: ops = key_ops; break; case SC_FILE_TYPE_DF: ops = df_ops; break; default: return; } for (i = 0; i < 8; i++) { if (ops[i] == -1) continue; if ((i & 1) == 0) add_acl_entry(file, ops[i], (u8)(buf[i / 2] >> 4)); else add_acl_entry(file, ops[i], (u8)(buf[i / 2] & 0x0F)); } } static int miocos_get_acl(sc_card_t *card, sc_file_t *file) { sc_apdu_t apdu; u8 rbuf[256]; const u8 *seq = rbuf; size_t left; int acl_types[16], r; unsigned int i; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, 0x01); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.resplen == 0) return sc_check_sw(card, apdu.sw1, apdu.sw2); for (i = 0; i < 16; i++) acl_types[i] = SC_AC_KEY_REF_NONE; left = apdu.resplen; seq = sc_asn1_skip_tag(card->ctx, &seq, &left, SC_ASN1_SEQUENCE | SC_ASN1_CONS, &left); if (seq == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to process reply"); for (i = 1; i < 15; i++) { int j; const u8 *tag; size_t taglen; tag = sc_asn1_skip_tag(card->ctx, &seq, &left, SC_ASN1_CTX | i, &taglen); if (tag == NULL || taglen == 0) continue; for (j = 0; j < SC_MAX_AC_OPS; j++) { sc_acl_entry_t *e; e = (sc_acl_entry_t *) sc_file_get_acl_entry(file, j); if (e == NULL) continue; if (e->method != SC_AC_CHV) continue; if (e->key_ref != i) continue; switch (tag[0]) { case 0x01: e->method = SC_AC_CHV; break; case 0x02: e->method = SC_AC_AUT; break; default: e->method = SC_AC_UNKNOWN; break; } } } return 0; } static int miocos_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file) { int r; r = iso_ops->select_file(card, in_path, file); if (r) return r; if (file != NULL) { parse_sec_attr(*file, (*file)->sec_attr, (*file)->sec_attr_len); miocos_get_acl(card, *file); } return 0; } static int miocos_list_files(sc_card_t *card, u8 *buf, size_t buflen) { sc_apdu_t apdu; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, 0); apdu.resp = buf; apdu.resplen = buflen; apdu.le = buflen > 256 ? 256 : buflen; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.resplen == 0) return sc_check_sw(card, apdu.sw1, apdu.sw2); return apdu.resplen; } static int miocos_delete_file(sc_card_t *card, const sc_path_t *path) { int r; sc_apdu_t apdu; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (path->type != SC_PATH_TYPE_FILE_ID && path->len != 2) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File type has to be SC_PATH_TYPE_FILE_ID\n"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); } r = sc_select_file(card, path, NULL); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to select file to be deleted"); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xE4, 0x00, 0x00); apdu.cla = 0xA0; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } static int miocos_create_ac(sc_card_t *card, struct sc_cardctl_miocos_ac_info *ac) { sc_apdu_t apdu; u8 sbuf[20]; int miocos_type, r; size_t sendsize; if (ac->max_tries > 15) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); switch (ac->type) { case SC_CARDCTL_MIOCOS_AC_PIN: if (ac->max_unblock_tries > 15) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); miocos_type = 0x01; sbuf[0] = (ac->max_tries << 4) | ac->max_tries; sbuf[1] = 0xFF; /* FIXME... */ memcpy(sbuf + 2, ac->key_value, 8); sbuf[10] = (ac->max_unblock_tries << 4) | ac->max_unblock_tries; sbuf[11] = 0xFF; memcpy(sbuf + 12, ac->unblock_value, 8); sendsize = 20; break; default: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "AC type %d not supported\n", ac->type); return SC_ERROR_NOT_SUPPORTED; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x1E, miocos_type, ac->ref); apdu.lc = sendsize; apdu.datalen = sendsize; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } static int miocos_card_ctl(sc_card_t *card, unsigned long cmd, void *arg) { switch (cmd) { case SC_CARDCTL_MIOCOS_CREATE_AC: return miocos_create_ac(card, (struct sc_cardctl_miocos_ac_info *) arg); } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "card_ctl command 0x%X not supported\n", cmd); return SC_ERROR_NOT_SUPPORTED; } static struct sc_card_driver * sc_get_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); miocos_ops = *iso_drv->ops; miocos_ops.match_card = miocos_match_card; miocos_ops.init = miocos_init; if (iso_ops == NULL) iso_ops = iso_drv->ops; miocos_ops.create_file = miocos_create_file; miocos_ops.set_security_env = miocos_set_security_env; miocos_ops.select_file = miocos_select_file; miocos_ops.list_files = miocos_list_files; miocos_ops.delete_file = miocos_delete_file; miocos_ops.card_ctl = miocos_card_ctl; return &miocos_drv; } #if 1 struct sc_card_driver * sc_get_miocos_driver(void) { return sc_get_driver(); } #endif opensc-0.13.0/src/libopensc/apdu.c0000644000015201777760000006302412057406034013674 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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 */ static 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 hte output buffer * @return SC_SUCCESS on success and an error code otherwise */ static int sc_apdu2bytes(sc_context_t *ctx, const sc_apdu_t *apdu, 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; } void sc_apdu_log(sc_context_t *ctx, int level, const u8 *data, size_t len, int is_out) { size_t blen = len * 5 + 128; char *buf = malloc(blen); if (buf == NULL) return; sc_hex_dump(ctx, level, data, len, buf, blen); sc_debug(ctx, level, "\n%s APDU data [%5u bytes] =====================================\n" "%s" "======================================================================\n", is_out != 0 ? "Outgoing" : "Incoming", len, buf); free(buf); } 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) 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; } #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 } }; static int sc_sm_parse_answer(struct sc_context *ctx, 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 r; 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); r = sc_asn1_decode(ctx, asn1_sm_response, resp_data, resp_len, NULL, NULL); if (r) return r; if (asn1_sm_response[1].flags & SC_ASN1_PRESENT) { 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; } /* TODO: to be continued ... */ 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 */ static 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->ctx, resp_data, resp_len, &sm_resp); if (r) return r; else if (!sm_resp.sw1 && !sm_resp.sw2) return SC_ERROR_INVALID_DATA; else if (ref_rv != sc_check_sw(card, sm_resp.sw1, sm_resp.sw2)) return SC_ERROR_INVALID_DATA; 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; } #endif /*********************************************************************/ /* 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->tranmit */ /** 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 */ static 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)) 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) 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; /* return buffer to small */ if ((apdu->le == 0 && apdu->resplen < SC_MAX_APDU_BUFFER_SIZE-2) || (apdu->resplen < apdu->le)) goto error; break; case SC_APDU_CASE_3_SHORT: /* data is sent */ 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; /* return buffer to small */ if ((apdu->le == 0 && apdu->resplen < SC_MAX_APDU_BUFFER_SIZE-2) || (apdu->resplen < apdu->le)) goto error; /* inconsistent datalen */ if (apdu->datalen != apdu->lc) goto error; 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", 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); 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 maximun 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; } } #ifdef ENABLE_SM static int sc_single_sm_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); } 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); LOG_TEST_RET(ctx, rv, "cannot validate SM encoded APDU"); } /* send APDU to the reader driver */ rv = card->reader->ops->transmit(card->reader, sm_apdu); 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); LOG_FUNC_RETURN(ctx, rv); } #endif 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(%i) %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) return sc_single_sm_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_GET_RESP; /* set the new expected length */ apdu->resplen = olen; apdu->le = nlen; /* Belpic V1 applets have a problem: if the card sends a 6C XX (only XX bytes available), * and we resend the command too soon (i.e. the reader is too fast), the card doesn't respond. * So we build in a delay. */ if (card->type == SC_CARD_TYPE_BELPIC_EID) msleep(40); /* 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 retuns 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; /* 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(ctx, "SM response data %s", sc_dump_hex(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; /* we have all the data the caller requested even if the card has more data */ if (buflen == 0) break; minlen -= le; if (rv != 0) le = minlen = (size_t)rv; 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-trasmitted 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 ((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 = card->max_send_size > 0 ? card->max_send_size : 255; 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; tapdu.le = 0; /* the intermediate APDU don't expect data */ tapdu.lc = 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); /* 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: %lu bytes:\tins=%02x p1=%02x p2=%02x lc=%04x le=%04x", (unsigned long) 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 %lu more bytes)", (unsigned long) 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 %lu more bytes)", (unsigned long) 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, %lu bytes:\tins=%02x p1=%02x p2=%02x lc=%04x le=%04x", apdu->cse & SC_APDU_SHORT_MASK, (apdu->cse & SC_APDU_EXT) != 0 ? "extended" : "short", (unsigned long) len0, apdu->ins, apdu->p1, apdu->p2, apdu->lc, apdu->le); return SC_SUCCESS; } opensc-0.13.0/src/libopensc/pkcs15-sc-hsm.c0000644000015201777760000005260212057406034015241 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include "internal.h" #include "pkcs15.h" #include "asn1.h" #include "common/compat_strlcpy.h" #include "card-sc-hsm.h" extern struct sc_aid sc_hsm_aid; void sc_hsm_set_serialnr(sc_card_t *card, char *serial); #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, 0, 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 } }; /* * 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,tag; size_t taglen; size_t lenchr = sizeof(cvc->chr); size_t lencar = sizeof(cvc->car); size_t lenoutercar = sizeof(cvc->outer_car); const u8 *tbuf; int r; memset(cvc, 0, sizeof(cvc)); sc_copy_asn1_entry(c_asn1_req, asn1_req); sc_copy_asn1_entry(c_asn1_authreq, asn1_authreq); sc_copy_asn1_entry(c_asn1_cvc, asn1_cvc); sc_copy_asn1_entry(c_asn1_cvcert, asn1_cvcert); sc_copy_asn1_entry(c_asn1_cvc_body, asn1_cvc_body); sc_copy_asn1_entry(c_asn1_cvc_pubkey, asn1_cvc_pubkey); sc_format_asn1_entry(asn1_cvc_pubkey , &cvc->pukoid, NULL, 0); sc_format_asn1_entry(asn1_cvc_pubkey + 1, &cvc->primeOrModulus, &cvc->primeOrModuluslen, 0); sc_format_asn1_entry(asn1_cvc_pubkey + 2, &cvc->coefficientAorExponent, &cvc->coefficientAorExponentlen, 0); sc_format_asn1_entry(asn1_cvc_pubkey + 3, &cvc->coefficientB, &cvc->coefficientBlen, 0); sc_format_asn1_entry(asn1_cvc_pubkey + 4, &cvc->basePointG, &cvc->basePointGlen, 0); sc_format_asn1_entry(asn1_cvc_pubkey + 5, &cvc->order, &cvc->orderlen, 0); sc_format_asn1_entry(asn1_cvc_pubkey + 6, &cvc->publicPoint, &cvc->publicPointlen, 0); sc_format_asn1_entry(asn1_cvc_pubkey + 7, &cvc->cofactor, &cvc->cofactorlen, 0); sc_format_asn1_entry(asn1_cvc_pubkey + 8, &cvc->modulusSize, NULL, 0); sc_format_asn1_entry(asn1_cvc_body , &cvc->cpi, NULL, 0); sc_format_asn1_entry(asn1_cvc_body + 1, &cvc->car, &lencar, 0); sc_format_asn1_entry(asn1_cvc_body + 2, &asn1_cvc_pubkey, NULL, 0); sc_format_asn1_entry(asn1_cvc_body + 3, &cvc->chr, &lenchr, 0); sc_format_asn1_entry(asn1_cvcert , &asn1_cvc_body, NULL, 0); sc_format_asn1_entry(asn1_cvcert + 1, &cvc->signature, &cvc->signatureLen, 0); sc_format_asn1_entry(asn1_cvc , &asn1_cvcert, NULL, 0); sc_format_asn1_entry(asn1_authreq , &asn1_cvcert, NULL, 0); sc_format_asn1_entry(asn1_authreq + 1, &cvc->outer_car, &lenoutercar, 0); sc_format_asn1_entry(asn1_authreq + 2, &cvc->outerSignature, &cvc->outerSignatureLen, 0); 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"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /* * 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]; unsigned int cla,tag; size_t taglen; size_t lenchr; size_t lencar; 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); lencar = strlen(cvc->car); sc_format_asn1_entry(asn1_cvc_body + 1, &cvc->car, &lencar, 1); sc_format_asn1_entry(asn1_cvc_body + 2, &asn1_cvc_pubkey, NULL, 1); lenchr = strlen(cvc->chr); sc_format_asn1_entry(asn1_cvc_body + 3, &cvc->chr, &lenchr, 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); } void sc_pkcs15emu_sc_hsm_free_cvc(sc_cvc_t *cvc) { if (cvc->signature) { free(cvc->signature); } if (cvc->primeOrModulus) { free(cvc->primeOrModulus); } if (cvc->coefficientAorExponent) { free(cvc->coefficientAorExponent); } if (cvc->coefficientB) { free(cvc->coefficientB); } if (cvc->basePointG) { free(cvc->basePointG); } if (cvc->order) { free(cvc->order); } if (cvc->publicPoint) { free(cvc->publicPoint); } if (cvc->cofactor) { free(cvc->cofactor); } } static int sc_pkcs15emu_sc_hsm_add_pubkey(sc_pkcs15_card_t *p15card, sc_pkcs15_prkey_info_t *key_info, char *label) { sc_card_t *card = p15card->card; sc_pkcs15_pubkey_info_t pubkey_info; sc_pkcs15_object_t pubkey_obj; struct sc_pkcs15_pubkey pubkey; u8 efbin[512]; sc_cvc_t cvc; u8 *cvcpo; size_t cvclen; int r; // EF.CERT is selected r = sc_read_binary(p15card->card, 0, efbin, sizeof(efbin), 0); LOG_TEST_RET(card->ctx, r, "Could not read CSR from EF"); cvcpo = efbin; cvclen = r; memset(&cvc, 0, sizeof(cvc)); r = sc_pkcs15emu_sc_hsm_decode_cvc(p15card, (const u8 **)&cvcpo, &cvclen, &cvc); LOG_TEST_RET(card->ctx, r, "Could decode certificate signing request"); if (cvc.publicPoint || cvc.publicPointlen) { // ToDo implement support for EC Public Keys return SC_SUCCESS; } else { pubkey.algorithm = SC_ALGORITHM_RSA; pubkey.u.rsa.modulus.data = cvc.primeOrModulus; pubkey.u.rsa.modulus.len = cvc.primeOrModuluslen; pubkey.u.rsa.exponent.data = cvc.coefficientAorExponent; pubkey.u.rsa.exponent.len = cvc.coefficientAorExponentlen; } memset(&pubkey_info, 0, sizeof(pubkey_info)); memset(&pubkey_obj, 0, sizeof(pubkey_obj)); sc_pkcs15_encode_pubkey(p15card->card->ctx, &pubkey, &pubkey_obj.content.value, &pubkey_obj.content.len); pubkey_info.id = key_info->id; strlcpy(pubkey_obj.label, label, sizeof(pubkey_obj.label)); r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info); LOG_TEST_RET(card->ctx, r, "Could not add public key"); sc_pkcs15emu_sc_hsm_free_cvc(&cvc); 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; sc_file_t *file = NULL; sc_path_t path; u8 fid[2]; u8 efbin[512]; u8 *ptr; size_t len; int r, i; fid[0] = PRKD_PREFIX; fid[1] = 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, &file); if (r != SC_SUCCESS) { return SC_SUCCESS; } sc_file_free(file); r = sc_read_binary(p15card->card, 0, efbin, sizeof(efbin), 0); LOG_TEST_RET(card->ctx, r, "Could not read EF.PRKD"); memset(&prkd, 0, sizeof(prkd)); ptr = efbin; len = r; r = sc_pkcs15_decode_prkdf_entry(p15card, &prkd, (const u8 **)&ptr, &len); LOG_TEST_RET(card->ctx, r, "Could not decode 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; if (prkd.type == SC_PKCS15_TYPE_PRKEY_RSA) { r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkd, key_info); } else { 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; sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0); r = sc_select_file(card, &path, &file); if (r != SC_SUCCESS) { return SC_SUCCESS; } sc_file_free(file); /* Check if the certificate is a X.509 certificate */ r = sc_read_binary(p15card->card, 0, efbin, 1, 0); if (r < 0) { return SC_SUCCESS; } if (efbin[0] == 0x67) { // Decode CSR and create public key object sc_pkcs15emu_sc_hsm_add_pubkey(p15card, key_info, prkd.label); return SC_SUCCESS; // Ignore any errors } if (efbin[0] != 0x30) { return SC_SUCCESS; } memset(&cert_info, 0, sizeof(cert_info)); memset(&cert_obj, 0, sizeof(cert_obj)); cert_info.id = key_info->id; cert_info.path = path; cert_info.path.count = -1; strlcpy(cert_obj.label, prkd.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"); 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; sc_file_t *file = NULL; sc_path_t path; u8 fid[2]; u8 efbin[512]; const u8 *ptr; size_t len; int r, i; fid[0] = DCOD_PREFIX; fid[1] = id; /* Try to select a related EF containing the PKCS#15 description of the data */ sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0); r = sc_select_file(card, &path, &file); if (r != SC_SUCCESS) { return SC_SUCCESS; } sc_file_free(file); r = sc_read_binary(p15card->card, 0, efbin, sizeof(efbin), 0); LOG_TEST_RET(card->ctx, r, "Could not read EF.DCOD"); memset(&data_obj, 0, sizeof(data_obj)); ptr = efbin; len = r; r = sc_pkcs15_decode_dodf_entry(p15card, &data_obj, &ptr, &len); LOG_TEST_RET(card->ctx, r, "Could not decode 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; sc_file_t *file = NULL; sc_path_t path; u8 fid[2]; u8 efbin[512]; const u8 *ptr; size_t len; int r, i; fid[0] = CD_PREFIX; fid[1] = id; /* Try to select a related EF containing the PKCS#15 description of the data */ sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0); r = sc_select_file(card, &path, &file); if (r != SC_SUCCESS) { return SC_SUCCESS; } sc_file_free(file); r = sc_read_binary(p15card->card, 0, efbin, sizeof(efbin), 0); LOG_TEST_RET(card->ctx, r, "Could not read EF.DCOD"); memset(&obj, 0, sizeof(obj)); ptr = efbin; len = r; r = sc_pkcs15_decode_cdf_entry(p15card, &obj, &ptr, &len); LOG_TEST_RET(card->ctx, r, "Could not decode EF.CD"); 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; } /* * 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_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; u8 fid[2]; u8 efbin[512]; u8 *ptr; size_t len; LOG_FUNC_CALLED(card->ctx); p15card->tokeninfo->label = strdup("SmartCard-HSM"); p15card->tokeninfo->manufacturer_id = strdup("www.CardContact.de"); appinfo = calloc(1, sizeof(struct sc_app_info)); if (appinfo == NULL) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } appinfo->label = strdup(p15card->tokeninfo->label); 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; 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 sc_path_set(&path, SC_PATH_TYPE_FILE_ID, "\x2F\x02", 2, 0, 0); r = sc_select_file(card, &path, &file); LOG_TEST_RET(card->ctx, r, "Could not select EF.C_DevAut"); sc_file_free(file); r = sc_read_binary(p15card->card, 0, efbin, sizeof(efbin), 0); LOG_TEST_RET(card->ctx, r, "Could not read EF.C_DevAut"); ptr = efbin; len = r; 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"); len = strlen(devcert.chr); // Strip last 5 digit sequence number from CHR assert(len >= 8); len -= 5; p15card->tokeninfo->serial_number = calloc(len + 1, 1); if (p15card->tokeninfo->serial_number == NULL) { 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.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; 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_UNBLOCK_DISABLED|SC_PKCS15_PIN_FLAG_EXCHANGE_REF_DATA; 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.attrs.pin.pad_char = '\0'; pin_info.tries_left = 3; pin_info.max_tries = 3; strlcpy(pin_obj.label, "UserPIN", sizeof(pin_obj.label)); pin_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE|SC_PKCS15_CO_FLAG_MODIFIABLE; r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); if (r < 0) LOG_FUNC_RETURN(card->ctx, r); 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.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; pin_info.attrs.pin.reference = 0x88; pin_info.attrs.pin.flags = SC_PKCS15_PIN_FLAG_LOCAL|SC_PKCS15_PIN_FLAG_CHANGE_DISABLED|SC_PKCS15_PIN_FLAG_INITIALIZED|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 = 3; pin_info.max_tries = 3; strlcpy(pin_obj.label, "SOPIN", sizeof(pin_obj.label)); pin_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); if (r < 0) LOG_FUNC_RETURN(card->ctx, r); filelistlength = sc_list_files(card, filelist, sizeof(filelist)); 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; } LOG_TEST_RET(card->ctx, r, "Error adding elements to framework"); } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } int sc_pkcs15emu_sc_hsm_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts) { if (opts && (opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)) { return sc_pkcs15emu_sc_hsm_init(p15card); } else { if (p15card->card->type != SC_CARD_TYPE_SC_HSM) { return SC_ERROR_WRONG_CARD; } return sc_pkcs15emu_sc_hsm_init(p15card); } } opensc-0.13.0/src/libopensc/pkcs15-tccardos.c0000644000015201777760000002331612057406034015651 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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 int sc_pkcs15emu_tccardos_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts); 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 || !fid) return r; if (fid->size < *len) *len = fid->size; r = sc_read_binary(card, 0, buf, *len, 0); 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 */ 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 key_num, i; struct sc_context *ctx = p15card->card->ctx; /* read EF_CardInfo1 */ r = read_file(p15card->card, "3F001003b200", info1, &info1_len); if (r != SC_SUCCESS) 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 = info1[info1_len-1] | (info1[info1_len-2] << 8) | (info1[info1_len-3] << 16) | (info1[info1_len-4] << 24); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "found %d private keys\n", (int)key_num); /* set p1 to the address of the first key descriptor */ p1 = info1 + (info1_len - 4 - key_num * 2); p2 = info2; for (i=0; icard; /* 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 */ if (p15card->tokeninfo->label != NULL) free(p15card->tokeninfo->label); p15card->tokeninfo->label = strdup(TC_CARDOS_LABEL); if (p15card->tokeninfo->label == NULL) return SC_ERROR_OUT_OF_MEMORY; /* set the manufacturer ID */ if (p15card->tokeninfo->manufacturer_id != NULL) free(p15card->tokeninfo->manufacturer_id); p15card->tokeninfo->manufacturer_id = strdup(MANU_ID); if (p15card->tokeninfo->manufacturer_id == NULL) return SC_ERROR_OUT_OF_MEMORY; /* set the serial number */ r = read_file(p15card->card, "3F002F02", gdo, &gdo_len); if (r != SC_SUCCESS) return SC_ERROR_INTERNAL; sc_bin_to_hex(gdo + 7, 8, hex_buf, sizeof(hex_buf), 0); p15card->tokeninfo->serial_number = strdup(hex_buf); if (p15card->tokeninfo->serial_number == NULL) return SC_ERROR_OUT_OF_MEMORY; /* select the application DF */ sc_format_path(TC_CARDOS_APP_DF, &path); r = sc_select_file(card, &path, &file); if (r != SC_SUCCESS || file == NULL) return SC_ERROR_INTERNAL; /* set the application DF */ if (p15card->file_app) free(p15card->file_app); p15card->file_app = file; return SC_SUCCESS; } int sc_pkcs15emu_tccardos_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts) { return sc_pkcs15_tccardos_init_func(p15card); } opensc-0.13.0/src/libopensc/card-sc-hsm.h0000644000015201777760000001046312057406034015050 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef SC_HSM_H_ #define SC_HSM_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_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_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_DH 0x80 /* ECDH key derivation */ #define ID_USER_PIN 0x81 /* User PIN identifier */ #define ID_SO_PIN 0x88 /* Security officer PIN identifier */ /* Information the driver maintains between calls */ typedef struct sc_hsm_private_data { const sc_security_env_t *env; u8 algorithm; int noExtLength; char *serialno; char initpw[6]; // Initial user PIN set at initialization (first 6 digits of token pin) } sc_hsm_private_data_t; struct sc_cvc { int cpi; // Certificate profile indicator (0) char car[17]; // Certification authority reference 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[17]; // Certificate holder reference u8 *signature; // Certificate signature or request self-signed signature size_t signatureLen; char outer_car[17]; // Instance signing the request u8 *outerSignature; // Request authenticating signature size_t outerSignatureLen; }; typedef struct sc_cvc sc_cvc_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_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); #endif /* SC_HSM_H_ */ opensc-0.13.0/src/libopensc/p15card-helper.c0000644000015201777760000002517612057406034015465 00000000000000/* * p15card-helper.c: Utility library to assist in PKCS#15 emulation on Non-filesystem cards * * Copyright (C) 2006, Identity Alliance, Thomas Harning * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #if ENABLE_OPENSSL /* empty file without openssl */ #include #include #include #include #include #include "internal.h" #include "p15card-helper.h" #include "opensc.h" #include "types.h" #include "log.h" #include "pkcs15.h" int sc_pkcs15emu_initialize_objects(sc_pkcs15_card_t *p15card, p15data_items *items) { sc_card_t* card = p15card->card; const objdata* objects = items->objects; int i, r; if(!objects) return SC_SUCCESS; for (i = 0; objects[i].label; i++) { struct sc_pkcs15_data_info obj_info; struct sc_pkcs15_object obj_obj; memset(&obj_info, 0, sizeof(obj_info)); memset(&obj_obj, 0, sizeof(obj_obj)); sc_pkcs15_format_id(objects[i].id, &obj_info.id); sc_format_path(objects[i].path, &obj_info.path); strncpy(obj_info.app_label, objects[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); r = sc_format_oid(&obj_info.app_oid, objects[i].aoid); if (r != SC_SUCCESS) return r; strncpy(obj_obj.label, objects[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); obj_obj.flags = objects[i].obj_flags; r = sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_DATA_OBJECT, &obj_obj, &obj_info); if (r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } return SC_SUCCESS; } static const prdata* get_prkey_by_cert(p15data_items* items, const cdata* cert) { const prdata* keys; if(!items->private_keys) return NULL; for(keys = items->private_keys; keys->id; keys++) { if(0 == strcmp(cert->id, keys->id)) return keys; } return NULL; } static int add_private_key(sc_pkcs15_card_t *p15card, const prdata* key, int usage, int modulus_length) { struct sc_pkcs15_prkey_info prkey_info; struct sc_pkcs15_object prkey_obj; memset(&prkey_info, 0, sizeof(prkey_info)); memset(&prkey_obj, 0, sizeof(prkey_obj)); sc_pkcs15_format_id(key->id, &prkey_info.id); prkey_info.native = 1; prkey_info.key_reference = key->ref; if(!modulus_length) modulus_length = key->modulus_len; prkey_info.modulus_length= modulus_length; sc_format_path(key->path, &prkey_info.path); strncpy(prkey_obj.label, key->label, SC_PKCS15_MAX_LABEL_SIZE - 1); prkey_obj.flags = key->obj_flags; /* Setup key usage */ if(!usage) usage = key->usage; prkey_info.usage = usage; if (key->auth_id) sc_pkcs15_format_id(key->auth_id, &prkey_obj.auth_id); return sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); } static int add_public_key(sc_pkcs15_card_t *p15card, const pubdata *key, int usage, int modulus_length) { struct sc_pkcs15_pubkey_info pubkey_info; struct sc_pkcs15_object pubkey_obj; memset(&pubkey_info, 0, sizeof(pubkey_info)); memset(&pubkey_obj, 0, sizeof(pubkey_obj)); sc_pkcs15_format_id(key->id, &pubkey_info.id); if(!usage) usage = key->usage; pubkey_info.usage = usage; pubkey_info.native = 1; pubkey_info.key_reference = key->ref; if(!modulus_length) modulus_length = key->modulus_len; pubkey_info.modulus_length= modulus_length; /* we really don't know how many bits or module length, * we will assume 1024 for now */ sc_format_path(key->path, &pubkey_info.path); strncpy(pubkey_obj.label, key->label, SC_PKCS15_MAX_LABEL_SIZE - 1); pubkey_obj.flags = key->obj_flags; if (key->auth_id) sc_pkcs15_format_id(key->auth_id, &pubkey_obj.auth_id); return sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info); } /* int default_cert_handle(sc_pkcs15_card_t *p15card, p15data_items* items, cdata* cert, u8* data, size_t length) { */ CERT_HANDLE_FUNCTION(default_cert_handle) { /* Certificate data exists, parse it */ int r; X509 *cert_data = NULL; EVP_PKEY *pkey = NULL; int certtype = 0; int modulus_len = 0; const prdata* key = get_prkey_by_cert(items, cert); if(!key) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Error: No key for this certificate"); return SC_ERROR_INTERNAL; } if(!d2i_X509(&cert_data, (const u8**)&data, length)) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Error converting certificate"); return SC_ERROR_INTERNAL; } pkey = X509_get_pubkey(cert_data); if(pkey == NULL) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Error: no public key associated with the certificate"); r = SC_ERROR_INTERNAL; goto err; } certtype = X509_certificate_type(cert_data, pkey); if(! (EVP_PK_RSA & certtype)) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Error: certificate is not for an RSA key"); r = SC_ERROR_INTERNAL; goto err; } if(pkey->pkey.rsa->n == NULL) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Error: no modulus associated with the certificate"); r = SC_ERROR_INTERNAL; goto err; } modulus_len = 8 * BN_num_bytes(pkey->pkey.rsa->n); /* converting to bits */ /* printf("Key Size: %d bits\n\n", modulus_len); */ /* cached_cert->modulusLength = modulus_len; */ if(key->label) { int usage = 0; if (certtype & EVP_PKT_SIGN) { usage |= SC_PKCS15_PRKEY_USAGE_SIGN; usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; } if (certtype & EVP_PKT_ENC) { usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT; usage |= SC_PKCS15_PRKEY_USAGE_DECRYPT; } if (certtype & EVP_PKT_EXCH) { usage |= SC_PKCS15_PRKEY_USAGE_WRAP; usage |= SC_PKCS15_PRKEY_USAGE_UNWRAP; } r = add_private_key(p15card, key, usage, modulus_len); if (r < 0) goto err; } r = SC_SUCCESS; err: if(pkey) { EVP_PKEY_free(pkey); pkey = NULL; } if(cert_data) { X509_free(cert_data); cert_data = NULL; } SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r); } int sc_pkcs15emu_initialize_certificates(sc_pkcs15_card_t *p15card, p15data_items* items) { /* set certs */ sc_card_t* card = p15card->card; const cdata* certs = items->certs; int onFailResume = items->cert_continue; int i, r; if(!certs) return SC_SUCCESS; for (i = 0; certs[i].label; i++) { struct sc_pkcs15_cert_info cert_info; struct sc_pkcs15_object cert_obj; memset(&cert_info, 0, sizeof(cert_info)); memset(&cert_obj, 0, sizeof(cert_obj)); sc_pkcs15_format_id(certs[i].id, &cert_info.id); cert_info.authority = certs[i].authority; sc_format_path(certs[i].path, &cert_info.path); strncpy(cert_obj.label, certs[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); cert_obj.flags = certs[i].obj_flags; if(items->cert_load) { u8* cert_buffer = NULL; size_t cert_length = 0; int should_free = 0; if(SC_SUCCESS != sc_select_file(card, &cert_info.path, NULL)) { if(onFailResume) continue; else break; } if(SC_SUCCESS != (r = items->cert_load(card, &cert_buffer, &cert_length, &should_free))) { if(onFailResume) continue; else break; } /* Handle cert */ /* If no cert handler, add.. if cert handler succeeds.. add */ if(!items->cert_handle || SC_SUCCESS == (r = items->cert_handle(p15card, items, &certs[i], cert_buffer, cert_length))) { r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); } if(should_free) free(cert_buffer); if(SC_SUCCESS != r) { if(onFailResume) continue; else break; } } else { /* Automatically add */ if(SC_SUCCESS != sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info)) { if(onFailResume) continue; else break; } } } return SC_SUCCESS; } int sc_pkcs15emu_initialize_pins(sc_pkcs15_card_t *p15card, p15data_items* items) { /* set pins */ int i,r; const pindata* pins = items->pins; if(!pins) return SC_SUCCESS; for (i = 0; pins[i].label; i++) { struct sc_pkcs15_auth_info pin_info; struct sc_pkcs15_object pin_obj; memset(&pin_info, 0, sizeof(pin_info)); memset(&pin_obj, 0, sizeof(pin_obj)); pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; sc_pkcs15_format_id(pins[i].id, &pin_info.auth_id); pin_info.attrs.pin.reference = pins[i].ref; pin_info.attrs.pin.flags = pins[i].flags; pin_info.attrs.pin.type = pins[i].type; pin_info.attrs.pin.min_length = pins[i].minlen; pin_info.attrs.pin.stored_length = pins[i].storedlen; pin_info.attrs.pin.max_length = pins[i].maxlen; pin_info.attrs.pin.pad_char = pins[i].pad_char; sc_format_path(pins[i].path, &pin_info.path); pin_info.tries_left = -1; strncpy(pin_obj.label, pins[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); pin_obj.flags = pins[i].obj_flags; if(0 > (r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info))) SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r); } return SC_SUCCESS; } int sc_pkcs15emu_initialize_private_keys(sc_pkcs15_card_t *p15card, p15data_items* items) { const prdata *prkeys = items->private_keys; int i, r; if(!prkeys) return SC_SUCCESS; /* set private keys */ for (i = 0; prkeys[i].label; i++) { r = add_private_key(p15card, &prkeys[i], 0, 0); if (r < 0) SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r); } return SC_SUCCESS; } int sc_pkcs15emu_initialize_public_keys(sc_pkcs15_card_t *p15card, p15data_items *items) { const pubdata *keys = items->public_keys; int i, r; if(!keys) return SC_SUCCESS; /* set public keys */ for (i = 0; keys[i].label; i++) { r = add_public_key(p15card, &keys[i], 0, 0); if (r < 0) SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r); } return SC_SUCCESS; } int sc_pkcs15emu_initialize_all(sc_pkcs15_card_t *p15card, p15data_items* items) { int r; if(SC_SUCCESS != (r = sc_pkcs15emu_initialize_objects(p15card, items))) return r; if(SC_SUCCESS != (r = sc_pkcs15emu_initialize_certificates(p15card, items))) return r; if(SC_SUCCESS != (r = sc_pkcs15emu_initialize_pins(p15card, items))) return r; if(items->forced_private && (SC_SUCCESS != (r = sc_pkcs15emu_initialize_private_keys(p15card, items)))) return r; if(items->forced_public && (SC_SUCCESS != (r = sc_pkcs15emu_initialize_public_keys(p15card, items)))) return r; return SC_SUCCESS; } #endif /* ENABLE_OPENSSL */ opensc-0.13.0/src/libopensc/card-epass2003.c0000644000015201777760000017106612057406034015300 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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 "internal.h" #include "asn1.h" #include "cardctl.h" static struct sc_atr_table epass2003_atrs[] = { /* This is a FIPS certified card using SCP01 security messaging. */ {"3B:9F:95:81:31:FE:9F:00:66:46:53:05:10:00:11:71:df:00:00:00:6a:82:5e", "FF:FF:FF:FF:FF:00:FF:FF:FF:FF:FF:FF:00:00:00:ff:00:ff:ff:00:00:00:00", "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 */ static unsigned char g_smtype; /* sm cryption algorithm type */ #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_sm; /* if perform sm or not */ 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] = { 0xBF, 0xC3, 0x29, 0x11, 0xC7, 0x18, 0xC3, 0x40 }; static unsigned char g_sk_enc[16] = { 0 }; /* encrypt session key */ static unsigned char g_sk_mac[16] = { 0 }; /* mac session key */ static unsigned char g_icv_mac[16] = { 0 }; /* instruction counter vector(for sm) */ #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 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); 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; int outl = 0; int outl_tmp = 0; unsigned char iv_tmp[EVP_MAX_IV_LENGTH] = { 0 }; memcpy(iv_tmp, iv, EVP_MAX_IV_LENGTH); EVP_CIPHER_CTX_init(&ctx); EVP_CIPHER_CTX_set_padding(&ctx, 0); EVP_EncryptInit_ex(&ctx, cipher, NULL, key, iv_tmp); if (!EVP_EncryptUpdate(&ctx, output, &outl, input, length)) goto out; if (!EVP_EncryptFinal_ex(&ctx, output + outl, &outl_tmp)) goto out; if (!EVP_CIPHER_CTX_cleanup(&ctx)) goto out; r = SC_SUCCESS; out: 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; int outl = 0; int outl_tmp = 0; unsigned char iv_tmp[EVP_MAX_IV_LENGTH] = { 0 }; memcpy(iv_tmp, iv, EVP_MAX_IV_LENGTH); EVP_CIPHER_CTX_init(&ctx); EVP_CIPHER_CTX_set_padding(&ctx, 0); EVP_DecryptInit_ex(&ctx, cipher, NULL, key, iv_tmp); if (!EVP_DecryptUpdate(&ctx, output, &outl, input, length)) goto out; if (!EVP_DecryptFinal_ex(&ctx, output + outl, &outl_tmp)) goto out; if (!EVP_CIPHER_CTX_cleanup(&ctx)) goto out; r = SC_SUCCESS; out: return r; } static int aes128_encrypt_ecb(const unsigned char *key, int keysize, const unsigned char *input, size_t length, unsigned char *output) { unsigned char iv[EVP_MAX_IV_LENGTH] = { 0 }; return openssl_enc(EVP_aes_128_ecb(), key, iv, input, length, output); } static int aes128_encrypt_cbc(const unsigned char *key, int keysize, unsigned char iv[16], const unsigned char *input, size_t length, unsigned char *output) { return openssl_enc(EVP_aes_128_cbc(), key, iv, input, length, output); } static int aes128_decrypt_cbc(const unsigned char *key, int keysize, unsigned char iv[16], const unsigned char *input, size_t length, unsigned char *output) { return openssl_dec(EVP_aes_128_cbc(), key, iv, input, length, output); } static int des3_encrypt_ecb(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 }; if (keysize == 16) { memcpy(&bKey[0], key, 16); memcpy(&bKey[16], key, 8); } else { memcpy(&bKey[0], key, 24); } return openssl_enc(EVP_des_ede3(), bKey, iv, input, length, output); } static int des3_encrypt_cbc(const unsigned char *key, int keysize, unsigned char iv[8], const unsigned char *input, size_t length, unsigned char *output) { unsigned char bKey[24] = { 0 }; if (keysize == 16) { memcpy(&bKey[0], key, 16); memcpy(&bKey[16], key, 8); } else { memcpy(&bKey[0], key, 24); } return openssl_enc(EVP_des_ede3_cbc(), bKey, iv, input, length, output); } static int des3_decrypt_cbc(const unsigned char *key, int keysize, unsigned char iv[8], const unsigned char *input, size_t length, unsigned char *output) { unsigned char bKey[24] = { 0 }; if (keysize == 16) { memcpy(&bKey[0], key, 16); memcpy(&bKey[16], key, 8); } else { memcpy(&bKey[0], key, 24); } return openssl_dec(EVP_des_ede3_cbc(), bKey, iv, input, length, output); } static int des_encrypt_cbc(const unsigned char *key, int keysize, unsigned char iv[8], const unsigned char *input, size_t length, unsigned char *output) { return openssl_enc(EVP_des_cbc(), key, iv, input, length, output); } static int des_decrypt_cbc(const unsigned char *key, int keysize, unsigned char iv[8], const unsigned char *input, size_t length, unsigned char *output) { return openssl_dec(EVP_des_cbc(), key, iv, input, length, output); } static int openssl_dig(const EVP_MD * digest, const unsigned char *input, size_t length, unsigned char *output) { EVP_MD_CTX ctx; unsigned outl = 0; EVP_MD_CTX_init(&ctx); EVP_DigestInit_ex(&ctx, digest, NULL); if (!EVP_DigestUpdate(&ctx, input, length)) return SC_ERROR_INTERNAL; if (!EVP_DigestFinal_ex(&ctx, output, &outl)) return SC_ERROR_INTERNAL; if (!EVP_MD_CTX_cleanup(&ctx)) return SC_ERROR_INTERNAL; return SC_SUCCESS; } static int sha1_digest(const unsigned char *input, size_t length, unsigned char *output) { return openssl_dig(EVP_sha1(), input, length, output); } 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 long blocksize = 0; unsigned char cryptogram[256] = { 0 }; /* host cryptogram */ unsigned char iv[16] = { 0 }; LOG_FUNC_CALLED(card->ctx); sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x50, 0x00, 0x00); apdu.cla = 0x80; apdu.lc = apdu.datalen = sizeof(g_random); apdu.data = g_random; /* host random */ apdu.le = apdu.resplen = 28; apdu.resp = result; /* card random is result[12~19] */ tmp_sm = g_sm; g_sm = SM_PLAIN; r = epass2003_transmit_apdu(card, &apdu); g_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 */ memcpy(data, &result[16], 4); memcpy(&data[4], g_random, 4); memcpy(&data[8], &result[12], 4); memcpy(&data[12], &g_random[4], 4); /* Step 2,3 - Create S-ENC/S-MAC Session Key */ if (KEY_TYPE_AES == key_type) { aes128_encrypt_ecb(key_enc, 16, data, 16, g_sk_enc); aes128_encrypt_ecb(key_mac, 16, data, 16, g_sk_mac); } else { des3_encrypt_ecb(key_enc, 16, data, 16, g_sk_enc); des3_encrypt_ecb(key_mac, 16, data, 16, g_sk_mac); } memcpy(data, g_random, 8); memcpy(&data[8], &result[12], 8); data[16] = 0x80; blocksize = (key_type == KEY_TYPE_AES ? 16 : 8); memset(&data[17], 0x00, blocksize - 1); /* calculate host cryptogram */ if (KEY_TYPE_AES == key_type) aes128_encrypt_cbc(g_sk_enc, 16, iv, data, 16 + blocksize, cryptogram); else des3_encrypt_cbc(g_sk_enc, 16, iv, data, 16 + blocksize, cryptogram); /* verify card cryptogram */ if (0 != memcmp(&cryptogram[16], &result[20], 8)) LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); LOG_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; LOG_FUNC_CALLED(card->ctx); memcpy(data, ran_key, 8); memcpy(&data[8], g_random, 8); data[16] = 0x80; memset(&data[17], 0x00, blocksize - 1); memset(iv, 0, 16); /* calculate host cryptogram */ if (KEY_TYPE_AES == key_type) { aes128_encrypt_cbc(g_sk_enc, 16, iv, data, 16 + blocksize, cryptogram); } else { des3_encrypt_cbc(g_sk_enc, 16, iv, data, 16 + blocksize, cryptogram); } memset(data, 0, sizeof(data)); memcpy(data, "\x84\x82\x03\x00\x10", 5); memcpy(&data[5], &cryptogram[16], 8); memcpy(&data[13], "\x80\x00\x00", 3); /* calculate mac icv */ memset(iv, 0x00, 16); if (KEY_TYPE_AES == key_type) { aes128_encrypt_cbc(g_sk_mac, 16, iv, data, 16, mac); i = 0; } else { des3_encrypt_cbc(g_sk_mac, 16, iv, data, 16, mac); i = 8; } /* save mac icv */ memset(g_icv_mac, 0x00, 16); memcpy(g_icv_mac, &mac[i], 8); /* verify host cryptogram */ 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 = g_sm; g_sm = SM_PLAIN; r = epass2003_transmit_apdu(card, &apdu); g_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 }; LOG_FUNC_CALLED(ctx); r = gen_init_key(card, key_enc, key_mac, result, g_smtype); LOG_TEST_RET(ctx, r, "gen_init_key failed"); memcpy(ran_key, &result[12], 8); r = verify_init_key(card, ran_key, g_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; if (g_sm) { r = mutual_auth(card, g_init_key_enc, g_init_key_mac); LOG_TEST_RET(card->ctx, r, "mutual_auth failed"); } return r; } /* Data(TLV)=0x87|L|0x01+Cipher */ static int construct_data_tlv(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 }; /* 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) aes128_encrypt_cbc(g_sk_enc, 16, iv, pad, pad_len, apdu_buf + block_size + tlv_more); else des3_encrypt_cbc(g_sk_enc, 16, iv, pad, pad_len, apdu_buf + block_size + tlv_more); 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(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 i = (KEY_TYPE_AES == key_type ? 15 : 7); 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 (g_icv_mac[i] == 0xff) { g_icv_mac[i] = 0; } else { g_icv_mac[i]++; break; } } /* calculate MAC */ memset(icv, 0, sizeof(icv)); memcpy(icv, g_icv_mac, 16); if (KEY_TYPE_AES == key_type) { aes128_encrypt_cbc(g_sk_mac, 16, icv, apdu_buf, mac_len, mac); memcpy(mac_tlv + 2, &mac[mac_len - 16], 8); } else { unsigned char iv[8] = { 0 }; unsigned char tmp[8] = { 0 }; des_encrypt_cbc(g_sk_mac, 8, icv, apdu_buf, mac_len, mac); des_decrypt_cbc(&g_sk_mac[8], 8, iv, &mac[mac_len - 8], 8, tmp); memset(iv, 0x00, 8); des_encrypt_cbc(g_sk_mac, 8, iv, tmp, 8, mac_tlv + 2); } *mac_tlv_len = 2 + 8; return 0; } #if 0 static size_t calc_le(size_t le) { size_t le_new = 0; size_t resp_len = 0; size_t sw_len = 4; /* T 1 L 1 V 2 */ size_t mac_len = 10; /* T 1 L 1 V 8 */ size_t mod = 16; /* padding first */ resp_len = 1 + ((le + (mod - 1)) / mod) * mod; if (0x7f < resp_len) { resp_len += 0; } else if (0x7f <= resp_len && resp_len < 0xff) { resp_len += 1; } else if (0xff <= resp_len) { resp_len += 2; } resp_len += 2; /* +T+L */ le_new = resp_len + sw_len + mac_len; return le_new; } #endif /* 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_apdu *plain, struct sc_apdu *sm, unsigned char *apdu_buf, size_t * apdu_buf_len) { size_t block_size = (KEY_TYPE_DES == g_smtype ? 16 : 8); 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 }; mac_tlv[0] = 0x8E; mac_tlv[1] = 8; /* size_t plain_le = 0; */ 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 */ apdu_buf[4] = 0x80; memset(&apdu_buf[5], 0x00, block_size - 5); /* Data -> Data' */ if (plain->lc != 0) if (0 != construct_data_tlv(plain, apdu_buf, dataTLV, &data_tlv_len, g_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, g_smtype)) return -1; if (0 != construct_mac_tlv(apdu_buf, data_tlv_len, le_tlv_len, mac_tlv, &mac_tlv_len, g_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); LOG_FUNC_CALLED(card->ctx); if (g_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; sm->data = plain->data; sm->resplen = plain->resplen; sm->resp = plain->resp; break; case 0x0C: memset(buf, 0, sizeof(buf)); if (0 != encode_apdu(plain, sm, buf, &buf_len)) return SC_ERROR_CARD_CMD_FAILED; break; default: return SC_ERROR_INCORRECT_PARAMETERS; } return SC_SUCCESS; } /* 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(unsigned char *in, unsigned char *out, size_t * out_len) { size_t in_len; size_t i; unsigned char iv[16] = { 0 }; unsigned char plaintext[4096] = { 0 }; /* no cipher */ if (in[0] == 0x99) return 0; /* parse cipher length */ if (0x01 == in[2] && 0x82 != in[1]) { in_len = in[1]; i = 3; } else if (0x01 == in[3] && 0x81 == in[1]) { in_len = in[2]; i = 4; } else if (0x01 == in[4] && 0x82 == in[1]) { in_len = in[2] * 0x100; in_len += in[3]; i = 5; } else { return -1; } /* decrypt */ if (KEY_TYPE_AES == g_smtype) aes128_decrypt_cbc(g_sk_enc, 16, iv, &in[i], in_len - 1, plaintext); else des3_decrypt_cbc(g_sk_enc, 16, iv, &in[i], in_len - 1, plaintext); /* unpadding */ while (0x80 != plaintext[in_len - 2] && (in_len - 2 > 0)) in_len--; if (2 == in_len) return -1; memcpy(out, plaintext, in_len - 2); *out_len = in_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; LOG_FUNC_CALLED(card->ctx); r = sc_check_sw(card, sm->sw1, sm->sw2); if (r == SC_SUCCESS) { if (g_sm) { if (0 != decrypt_response(sm->resp, 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 %i, 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) free((*sm_apdu)->data); if ((*sm_apdu)->resp) free((*sm_apdu)->resp); free(*sm_apdu); *sm_apdu = NULL; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } 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) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); apdu->data = calloc (1, SC_MAX_EXT_APDU_BUFFER_SIZE); if (!apdu->data) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); apdu->resp = calloc (1, SC_MAX_EXT_APDU_BUFFER_SIZE); if (!apdu->resp) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); 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); LOG_FUNC_RETURN(ctx, rv); } *sm_apdu = apdu; 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(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; 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 = g_sm; g_sm = SM_PLAIN; r = sc_transmit_apdu(card, &apdu); g_sm = tmp_sm; } else { r = sc_transmit_apdu(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 char data[SC_MAX_APDU_BUFFER_SIZE] = { 0 }; size_t datalen = SC_MAX_APDU_BUFFER_SIZE; LOG_FUNC_CALLED(card->ctx); card->name = "epass2003"; card->cla = 0x00; card->drv_data = NULL; /* VT card->ctx->use_sm = 1; */ g_sm = SM_SCP01; /* g_sm = SM_PLAIN; */ /* decide FIPS/Non-FIPS mode */ if (SC_SUCCESS != get_data(card, 0x86, data, datalen)) return SC_ERROR_CARD_CMD_FAILED; if (0x01 == data[2]) g_smtype = KEY_TYPE_AES; else g_smtype = KEY_TYPE_DES; /* 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, 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, 2048, flags, 0x10001); card->caps = SC_CARD_CAP_RNG | SC_CARD_CAP_APDU_EXT; LOG_FUNC_RETURN(card->ctx, 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 = path->value[path->len - 2]; u8 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 void epass2003_hook_file(struct sc_file *file, int inc) { int fidl = file->id & 0xff; int fidh = file->id & 0xff00; if (epass2003_hook_path(&file->path, inc)) { if (inc) file->id = fidh + fidl * FID_STEP; else file->id = fidh + fidl / FID_STEP; } } static int epass2003_select_fid_(struct sc_card *card, sc_path_t * in_path, sc_file_t ** file_out) { sc_context_t *ctx; struct sc_apdu apdu; u8 buf[SC_MAX_APDU_BUFFER_SIZE] = { 0 }; u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; int r, pathlen; sc_file_t *file = NULL; ctx = card->ctx; epass2003_hook_path(in_path, 1); 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 prvate 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(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 = 0; 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->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; 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; int bMatch = -1; 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; for (i = 0; i < pathlen; i++) n_pathbuf[i + 2] = pathbuf[i]; path = n_pathbuf; pathlen += 2; } /* check current working directory */ if (card->cache.valid && card->cache.current_path.type == SC_PATH_TYPE_PATH && card->cache.current_path.len >= 2 && card->cache.current_path.len <= pathlen) { bMatch = 0; for (i = 0; i < card->cache.current_path.len; i += 2) if (card->cache.current_path.value[i] == path[i] && card->cache.current_path.value[i + 1] == path[i + 1]) bMatch += 2; } if (card->cache.valid && bMatch > 2) { if (pathlen - bMatch == 2) { /* we are in the rigth directory */ return 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: %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; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0); switch (env->operation) { case SC_SEC_OPERATION_DECIPHER: apdu.p2 = 0xB8; break; case SC_SEC_OPERATION_SIGN: apdu.p2 = 0xB8; break; default: return SC_ERROR_INVALID_ARGUMENTS; } 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 = 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, 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 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 }; 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(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, 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); } 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: %d", 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; case 0x03: case 0x05: case 0x06: case 0x07: 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) { char tbuf[128]; memcpy(file->name, tag, taglen); file->namelen = taglen; sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, file->name, file->namelen, tbuf, sizeof(tbuf)); sc_log(ctx, "File name: %s", tbuf); 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) sc_file_set_sec_attr(file, tag, taglen); 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) { buf[0] = 0x11; buf[1] = 0x00; } else if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_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) { 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) { 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) { 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); } /* VT ??? */ if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { unsigned char data[2] = {0x00, 0x66}; sc_asn1_put_tag(0x87, data, sizeof(data), p, *outlen - (p - out), &p); } 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(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); epass2003_hook_path((struct sc_path *)path, 1); if (r == SC_SUCCESS) { sbuf[0] = path->value[path->len - 2]; sbuf[1] = path->value[path->len - 1]; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00); apdu.lc = 2; apdu.datalen = 2; apdu.data = sbuf; } else { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } 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, "Delete file failed"); LOG_FUNC_RETURN(card->ctx, r); } #if 0 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_2_SHORT, 0x34, 0x00, 0x00); apdu.cla = 0x80; apdu.le = 0x40; 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 == 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, buflen); } #endif 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(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(unsigned char *data, size_t datalen, unsigned char *hash) { unsigned char data_hash[24] = { 0 }; size_t len = 0; if ((NULL == data) || (NULL == hash)) return SC_ERROR_INVALID_ARGUMENTS; sha1_digest(data, datalen, data_hash); len = REVERSE_ORDER4(datalen); memcpy(&data_hash[20], &len, 4); memcpy(hash, data_hash, 24); 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(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(pin->key_data.es_secret.key_val, pin->key_data.es_secret.key_len, hash); 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); 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(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "generate keypair failed"); /* read public key */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xb4, 0x02, 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(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); data->modulus = (u8 *) malloc(len); if (!data->modulus) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(data->modulus, rbuf, len); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int epass2003_erase_card(struct sc_card *card) { int r; LOG_FUNC_CALLED(card->ctx); card->cache.valid = 0; r = sc_delete_file(card, sc_get_mf_path()); 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); 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} }; struct sc_file *ef_file; int ret; ret = sc_select_file(card, &file_path, &ef_file); 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_2_SHORT, 0x82, 0x01, 0x80 | kid); apdu.resp = NULL; apdu.resplen = 0; r = sc_transmit_apdu(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 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(data, datalen, hash); LOG_TEST_RET(card->ctx, r, "hash data failed"); des3_encrypt_cbc(hash, HASH_LEN, iv, random, 8, tmp_data); 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(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, 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(data, datalen, hash); 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(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; 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; /* get pin retries */ if (data->cmd == SC_PIN_CMD_GET_INFO) { u8 retries = 0; r = get_external_key_retries(card, 0x80 | kid, &retries); if (r == SC_SUCCESS) { data->pin1.tries_left = retries; if (tries_left) *tries_left = retries; r = get_external_key_maxtries(card, &maxtries); LOG_TEST_RET(card->ctx, r, "get max counter failed"); data->pin1.max_tries = maxtries; } return r; } /* verify */ if (data->cmd == SC_PIN_CMD_UNBLOCK) { 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 { r = external_key_auth(card, kid, (unsigned char *)data->pin1.data, data->pin1.len); LOG_TEST_RET(card->ctx, r, "verify pin failed"); } if (data->cmd == SC_PIN_CMD_CHANGE || data->cmd == SC_PIN_CMD_UNBLOCK) { /* change */ r = update_secret_key(card, 0x04, kid, (unsigned char *)data->pin2.data, (unsigned long)data->pin2.len); LOG_TEST_RET(card->ctx, r, "verify pin failed"); } return 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; epass2003_ops = *iso_ops; epass2003_ops.match_card = epass2003_match_card; epass2003_ops.init = epass2003_init; 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; 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.13.0/src/libopensc/card-jcop.c0000644000015201777760000007110312057406034014602 00000000000000/* * card-jcop.c * * Copyright (C) 2003 Chaskiel Grundman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include "internal.h" #include "cardctl.h" static struct sc_atr_table jcop_atrs[] = { { "3B:E6:00:FF:81:31:FE:45:4A:43:4F:50:33:31:06", NULL, NULL, SC_CARD_TYPE_JCOP_GENERIC, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } }; static struct sc_card_operations jcop_ops; static struct sc_card_driver jcop_drv = { "JCOP cards with BlueZ PKCS#15 applet", "jcop", &jcop_ops, NULL, 0, NULL }; #define SELECT_MF 0 #define SELECT_EFDIR 1 #define SELECT_APPDF 2 #define SELECT_EF 3 #define SELECT_UNKNOWN 4 #define SELECTING_TARGET 0xf #define SELECTING_ABS 0x80 #define SELECTING_VIA_APPDF 0x100 struct jcop_private_data { sc_file_t *virtmf; sc_file_t *virtdir; sc_path_t aid; int selected; int invalid_senv; int nfiles; u8 *filelist; }; #define DRVDATA(card) ((struct jcop_private_data *) ((card)->drv_data)) static int jcop_finish(sc_card_t *card) { struct jcop_private_data *drvdata=DRVDATA(card); if (drvdata) { sc_file_free(drvdata->virtmf); sc_file_free(drvdata->virtdir); free(drvdata); card->drv_data=NULL; } return 0; } static int jcop_match_card(sc_card_t *card) { int i; i = _sc_match_atr(card, jcop_atrs, &card->type); if (i < 0) return 0; return 1; } static unsigned char ef_dir_contents[128] = { 0x61, 0x21, 0x4f, 0xc, 0xA0, 0x0, 0x0, 0x0, 0x63, 'P', 'K', 'C', 'S', '-', '1', '5', 0x50, 0xb, 'O', 'p', 'e', 'n', 'S', 'C', ' ', 'C', 'a', 'r', 'd', 0x51, 0x04, 0x3f, 0x00, 0x50, 0x15 }; static int jcop_init(sc_card_t *card) { struct jcop_private_data *drvdata; sc_file_t *f; int flags; drvdata=malloc(sizeof(struct jcop_private_data)); if (!drvdata) return SC_ERROR_OUT_OF_MEMORY; memset(drvdata, 0, sizeof(struct jcop_private_data)); sc_format_path("A000:0000:6350:4B43:532D:3135", &drvdata->aid); drvdata->aid.type = SC_PATH_TYPE_DF_NAME; drvdata->selected=SELECT_MF; drvdata->invalid_senv=1; drvdata->nfiles=-1; drvdata->filelist=NULL; f=sc_file_new(); if (!f){ free(drvdata); return SC_ERROR_OUT_OF_MEMORY; } sc_format_path("3f00", &f->path); f->type=SC_FILE_TYPE_DF; f->shareable=0; f->ef_structure=SC_FILE_EF_UNKNOWN; f->size=0; f->id=0x3f00; f->status=SC_FILE_STATUS_ACTIVATED; sc_file_add_acl_entry(f, SC_AC_OP_SELECT, SC_AC_NONE, 0); sc_file_add_acl_entry(f, SC_AC_OP_LIST_FILES, SC_AC_NONE, 0); sc_file_add_acl_entry(f, SC_AC_OP_LOCK, SC_AC_NEVER, 0); sc_file_add_acl_entry(f, SC_AC_OP_DELETE, SC_AC_NEVER, 0); sc_file_add_acl_entry(f, SC_AC_OP_CREATE, SC_AC_NEVER, 0); drvdata->virtmf=f; f=sc_file_new(); if (!f){ sc_file_free(drvdata->virtmf); free(drvdata); return SC_ERROR_OUT_OF_MEMORY; } sc_format_path("3f002f00", &f->path); f->type=SC_FILE_TYPE_WORKING_EF; f->shareable=0; f->ef_structure=SC_FILE_EF_TRANSPARENT; f->size=128; f->id=0x2f00; f->status=SC_FILE_STATUS_ACTIVATED; sc_file_add_acl_entry(f, SC_AC_OP_READ, SC_AC_NONE, 0); sc_file_add_acl_entry(f, SC_AC_OP_LOCK, SC_AC_NEVER, 0); sc_file_add_acl_entry(f, SC_AC_OP_ERASE, SC_AC_NEVER, 0); sc_file_add_acl_entry(f, SC_AC_OP_UPDATE, SC_AC_NEVER, 0); sc_file_add_acl_entry(f, SC_AC_OP_WRITE, SC_AC_NEVER, 0); sc_file_add_acl_entry(f, SC_AC_OP_CRYPTO, SC_AC_NEVER, 0); drvdata->virtdir=f; card->drv_data = drvdata; card->cla = 0x00; /* card supports host-side padding, but not raw rsa */ flags = SC_ALGORITHM_RSA_PAD_PKCS1; flags |= SC_ALGORITHM_RSA_HASH_NONE; flags |= SC_ALGORITHM_RSA_HASH_SHA1; flags |= SC_ALGORITHM_RSA_HASH_MD5; /* only supports keygen with 3 and F-4 exponents */ flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; _sc_card_add_rsa_alg(card, 512, flags, 0); _sc_card_add_rsa_alg(card, 768, flags, 0); _sc_card_add_rsa_alg(card, 1024, flags, 0); _sc_card_add_rsa_alg(card, 2048, flags, 0); /* State that we have an RNG */ card->caps |= SC_CARD_CAP_RNG; return 0; } static int jcop_get_default_key(sc_card_t *card, struct sc_cardctl_default_key *data) { const char *key; if (data->method != SC_AC_PRO || data->key_ref > 2) return SC_ERROR_NO_DEFAULT_KEY; key = "40:41:42:43:44:45:46:47:48:49:4A:4B:4C:4D:4E:4F"; return sc_hex_to_bin(key, data->key_data, &data->len); } /* since the card is actually a javacard, we're expected to use ISO 7816-4 direct application selection instead of reading the DIR ourselves and selecting the AppDF by path. Since opensc doesn' do that, I fake an MF containing the AppDF and a fixed DIR pointing at the fake AppDF. This has the added advantage of allowing opensc-explorer to be used with this driver */ static int jcop_select_file(sc_card_t *card, const sc_path_t *path, sc_file_t **file) { struct jcop_private_data *drvdata=DRVDATA(card); int r,selecting; struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); const struct sc_card_operations *iso_ops = iso_drv->ops; sc_path_t shortpath; sc_file_t *tfile, **fileptr; if (!drvdata) return SC_ERROR_FILE_NOT_FOUND; /* Something about the card does not like Case 4 APDU's to be sent as Case 3. you must send a length and accept a response. */ if (file) { fileptr=file; } else { fileptr=&tfile; } /* Selecting the MF. return a copy of the constructed MF */ if (path->len == 2 && memcmp(path->value, "\x3F\x00", 2) == 0) { drvdata->selected=SELECT_MF; if (file) { sc_file_dup(file, drvdata->virtmf); if (*file == NULL) return SC_ERROR_OUT_OF_MEMORY; } return 0; } /* Selecting the EF(DIR). return a copy of the constructed EF(DIR) */ if ((path->len == 4 && memcmp(path->value, "\x3F\x00\x2F\x00", 4) == 0) || (drvdata->selected == SELECT_MF && path->len == 2 && memcmp(path->value, "\x2F\x00", 2) == 0)) { drvdata->selected=SELECT_EFDIR; if (file) { sc_file_dup(file, drvdata->virtdir); if (*file == NULL) return SC_ERROR_OUT_OF_MEMORY; } return 0; } /* selecting the PKCS15 AppDF or a file in it. Select the applet, then pass through any remaining path components to the applet's select command */ selecting=SELECT_UNKNOWN; if (path->len >= 4 && memcmp(path->value, "\x3F\x00\x50\x15", 4) == 0) { if (path->len == 4) selecting = SELECTING_ABS | SELECT_APPDF; else selecting = SELECTING_ABS | SELECT_EF; } if (drvdata->selected==SELECT_MF && memcmp(path->value, "\x50\x15", 2) == 0) { if (path->len == 2) selecting = SELECTING_VIA_APPDF | SELECT_APPDF; else selecting = SELECTING_VIA_APPDF | SELECT_EF; } if (selecting & (SELECTING_ABS|SELECTING_VIA_APPDF)) { if (file == NULL && (selecting & SELECTING_TARGET) == SELECT_APPDF && drvdata->selected == SELECT_APPDF) { return 0; } if ((r = iso_ops->select_file(card, &drvdata->aid, fileptr)) < 0) return r; if ((selecting & SELECTING_TARGET) == SELECT_APPDF) { (*fileptr)->type = SC_FILE_TYPE_DF; drvdata->selected=SELECT_APPDF; goto select_ok; } sc_file_free(*fileptr); *fileptr=NULL; memset(&shortpath, 0, sizeof(sc_path_t)); if (selecting & SELECTING_ABS) { memcpy(&shortpath.value, &path->value[4], path->len-4); shortpath.len=path->len-4; } else { memcpy(&shortpath.value, &path->value[2], path->len-2); shortpath.len=path->len-2; } shortpath.type = shortpath.len == 2 ? SC_PATH_TYPE_FILE_ID : path->type; shortpath.index=path->index; shortpath.count=path->count; path=&shortpath; } else { /* There seems to be better debugging output if I call sc_check_sw * with appropriate input than if I just return the appropriate * SC_ERROR_*, so that's what I do for all errors returned by code * related to the MF/DIR emulation */ if (drvdata->selected == SELECT_MF || drvdata->selected == SELECT_EFDIR) return sc_check_sw(card, 0x6A, 0x82); } r = iso_ops->select_file(card, path, fileptr); if (r) return r; drvdata->selected=SELECT_EF; select_ok: if (!file) { sc_file_free(*fileptr); } return 0; } static int jcop_read_binary(sc_card_t *card, unsigned int idx, u8 * buf, size_t count, unsigned long flags) { struct jcop_private_data *drvdata=DRVDATA(card); struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); const struct sc_card_operations *iso_ops = iso_drv->ops; sc_file_t *tfile; int r; if (drvdata->selected == SELECT_MF) { return sc_check_sw(card, 0x69, 0x86); } if (drvdata->selected == SELECT_EFDIR) { if (idx > 127) { return sc_check_sw(card, 0x6A, 0x86); } if (idx + count > 128) { count=128-idx; } r = iso_ops->select_file(card, &drvdata->aid, &tfile); if (r < 0) { /* no pkcs15 app, so return empty DIR. */ memset(buf, 0, count); } else { sc_file_free(tfile); memcpy(buf, (u8 *)(ef_dir_contents + idx), count); } return count; } return iso_ops->read_binary(card, idx, buf, count, flags); } static int jcop_list_files(sc_card_t *card, u8 *buf, size_t buflen) { struct jcop_private_data *drvdata=DRVDATA(card); struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); const struct sc_card_operations *iso_ops = iso_drv->ops; sc_file_t *tfile; int r; if (drvdata->selected == SELECT_MF) { if (buflen < 2) return 0; memcpy(buf, "\x2f\x00", 2); if (buflen < 4) return 2; /* AppDF only exists if applet is selectable */ r = iso_ops->select_file(card, &drvdata->aid, &tfile); if (r < 0) { return 2; } else { sc_file_free(tfile); memcpy(buf+2, "\x50\x15", 2); return 4; } } if (drvdata->nfiles == -1) return SC_ERROR_NOT_ALLOWED; if (drvdata->nfiles == 0) return 0; if (buflen > 2 * (size_t)drvdata->nfiles) buflen=2*drvdata->nfiles; memcpy(buf, drvdata->filelist, buflen); return buflen; } static int sa_to_acl(sc_file_t *file, unsigned int operation, int nibble) { switch (nibble & 0x7) { case 0: sc_file_add_acl_entry(file, operation, SC_AC_NONE, SC_AC_KEY_REF_NONE); break; case 1: sc_file_add_acl_entry(file, operation, SC_AC_NEVER, SC_AC_KEY_REF_NONE); break; case 2: sc_file_add_acl_entry(file, operation, SC_AC_CHV, 1); break; case 3: sc_file_add_acl_entry(file, operation, SC_AC_CHV, 2); break; case 4: sc_file_add_acl_entry(file, operation, SC_AC_CHV, 3); break; case 5: sc_file_add_acl_entry(file, operation, SC_AC_AUT, SC_AC_KEY_REF_NONE); break; case 6: sc_file_add_acl_entry(file, operation, SC_AC_PRO, SC_AC_KEY_REF_NONE); break; default: sc_file_add_acl_entry(file, operation, SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE); } return 0; } static int jcop_process_fci(sc_card_t *card, sc_file_t *file, const u8 *buf, size_t buflen) { struct jcop_private_data *drvdata=DRVDATA(card); struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); const struct sc_card_operations *iso_ops = iso_drv->ops; u8 *sa; int r; /* the FCI for EF's includes a bogus length for the overall structure! */ if (buflen == 19) buflen=24; r=iso_ops->process_fci(card, file, buf, buflen); if (r < 0) return r; if (file->type != SC_FILE_TYPE_DF) { if (drvdata->nfiles) { drvdata->nfiles=-1; free(drvdata->filelist); drvdata->filelist=NULL; } if(file->sec_attr_len >=3) { /* The security attribute bytes are divided into nibbles and are as follows: READ | MODIFY || SIGN | ENCIPHER || DECIPHER | DELETE */ sa=file->sec_attr; sa_to_acl(file, SC_AC_OP_READ, sa[0] >> 4); sa_to_acl(file, SC_AC_OP_UPDATE, sa[0] & 0xf); /* Files may be locked by anyone who can MODIFY. */ /* opensc seems to think LOCK ACs are only on DFs */ /* sa_to_acl(file, SC_AC_OP_LOCK, sa[0] & 0xf); */ /* there are seperate SIGN, ENCIPHER, and DECIPHER ACs. I use SIGN for SC_AC_OP_CRYPTO unless it is NEVER, in which case I use DECIPHER */ if ((sa[1] & 0xf0) == 0x10) sa_to_acl(file, SC_AC_OP_CRYPTO, sa[1] >> 4); else sa_to_acl(file, SC_AC_OP_CRYPTO, sa[2] >> 4); sa_to_acl(file, SC_AC_OP_ERASE, sa[2] & 0xf); } } else { /* No AC information is reported for the AppDF */ sc_file_add_acl_entry(file, SC_AC_OP_SELECT, SC_AC_NONE, 0); sc_file_add_acl_entry(file, SC_AC_OP_CREATE, SC_AC_CHV, 3); sc_file_add_acl_entry(file, SC_AC_OP_DELETE, SC_AC_NONE, 0); sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES, SC_AC_NONE, 0); if (drvdata->nfiles) { drvdata->nfiles=0; free(drvdata->filelist); drvdata->filelist=NULL; } /* the format of the poprietary attributes is: 4 bytes unique id 1 byte # files in DF 2 bytes 1st File ID 2 bytes 2nd File ID ... */ if (file->prop_attr_len > 4) { int nfiles; u8 *filelist; nfiles=file->prop_attr[4]; if (nfiles) { filelist=malloc(2*nfiles); if (!filelist) return SC_ERROR_OUT_OF_MEMORY; memcpy(filelist, &file->prop_attr[5], 2*nfiles); drvdata->nfiles=nfiles; drvdata->filelist=filelist; } } } return r; } static int acl_to_ac_nibble(const sc_acl_entry_t *e) { if (e == NULL) return -1; if (e->next != NULL) /* FIXME */ return -1; switch (e->method) { case SC_AC_NONE: return 0x00; case SC_AC_NEVER: return 0x01; case SC_AC_CHV: switch (e->key_ref) { case 1: return 0x02; case 2: return 0x03; case 3: return 0x04; } return -1; case SC_AC_AUT: return 0x05; case SC_AC_PRO: return 0x06; } return -1; } static int jcop_create_file(sc_card_t *card, sc_file_t *file) { struct jcop_private_data *drvdata=DRVDATA(card); unsigned char sec_attr_data[3]; int ops[6]; int i, r; struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); const struct sc_card_operations *iso_ops = iso_drv->ops; if (drvdata->selected == SELECT_MF || drvdata->selected == SELECT_EFDIR ) return sc_check_sw(card, 0x69, 0x82); /* Can't create DFs */ if (file->type != SC_FILE_TYPE_WORKING_EF) return sc_check_sw(card, 0x6A, 0x80); ops[0] = SC_AC_OP_READ; /* read */ ops[1] = SC_AC_OP_UPDATE; /* modify */ ops[2] = SC_AC_OP_CRYPTO; /* sign */ ops[3] = -1; /* encipher */ ops[4] = SC_AC_OP_CRYPTO; /* decipher */ ops[5] = SC_AC_OP_ERASE; /* delete */ memset(sec_attr_data, 0, 3); for (i = 0; i < 6; i++) { const sc_acl_entry_t *entry; if (ops[i] == -1) { sec_attr_data[i/2] |= 1 << ((i % 2) ? 0 : 4); continue; } entry = sc_file_get_acl_entry(file, ops[i]); r = acl_to_ac_nibble(entry); sec_attr_data[i/2] |= r << ((i % 2) ? 0 : 4); } sc_file_set_sec_attr(file, sec_attr_data, 3); r=iso_ops->create_file(card, file); if (r > 0) drvdata->selected=SELECT_EF; return r; } /* We need to trap these functions so that proper errors can be returned when one of the virtual files is selected */ static int jcop_write_binary(sc_card_t *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) { struct jcop_private_data *drvdata=DRVDATA(card); struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); const struct sc_card_operations *iso_ops = iso_drv->ops; if (drvdata->selected == SELECT_MF) return sc_check_sw(card, 0x6A, 0x86); if (drvdata->selected == SELECT_EFDIR) return sc_check_sw(card, 0x69, 0x82); return iso_ops->write_binary(card, idx, buf, count, flags); } static int jcop_update_binary(sc_card_t *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) { struct jcop_private_data *drvdata=DRVDATA(card); struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); const struct sc_card_operations *iso_ops = iso_drv->ops; if (drvdata->selected == SELECT_MF) return sc_check_sw(card, 0x69, 0x86); if (drvdata->selected == SELECT_EFDIR) return sc_check_sw(card, 0x69, 0x82); return iso_ops->update_binary(card, idx, buf, count, flags); } static int jcop_delete_file(sc_card_t *card, const sc_path_t *path) { struct jcop_private_data *drvdata=DRVDATA(card); struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); const struct sc_card_operations *iso_ops = iso_drv->ops; if (drvdata->selected == SELECT_MF || drvdata->selected == SELECT_EFDIR ) return sc_check_sw(card, 0x69, 0x82); return iso_ops->delete_file(card, path); } /* BlueZ doesn't support stored security environments. you have to construct one with SET every time */ static int jcop_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { sc_apdu_t apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 *p; int r; struct jcop_private_data *drvdata=DRVDATA(card); assert(card != NULL && env != NULL); if (se_num) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); if (drvdata->selected == SELECT_MF || drvdata->selected == SELECT_EFDIR) { drvdata->invalid_senv=1; return 0; } if (env->flags & SC_SEC_ENV_ALG_PRESENT) { sc_security_env_t tmp; tmp = *env; tmp.flags &= ~SC_SEC_ENV_ALG_PRESENT; tmp.flags |= SC_SEC_ENV_ALG_REF_PRESENT; if (tmp.algorithm != SC_ALGORITHM_RSA) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Only RSA algorithm supported.\n"); return SC_ERROR_NOT_SUPPORTED; } if (!(env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1)){ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Card requires RSA padding\n"); return SC_ERROR_NOT_SUPPORTED; } tmp.algorithm_ref = 0x02; /* potential FIXME: return an error, if an unsupported * pad or hash was requested, although this shouldn't happen. */ if (tmp.algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) tmp.algorithm_ref |= 0x10; if (tmp.algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5) tmp.algorithm_ref |= 0x20; memcpy(env, &tmp, sizeof(struct sc_security_env)); } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xC1, 0); switch (env->operation) { case SC_SEC_OPERATION_DECIPHER: apdu.p2 = 0xB8; break; case SC_SEC_OPERATION_SIGN: apdu.p2 = 0xB6; break; default: return SC_ERROR_INVALID_ARGUMENTS; } apdu.le = 0; if (!env->flags & SC_SEC_ENV_ALG_REF_PRESENT) return SC_ERROR_INVALID_ARGUMENTS; if (!env->flags & SC_SEC_ENV_FILE_REF_PRESENT) return SC_ERROR_INVALID_ARGUMENTS; if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { if (env->key_ref_len > 1 || env->key_ref[0] != 0) return SC_ERROR_INVALID_ARGUMENTS; } p = sbuf; *p++ = 0x80; /* algorithm reference */ *p++ = 0x01; *p++ = env->algorithm_ref & 0xFF; *p++ = 0x81; *p++ = env->file_ref.len; memcpy(p, env->file_ref.value, env->file_ref.len); p += env->file_ref.len; r = p - sbuf; apdu.lc = r; apdu.datalen = r; apdu.data = sbuf; apdu.resplen = 0; r = sc_transmit_apdu(card, &apdu); if (r) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s: APDU transmit failed", sc_strerror(r)); return r; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s: Card returned error", sc_strerror(r)); return r; } drvdata->invalid_senv=0; return 0; } static int jcop_compute_signature(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { int r; sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; struct jcop_private_data *drvdata=DRVDATA(card); assert(card != NULL && data != NULL && out != NULL); if (datalen > 256) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); if (drvdata->invalid_senv) return sc_check_sw(card, 0x69, 0x88); /* INS: 0x2A PERFORM SECURITY OPERATION * P1: 0x9E Resp: Digital Signature * P2: 0x9A Cmd: Input for Digital Signature */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); /* FIXME */ apdu.le = 256; if (datalen == 256) { apdu.p2 = data[0]; memcpy(sbuf, data+1, datalen-1); apdu.lc = datalen - 1; apdu.datalen = datalen - 1; } else { memcpy(sbuf, data, datalen); apdu.lc = datalen; apdu.datalen = datalen; } apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { int len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } static int jcop_decipher(sc_card_t *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen) { int r; sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; struct jcop_private_data *drvdata=DRVDATA(card); assert(card != NULL && crgram != NULL && out != NULL); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); if (crgram_len > 256) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); if (drvdata->invalid_senv) return sc_check_sw(card, 0x69, 0x88); /* INS: 0x2A PERFORM SECURITY OPERATION * P1: 0x80 Resp: Plain value * P2: 0x86 Cmd: Padding indicator byte followed by cryptogram */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); /* FIXME */ apdu.le = crgram_len; if (crgram_len == 256) { apdu.p2 = crgram[0]; memcpy(sbuf, crgram+1, crgram_len-1); apdu.lc = crgram_len - 1; apdu.datalen = crgram_len -1; } else { sbuf[0] = 0; /* padding indicator byte, 0x00 = No further indication */ memcpy(sbuf + 1, crgram, crgram_len); apdu.lc = crgram_len + 1; apdu.datalen = crgram_len + 1; } apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { int len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } static int jcop_generate_key(sc_card_t *card, struct sc_cardctl_jcop_genkey *a) { int modlen; int r; sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 *p; int is_f4; struct jcop_private_data *drvdata=DRVDATA(card); if (drvdata->selected == SELECT_MF || drvdata->selected == SELECT_EFDIR ) return sc_check_sw(card, 0x6A, 0x82); is_f4=0; if (a->exponent == 0x10001) { is_f4=1; } else if (a->exponent != 3) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s: Invalid exponent", sc_strerror(SC_ERROR_NOT_SUPPORTED)); return SC_ERROR_NOT_SUPPORTED; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xC1, 0xB6); p = sbuf; *p++ = 0x80; /* algorithm reference */ *p++ = 0x01; *p++ = is_f4 ? 0x6E : 0x6D; *p++ = 0x81; *p++ = a->pub_file_ref.len; memcpy(p, a->pub_file_ref.value, a->pub_file_ref.len); p += a->pub_file_ref.len; *p++ = 0x81; *p++ = a->pri_file_ref.len; memcpy(p, a->pri_file_ref.value, a->pri_file_ref.len); p += a->pri_file_ref.len; r = p - sbuf; apdu.lc = r; apdu.datalen = r; apdu.data = sbuf; apdu.resplen = 0; r = sc_transmit_apdu(card, &apdu); if (r) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s: APDU transmit failed", sc_strerror(r)); return r; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s: Card returned error", sc_strerror(r)); return r; } sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x46, 0, 0); apdu.le = 256; apdu.resp=rbuf; apdu.resplen = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); if (r) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s: APDU transmit failed", sc_strerror(r)); return r; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s: Card returned error", sc_strerror(r)); return r; } if (rbuf[0] != 0x4) { return SC_ERROR_INVALID_DATA; } modlen=rbuf[1] * 32; if (a->pubkey_len < rbuf[1]) return SC_ERROR_BUFFER_TOO_SMALL; a->pubkey_len=rbuf[1] * 4; memcpy(a->pubkey, &rbuf[2], a->pubkey_len); return 0; } static int jcop_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { switch (cmd) { case SC_CARDCTL_GET_DEFAULT_KEY: return jcop_get_default_key(card, (struct sc_cardctl_default_key *) ptr); case SC_CARDCTL_JCOP_GENERATE_KEY: return jcop_generate_key(card, (struct sc_cardctl_jcop_genkey *) ptr); } return SC_ERROR_NOT_SUPPORTED; } static struct sc_card_driver * sc_get_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); jcop_ops = *iso_drv->ops; jcop_ops.match_card = jcop_match_card; jcop_ops.init = jcop_init; jcop_ops.finish = jcop_finish; /* no record oriented file services */ jcop_ops.read_record = NULL; jcop_ops.write_record = NULL; jcop_ops.append_record = NULL; jcop_ops.update_record = NULL; jcop_ops.read_binary = jcop_read_binary; jcop_ops.write_binary = jcop_write_binary; jcop_ops.update_binary = jcop_update_binary; jcop_ops.select_file = jcop_select_file; jcop_ops.create_file = jcop_create_file; jcop_ops.delete_file = jcop_delete_file; jcop_ops.list_files = jcop_list_files; jcop_ops.set_security_env = jcop_set_security_env; jcop_ops.compute_signature = jcop_compute_signature; jcop_ops.decipher = jcop_decipher; jcop_ops.process_fci = jcop_process_fci; jcop_ops.card_ctl = jcop_card_ctl; return &jcop_drv; } struct sc_card_driver * sc_get_jcop_driver(void) { return sc_get_driver(); } opensc-0.13.0/src/libopensc/card-itacns.c0000644000015201777760000003205312057406034015131 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 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}, { NULL, NULL, NULL, 0, 0, NULL} }; /* Output debug info */ #define matchdebug(idx, c) do { \ sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, \ "Matching %x against atr[%d] == %x", c, idx, atr[idx]); \ } while(0); /* Check that we are not looking at values beyond the ATR's length. * If we are, then the card does not match. */ #define itacns_atr_l(idx) do {if (idx >= card->atr.len) return 0;} while(0); /* Match byte exactly and increment index. */ #define itacns_atr_match(idx, c) do { \ itacns_atr_l(idx); \ matchdebug(idx, c); \ if (((u8)atr[idx]) != c) return 0; \ idx++; \ } while(0); /* Match masked bits and increment index. */ #define itacns_atr_mmatch(idx, c, mask) do { \ itacns_atr_l(idx); \ if ((((u8)atr[idx]) & mask) != c) return 0; \ idx ++; \ } while(0); /* Macro to access private driver data. */ #define DRVDATA(card) ((itacns_drv_data_t *) card->drv_data) static int itacns_match_cns_card(sc_card_t *card, unsigned int i) { unsigned char *atr = card->atr.value; sc_context_t *ctx; ctx = card->ctx; itacns_atr_match(i, 0x01); /* H7 */ i += 2; /* H8, H9 */ itacns_atr_match(i, 'C'); /* H10 */ itacns_atr_match(i, 'N'); /* H11 */ itacns_atr_match(i, 'S'); /* H12 */ /* H13 */ /* Version byte: h.l, h in the high nibble, l in the low nibble. */ if(card->driver) { DRVDATA(card)->cns_version = atr[i]; } /* Warn if the version is not 1.0. */ if(atr[i] != 0x10) { char version[8]; snprintf(version, sizeof(version), "%d.%d", (atr[i] >> 4) & 0x0f, atr[i] & 0x0f); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "CNS card version %s; no official specifications " "are published. Proceeding anyway.\n", version); } i++; itacns_atr_match(i, 0x31); /* H14 */ itacns_atr_match(i, 0x80); /* H15 */ card->type = SC_CARD_TYPE_ITACNS_CNS; return 1; } static int itacns_match_cie_card(sc_card_t *card, unsigned int i) { unsigned char *atr = card->atr.value; sc_context_t *ctx; ctx = card->ctx; itacns_atr_match(i, 0x02); /* H7 */ itacns_atr_match(i, 'I'); /* H8 */ itacns_atr_match(i, 'T'); /* H9 */ itacns_atr_match(i, 'I'); /* H10 */ itacns_atr_match(i, 'D'); /* H11 */ itacns_atr_match(i, 0x20); /* H12 */ itacns_atr_match(i, 0x20); /* H13 */ itacns_atr_match(i, 0x31); /* H14 */ itacns_atr_match(i, 0x80); /* H15 */ card->type = SC_CARD_TYPE_ITACNS_CIE_V2; return 1; } static int itacns_match_card(sc_card_t *card) { unsigned int i = 0; int r; unsigned char *atr = card->atr.value; int td1_idx; sc_context_t *ctx; ctx = card->ctx; /* Try table first */ r = _sc_match_atr(card, itacns_atrs, &card->type); if(r >= 0) return 1; /* The ATR was not recognized; try to match it according to the official specs. */ /* Check ATR up to byte H6 */ itacns_atr_match(i, 0x3b); /* TS */ itacns_atr_mmatch(i, 0x8f, 0x8f); /* T0 */ /* TA1, TB1, TC1 */ if(atr[1] & 0x40) i++; if(atr[1] & 0x20) i++; if(atr[1] & 0x10) i++; /* TD1 */ td1_idx = i; itacns_atr_mmatch(i, 0x81, 0x8f); /* TA2, TB2, TC2 */ if(atr[td1_idx] & 0x40) i++; if(atr[td1_idx] & 0x20) i++; if(atr[td1_idx] & 0x10) i++; /* TD2 */ itacns_atr_match(i, 0x31); i += 2; /* TA3, TB3 */ itacns_atr_match(i, 0x00); /* H1 */ itacns_atr_match(i, 0x6b); /* H2 */ /* Store interesting data */ if(card->driver) { DRVDATA(card)->ic_manufacturer_code = card->atr.value[i]; DRVDATA(card)->mask_manufacturer_code = card->atr.value[i+1]; DRVDATA(card)->os_version_h = card->atr.value[i+2]; DRVDATA(card)->os_version_l = card->atr.value[i+3]; } i += 4; /* H3, H4, H5, H6 */ /* Check final part. */ if (itacns_match_cns_card(card, i)) return 1; if (itacns_match_cie_card(card, i)) return 1; /* 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)); /* Match ATR again to find the card data. */ itacns_match_card(card); /* Set up algorithm info. */ flags = SC_ALGORITHM_NEED_USAGE | SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_HASHES ; _sc_card_add_rsa_alg(card, 1024, flags, 0); return 0; } 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); SC_FUNC_RETURN(card->ctx, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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)); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xF1, 0); switch (env->operation) { case SC_SEC_OPERATION_DECIPHER: apdu.p2 = 0xB8; break; case SC_SEC_OPERATION_SIGN: apdu.p2 = 0xB6; break; case SC_SEC_OPERATION_AUTHENTICATE: apdu.p2 = 0xA4; 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); SC_FUNC_RETURN(card->ctx, 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; int requested; int r; while(1) { requested = count - already_read; if(requested > ITACNS_MAX_PAYLOAD) requested = ITACNS_MAX_PAYLOAD; r = default_ops->read_binary(card, idx+already_read, &buf[already_read], requested, flags); if(r < 0) return r; already_read += r; if (r == 0 || r < requested || already_read == count) { /* We have finished */ return 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 { list_ops = sc_get_incrypto34_driver()->ops; } 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 (-> cylic fixed files) */ -1 /* DEC */ }; static void parse_sec_attr(sc_file_t *file, const u8 *buf, size_t len) { size_t i; const int *idx; idx = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl; /* acl defaults to 0xFF if unspecified */ for (i = 0; i < 9; i++) { if (idx[i] != -1) { add_acl_entry(file, idx[i], (u8)((i < len) ? buf[i] : 0xFF)); } } } static int 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); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } 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; return &itacns_drv; } struct sc_card_driver * sc_get_itacns_driver(void) { return sc_get_driver(); } opensc-0.13.0/src/libopensc/card-westcos.c0000644000015201777760000010043412057406034015336 00000000000000/* * card-westcos.c: support for westcos card * * Copyright (C) 2009 francois.leblanc@cev-sa.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" #ifdef ENABLE_OPENSSL #include #include #include #include #include #include #endif #ifndef min #define min(a,b) (((a)<(b))?(a):(b)) #endif #define DEFAULT_TRANSPORT_KEY "6f:59:b0:ed:6e:62:46:4a:5d:25:37:68:23:a8:a2:2d" #define JAVACARD (0x01) /* westcos applet on javacard */ #define RSA_CRYPTO_COMPONENT (0x02) /* card component can do crypto */ #define WESTCOS_RSA_NO_HASH_NO_PAD (0x20) #define WESTCOS_RSA_NO_HASH_PAD_PKCS1 (0x21) #ifdef ENABLE_OPENSSL #define DEBUG_SSL #ifdef DEBUG_SSL static void print_openssl_error(void) { static int charge = 0; long r; if (!charge) { ERR_load_crypto_strings(); charge = 1; } while ((r = ERR_get_error()) != 0) fprintf(stderr, "%s\n", ERR_error_string(r, NULL)); } #endif #endif typedef struct { sc_security_env_t env; sc_autkey_t default_key; int flags; int file_id; } priv_data_t; static const struct sc_card_operations *iso_ops = NULL; static struct sc_card_operations westcos_ops; static struct sc_card_driver westcos_drv = { "WESTCOS compatible cards", "westcos", &westcos_ops, NULL, 0, NULL }; static int westcos_get_default_key(sc_card_t * card, struct sc_cardctl_default_key *data) { const char *default_key; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "westcos_get_default_key:data->method=%d, data->key_ref=%d\n", data->method, data->key_ref); if (data->method != SC_AC_AUT || data->key_ref != 0) return SC_ERROR_NO_DEFAULT_KEY; default_key = scconf_get_str(card->ctx->conf_blocks[0], "westcos_default_key", DEFAULT_TRANSPORT_KEY); return sc_hex_to_bin(default_key, data->key_data, &data->len); } #define CRC_A 1 #define CRC_B 2 static unsigned short westcos_update_crc(unsigned char ch, unsigned short *lpwCrc) { ch = (ch ^ (unsigned char)((*lpwCrc) & 0x00FF)); ch = (ch ^ (ch << 4)); *lpwCrc = (*lpwCrc >> 8) ^ ((unsigned short)ch << 8) ^ ((unsigned short)ch << 3) ^ ((unsigned short) ch >> 4); return (*lpwCrc); } static void westcos_compute_aetb_crc(int CRCType, unsigned char *Data, size_t Length, unsigned char * TransmitFirst, unsigned char * TransmitSecond) { unsigned char chBlock; unsigned short wCrc; switch (CRCType) { case CRC_A: wCrc = 0x6363; /* ITU-V.41 */ break; case CRC_B: wCrc = 0xFFFF; /* ISO 3309 */ break; default: return; } do { chBlock = *Data++; westcos_update_crc(chBlock, &wCrc); } while (--Length); if (CRCType == CRC_B) wCrc = ~wCrc; /* ISO 3309 */ *TransmitFirst = (unsigned char) (wCrc & 0xFF); *TransmitSecond = (unsigned char) ((wCrc >> 8) & 0xFF); return; } static int westcos_check_sw(sc_card_t * card, unsigned int sw1, unsigned int sw2) { if ((sw1 == 0x69) && (sw2 == 0x88)) return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; assert(iso_ops && iso_ops->check_sw); return iso_ops->check_sw(card, sw1, sw2); } static struct sc_atr_table westcos_atrs[] = { /* westcos 2ko */ { "3F:69:00:00:00:64:01:00:00:00:80:90:00", "ff:ff:ff:ff:ff:ff:ff:00:00:00:f0:ff:ff", NULL, 0x00, 0, NULL }, /* westcos applet */ { "3B:95:94:80:1F:C3:80:73:C8:21:13:54", "ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff", NULL, JAVACARD, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } }; static int westcos_finish(sc_card_t * card) { if (card->algorithms) free(card->algorithms); card->algorithms = NULL; card->algorithm_count = 0; if (card->drv_data) free(card->drv_data); return 0; } static int westcos_match_card(sc_card_t * card) { int i; i = _sc_match_atr(card, westcos_atrs, &card->type); if (i < 0) return 0; /* JAVACARD, look for westcos applet */ if (i == 1) { int r; sc_apdu_t apdu; u8 aid[] = { 0xA0, 0x00, 0xCE, 0x00, 0x07, 0x01 }; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x04, 0); apdu.cla = 0x00; apdu.lc = sizeof(aid); apdu.datalen = sizeof(aid); apdu.data = aid; r = sc_transmit_apdu(card, &apdu); if (r) return 0; r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) return 0; } return 1; } static int westcos_init(sc_card_t * card) { int r; const char *default_key; unsigned long exponent, flags; priv_data_t *priv_data; if (card == NULL) return SC_ERROR_INVALID_ARGUMENTS; card->drv_data = malloc(sizeof(priv_data_t)); if (card->drv_data == NULL) return SC_ERROR_OUT_OF_MEMORY; memset(card->drv_data, 0, sizeof(card->drv_data)); priv_data = (priv_data_t *) card->drv_data; if (card->type & JAVACARD) { priv_data->flags |= JAVACARD; } /* check for crypto component */ if(card->atr.value[9] == 0xD0) { priv_data->flags |= RSA_CRYPTO_COMPONENT; } card->cla = 0x00; card->max_send_size = 240; card->max_recv_size = 240; exponent = 0; flags = SC_ALGORITHM_RSA_RAW; flags |= SC_ALGORITHM_RSA_HASH_NONE; flags |= SC_ALGORITHM_RSA_PAD_NONE | SC_ALGORITHM_RSA_PAD_PKCS1; flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; _sc_card_add_rsa_alg(card, 128, flags, exponent); _sc_card_add_rsa_alg(card, 256, flags, exponent); _sc_card_add_rsa_alg(card, 512, flags, exponent); _sc_card_add_rsa_alg(card, 768, flags, exponent); _sc_card_add_rsa_alg(card, 1024, flags, exponent); _sc_card_add_rsa_alg(card, 1100, flags, exponent); _sc_card_add_rsa_alg(card, 1200, flags, exponent); _sc_card_add_rsa_alg(card, 1300, flags, exponent); _sc_card_add_rsa_alg(card, 1400, flags, exponent); _sc_card_add_rsa_alg(card, 1536, flags, exponent); _sc_card_add_rsa_alg(card, 2048, flags, exponent); default_key = scconf_get_str(card->ctx->conf_blocks[0], "westcos_default_key", DEFAULT_TRANSPORT_KEY); if (default_key) { priv_data = (priv_data_t *) (card->drv_data); priv_data->default_key.key_reference = 0; priv_data->default_key.key_len = sizeof(priv_data->default_key.key_value); r = sc_hex_to_bin(default_key, priv_data->default_key.key_value, &(priv_data->default_key.key_len)); if (r) return (r); } return 0; } static int westcos_select_file(sc_card_t * card, const sc_path_t * in_path, sc_file_t ** file_out) { priv_data_t *priv_data = (priv_data_t *) card->drv_data; assert(iso_ops && iso_ops->select_file); priv_data->file_id = 0; return iso_ops->select_file(card, in_path, file_out); } static int _westcos2opensc_ac(u8 flag) { if (flag == 0) return SC_AC_NEVER; else if (flag == 1) return SC_AC_CHV; else if (flag == 2) return SC_AC_AUT; else if (flag == 15) return SC_AC_NONE; return SC_AC_UNKNOWN; } static int westcos_process_fci(sc_card_t * card, sc_file_t * file, const u8 * buf, size_t buflen) { sc_context_t *ctx = card->ctx; size_t taglen, len = buflen; const u8 *tag = NULL, *p = buf; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "processing FCI bytes\n"); tag = sc_asn1_find_tag(ctx, p, len, 0x83, &taglen); if (tag != NULL && taglen == 2) { file->id = (tag[0] << 8) | tag[1]; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, " file identifier: 0x%02X%02X\n", tag[0], tag[1]); } tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen); if (tag != NULL && taglen >= 2) { int bytes = (tag[0] << 8) + tag[1]; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, " bytes in file: %d\n", bytes); file->size = bytes; } if (tag == NULL) { tag = sc_asn1_find_tag(ctx, p, len, 0x81, &taglen); if (tag != NULL && taglen >= 2) { int bytes = (tag[0] << 8) + tag[1]; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, " bytes in file: %d\n", bytes); file->size = bytes; } } tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen); if (tag != NULL) { if (taglen > 0) { unsigned char byte = tag[0]; const char *type; file->shareable = 0; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, " shareable: %s\n", (file->shareable) ? "yes" : "no"); file->ef_structure = SC_FILE_EF_UNKNOWN; switch (byte) { case 0x38: type = "DF"; file->type = SC_FILE_TYPE_DF; break; case 0x01: type = "working or internal EF"; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; break; case 0x02: type = "working or internal EF"; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_LINEAR_FIXED; break; case 0x06: type = "working or internal EF"; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_CYCLIC; break; default: type = "unknow"; } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, " type: %s\n", type); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, " EF structure: %d\n", file->ef_structure); } } tag = sc_asn1_find_tag(ctx, p, len, 0x84, &taglen); if (tag != NULL && taglen > 0 && taglen <= 16) { memcpy(file->name, tag, taglen); file->namelen = taglen; { char tbuf[128]; sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, file->name, file->namelen, tbuf, sizeof(tbuf)); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, " File name: %s\n", tbuf); } } if (file->type == SC_FILE_TYPE_DF) { tag = sc_asn1_find_tag(ctx, p, len, 0x85, &taglen); if (tag != NULL && taglen == 3) { file->size = tag[1] * 256 + tag[2]; } else file->size = 0; } tag = sc_asn1_find_tag(ctx, p, len, 0xA5, &taglen); if (tag != NULL && taglen) { sc_file_set_prop_attr(file, tag, taglen); } tag = sc_asn1_find_tag(ctx, p, len, 0x86, &taglen); if (tag != NULL && taglen) { sc_file_set_sec_attr(file, tag, taglen); /* FIXME: compact file system only */ if (file->type == SC_FILE_TYPE_DF) { sc_file_add_acl_entry(file, SC_AC_OP_SELECT, _westcos2opensc_ac(tag[0] >> 4), tag[0 + 4] >> 4); sc_file_add_acl_entry(file, SC_AC_OP_CREATE, _westcos2opensc_ac(tag[0] & 0x0f), tag[0 + 4] & 0x0f); sc_file_add_acl_entry(file, SC_AC_OP_INVALIDATE, _westcos2opensc_ac(tag[1] >> 4), tag[1 + 4] >> 4); } else { if (file->ef_structure == SC_FILE_EF_TRANSPARENT) { sc_file_add_acl_entry(file, SC_AC_OP_READ, _westcos2opensc_ac(tag[0] >> 4), tag[0 + 4] >> 4); sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, _westcos2opensc_ac(tag[0] & 0x0f), tag[0 + 4] & 0x0f); sc_file_add_acl_entry(file, SC_AC_OP_INVALIDATE, _westcos2opensc_ac(tag[1] >> 4), tag[1 + 4] >> 4); sc_file_add_acl_entry(file, SC_AC_OP_ERASE, _westcos2opensc_ac(tag[1] & 0x0f), tag[1 + 4] & 0x0f); } else { sc_file_add_acl_entry(file, SC_AC_OP_READ, _westcos2opensc_ac(tag[0] >> 4), tag[0 + 4] >> 4); sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, _westcos2opensc_ac(tag[0] & 0x0f), tag[0 + 4] & 0x0f); sc_file_add_acl_entry(file, SC_AC_OP_INVALIDATE, _westcos2opensc_ac(tag[1] >> 4), tag[1 + 4] >> 4); } } } return 0; } #define HIGH (0) #define LOW (1) static int _convertion_ac_methode(sc_file_t * file, int low, unsigned int operation, u8 * buf, u8 * buf_key) { const struct sc_acl_entry *acl; acl = sc_file_get_acl_entry(file, operation); if (acl == NULL) { /* par defaut always */ *buf = 0xff; *buf_key = 0x00; return 0; } switch (acl->method) { case SC_AC_NONE: if (low) *buf |= 0x0f; else *buf |= 0xf0; break; case SC_AC_CHV: /* Card Holder Verif. */ if (low) *buf |= 0x01; else *buf |= 0x10; break; case SC_AC_TERM: /* Terminal auth. */ return SC_ERROR_NOT_SUPPORTED; case SC_AC_PRO: /* Secure Messaging */ return SC_ERROR_NOT_SUPPORTED; case SC_AC_AUT: /* Key auth. */ if (low) *buf |= 0x02; else *buf |= 0x20; if (acl->key_ref > 15) return SC_ERROR_NOT_SUPPORTED; if (low) *buf_key |= acl->key_ref; else *buf_key |= (acl->key_ref) << 4; break; case SC_AC_NEVER: *buf |= 0; break; default: return SC_ERROR_NOT_SUPPORTED; } return 0; } static int westcos_create_file(sc_card_t *card, struct sc_file *file) { int r; sc_apdu_t apdu; u8 buf[12], p1 = 0, p2 = 0; int buflen; if (card == NULL) return SC_ERROR_INVALID_ARGUMENTS; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "westcos_create_file\n"); memset(buf, 0, sizeof(buf)); /* transport key */ r = sc_card_ctl(card, SC_CARDCTL_WESTCOS_AUT_KEY, NULL); if (r) return (r); buflen = sizeof(buf); switch (file->type) { case SC_FILE_TYPE_DF: buf[0] = 0x00; buf[1] = 0x01; _convertion_ac_methode(file, HIGH, SC_AC_OP_SELECT, &buf[2], &buf[2 + 4]); _convertion_ac_methode(file, LOW, SC_AC_OP_CREATE, &buf[2], &buf[2 + 4]); _convertion_ac_methode(file, HIGH, SC_AC_OP_INVALIDATE, &buf[3], &buf[3 + 4]); buflen = 10; break; case SC_FILE_TYPE_INTERNAL_EF: buf[0] |= 0x80; case SC_FILE_TYPE_WORKING_EF: switch (file->ef_structure) { case SC_FILE_EF_TRANSPARENT: buf[0] |= 0x20; /* no transaction support */ buf[1] |= 0; _convertion_ac_methode(file, HIGH, SC_AC_OP_READ, &buf[2], &buf[2 + 4]); _convertion_ac_methode(file, LOW, SC_AC_OP_UPDATE, &buf[2], &buf[2 + 4]); _convertion_ac_methode(file, HIGH, SC_AC_OP_INVALIDATE, &buf[3], &buf[3 + 4]); _convertion_ac_methode(file, LOW, SC_AC_OP_ERASE, &buf[3], &buf[3 + 4]); buf[10] = (u8) ((file->size) / 256); buf[11] = (u8) ((file->size) % 256); break; case SC_FILE_EF_LINEAR_FIXED: buf[0] |= 0x40; /* no transaction support */ buf[1] |= 0; _convertion_ac_methode(file, HIGH, SC_AC_OP_READ, &buf[2], &buf[2 + 4]); _convertion_ac_methode(file, LOW, SC_AC_OP_UPDATE, &buf[2], &buf[2 + 4]); _convertion_ac_methode(file, HIGH, SC_AC_OP_INVALIDATE, &buf[3], &buf[3 + 4]); buf[10] = file->record_count; buf[11] = file->record_length; break; case SC_FILE_EF_CYCLIC: buf[0] |= 0x60; /* no transaction support */ buf[1] |= 0; _convertion_ac_methode(file, HIGH, SC_AC_OP_READ, &buf[2], &buf[2 + 4]); _convertion_ac_methode(file, LOW, SC_AC_OP_UPDATE, &buf[2], &buf[2 + 4]); _convertion_ac_methode(file, HIGH, SC_AC_OP_INVALIDATE, &buf[3], &buf[3 + 4]); buf[10] = file->record_count; buf[11] = file->record_length; break; case SC_FILE_EF_LINEAR_VARIABLE: case SC_FILE_EF_UNKNOWN: case SC_FILE_EF_LINEAR_FIXED_TLV: case SC_FILE_EF_LINEAR_VARIABLE_TLV: case SC_FILE_EF_CYCLIC_TLV: default: return SC_ERROR_NOT_SUPPORTED; } break; default: return SC_ERROR_NOT_SUPPORTED; } if (file->shareable) buf[0] |= 0x08; if (file->path.len >= 2) { p1 = file->path.value[file->path.len - 2]; p2 = file->path.value[file->path.len - 1]; } else if (file->id) { p1 = (file->id) / 256; p2 = (file->id) % 256; } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "create file %s, id %X size %d\n", file->path.value, file->id, file->size); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, p1, p2); apdu.cla = 0x80; apdu.lc = buflen; apdu.datalen = buflen; apdu.data = buf; r = sc_transmit_apdu(card, &apdu); if (r) return (r); r = sc_check_sw(card, apdu.sw1, apdu.sw2); return r; } static int westcos_delete_file(sc_card_t * card, const sc_path_t * path_in) { int r; sc_apdu_t apdu; if (card == NULL || path_in == NULL || path_in->len < 2) return SC_ERROR_INVALID_ARGUMENTS; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "westcos_delete_file\n"); if (path_in->len > 2) { r = sc_select_file(card, path_in, NULL); if (r) return (r); } sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xE4, path_in->value[path_in->len - 2], path_in->value[path_in->len - 1]); apdu.cla = 0x80; r = sc_transmit_apdu(card, &apdu); if (r) return (r); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) return (r); return 0; } static int westcos_list_files(sc_card_t * card, u8 * buf, size_t buflen) { int r; sc_apdu_t apdu; if (card == NULL) return SC_ERROR_INVALID_ARGUMENTS; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "westcos_list_files\n"); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x34, 0x00, 0x00); apdu.cla = 0x80; apdu.le = buflen; apdu.resplen = buflen; apdu.resp = buf; r = sc_transmit_apdu(card, &apdu); if (r) return (r); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) return (r); return apdu.resplen; } static int westcos_get_crypte_challenge(sc_card_t * card, const u8 * key, u8 * result, size_t * len) { int r; #ifdef ENABLE_OPENSSL DES_key_schedule ks1, ks2; #endif u8 buf[8]; if ((*len) < sizeof(buf)) return SC_ERROR_INVALID_ARGUMENTS; *len = 8; r = sc_get_challenge(card, buf, *len); if (r) return r; #ifdef ENABLE_OPENSSL DES_set_key((const_DES_cblock *) & key[0], &ks1); DES_set_key((const_DES_cblock *) & key[8], &ks2); DES_ecb2_encrypt((const_DES_cblock *)buf, (DES_cblock*)result, &ks1, &ks2, DES_ENCRYPT); return SC_SUCCESS; #else return SC_ERROR_NOT_SUPPORTED; #endif } static int westcos_pin_cmd(sc_card_t * card, struct sc_pin_cmd_data *data, int *tries_left) { int r; u8 buf[20]; sc_apdu_t apdu; size_t len = 0; int pad = 0, use_pin_pad = 0, ins, p1 = 0; if (card == NULL) return SC_ERROR_INVALID_ARGUMENTS; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "westcos_pin_cmd:data->pin_type=%X, data->cmd=%X\n", data->pin_type, data->cmd); if (tries_left) *tries_left = -1; switch (data->pin_type) { case SC_AC_AUT: len = sizeof(buf); r = westcos_get_crypte_challenge(card, data->pin1.data, buf, &len); if (r) return (r); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x82, 0x00, data->pin_reference); apdu.lc = len; apdu.datalen = len; apdu.data = buf; r = sc_transmit_apdu(card, &apdu); if (r) return (r); return sc_check_sw(card, apdu.sw1, apdu.sw2); break; case SC_AC_CHV: if (data->flags & SC_PIN_CMD_NEED_PADDING) pad = 1; if (data->flags & SC_PIN_CMD_USE_PINPAD) use_pin_pad = 1; data->pin1.offset = 0; data->pin1.encoding = SC_PIN_ENCODING_GLP; if (data->pin1.min_length == 0) data->pin1.min_length = 4; if (data->pin1.max_length == 0) data->pin1.max_length = 12; switch (data->cmd) { case SC_PIN_CMD_VERIFY: ins = 0x20; if ((r = sc_build_pin(buf, sizeof(buf), &data->pin1, pad)) < 0) return r; len = r; break; case SC_PIN_CMD_CHANGE: ins = 0x24; if (data->pin1.len != 0 || use_pin_pad) { if ((r = sc_build_pin(buf, sizeof(buf), &data->pin1, pad)) < 0) return r; len += r; } else { /* implicit test */ p1 = 1; } data->pin2.offset = data->pin1.offset + len; data->pin2.encoding = SC_PIN_ENCODING_GLP; if ((r = sc_build_pin(buf + len, sizeof(buf) - len, &data->pin2, pad)) < 0) return r; len += r; break; case SC_PIN_CMD_UNBLOCK: ins = 0x2C; if (data->pin1.len != 0 || use_pin_pad) { if ((r = sc_build_pin(buf, sizeof(buf), &data->pin1, pad)) < 0) return r; len += r; } else { p1 |= 0x02; } if (data->pin2.len != 0 || use_pin_pad) { data->pin2.offset = data->pin1.offset + len; data->pin2.encoding = SC_PIN_ENCODING_GLP; if ((r = sc_build_pin(buf + len, sizeof(buf) - len, &data->pin2, pad)) < 0) return r; len += r; } else { p1 |= 0x01; } break; default: return SC_ERROR_NOT_SUPPORTED; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, ins, p1, data->pin_reference); apdu.lc = len; apdu.datalen = len; apdu.data = buf; apdu.resplen = 0; if (!use_pin_pad) { /* Transmit the APDU to the card */ r = sc_transmit_apdu(card, &apdu); /* Clear the buffer - it may contain pins */ sc_mem_clear(buf, sizeof(buf)); } else { data->apdu = &apdu; if (card->reader && card->reader->ops && card->reader->ops->perform_verify) { r = card->reader->ops->perform_verify(card-> reader, data); } else { r = SC_ERROR_NOT_SUPPORTED; } } if (r) return (r); return sc_check_sw(card, apdu.sw1, apdu.sw2); default: return SC_ERROR_NOT_SUPPORTED; } } static int sc_get_atr(sc_card_t * card) { int r; sc_apdu_t apdu; u8 buf[sizeof(card->atr.value)]; if (card == NULL) return SC_ERROR_INVALID_ARGUMENTS; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xEC, 0x00, 0x00); apdu.cla = 0x80; apdu.le = 0x0d; apdu.resplen = 0x0d; apdu.resp = buf; r = sc_transmit_apdu(card, &apdu); if (r) return (r); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) return (r); memcpy(card->atr.value, buf, sizeof(card->atr.value)); card->atr.len = apdu.resplen; return r; } static int sc_lock_phase(sc_card_t * card, u8 phase) { int r; sc_apdu_t apdu; if (card == NULL) return SC_ERROR_INVALID_ARGUMENTS; sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x16, phase, 0x00); apdu.cla = 0x80; r = sc_transmit_apdu(card, &apdu); if (r) return (r); return sc_check_sw(card, apdu.sw1, apdu.sw2); } static int westcos_card_ctl(sc_card_t * card, unsigned long cmd, void *ptr) { unsigned int i; int r; size_t buflen; u8 buf[256]; sc_apdu_t apdu; struct sc_pin_cmd_data data; sc_serial_number_t *serialnr; priv_data_t *priv_data = NULL; if (card == NULL) return SC_ERROR_INVALID_ARGUMENTS; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "westcos_card_ctl cmd = %X\n", cmd); priv_data = (priv_data_t *) card->drv_data; switch (cmd) { case SC_CARDCTL_GET_DEFAULT_KEY: return westcos_get_default_key(card, (struct sc_cardctl_default_key *)ptr); break; case SC_CARDCTL_LIFECYCLE_SET: if (1) { int mode = *((int *)ptr); switch (mode) { case SC_CARDCTRL_LIFECYCLE_ADMIN: if (priv_data->flags & JAVACARD) { return 0; } if (card->atr.value[10] == 0x80 || card->atr.value[10] == 0x81) return 0; return SC_ERROR_CARD_CMD_FAILED; case SC_CARDCTRL_LIFECYCLE_USER: if (card->atr.value[10] == 0x80) { r = sc_lock_phase(card, 0x02); if (r) return (r); r = sc_get_atr(card); if (r) return (r); r = sc_card_ctl(card, SC_CARDCTL_WESTCOS_AUT_KEY, NULL); if (r) return (r); } if (card->atr.value[10] == 0x81) { r = sc_lock_phase(card, 0x01); if (r) return (r); r = sc_get_atr(card); if (r) return (r); return 0; } return SC_ERROR_CARD_CMD_FAILED; case SC_CARDCTRL_LIFECYCLE_OTHER: default: break; } } break; case SC_CARDCTL_GET_SERIALNR: sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xEE, 0x00, 0x00); apdu.cla = 0xb0; apdu.le = 8; apdu.resp = buf; apdu.resplen = 10; /* include SW's */ r = sc_transmit_apdu(card, &apdu); if (r) return (r); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) return (r); if (SC_MAX_SERIALNR < 8) return SC_ERROR_NOT_SUPPORTED; serialnr = (sc_serial_number_t *) ptr; serialnr->len = 8; memcpy(serialnr->value, buf, serialnr->len); return 0; case SC_CARDCTL_WESTCOS_CREATE_MF: buf[0] = *((u8 *) ptr); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x3F, 0x00); apdu.cla = 0x80; apdu.lc = 1; apdu.datalen = 1; apdu.data = buf; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); if (r) return (r); return sc_check_sw(card, apdu.sw1, apdu.sw2); case SC_CARDCTL_WESTCOS_COMMIT: sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2C, 0x00, 0x00); apdu.cla = 0x80; r = sc_transmit_apdu(card, &apdu); if (r) return (r); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) return (r); return r; case SC_CARDCTL_WESTCOS_ROLLBACK: sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x24, 0x00, 0x00); apdu.cla = 0x80; r = sc_transmit_apdu(card, &apdu); if (r) return (r); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) return (r); return r; case SC_CARDCTL_WESTCOS_AUT_KEY: if (ptr != NULL) priv_data->default_key = *((sc_autkey_t *) ptr); memset(&data, 0, sizeof(data)); data.pin_type = SC_AC_AUT; data.pin_reference = priv_data->default_key.key_reference; data.pin1.len = priv_data->default_key.key_len; data.pin1.data = priv_data->default_key.key_value; return sc_pin_cmd(card, &data, NULL); case SC_CARDCTL_WESTCOS_CHANGE_KEY: if (1) { int lrc; u8 temp[7]; sc_changekey_t *ck = (sc_changekey_t *) ptr; sc_autkey_t master_key; if (ck->master_key.key_len != 0) master_key = ck->master_key; else master_key = priv_data->default_key; memcpy(temp, ck->key_template, sizeof(temp)); westcos_compute_aetb_crc(CRC_A, ck->new_key.key_value, ck->new_key.key_len, &temp[5], &temp[6]); for (i = 0, temp[4] = 0xAA, lrc = 0; i < sizeof(temp); i++) lrc += temp[i]; temp[4] = (lrc % 256); buflen = sizeof(buf); r = westcos_get_crypte_challenge(card, master_key.key_value, buf, &buflen); if (r) return (r); memcpy(&buf[buflen], temp, sizeof(temp)); buflen += sizeof(temp); memcpy(&buf[buflen], ck->new_key.key_value, ck->new_key.key_len); buflen += ck->new_key.key_len; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD8, ck->new_key.key_reference, master_key.key_reference); apdu.cla = 0x80; apdu.lc = buflen; apdu.datalen = buflen; apdu.data = buf; r = sc_transmit_apdu(card, &apdu); if (r) return (r); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) return (r); return r; } case SC_CARDCTL_WESTCOS_SET_DEFAULT_KEY: priv_data->default_key = *((sc_autkey_t *) ptr); return 0; case SC_CARDCTL_WESTCOS_LOAD_DATA: /* ptr[0] = 0x01 pour generique appli, 0x81 pour appli avec pme */ buf[0] = *((u8 *) ptr); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xB2, 0x80, 0x14); apdu.cla = 0xB0; apdu.lc = 1; apdu.datalen = 1; apdu.data = buf; r = sc_transmit_apdu(card, &apdu); if (r) return (r); return sc_check_sw(card, apdu.sw1, apdu.sw2); } return SC_ERROR_NOT_SUPPORTED; } static int westcos_set_security_env(sc_card_t *card, const struct sc_security_env *env, int se_num) { int r = 0; priv_data_t *priv_data = NULL; if (card == NULL) return SC_ERROR_INVALID_ARGUMENTS; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "westcos_set_security_env\n"); priv_data = (priv_data_t *) card->drv_data; priv_data->env = *env; if(priv_data->flags & RSA_CRYPTO_COMPONENT) { sc_apdu_t apdu; unsigned char mode = 0; u8 buf[128]; if ((priv_data->env.flags) & SC_ALGORITHM_RSA_PAD_PKCS1) mode = WESTCOS_RSA_NO_HASH_PAD_PKCS1; else if ((priv_data->env.flags) & SC_ALGORITHM_RSA_RAW) mode = WESTCOS_RSA_NO_HASH_NO_PAD; r = sc_path_print((char *)buf, sizeof(buf), &(env->file_ref)); if(r) return r; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xf0, mode); apdu.cla = 0x00; apdu.lc = strlen((char *)buf); apdu.datalen = apdu.lc; apdu.data = buf; r = sc_transmit_apdu(card, &apdu); if (r) return (r); r = sc_check_sw(card, apdu.sw1, apdu.sw2); } return r; } static int westcos_restore_security_env(sc_card_t *card, int se_num) { if (card == NULL) return SC_ERROR_INVALID_ARGUMENTS; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "westcos_restore_security_env\n"); return 0; } static int westcos_sign_decipher(int mode, sc_card_t *card, const u8 * data, size_t data_len, u8 * out, size_t outlen) { int r; int idx = 0; u8 buf[180]; sc_file_t *keyfile = sc_file_new(); priv_data_t *priv_data = NULL; int pad; #ifdef ENABLE_OPENSSL RSA *rsa = NULL; BIO *mem = BIO_new(BIO_s_mem()); #endif if (card == NULL) return SC_ERROR_INVALID_ARGUMENTS; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "westcos_sign_decipher outlen=%d\n", outlen); priv_data = (priv_data_t *) card->drv_data; if(priv_data->flags & RSA_CRYPTO_COMPONENT) { sc_apdu_t apdu; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x00, mode); apdu.datalen = data_len; apdu.data = data; apdu.lc = data_len; apdu.le = outlen > 240 ? 240 : outlen; apdu.resp = out; apdu.resplen = outlen; r = sc_transmit_apdu(card, &apdu); if (r) goto out2; r = sc_check_sw(card, apdu.sw1, apdu.sw2); if(r) goto out2; /* correct */ r = apdu.resplen; goto out2; } #ifndef ENABLE_OPENSSL r = SC_ERROR_NOT_SUPPORTED; #else if (keyfile == NULL || mem == NULL || priv_data == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto out; } if ((priv_data->env.flags) & SC_ALGORITHM_RSA_PAD_PKCS1) pad = RSA_PKCS1_PADDING; else if ((priv_data->env.flags) & SC_ALGORITHM_RSA_RAW) pad = RSA_NO_PADDING; else { r = SC_ERROR_INVALID_ARGUMENTS; goto out; } r = sc_select_file(card, &(priv_data->env.file_ref), &keyfile); if (r) goto out; do { int alire; alire = min(((keyfile->size) - idx), sizeof(buf)); if (alire <= 0) break; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "idx = %d, alire=%d\n", idx, alire); r = sc_read_binary(card, idx, buf, alire, 0); if (r < 0) goto out; BIO_write(mem, buf, r); idx += r; } while (1); BIO_set_mem_eof_return(mem, -1); if (!d2i_RSAPrivateKey_bio(mem, &rsa)) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "RSA key invalid, %d\n", ERR_get_error()); r = SC_ERROR_UNKNOWN; goto out; } /* pkcs11 reset openssl functions */ rsa->meth = RSA_PKCS1_SSLeay(); if ((size_t)RSA_size(rsa) > outlen) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Buffer too small\n"); r = SC_ERROR_OUT_OF_MEMORY; goto out; } #if 1 if (mode) { /* decipher */ r = RSA_private_decrypt(data_len, data, out, rsa, pad); if (r == -1) { #ifdef DEBUG_SSL print_openssl_error(); #endif sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Decipher error %d\n", ERR_get_error()); r = SC_ERROR_UNKNOWN; goto out; } } else { /* sign */ r = RSA_private_encrypt(data_len, data, out, rsa, pad); if (r == -1) { #ifdef DEBUG_SSL print_openssl_error(); #endif sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Signature error %d\n", ERR_get_error()); r = SC_ERROR_UNKNOWN; goto out; } } #else if (RSA_sign(nid, data, data_len, out, &outlen, rsa) != 1) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "RSA_sign error %d \n", ERR_get_error()); r = SC_ERROR_UNKNOWN; goto out; } r = outlen; #endif out: if (mem) BIO_free(mem); if (rsa) RSA_free(rsa); #endif /* ENABLE_OPENSSL */ out2: if (keyfile) sc_file_free(keyfile); return r; } static int westcos_compute_signature(sc_card_t *card, const u8 * data, size_t data_len, u8 * out, size_t outlen) { return westcos_sign_decipher(0, card, data, data_len, out, outlen); } static int westcos_decipher(sc_card_t *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen) { return westcos_sign_decipher(1, card, crgram, crgram_len, out, outlen); } struct sc_card_driver *sc_get_westcos_driver(void) { if (iso_ops == NULL) iso_ops = sc_get_iso7816_driver()->ops; westcos_ops = *iso_ops; westcos_ops.match_card = westcos_match_card; westcos_ops.init = westcos_init; westcos_ops.finish = westcos_finish; /* read_binary */ /* write_binary */ /* update_binary */ /* read_record */ /* write_record */ /* append_record */ /* update_record */ westcos_ops.select_file = westcos_select_file; /* get_response */ /* get_challenge */ westcos_ops.restore_security_env = westcos_restore_security_env; westcos_ops.set_security_env = westcos_set_security_env; westcos_ops.decipher = westcos_decipher; westcos_ops.compute_signature = westcos_compute_signature; westcos_ops.create_file = westcos_create_file; westcos_ops.delete_file = westcos_delete_file; westcos_ops.list_files = westcos_list_files; westcos_ops.check_sw = westcos_check_sw; westcos_ops.card_ctl = westcos_card_ctl; westcos_ops.process_fci = westcos_process_fci; westcos_ops.construct_fci = NULL; westcos_ops.pin_cmd = westcos_pin_cmd; return &westcos_drv; } opensc-0.13.0/src/libopensc/card-tcos.c0000644000015201777760000006223612057406034014626 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" static struct sc_atr_table tcos_atrs[] = { /* Infineon SLE44 */ { "3B:BA:13:00:81:31:86:5D:00:64:05:0A:02:01:31:80:90:00:8B", NULL, NULL, SC_CARD_TYPE_TCOS_V2, 0, NULL }, /* Infineon SLE66S */ { "3B:BA:14:00:81:31:86:5D:00:64:05:14:02:02:31:80:90:00:91", NULL, NULL, SC_CARD_TYPE_TCOS_V2, 0, NULL }, /* Infineon SLE66CX320P */ { "3B:BA:96:00:81:31:86:5D:00:64:05:60:02:03:31:80:90:00:66", NULL, NULL, SC_CARD_TYPE_TCOS_V2, 0, NULL }, /* Infoneon SLE66CX322P */ { "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 }, /* 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 int 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) { if (file->namelen > 16 || !file->name) return SC_ERROR_INVALID_ARGUMENTS; 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown security command byte %02x\n", buf[0]); continue; } if (!buf[1]) sc_file_add_acl_entry (file, op, 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; unsigned int i; int r, 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; 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r || file_out == NULL) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); if (apdu.resplen < 1 || apdu.resp[0] != 0x62){ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "received invalid template %02X\n", apdu.resp[0]); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED); } file = sc_file_new(); if (file == NULL) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); file->path = *in_path; for(i=2; i+1size=0; for(j=0; jsize = (file->size<<8) | d[j]; break; case 0x82: file->shareable = (d[0] & 0x40) ? 1 : 0; file->ef_structure = d[0] & 7; switch ((d[0]>>3) & 7) { case 0: file->type = SC_FILE_TYPE_WORKING_EF; break; case 7: file->type = SC_FILE_TYPE_DF; break; default: sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "invalid file type %02X in file descriptor\n", d[0]); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED); } break; case 0x83: file->id = (d[0]<<8) | d[1]; break; case 0x84: memcpy(file->name, d, len); file->namelen = len; break; case 0x86: sc_file_set_sec_attr(file, d, len); break; default: if (len>0) sc_file_set_prop_attr(file, d, len); } } file->magic = SC_FILE_MAGIC; *file_out = file; 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); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1==0x6A && (apdu.sw2==0x82 || apdu.sw2==0x88)) continue; r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "List Dir failed"); if (apdu.resplen > buflen) return SC_ERROR_BUFFER_TOO_SMALL; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "got %d %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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File type has to be SC_PATH_TYPE_FILE_ID\n"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); } 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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)){ SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); } if(!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT)) sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "No Key-Reference in SecEnvironment\n"); else sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Key-Reference %02X (len=%d)\n", env->key_ref[0], env->key_ref_len); /* Key-Reference 0x80 ?? */ default_key= !(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || (env->key_ref_len==1 && env->key_ref[0]==0x80); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "TCOS3:%d PKCS1:%d\n", tcos3, !!(env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1)); data->pad_flags = env->algorithm_flags; data->next_sign = default_key; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, tcos3 ? 0x41 : 0xC1, 0xB8); p = sbuf; *p++=0x80; *p++=0x01; *p++=tcos3 ? 0x0A : 0x10; if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { *p++ = (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) ? 0x83 : 0x84; *p++ = env->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_debug(ctx, SC_LOG_DEBUG_NORMAL, "%s: APDU transmit failed", sc_strerror(r)); return r; } if (apdu.sw1==0x6A && (apdu.sw2==0x81 || apdu.sw2==0x88)) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "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 i, dlen=datalen; sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; int tcos3, r; assert(card != NULL && data != NULL && out != NULL); tcos3=(card->type==SC_CARD_TYPE_TCOS_V3); if (datalen > 255) 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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 { int keylen= tcos3 ? 256 : 128; sc_format_apdu(card, &apdu, keylen>255 ? SC_APDU_CASE_4_EXT : SC_APDU_CASE_4_SHORT, 0x2A,0x80,0x86); for(i=0; ictx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (tcos3 && apdu.p1==0x80 && apdu.sw1==0x6A && apdu.sw2==0x87) { int keylen=128; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A,0x80,0x86); for(i=0; ictx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); } if (apdu.sw1==0x90 && apdu.sw2==0x00) { size_t len = apdu.resplen>outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 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; assert(card != NULL && crgram != NULL && out != NULL); ctx = card->ctx; tcos3=(card->type==SC_CARD_TYPE_TCOS_V3); data=(tcos_data *)card->drv_data; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "TCOS3:%d PKCS1:%d\n",tcos3, !!(data->pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1)); sc_format_apdu(card, &apdu, crgram_len>255 ? SC_APDU_CASE_4_EXT : SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = crgram_len; apdu.data = sbuf; apdu.lc = apdu.datalen = crgram_len+1; sbuf[0] = tcos3 ? 0x00 : ((data->pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1) ? 0x81 : 0x02); memcpy(sbuf+1, crgram, crgram_len); r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1==0x90 && apdu.sw2==0x00) { size_t len= (apdu.resplen>outlen) ? outlen : apdu.resplen; unsigned int offset=0; if(tcos3 && (data->pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1) && apdu.resp[0]==0 && apdu.resp[1]==2){ offset=2; while(offsetctx, SC_LOG_DEBUG_VERBOSE, len-offset); } SC_FUNC_RETURN(card->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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } static int tcos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) { int r; u8 buf[64]; size_t len; sc_path_t tpath; sc_file_t *tfile = NULL; if (!serial) return SC_ERROR_INVALID_ARGUMENTS; /* see if we have cached serial number */ if (card->serialnr.len) { memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } sc_format_path("3F002F02", &tpath); r = sc_select_file(card, &tpath, &tfile); if (r < 0) return r; len = tfile->size; sc_file_free(tfile); if (len > sizeof(buf) || len < 12) return SC_ERROR_INTERNAL; r = sc_read_binary(card, 0, buf, len, 0); if (r < 0) return r; if (buf[0] != 0x5a || buf[1] > len - 2) return SC_ERROR_INTERNAL; card->serialnr.len = buf[1]; memcpy(card->serialnr.value, buf+2, buf[1]); 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.set_security_env = tcos_set_security_env; tcos_ops.compute_signature = tcos_compute_signature; tcos_ops.decipher = tcos_decipher; tcos_ops.restore_security_env = tcos_restore_security_env; tcos_ops.card_ctl = tcos_card_ctl; return &tcos_drv; } opensc-0.13.0/src/libopensc/pkcs15-openpgp.c0000644000015201777760000002764412057406034015527 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include "common/compat_strlcpy.h" #include "internal.h" #include "pkcs15.h" #include "log.h" #ifdef _WIN32 typedef USHORT ushort; #endif int sc_pkcs15emu_openpgp_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_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) typedef struct _pgp_pin_cfg { const char *label; int reference; unsigned int flags; int min_length; int do_index; } pgp_pin_cfg_t; /* 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] = { { "Signature PIN", 0x01, PGP_USER_PIN_FLAGS, 6, 0 }, // used for PSO:CDS { "Encryption PIN", 0x02, PGP_USER_PIN_FLAGS, 6, 1 }, // used for PSO:DEC, INT-AUT, {GET,PUT} DATA { "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 (sig)", 0x01, PGP_USER_PIN_FLAGS, 6, 0 }, // used for PSO:CDS { "User PIN", 0x02, PGP_USER_PIN_FLAGS, 6, 0 }, // used for PSO:DEC, INT-AUT, {GET,PUT} DATA { "Admin PIN", 0x03, PGP_ADMIN_PIN_FLAGS, 8, 2 } }; #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; 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 } }; typedef struct _pgp_manuf_map { ushort id; const char *name; } pgp_manuf_map_t; static const pgp_manuf_map_t manuf_map[] = { { 0x0001, "PPC Card Systems" }, { 0x0002, "Prism" }, { 0x0003, "OpenFortress" }, { 0x0004, "Wewid AB" }, { 0x0005, "ZeitControl" }, { 0x002A, "Magrathea" }, { 0xF517, "FSIJ" }, { 0x0000, "test card" }, { 0xffff, "test card" }, { 0, NULL } }; static void set_string(char **strp, const char *value) { if (*strp) free(*strp); *strp = value? strdup(value) : NULL; } /* * 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; 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; char string[256]; u8 c4data[10]; u8 c5data[70]; int r, i; const pgp_pin_cfg_t *pin_cfg = (card->type == SC_CARD_TYPE_OPENPGP_V2) ? pin_cfg_v2 : pin_cfg_v1; sc_path_t path; sc_file_t *file; 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) { ushort manuf_id = bebytes2ushort(card->serialnr.value); int j; sc_bin_to_hex(card->serialnr.value, card->serialnr.len, string, sizeof(string)-1, 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 */ if ((r = read_file(card, "006E:0073:00C4", c4data, sizeof(c4data))) < 0) goto failed; if (r != 7) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "CHV status bytes have unexpected length (expected 7, got %d)\n", r); return SC_ERROR_OBJECT_NOT_VALID; } /* 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] = i + 1; 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]; 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; r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); if (r < 0) return SC_ERROR_INTERNAL; } /* 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 */ if ((r = read_file(card, "006E:0073:00C5", c5data, sizeof(c5data))) < 0) goto failed; if (r != 60) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "finger print bytes have unexpected length (expected 60, got %d)\n", r); return SC_ERROR_OBJECT_NOT_VALID; } /* XXX: check if "halfkeys" can be stored with gpg2. If not, add keypairs in one loop */ for (i = 0; i < 3; i++) { sc_pkcs15_prkey_info_t prkey_info; sc_pkcs15_object_t prkey_obj; u8 cxdata[10]; char path_template[] = "006E:0073:00Cx"; int j; memset(&prkey_info, 0, sizeof(prkey_info)); memset(&prkey_obj, 0, sizeof(prkey_obj)); path_template[13] = '1' + i; /* The needed tags are C1 C2 and C3 */ if ((r = read_file(card, path_template, cxdata, sizeof(cxdata))) < 0) goto failed; if (r != 6) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Key info bytes have unexpected length (expected 6, got %d)\n", r); return SC_ERROR_INTERNAL; } /* check validity using finger prints */ for (j = 19; j >= 0; j--) { 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) { prkey_info.id.len = 1; prkey_info.id.value[0] = i + 1; prkey_info.usage = key_cfg[i].prkey_usage; prkey_info.native = 1; prkey_info.key_reference = i; prkey_info.modulus_length = bebytes2ushort(cxdata + 1); strlcpy(prkey_obj.label, key_cfg[i].label, sizeof(prkey_obj.label)); prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE | SC_PKCS15_CO_FLAG_MODIFIABLE; prkey_obj.auth_id.len = 1; prkey_obj.auth_id.value[0] = key_cfg[i].prkey_pin; r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); if (r < 0) return SC_ERROR_INTERNAL; } } /* Add public keys */ for (i = 0; i < 3; i++) { sc_pkcs15_pubkey_info_t pubkey_info; sc_pkcs15_object_t pubkey_obj; u8 cxdata[10]; char path_template[] = "006E:0073:00Cx"; int j; memset(&pubkey_info, 0, sizeof(pubkey_info)); memset(&pubkey_obj, 0, sizeof(pubkey_obj)); path_template[13] = '1' + i; /* The needed tags are C1 C2 and C3 */ if ((r = read_file(card, path_template, cxdata, sizeof(cxdata))) < 0) goto failed; if (r != 6) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Key info bytes have unexpected length (expected 6, got %d)\n", r); return SC_ERROR_INTERNAL; } /* check validity using finger prints */ for (j = 19; j >= 0; j--) { 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) { pubkey_info.id.len = 1; pubkey_info.id.value[0] = i + 1; pubkey_info.modulus_length = bebytes2ushort(cxdata + 1); pubkey_info.usage = key_cfg[i].pubkey_usage; sc_format_path(key_cfg[i].pubkey_path, &pubkey_info.path); strlcpy(pubkey_obj.label, key_cfg[i].label, sizeof(pubkey_obj.label)); pubkey_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE; r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info); if (r < 0) return SC_ERROR_INTERNAL; } } /* Check if certificate DO 7F21 holds data */ sc_format_path("7F21", &path); r = sc_select_file(card, &path, &file); if (r < 0) goto failed; /* If DO 7F21 holds data, we declare a cert object for pkcs15 */ if (file->size > 0) { 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)); /* Certificate ID. We use the same ID as the authentication key */ cert_info.id.value[0] = 3; cert_info.id.len = 1; /* Authority, flag is zero */ /* The path following which PKCS15 will find the content of the object */ sc_format_path("3F007F21", &cert_info.path); /* Object label */ strlcpy(cert_obj.label, "Cardholder certificate", sizeof(cert_obj.label)); r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); if (r < 0) goto failed; } return 0; failed: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Failed to initialize OpenPGP emulation: %s\n", sc_strerror(r)); return r; } static int openpgp_detect_card(sc_pkcs15_card_t *p15card) { if (p15card->card->type == SC_CARD_TYPE_OPENPGP_V1 || p15card->card->type == SC_CARD_TYPE_OPENPGP_V2) return SC_SUCCESS; else return SC_ERROR_WRONG_CARD; } int sc_pkcs15emu_openpgp_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts) { if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) return sc_pkcs15emu_openpgp_init(p15card); else { int r = openpgp_detect_card(p15card); if (r) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_openpgp_init(p15card); } } opensc-0.13.0/src/libopensc/Makefile.in0000644000015201777760000010064312057406055014646 00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program 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. @SET_MAKE@ # Required to build Windows resource file VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in $(srcdir)/libopensc.pc.in \ $(top_srcdir)/win32/ltrc.inc @WIN32_TRUE@am__append_1 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_2 = -lws2_32 subdir = src/libopensc ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = libopensc.pc CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(mylibdir)" LTLIBRARIES = $(lib_LTLIBRARIES) am__DEPENDENCIES_1 = libopensc_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(top_builddir)/src/pkcs15init/libpkcs15init.la \ $(top_builddir)/src/scconf/libscconf.la \ $(top_builddir)/src/common/libscdl.la \ $(top_builddir)/src/common/libcompat.la $(am__DEPENDENCIES_1) am__libopensc_la_SOURCES_DIST = sc.c ctx.c log.c errors.c asn1.c \ base64.c sec.c card.c iso7816.c dir.c ef-atr.c padding.c \ apdu.c 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 muscle.c \ muscle-filesystem.c ctbcs.c reader-ctapi.c reader-pcsc.c \ reader-openct.c card-setcos.c card-miocos.c card-flex.c \ card-gpk.c card-cardos.c card-tcos.c card-default.c \ card-mcrd.c card-starcos.c card-openpgp.c card-jcop.c \ card-oberthur.c card-belpic.c card-atrust-acos.c \ card-entersafe.c card-epass2003.c card-incrypto34.c card-piv.c \ card-muscle.c card-acos5.c card-asepcos.c card-akis.c \ card-gemsafeV1.c card-rutoken.c card-rtecp.c card-westcos.c \ card-myeid.c card-ias.c card-javacard.c card-itacns.c \ card-authentic.c card-iasecc.c iasecc-sdo.c iasecc-sm.c \ card-sc-hsm.c pkcs15-openpgp.c pkcs15-infocamere.c \ pkcs15-starcert.c pkcs15-tcos.c pkcs15-esteid.c \ pkcs15-postecert.c pkcs15-gemsafeGPK.c pkcs15-actalis.c \ pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \ pkcs15-esinit.c pkcs15-westcos.c pkcs15-pteid.c \ pkcs15-oberthur.c pkcs15-itacns.c pkcs15-gemsafeV1.c \ pkcs15-sc-hsm.c compression.c p15card-helper.c \ libopensc.exports $(top_builddir)/win32/versioninfo.rc am__dirstamp = $(am__leading_dot)dirstamp @WIN32_TRUE@am__objects_1 = $(top_builddir)/win32/versioninfo.lo am_libopensc_la_OBJECTS = sc.lo ctx.lo log.lo errors.lo asn1.lo \ base64.lo sec.lo card.lo iso7816.lo dir.lo ef-atr.lo \ padding.lo apdu.lo pkcs15.lo pkcs15-cert.lo pkcs15-data.lo \ pkcs15-pin.lo pkcs15-prkey.lo pkcs15-pubkey.lo pkcs15-skey.lo \ pkcs15-sec.lo pkcs15-algo.lo pkcs15-cache.lo pkcs15-syn.lo \ muscle.lo muscle-filesystem.lo ctbcs.lo reader-ctapi.lo \ reader-pcsc.lo reader-openct.lo card-setcos.lo card-miocos.lo \ card-flex.lo card-gpk.lo card-cardos.lo card-tcos.lo \ card-default.lo card-mcrd.lo card-starcos.lo card-openpgp.lo \ card-jcop.lo card-oberthur.lo card-belpic.lo \ card-atrust-acos.lo card-entersafe.lo card-epass2003.lo \ card-incrypto34.lo card-piv.lo card-muscle.lo card-acos5.lo \ card-asepcos.lo card-akis.lo card-gemsafeV1.lo card-rutoken.lo \ card-rtecp.lo card-westcos.lo card-myeid.lo card-ias.lo \ card-javacard.lo card-itacns.lo card-authentic.lo \ card-iasecc.lo iasecc-sdo.lo iasecc-sm.lo card-sc-hsm.lo \ pkcs15-openpgp.lo pkcs15-infocamere.lo pkcs15-starcert.lo \ pkcs15-tcos.lo pkcs15-esteid.lo pkcs15-postecert.lo \ pkcs15-gemsafeGPK.lo pkcs15-actalis.lo pkcs15-atrust-acos.lo \ pkcs15-tccardos.lo pkcs15-piv.lo pkcs15-esinit.lo \ pkcs15-westcos.lo pkcs15-pteid.lo pkcs15-oberthur.lo \ pkcs15-itacns.lo pkcs15-gemsafeV1.lo pkcs15-sc-hsm.lo \ compression.lo p15card-helper.lo $(am__objects_1) libopensc_la_OBJECTS = $(am_libopensc_la_OBJECTS) AM_V_lt = $(am__v_lt_$(V)) am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) am__v_lt_0 = --silent libopensc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(libopensc_la_LDFLAGS) $(LDFLAGS) -o $@ DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_$(V)) am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) am__v_CC_0 = @echo " CC " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_$(V)) am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) am__v_CCLD_0 = @echo " CCLD " $@; AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; SOURCES = $(libopensc_la_SOURCES) DIST_SOURCES = $(am__libopensc_la_SOURCES_DIST) DATA = $(mylib_DATA) HEADERS = $(noinst_HEADERS) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUG_FILE = @DEBUG_FILE@ DEFAULT_PCSC_PROVIDER = @DEFAULT_PCSC_PROVIDER@ DEFAULT_SM_MODULE = @DEFAULT_SM_MODULE@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GIT = @GIT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBRARY_BITNESS = @LIBRARY_BITNESS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCT_CFLAGS = @OPENCT_CFLAGS@ OPENCT_LIBS = @OPENCT_LIBS@ OPENSC_LT_AGE = @OPENSC_LT_AGE@ OPENSC_LT_CURRENT = @OPENSC_LT_CURRENT@ OPENSC_LT_OLDEST = @OPENSC_LT_OLDEST@ OPENSC_LT_REVISION = @OPENSC_LT_REVISION@ OPENSC_VERSION_FIX = @OPENSC_VERSION_FIX@ OPENSC_VERSION_MAJOR = @OPENSC_VERSION_MAJOR@ OPENSC_VERSION_MINOR = @OPENSC_VERSION_MINOR@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OPTIONAL_OPENCT_CFLAGS = @OPTIONAL_OPENCT_CFLAGS@ OPTIONAL_OPENCT_LIBS = @OPTIONAL_OPENCT_LIBS@ OPTIONAL_OPENSSL_CFLAGS = @OPTIONAL_OPENSSL_CFLAGS@ OPTIONAL_OPENSSL_LIBS = @OPTIONAL_OPENSSL_LIBS@ OPTIONAL_PCSC_CFLAGS = @OPTIONAL_PCSC_CFLAGS@ OPTIONAL_READLINE_CFLAGS = @OPTIONAL_READLINE_CFLAGS@ OPTIONAL_READLINE_LIBS = @OPTIONAL_READLINE_LIBS@ OPTIONAL_ZLIB_CFLAGS = @OPTIONAL_ZLIB_CFLAGS@ OPTIONAL_ZLIB_LIBS = @OPTIONAL_ZLIB_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCSC_CFLAGS = @PCSC_CFLAGS@ PCSC_LIBS = @PCSC_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RC = @RC@ READLINE_CFLAGS = @READLINE_CFLAGS@ READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ WIN_LIBPREFIX = @WIN_LIBPREFIX@ XSLTPROC = @XSLTPROC@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ git = @git@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkcs11dir = @pkcs11dir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ xslstylesheetsdir = @xslstylesheetsdir@ RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE) MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak lib_LTLIBRARIES = libopensc.la noinst_HEADERS = cards.h ctbcs.h internal.h esteid.h muscle.h muscle-filesystem.h \ internal-winscard.h p15card-helper.h \ opensc.h pkcs15.h \ cardctl.h asn1.h log.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 AM_CPPFLAGS = -DOPENSC_CONF_PATH=\"$(sysconfdir)/opensc.conf\" \ -I$(top_srcdir)/src AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_OPENCT_CFLAGS) \ $(OPTIONAL_PCSC_CFLAGS) $(OPTIONAL_ZLIB_CFLAGS) libopensc_la_SOURCES = sc.c ctx.c log.c errors.c asn1.c base64.c sec.c \ card.c iso7816.c dir.c ef-atr.c padding.c apdu.c 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 muscle.c muscle-filesystem.c \ ctbcs.c reader-ctapi.c reader-pcsc.c reader-openct.c \ card-setcos.c card-miocos.c card-flex.c card-gpk.c \ card-cardos.c card-tcos.c card-default.c card-mcrd.c \ card-starcos.c card-openpgp.c card-jcop.c card-oberthur.c \ card-belpic.c card-atrust-acos.c card-entersafe.c \ card-epass2003.c card-incrypto34.c card-piv.c card-muscle.c \ card-acos5.c card-asepcos.c card-akis.c card-gemsafeV1.c \ card-rutoken.c card-rtecp.c card-westcos.c card-myeid.c \ card-ias.c card-javacard.c card-itacns.c card-authentic.c \ card-iasecc.c iasecc-sdo.c iasecc-sm.c card-sc-hsm.c \ pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \ pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c \ pkcs15-gemsafeGPK.c pkcs15-actalis.c pkcs15-atrust-acos.c \ pkcs15-tccardos.c pkcs15-piv.c pkcs15-esinit.c \ pkcs15-westcos.c pkcs15-pteid.c pkcs15-oberthur.c \ pkcs15-itacns.c pkcs15-gemsafeV1.c pkcs15-sc-hsm.c \ compression.c p15card-helper.c libopensc.exports \ $(am__append_1) libopensc_la_LIBADD = $(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/common/libcompat.la $(am__append_2) libopensc_la_LDFLAGS = $(AM_LDFLAGS) \ -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@ \ -export-symbols "$(srcdir)/libopensc.exports" \ -no-undefined # def file required for MS users to build library @WIN32_TRUE@mylibdir = $(libdir) @WIN32_TRUE@mylib_DATA = .libs/@WIN_LIBPREFIX@opensc-@OPENSC_LT_OLDEST@.dll.def all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj .rc $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/win32/ltrc.inc $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/libopensc/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/libopensc/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): libopensc.pc: $(top_builddir)/config.status $(srcdir)/libopensc.pc.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ test "$$dir" != "$$p" || dir=.; \ echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done $(top_builddir)/win32/$(am__dirstamp): @$(MKDIR_P) $(top_builddir)/win32 @: > $(top_builddir)/win32/$(am__dirstamp) $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) $(top_builddir)/win32/$(DEPDIR) @: > $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp) $(top_builddir)/win32/versioninfo.lo: \ $(top_builddir)/win32/$(am__dirstamp) \ $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp) libopensc.la: $(libopensc_la_OBJECTS) $(libopensc_la_DEPENDENCIES) $(AM_V_CCLD)$(libopensc_la_LINK) -rpath $(libdir) $(libopensc_la_OBJECTS) $(libopensc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f $(top_builddir)/win32/versioninfo.$(OBJEXT) -rm -f $(top_builddir)/win32/versioninfo.lo distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apdu.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-acos5.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-akis.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-asepcos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-atrust-acos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-authentic.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-belpic.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-cardos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-default.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-entersafe.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-epass2003.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-flex.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-gemsafeV1.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-gpk.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-ias.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-iasecc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-incrypto34.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-itacns.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-javacard.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-jcop.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-mcrd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-miocos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-muscle.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-myeid.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-oberthur.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-openpgp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-piv.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-rtecp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-rutoken.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-sc-hsm.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-setcos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-starcos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-tcos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-westcos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compression.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctbcs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctx.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dir.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ef-atr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errors.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iasecc-sdo.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iasecc-sm.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iso7816.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/muscle-filesystem.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/muscle.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p15card-helper.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/padding.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-actalis.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-algo.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-atrust-acos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-cert.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-data.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-esinit.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-esteid.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-gemsafeGPK.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-gemsafeV1.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-infocamere.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-itacns.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-oberthur.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-openpgp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-pin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-piv.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-postecert.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-prkey.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-pteid.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-pubkey.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-sc-hsm.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-sec.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-skey.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-starcert.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-syn.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-tccardos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-tcos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-westcos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reader-ctapi.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reader-openct.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reader-pcsc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sec.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf $(top_builddir)/win32/.libs $(top_builddir)/win32/_libs -rm -rf .libs _libs install-mylibDATA: $(mylib_DATA) @$(NORMAL_INSTALL) test -z "$(mylibdir)" || $(MKDIR_P) "$(DESTDIR)$(mylibdir)" @list='$(mylib_DATA)'; test -n "$(mylibdir)" || list=; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(mylibdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(mylibdir)" || exit $$?; \ done uninstall-mylibDATA: @$(NORMAL_UNINSTALL) @list='$(mylib_DATA)'; test -n "$(mylibdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ test -n "$$files" || exit 0; \ echo " ( cd '$(DESTDIR)$(mylibdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(mylibdir)" && rm -f $$files ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: CTAGS CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(mylibdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -test -z "$(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp)" || rm -f $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp) -test -z "$(top_builddir)/win32/$(am__dirstamp)" || rm -f $(top_builddir)/win32/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-mylibDATA install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-libLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-libLTLIBRARIES uninstall-mylibDATA .MAKE: install-am install-strip .PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ clean-libLTLIBRARIES clean-libtool ctags distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am \ install-libLTLIBRARIES install-man install-mylibDATA \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags uninstall uninstall-am \ uninstall-libLTLIBRARIES uninstall-mylibDATA .rc.lo: $(LTRCCOMPILE) -i "$<" -o "$@" .rc.o: $(RCCOMPILE) -i "$<" -o "$@" @WIN32_TRUE@.libs/@WIN_LIBPREFIX@opensc-@OPENSC_LT_OLDEST@.dll.def: libopensc.la # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: opensc-0.13.0/src/libopensc/asn1.c0000644000015201777760000013667712057406034013624 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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", "OBJECT DESCRIPTOR", "EXTERNAL", "REAL", /* 5-9 */ "ENUMERATED", "", "UTF8STRING", "", /* 10-13 */ "", "", "SEQUENCE", "SET", /* 15-17 */ "NUMERICSTRING", "PRINTABLESTRING", "T61STRING", /* 18-20 */ "VIDEOTEXSTRING", "IA5STRING", "UTCTIME", "GENERALIZEDTIME", /* 21-24 */ "GRAPHICSTRING", "VISIBLESTRING", "GENERALSTRING", /* 25-27 */ "UNIVERSALSTRING", "", "BMPSTRING" /* 28-30 */ }; 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; if (left < 2) return SC_ERROR_INVALID_ASN1_OBJECT; *buf = NULL; if (*p == 0xff || *p == 0) /* end of data reached */ return SC_SUCCESS; /* parse tag byte(s) */ cla = (*p & SC_ASN1_TAG_CLASS) | (*p & SC_ASN1_TAG_CONSTRUCTED); tag = *p & SC_ASN1_TAG_PRIMITIVE; p++; left--; if (tag == SC_ASN1_TAG_PRIMITIVE) { /* high tag number */ size_t n = sizeof(int) - 1; /* search the last tag octet */ while (left-- != 0 && n != 0) { tag <<= 8; tag |= *p; if ((*p++ & 0x80) == 0) break; n--; } if (left == 0 || n == 0) /* either an invalid tag or it doesn't fit in * unsigned int */ return SC_ERROR_INVALID_ASN1_OBJECT; } if (left == 0) return SC_ERROR_INVALID_ASN1_OBJECT; /* parse length byte(s) */ len = *p & 0x7f; if (*p++ & 0x80) { unsigned int a = 0; if (len > 4 || len > left) return SC_ERROR_INVALID_ASN1_OBJECT; left -= len; for (i = 0; i < len; i++) { a <<= 8; a |= *p; p++; } len = a; } if (len > left) return SC_ERROR_INVALID_ASN1_OBJECT; *cla_out = cla; *tag_out = tag; *taglen = len; *buf = p; 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 sc_asn1_print_octet_string(const u8 * buf, size_t buflen) { size_t i; for (i = 0; i < buflen; i++) printf("%02X", buf[i]); } static void sc_asn1_print_utf8string(const u8 * buf, size_t buflen) { size_t i; for (i = 0; i < buflen; i++) printf("%c", buf[i]); } static void sc_asn1_print_integer(const u8 * buf, size_t buflen) { #ifndef _WIN32 long long a = 0; #else __int64 a = 0; #endif size_t i; if (buflen > sizeof(a)) { printf("too long"); return; } for (i = 0; i < buflen; i++) { a <<= 8; a |= buf[i]; } printf("%lld", 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) { #ifndef _WIN32 long long a = 0; #else __int64 a = 0; #endif int r, i; if (buflen > sizeof(a) + 1) { printf("too long"); return; } r = sc_asn1_decode_bit_string(buf, buflen, &a, sizeof(a)); if (r < 0) { printf("decode error"); return; } for (i = r - 1; i >= 0; i--) { printf("%c", ((a >> i) & 1) ? '1' : '0'); } } static void sc_asn1_print_object_id(const u8 * buf, size_t buflen) { struct sc_object_id oid; int i = 0; char sbuf[256]; if (sc_asn1_decode_object_id(buf, buflen, &oid)) { printf("decode error"); return; } sbuf[0] = 0; for (i = 0; (i < SC_MAX_OBJECT_ID_OCTETS) && (oid.value[i] != -1); i++) { char tmp[12]; if (i) strcat(sbuf, "."); sprintf(tmp, "%d", oid.value[i]); strcat(sbuf, tmp); } printf("%s", sbuf); } static void sc_asn1_print_generalizedtime(const u8 * buf, size_t buflen) { size_t ii; for (ii=0; ii= 2) { unsigned int cla = 0, tag = 0, hlen; const u8 *tagp = p; size_t len; r = sc_asn1_read_tag(&tagp, bytesleft, &cla, &tag, &len); if (r != SC_SUCCESS) { printf("Error in decoding.\n"); return; } hlen = tagp - p; if (cla == 0 && tag == 0) { printf("Zero tag, finishing\n"); break; } for (i = 0; i < depth; i++) { putchar(' '); putchar(' '); } printf("%02X %s: tag 0x%02X, length %3d: ", cla | tag, classes[cla >> 6], tag & 0x1f, (int) len); if (len + hlen > bytesleft) { printf(" Illegal length!\n"); return; } p += hlen + len; bytesleft -= hlen + len; if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_UNIVERSAL) printf("%s", tag2str(tag)); if (cla & SC_ASN1_TAG_CONSTRUCTED) { putchar('\n'); print_tags_recursive(buf0, tagp, len, depth + 1); continue; } if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_UNIVERSAL) { printf(" ["); switch (tag) { case SC_ASN1_TAG_BIT_STRING: sc_asn1_print_bit_string(tagp, len); break; case SC_ASN1_TAG_OCTET_STRING: sc_asn1_print_octet_string(tagp, len); break; case SC_ASN1_TAG_OBJECT: sc_asn1_print_object_id(tagp, len); break; case SC_ASN1_TAG_INTEGER: case SC_ASN1_TAG_ENUMERATED: sc_asn1_print_integer(tagp, len); break; case SC_ASN1_TAG_T61STRING: case SC_ASN1_TAG_PRINTABLESTRING: case SC_ASN1_TAG_UTF8STRING: sc_asn1_print_utf8string(tagp, len); break; case SC_ASN1_TAG_BOOLEAN: sc_asn1_print_boolean(tagp, len); break; case SC_ASN1_GENERALIZEDTIME: sc_asn1_print_generalizedtime(tagp, len); break; } printf("]"); } if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_APPLICATION) printf(" [%s]", sc_dump_hex(tagp, len)); if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_CONTEXT) printf(" [%s]", sc_dump_hex(tagp, len)); putchar('\n'); } return; } void sc_asn1_print_tags(const u8 * buf, size_t buflen) { printf("Printing tags for buffer of length %d\n", (int) buflen); print_tags_recursive(buf, buf, buflen, 0); } 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, tag, mask = 0xff00; buf = p; /* read a tag */ if (sc_asn1_read_tag(&p, left, &cla, &tag, &taglen) != SC_SUCCESS) return NULL; if (left < (size_t)(p - buf)) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "invalid TLV object\n"); 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 */ if (left < taglen) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "invalid TLV object\n"); return NULL; } 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, tag; if (sc_asn1_read_tag((const u8 **) &p, len, &cla, &tag, &taglen) != SC_SUCCESS) 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 %d while only %d 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 u8 *in = inbuf; u8 *out = (u8 *) outbuf; int zero_bits = *in & 0x07; size_t octets_left = inlen - 1; int i, count = 0; memset(outbuf, 0, outlen); in++; if (outlen < octets_left) return SC_ERROR_BUFFER_TOO_SMALL; if (inlen < 1) return SC_ERROR_INVALID_ASN1_OBJECT; while (octets_left) { /* 1st octet of input: ABCDEFGH, where A is the MSB */ /* 1st octet of output: HGFEDCBA, where A is the LSB */ /* first bit in bit string is the LSB in first resulting octet */ int bits_to_go; *out = 0; if (octets_left == 1) bits_to_go = 8 - zero_bits; 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) { return decode_bit_string(inbuf, inlen, outbuf, outlen, 1); } int sc_asn1_decode_bit_string_ni(const u8 * inbuf, size_t inlen, void *outbuf, size_t outlen) { return decode_bit_string(inbuf, inlen, outbuf, outlen, 0); } 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; int 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) { int 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) { 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); if (n < 0) return n; for (i = 0; i < n; i += 8) { field |= (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 a = 0; size_t i; if (inlen > sizeof(int)) return SC_ERROR_INVALID_ASN1_OBJECT; if (inbuf[0] & 0x80) a = -1; for (i = 0; i < inlen; i++) { a <<= 8; a |= *inbuf++; } *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 a; 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; a = *p; *octet++ = a / 40; *octet++ = a % 40; inlen--; while (inlen) { p++; a = *p & 0x7F; inlen--; while (inlen && *p & 0x80) { p++; a <<= 7; a |= *p & 0x7F; inlen--; } *octet++ = a; if (octet - id->value >= SC_MAX_OBJECT_ID_OCTETS) { sc_init_oid(id); return SC_ERROR_INVALID_ASN1_OBJECT; } }; 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) return SC_ERROR_INVALID_ARGUMENTS; *p++ += k; break; 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; } int sc_asn1_put_tag(int tag, const u8 * data, size_t datalen, u8 * out, size_t outlen, u8 **ptr) { u8 *p = out; if (outlen < 2) return SC_ERROR_INVALID_ARGUMENTS; if (datalen > 127) return SC_ERROR_INVALID_ARGUMENTS; *p++ = tag & 0xFF; /* FIXME: Support longer tags */ outlen--; *p++ = datalen; outlen--; if (outlen < datalen) return SC_ERROR_INVALID_ARGUMENTS; 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; } 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; ses = calloc(SC_MAX_SE_NUM, sizeof(sc_pkcs15_sec_env_info_t *)); if (ses == NULL) return 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) { int i; for (i = 0; i < idx; i++) if (ses[i]) free(ses[i]); free(ses); } return 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; 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; out = (unsigned char *) realloc(out, outlen + ptrlen); if (!out) { ret = SC_ERROR_OUT_OF_MEMORY; goto err; } 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'\n", depth, depth, "", entry->name); 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: %d\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); 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; *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); 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); 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) && obj[0] == 0x00 && objlen > 1) { objlen--; obj++; } /* Allocate buffer if needed */ if (entry->flags & SC_ASN1_ALLOC) { u8 **buf = (u8 **) parm; *buf = malloc(objlen); if (*buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; break; } 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; *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 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, "%*.*scalled, left=%u, depth %d%s\n", depth, depth, "", left, depth, choice ? ", choice" : ""); 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, "not present\n"); 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_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=%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: assert(len != NULL); if (entry->type == SC_ASN1_BIT_STRING) r = encode_bit_string((const u8 *) parm, *len, &buf, &buflen, 1); else r = encode_bit_string((const u8 *) parm, *len, &buf, &buflen, 0); break; case SC_ASN1_BIT_FIELD: assert(len != NULL); r = encode_bit_field((const u8 *) parm, *len, &buf, &buflen); break; case SC_ASN1_PRINTABLESTRING: case SC_ASN1_OCTET_STRING: case SC_ASN1_UTF8STRING: assert(len != NULL); buf = malloc(*len + 1); if (buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; break; } buflen = 0; /* If the integer is supposed to be unsigned, insert * a padding byte if the MSB is one */ if ((entry->flags & SC_ASN1_UNSIGNED) && (((u8 *) parm)[0] & 0x80)) { buf[buflen++] = 0x00; } memcpy(buf + buflen, parm, *len); buflen += *len; break; case SC_ASN1_GENERALIZEDTIME: assert(len != NULL); buf = malloc(*len); if (buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; break; } memcpy(buf, parm, *len); buflen = *len; 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=%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; 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) { memset(dst, 0, sizeof(*dst)); if (src->len) { 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; } opensc-0.13.0/src/libopensc/card-authentic.c0000644000015201777760000021101512057406034015631 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 #include #include #include #include #include #include #include #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 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); 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, 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 *out) { if (!in || !out) return SC_ERROR_INVALID_ARGUMENTS; if (*in < 0x80) { *out = *in; return 1; } else if (*in == 0x81) { *out = *(in + 1); return 2; } else if (*in == 0x82) { *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; 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)) { tag = *(in + offs) * 0x100 + *(in + offs + 1); tag_len = 2; } else { tag = *(in + offs); tag_len = 1; } size_len = authentic_parse_size(in + offs + tag_len, &size); LOG_TEST_RET(ctx, size_len, "parse error: invalid size data"); if (tag == in_tag) { *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, 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"); 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:%i", data_len); if (data_len == 10) { for (ii=0; ii<5; ii++) { unsigned char acl = *(data + ii*2); unsigned char cred_id = *(data + ii*2 + 1); unsigned sc = acl * 0x100 + cred_id; sc_log(ctx, "%i: SC:%X", ii, sc); if (!sc) continue; if (acl & AUTHENTIC_AC_SM_MASK) { pin_cmd->pin1.acls[ii].method = SC_AC_SCB; pin_cmd->pin1.acls[ii].key_ref = sc; } else if (acl!=0xFF && cred_id) { sc_log(ctx, "%i: ACL(method:SC_AC_CHV,id:%i)", ii, cred_id); pin_cmd->pin1.acls[ii].method = SC_AC_CHV; pin_cmd->pin1.acls[ii].key_ref = cred_id; } else { pin_cmd->pin1.acls[ii].method = SC_AC_NEVER; pin_cmd->pin1.acls[ii].key_ref = 0; } } } 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(ctx, "try to match card with ATR %s", sc_dump_hex(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; _sc_card_add_rsa_alg(card, 1024, flags, 0x10001); _sc_card_add_rsa_alg(card, 2048, flags, 0x10001); card->caps = SC_CARD_CAP_RNG; card->caps |= SC_CARD_CAP_APDU_EXT; card->caps |= SC_CARD_CAP_USE_FCI_AC; #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"); LOG_FUNC_RETURN(ctx, rv); } static int authentic_init(struct sc_card *card) { struct sc_context *ctx = card->ctx; int ii, rv = SC_ERROR_NO_CARD_SUPPORT; 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_NO_CARD_SUPPORT); card->cla = 0x00; card->drv_data = (struct authentic_private_data *) calloc(sizeof(struct authentic_private_data), 1); 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) rv = authentic_get_serialnr(card, 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_TEST_RET(ctx, rv, "'ERASE BINARY' failed"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } #if 0 static int authentic_resize_file(struct sc_card *card, unsigned file_id, unsigned new_size) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; unsigned char data[6] = { 0x62, 0x04, 0x80, 0x02, 0xFF, 0xFF }; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "try to set file size to %i bytes", new_size); data[4] = (new_size >> 8) & 0xFF; data[5] = new_size & 0xFF; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDB, (file_id >> 8) & 0xFF, file_id & 0xFF); apdu.data = data; apdu.datalen = sizeof(data); apdu.lc = sizeof(data); 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, "resize file failed"); if (card->cache.valid && card->cache.current_ef && card->cache.current_ef->id == file_id) card->cache.current_ef->size = new_size; LOG_FUNC_RETURN(ctx, rv); } #endif 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); LOG_TEST_RET(ctx, rv, "cannot set 'current file': FCI process error"); break; default: LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } if (file->type == SC_FILE_TYPE_DF) { struct sc_path cur_df_path; memset(&cur_df_path, 0, sizeof(cur_df_path)); if (card->cache.valid && card->cache.current_df) { cur_df_path = card->cache.current_df->path; sc_file_free(card->cache.current_df); } card->cache.current_df = NULL; sc_file_dup(&card->cache.current_df, file); if (cur_df_path.len) { memcpy(card->cache.current_df->path.value + cur_df_path.len, card->cache.current_df->path.value, card->cache.current_df->path.len); memcpy(card->cache.current_df->path.value, cur_df_path.value, cur_df_path.len); card->cache.current_df->path.len += cur_df_path.len; } if (card->cache.current_ef) { sc_file_free(card->cache.current_ef); card->cache.current_ef = NULL; } card->cache.valid = 1; } else { if (card->cache.current_ef) 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) { if (card->cache.current_df) sc_file_free(card->cache.current_df); card->cache.current_df = NULL; if (card->cache.current_ef) 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->len <= 2 || path->type == SC_PATH_TYPE_DF_NAME || !path) 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)) { memcpy(in_path.value + 2, in_path.value, in_path.len); memcpy(in_path.value, "\x3F\x00", 2); in_path.len += 2; } for (offs=0; offs < in_path.len && offs < cur_path.len; offs += 2) { if (cur_path.value[offs] != in_path.value[offs]) break; if (cur_path.value[offs + 1] != in_path.value[offs + 1]) break; } memcpy(in_path.value, in_path.value + offs, sizeof(in_path.value) - offs); in_path.len -= offs; *path = in_path; LOG_FUNC_RETURN(ctx, offs); } static void authentic_debug_select_file(struct sc_card *card, const struct sc_path *path) { struct sc_context *ctx = card->ctx; struct sc_card_cache *cache = &card->cache; if (path) sc_log(ctx, "try to select path(type:%i) %s", path->type, sc_print_path(path)); if (!cache->valid) return; if (cache->current_df) sc_log(ctx, "current_df(type=%i) %s", cache->current_df->path.type, sc_print_path(&cache->current_df->path)); else sc_log(ctx, "current_df empty"); if (cache->current_ef) sc_log(ctx, "current_ef(type=%i) %s", cache->current_ef->path.type, sc_print_path(&cache->current_ef->path)); else sc_log(ctx, "current_ef empty"); } static int authentic_is_selected(struct sc_card *card, const struct sc_path *path, struct sc_file **file_out) { if (!path->len) { if (file_out && card->cache.valid && card->cache.current_df) sc_file_dup(file_out, card->cache.current_df); return SC_SUCCESS; } else if (path->len == 2 && card->cache.valid && card->cache.current_ef) { if (!memcmp(card->cache.current_ef->path.value, path->value, 2)) { if (file_out) sc_file_dup(file_out, card->cache.current_ef); return SC_SUCCESS; } } return SC_ERROR_FILE_NOT_FOUND; } static int authentic_select_file(struct sc_card *card, const struct sc_path *path, struct sc_file **file_out) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; struct sc_path lpath; unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE]; int pathlen, 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"); memcpy(&lpath.value[0], &lpath.value[2], lpath.len - 2); lpath.len -= 2; if (!lpath.len) LOG_FUNC_RETURN(ctx, SC_SUCCESS); } if (lpath.type == SC_PATH_TYPE_PATH && (lpath.len == 2)) lpath.type = SC_PATH_TYPE_FILE_ID; rv = authentic_is_selected(card, &lpath, file_out); if (!rv) LOG_FUNC_RETURN(ctx, SC_SUCCESS); pathlen = lpath.len; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x00); 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_apdus_allocate(struct sc_apdu **head, struct sc_apdu **new) { struct sc_apdu *allocated_apdu = NULL, *tmp_apdu = NULL; if (!head) return SC_ERROR_INVALID_ARGUMENTS; allocated_apdu = calloc(1, sizeof(struct sc_apdu)); if (!allocated_apdu) return SC_ERROR_OUT_OF_MEMORY; if (*head == NULL) *head = allocated_apdu; if (new) *new = allocated_apdu; tmp_apdu = *head; while(tmp_apdu->next) tmp_apdu = tmp_apdu->next; tmp_apdu->next = allocated_apdu; return 0; } static void authentic_apdus_free(struct sc_apdu *apdu) { while(apdu) { struct sc_apdu *tmp_apdu = apdu->next; free(apdu); apdu = tmp_apdu; } } static int authentic_read_binary(struct sc_card *card, unsigned int idx, unsigned char *buf, size_t count, unsigned long flags) { struct sc_context *ctx = card->ctx; struct sc_apdu *apdus = NULL, *cur_apdu = NULL; size_t sz, rest; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "offs:%i,count:%i,max_recv_size:%i", idx, count, card->max_recv_size); /* Data size more then 256 bytes can happen when card reader is * configurated with max_send/recv_size more then 255/256 bytes * (for ex. 'remote-access' reader) . * In this case create chained 'read-binary' APDUs. */ sc_log(ctx, "reader flags 0x%X", card->reader->flags); if (count > 256 && !(card->reader->flags & SC_READER_HAS_WAITING_AREA)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid size of the data to read"); rest = count; while(rest) { if (authentic_apdus_allocate(&apdus, &cur_apdu)) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "cannot allocate APDU"); sz = rest > 256 ? 256 : rest; sc_format_apdu(card, cur_apdu, SC_APDU_CASE_2_SHORT, 0xB0, (idx >> 8) & 0x7F, idx & 0xFF); cur_apdu->le = sz; cur_apdu->resplen = count; cur_apdu->resp = buf; idx += sz; rest -= sz; } if (!apdus) { LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "authentic_read_binary() failed"); LOG_FUNC_RETURN(ctx, count); } rv = sc_transmit_apdu(card, apdus); if (!rv) rv = sc_check_sw(card, apdus->sw1, apdus->sw2); if (!rv) count = apdus->resplen; authentic_apdus_free(apdus); LOG_TEST_RET(ctx, rv, "authentic_read_binary() failed"); LOG_FUNC_RETURN(ctx, 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 *apdus = NULL, *cur_apdu = NULL; size_t sz, rest; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "offs:%i,count:%i,max_send_size:%i", idx, count, card->max_send_size); /* see comments for authentic_read_binary() */ sc_log(ctx, "reader flags 0x%X", card->reader->flags); if (count > 255 && !(card->reader->flags & SC_READER_HAS_WAITING_AREA)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid size of the data to read"); rest = count; while(rest) { if (authentic_apdus_allocate(&apdus, &cur_apdu)) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "cannot allocate APDU"); sz = rest > 255 ? 255 : rest; sc_format_apdu(card, cur_apdu, SC_APDU_CASE_3_SHORT, 0xD0, (idx >> 8) & 0x7F, idx & 0xFF); cur_apdu->lc = sz; cur_apdu->datalen = sz; cur_apdu->data = buf + count - rest; idx += sz; rest -= sz; } if (!apdus) { LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "authentic_write_binary() failed"); LOG_FUNC_RETURN(ctx, count); } rv = sc_transmit_apdu(card, apdus); if (!rv) rv = sc_check_sw(card, apdus->sw1, apdus->sw2); authentic_apdus_free(apdus); LOG_TEST_RET(ctx, rv, "authentic_write_binary() failed"); LOG_FUNC_RETURN(ctx, 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 *apdus = NULL, *cur_apdu = NULL; size_t sz, rest; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "offs:%i,count:%i,max_send_size:%i", idx, count, card->max_send_size); /* see comments for authentic_read_binary() */ sc_log(ctx, "reader flags 0x%X", card->reader->flags); if (count > 255 && !(card->reader->flags & SC_READER_HAS_WAITING_AREA)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid size of the data to read"); rest = count; while(rest) { if (authentic_apdus_allocate(&apdus, &cur_apdu)) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "cannot allocate APDU"); sz = rest > 255 ? 255 : rest; sc_format_apdu(card, cur_apdu, SC_APDU_CASE_3_SHORT, 0xD6, (idx >> 8) & 0x7F, idx & 0xFF); cur_apdu->lc = sz; cur_apdu->datalen = sz; cur_apdu->data = buf + count - rest; idx += sz; rest -= sz; } if (!apdus) { LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "authentic_update_binary() failed"); LOG_FUNC_RETURN(ctx, count); } rv = sc_transmit_apdu(card, apdus); if (!rv) rv = sc_check_sw(card, apdus->sw1, apdus->sw2); authentic_apdus_free(apdus); LOG_TEST_RET(ctx, rv, "authentic_update_binary() failed"); LOG_FUNC_RETURN(ctx, 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 %i", taglen); buf = tag; buflen = taglen; } tag = sc_asn1_find_tag(card->ctx, buf, buflen, 0x62, &taglen); if (tag != NULL) { sc_log(ctx, " FCP length %i", 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(ctx, "ACLs not found in data(%i) %s", buflen, sc_dump_hex(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(ctx, "ACL data(%i):%s", file->sec_attr_len, sc_dump_hex(file->sec_attr, file->sec_attr_len)); for (ii = 0; ii < file->sec_attr_len / 2; ii++) { 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_ID; 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, 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); sbuf_len = authentic_fcp_encode(card, file, sbuf + 2, sizeof(sbuf)-2); LOG_TEST_RET(ctx, sbuf_len, "FCP encode error"); sbuf[0] = ISO7816_TAG_FCP; sbuf[1] = sbuf_len; if (card->cache.valid && card->cache.current_df) { const struct sc_acl_entry *entry = sc_file_get_acl_entry(card->cache.current_df, SC_AC_OP_CREATE); sc_log(ctx, "CREATE method/reference %X/%X", entry->method, entry->key_ref); if (entry->method == SC_AC_SCB) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Not yet supported"); } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0, 0); apdu.data = sbuf; apdu.datalen = sbuf_len + 2; 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 && card->cache.current_ef) { 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:%i)", 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:%i)", 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); LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); if (pin_cmd->pin1.len > (int)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); 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: %i/%i/%i", pin_cmd.pin1.max_length, pin_cmd.pin1.min_length, pin_cmd.pin1.pad_length); sc_log(ctx, "PIN2 lengths max/min/pad: %i/%i/%i", 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); 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 > (int)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); 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 %i/%i/%i", 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_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, 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->flags |= SC_PIN_CMD_NEED_PADDING; sc_log(ctx, "PIN policy: size max/min/pad %i/%i/%i, 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_file *save_current = NULL; struct sc_pin_cmd_data pin_cmd, puk_cmd; struct sc_apdu apdu; unsigned reference; int rv, ii; LOG_FUNC_CALLED(ctx); sc_log(ctx, "reset PIN (ref:%i,lengths %i/%i)", data->pin_reference, data->pin1.len, data->pin2.len); 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; rv = authentic_pin_get_policy(card, &pin_cmd); LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); if (pin_cmd.pin1.acls[AUTHENTIC_ACL_NUM_PIN_RESET].method == SC_AC_CHV) { for (ii=0;ii<8;ii++) { unsigned char mask = 0x01 << ii; if (pin_cmd.pin1.acls[AUTHENTIC_ACL_NUM_PIN_RESET].key_ref & mask) { memset(&puk_cmd, 0, sizeof(puk_cmd)); puk_cmd.pin_reference = ii + 1; rv = authentic_pin_get_policy(card, &puk_cmd); 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"); } if (save_current) { struct sc_file *dummy_file = NULL; rv = authentic_select_file(card, &save_current->path, &dummy_file); LOG_TEST_RET(ctx, rv, "Cannot return to saved PATH"); } 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; LOG_FUNC_CALLED(ctx); sc_log(ctx, "PIN-CMD:%X,PIN(type:%X,ret:%i)", data->cmd, data->pin_type, data->pin_reference); sc_log(ctx, "PIN1(%p,len:%i,tries-left:%i)", data->pin1.data, data->pin1.len, data->pin1.tries_left); sc_log(ctx, "PIN2(%p,len:%i)", data->pin2.data, data->pin2.len); 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); 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); } /* 'GET CHALLENGE' returns always 24 bytes */ static int authentic_get_challenge(struct sc_card *card, unsigned char *rnd, size_t len) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; unsigned char rbuf[0x18]; int rv, nn; LOG_FUNC_CALLED(ctx); if (!rnd && len) return SC_ERROR_INVALID_ARGUMENTS; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x84, 0x00, 0x00); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = sizeof(rbuf); while (len > 0) { rv = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, rv, "PIN cmd failed"); if (apdu.resplen != sizeof(rbuf)) return sc_check_sw(card, apdu.sw1, apdu.sw2); nn = len > apdu.resplen ? apdu.resplen : len; memcpy(rnd, apdu.resp, nn); len -= nn; rnd += nn; } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } 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_RET(ctx, rv, "SDO RSA P encode error"); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_Q, rsa.q.data, rsa.q.len, &blob, &blob_len); LOG_TEST_RET(ctx, rv, "SDO RSA Q encode error"); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_PQ, rsa.iqmp.data, rsa.iqmp.len, &blob, &blob_len); LOG_TEST_RET(ctx, rv, "SDO RSA PQ encode error"); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_DP1, rsa.dmp1.data, rsa.dmp1.len, &blob, &blob_len); LOG_TEST_RET(ctx, rv, "SDO RSA DP1 encode error"); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_DQ1, rsa.dmq1.data, rsa.dmq1.len, &blob, &blob_len); LOG_TEST_RET(ctx, rv, "SDO RSA DQ1 encode error"); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE, blob, blob_len, &blob01, &blob01_len); LOG_TEST_RET(ctx, rv, "SDO RSA Private encode error"); free (blob); blob = NULL; blob_len = 0; /* Encode public RSA key part */ sc_log(ctx, "modulus.len:%i blob_len:%i", rsa.modulus.len, blob_len); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PUBLIC_MODULUS, rsa.modulus.data, rsa.modulus.len, &blob, &blob_len); LOG_TEST_RET(ctx, rv, "SDO RSA Modulus encode error"); sc_log(ctx, "exponent.len:%i blob_len:%i", rsa.exponent.len, blob_len); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PUBLIC_EXPONENT, rsa.exponent.data, rsa.exponent.len, &blob, &blob_len); LOG_TEST_RET(ctx, rv, "SDO RSA Exponent encode error"); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PUBLIC, blob, blob_len, &blob01, &blob01_len); LOG_TEST_RET(ctx, rv, "SDO RSA Private encode error"); free (blob); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA, blob01, blob01_len, out, out_len); LOG_TEST_RET(ctx, rv, "SDO RSA encode error"); free(blob01); 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_RET(ctx, rv, "DOCP MECH encode error"); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_DOCP_ID, &sdo->docp.id, sizeof(sdo->docp.id), &data, &data_len); LOG_TEST_RET(ctx, rv, "DOCP ID encode error"); if (cmd == SC_CARDCTL_AUTHENTIC_SDO_CREATE) { rv = authentic_update_blob(ctx, AUTHENTIC_TAG_DOCP_ACLS, sdo->docp.acl_data, sdo->docp.acl_data_len, &data, &data_len); LOG_TEST_RET(ctx, rv, "DOCP ACLs encode error"); if (sdo->docp.security_parameter) { rv = authentic_update_blob(ctx, AUTHENTIC_TAG_DOCP_SCP, &sdo->docp.security_parameter, sizeof(sdo->docp.security_parameter), &data, &data_len); LOG_TEST_RET(ctx, rv, "DOCP ACLs encode error"); } if (sdo->docp.usage_counter[0] || sdo->docp.usage_counter[1]) { rv = authentic_update_blob(ctx, AUTHENTIC_TAG_DOCP_USAGE_COUNTER, sdo->docp.usage_counter, sizeof(sdo->docp.usage_counter), &data, &data_len); LOG_TEST_RET(ctx, rv, "DOCP ACLs encode error"); } } 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_RET(ctx, rv, "SDO RSA encode error"); } else { LOG_TEST_RET(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_RET(ctx, rv, "SDO RSA Exponent encode error"); } data_tag = AUTHENTIC_TAG_RSA_GENERATE_DATA; } else if (cmd != SC_CARDCTL_AUTHENTIC_SDO_DELETE) { LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid SDO operation"); } rv = authentic_update_blob(ctx, data_tag, data, data_len, out, out_len); LOG_TEST_RET(ctx, rv, "SDO DOCP encode error"); free(data); sc_log(ctx, "encoded SDO operation data %s", sc_dump_hex(*out, *out_len)); 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 %i", 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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 %i", 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; LOG_TEST_RET(ctx, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, rv, "authentic_sdo_create() SDO put data error"); free(data); LOG_FUNC_RETURN(ctx, rv); } 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%X,algo_ref:0x%X,flags:0x%X), 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); } #if 0 apdu.flags |= SC_APDU_FLAGS_CAN_WAIT; #endif 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 %i; outlen %i", 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 = 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); } /* 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 = card->ctx; struct sm_type_params_gp *params_gp = &sm_info->session.gp.params; struct sc_remote_data rdata; int rv; sc_log(ctx, "called; command 0x%X\n", cmd); if (!card || !sm_info || !resp || !resp_len) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); 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, int 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, "Unsufficient 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((*sm_apdu)->data); if ((*sm_apdu)->resp) free((*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(%i) %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) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); if (plain->data && plain->datalen) memcpy(apdu->data, plain->data, plain->datalen); apdu->resp = calloc (1, plain->resplen + 32); if (!apdu->resp) 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); LOG_TEST_RET(ctx, rv, "SM: GET_APDUS failed"); *sm_apdu = apdu; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } #endif 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; return &authentic_drv; } struct sc_card_driver * sc_get_authentic_driver(void) { return sc_get_driver(); } #endif /* ENABLE_OPENSSL */ opensc-0.13.0/src/libopensc/compression.h0000644000015201777760000000243212057406034015305 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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_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.13.0/src/libopensc/pkcs15-algo.c0000644000015201777760000004213012057406034014764 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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]; int ivlen = 8, 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_params * ecp; sc_debug(ctx, SC_LOG_DEBUG_ASN1, "DEE - asn1_decode_ec_params %p:%d %d", buf, buflen, depth); memset(&curve, 0, sizeof(curve)); ecp = malloc(sizeof(struct sc_ec_params)); if (ecp == NULL) return SC_ERROR_OUT_OF_MEMORY; memset(ecp,0,sizeof(struct sc_ec_params)); /* 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 into asn1_ec_params */ sc_debug(ctx, SC_LOG_DEBUG_ASN1, "DEE - asn1_decode_ec_params r=%d", r); if (r < 0) return r; if (r <= 1) { ecp->der = malloc(buflen); if (ecp->der == NULL) return SC_ERROR_OUT_OF_MEMORY; ecp->der_len = buflen; sc_debug(ctx, SC_LOG_DEBUG_ASN1, "DEE - asn1_decode_ec_params paramp=%p %p:%d %d", ecp, ecp->der, ecp->der_len, ecp->type); memcpy(ecp->der, buf, buflen); /* copy der parameters */ } else r = 0; ecp->type = r; /* but 0 = ecparams if any, 1=named curve */ *paramp = ecp; return 0; }; static int asn1_encode_ec_params(sc_context_t *ctx, void *params, u8 **buf, size_t *buflen, int depth) { int r; /* TODO: -DEE EC paramameters are DER so is there anything to do? */ /* I have not needed this yet */ sc_debug(ctx, SC_LOG_DEBUG_ASN1, "DEE - asn1_encode_ec_params"); r = SC_ERROR_NOT_IMPLEMENTED; return r; } static void asn1_free_ec_params(void *params) { struct sc_ec_params * ecp = (struct sc_ec_params *) params; if (ecp) { if (ecp->der) free(ecp->der); 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_DSA { SC_ALGORITHM_DSA, {{ 1, 2, 840, 10040, 4, 3, -1}}, NULL, NULL, NULL }, #endif #ifdef SC_ALGORITHM_RSA /* really rsaEncryption */ { SC_ALGORITHM_RSA, {{ 1, 2, 840, 113549, 1, 1, 1, -1}}, NULL, NULL, NULL }, #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 of 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 { -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[6] = { { "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(sc_context_t *ctx, const u8 *in, size_t len, struct sc_algorithm_id *id, int depth) { struct sc_asn1_pkcs15_algorithm_info *alg_info; struct sc_asn1_entry asn1_alg_id[3]; int r; 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); if (r < 0) return r; /* 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; if ((alg_info = sc_asn1_get_algorithm_info(id)) != NULL) { id->algorithm = alg_info->id; if (alg_info->decode) { /* TODO: -DEE why the test for SC_ASN1_PRESENT? * If it looking for SC_ASN1_NULL, thats valid for EC, in some cases */ if (asn1_alg_id[1].flags & SC_ASN1_PRESENT) { sc_debug( ctx,SC_LOG_DEBUG_NORMAL,"SC_ASN1_PRESENT was set, so invalid"); return SC_ERROR_INVALID_ASN1_OBJECT; } r = alg_info->decode(ctx, &id->params, in, len, depth); } } return r; } int sc_asn1_encode_algorithm_id(sc_context_t *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; alg_info = sc_asn1_get_algorithm_info(id); if (alg_info == NULL) { sc_log(ctx, "Cannot encode unknown algorithm %u", id->algorithm); return 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_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 (!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); if (r < 0) return r; /* 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); return r; } } if (obj_len) { tmp = (u8 *) realloc(*buf, *len + obj_len); if (!tmp) { free(*buf); *buf = NULL; free(obj); return 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)); return 0; } 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.13.0/src/libopensc/card-entersafe.c0000644000015201777760000012741612057406034015634 00000000000000/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Initially written by Weitao Sun (weitao@ftsafe.com) 2008 */ #include "config.h" #ifdef ENABLE_OPENSSL /* empty file without openssl */ #include #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" static struct sc_atr_table entersafe_atrs[] = { { "3b:0f:00:65:46:53:05:19:05:71:df:00:00:00:00:00:00", "ff:ff:ff:ff:ff:ff:ff:00:ff:ff:ff:00:00:00:00:00:00", "ePass3000", SC_CARD_TYPE_ENTERSAFE_3K, 0, NULL }, { "3b:9f:95:81:31:fe:9f:00:65:46:53:05:30:06:71:df:00:00:00:80:6a:82:5e", "FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:00:FF:FF:FF:FF:FF:FF:00:00:00:00", "FTCOS/PK-01C", SC_CARD_TYPE_ENTERSAFE_FTCOS_PK_01C, 0, NULL }, { 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(buffctx, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "entersafe gen random failed"); if(apdu.resplen!=size) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL,SC_ERROR_INTERNAL); memcpy(buff,rbuf,size); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL,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; 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; EVP_CIPHER_CTX_init(&ctx); EVP_CIPHER_CTX_set_padding(&ctx,0); if(keylen == 8) EVP_EncryptInit_ex(&ctx, EVP_des_ecb(), NULL, key, iv); else if (keylen == 16) EVP_EncryptInit_ex(&ctx, EVP_des_ede(), NULL, key, iv); else SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); len = apdu->lc; if(!EVP_EncryptUpdate(&ctx, buff, &len, buff, buffsize)){ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "entersafe encryption error."); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); } apdu->lc = len; if (!EVP_CIPHER_CTX_cleanup(&ctx)){ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "entersafe encryption error."); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); } if(apdu->lc!=buffsize) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "entersafe build cipher apdu failed."); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INTERNAL); } apdu->data=buff; apdu->datalen=apdu->lc; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static int entersafe_mac_apdu(sc_card_t *card, sc_apdu_t *apdu, u8 * key,size_t keylen, u8 * buff,size_t buffsize) { int r; u8 iv[8]; u8 *tmp=0,*tmp_rounded=NULL; size_t tmpsize=0,tmpsize_rounded=0; int outl=0; EVP_CIPHER_CTX ctx; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); assert(card); assert(apdu); assert(key); assert(buff); if(apdu->cse != SC_APDU_CASE_3_SHORT) return SC_ERROR_INTERNAL; if(keylen!=8 && keylen!=16) return SC_ERROR_INTERNAL; r=entersafe_gen_random(card,iv,sizeof(iv)); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,r,"entersafe gen random failed"); /* encode the APDU in the buffer */ if ((r=sc_apdu_get_octets(card->ctx, apdu, &tmp, &tmpsize,SC_PROTO_RAW)) != SC_SUCCESS) goto out; /* round to 8 */ tmpsize_rounded=(tmpsize/8+1)*8; tmp_rounded = malloc(tmpsize_rounded); if (tmp_rounded == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto out; } /*build content and padded buffer by 0x80 0x00 0x00..... */ memset(tmp_rounded,0,tmpsize_rounded); memcpy(tmp_rounded,tmp,tmpsize); tmp_rounded[4]+=4; tmp_rounded[tmpsize]=0x80; /* block_size-1 blocks*/ EVP_CIPHER_CTX_init(&ctx); EVP_CIPHER_CTX_set_padding(&ctx,0); EVP_EncryptInit_ex(&ctx, EVP_des_cbc(), NULL, key, iv); if(tmpsize_rounded>8){ if(!EVP_EncryptUpdate(&ctx,tmp_rounded,&outl,tmp_rounded,tmpsize_rounded-8)){ r = SC_ERROR_INTERNAL; goto out; } } /* last block */ if(keylen==8) { if(!EVP_EncryptUpdate(&ctx,tmp_rounded+outl,&outl,tmp_rounded+outl,8)){ r = SC_ERROR_INTERNAL; goto out; } } else { EVP_EncryptInit_ex(&ctx, EVP_des_ede_cbc(), NULL, key,tmp_rounded+outl-8); if(!EVP_EncryptUpdate(&ctx,tmp_rounded+outl,&outl,tmp_rounded+outl,8)){ r = SC_ERROR_INTERNAL; goto out; } } if (!EVP_CIPHER_CTX_cleanup(&ctx)){ r = SC_ERROR_INTERNAL; goto out; } memcpy(buff,apdu->data,apdu->lc); /* use first 4 bytes of last block as mac value*/ memcpy(buff+apdu->lc,tmp_rounded+tmpsize_rounded-8,4); apdu->data=buff; apdu->lc+=4; apdu->datalen=apdu->lc; out: if(tmp) free(tmp); if(tmp_rounded) free(tmp_rounded); 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=0,*mac_data=0; size_t cipher_data_size,mac_data_size; int blocks; int r=SC_SUCCESS; u8 *sbuf=NULL; size_t ssize=0; 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, SC_LOG_DEBUG_VERBOSE, sbuf, ssize, 1); free(sbuf); if(cipher) { blocks=(apdu->lc+2)/8+1; cipher_data_size=blocks*8; cipher_data=malloc(cipher_data_size); if(!cipher) { 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); r = entersafe_mac_apdu(card,apdu,key,keylen,mac_data,mac_data_size); if(r<0) goto out; } r = sc_transmit_apdu(card,apdu); out: if(cipher_data) free(cipher_data); if(mac_data) free(mac_data); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.resplen == 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); memcpy(buf, recvbuf, apdu.resplen); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen); } 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "Card returned error"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Process fci failed"); if(file->namelen) { file->type = SC_FILE_TYPE_DF; file->ef_structure = SC_FILE_EF_UNKNOWN; } else { file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } 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=0; 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); /* update cache */ if (file->type == SC_FILE_TYPE_DF) { card->cache.current_path.type = SC_PATH_TYPE_PATH; card->cache.current_path.value[0] = 0x3f; card->cache.current_path.value[1] = 0x00; if (id_hi == 0x3f && id_lo == 0x00){ card->cache.current_path.len = 2; }else{ card->cache.current_path.len = 4; card->cache.current_path.value[2] = id_hi; card->cache.current_path.value[3] = id_lo; } } if (file_out) *file_out = 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); } } else { r = iso_ops->select_file(card,in_path,file_out); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); /* 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; int bMatch = -1; unsigned int i; int r; if (pathlen%2 != 0 || pathlen > 6 || pathlen <= 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); /* if pathlen == 6 then the first FID must be MF (== 3F00) */ if (pathlen == 6 && ( path[0] != 0x3f || path[1] != 0x00 )) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); /* unify path (the first FID should be MF) */ if (path[0] != 0x3f || path[1] != 0x00) { n_pathbuf[0] = 0x3f; n_pathbuf[1] = 0x00; for (i=0; i< pathlen; i++) n_pathbuf[i+2] = pathbuf[i]; path = n_pathbuf; pathlen += 2; } /* check current working directory */ if (card->cache.valid && card->cache.current_path.type == SC_PATH_TYPE_PATH && card->cache.current_path.len >= 2 && card->cache.current_path.len <= pathlen ) { bMatch = 0; for (i=0; i < card->cache.current_path.len; i+=2) if (card->cache.current_path.value[i] == path[i] && card->cache.current_path.value[i+1] == path[i+1] ) bMatch += 2; } if ( card->cache.valid && bMatch > 2 ) { if ( pathlen - bMatch == 2 ) { /* we are in the rigth directory */ return entersafe_select_fid(card, path[bMatch], path[bMatch+1], file_out); } else if ( pathlen - bMatch > 2 ) { /* two more steps to go */ sc_path_t new_path; /* first step: change directory */ r = entersafe_select_fid(card, path[bMatch], path[bMatch+1], NULL); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "SELECT FILE (DF-ID) failed"); memset(&new_path, 0, sizeof(sc_path_t)); new_path.type = SC_PATH_TYPE_PATH; new_path.len = pathlen - bMatch-2; memcpy(new_path.value, &(path[bMatch+2]), new_path.len); /* final step: select file */ return entersafe_select_file(card, &new_path, file_out); } else /* if (bMatch - pathlen == 0) */ { /* done: we are already in the * requested directory */ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "cache hit\n"); /* copy file info (if necessary) */ if (file_out) { sc_file_t *file = sc_file_new(); if (!file) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); file->id = (path[pathlen-2] << 8) + path[pathlen-1]; file->path = card->cache.current_path; file->type = SC_FILE_TYPE_DF; file->ef_structure = SC_FILE_EF_UNKNOWN; file->size = 0; file->namelen = 0; file->magic = SC_FILE_MAGIC; *file_out = file; } /* nothing left to do */ return SC_SUCCESS; } } else { /* no usable cache */ for ( i=0; ictx, SC_LOG_DEBUG_NORMAL, r, "SELECT FILE (DF-ID) failed"); } return entersafe_select_fid(card, path[pathlen-2], path[pathlen-1], file_out); } } 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "current path (%s, %s): %s (len: %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: { r = entersafe_transmit_apdu(card, &apdu,trans_code_ftcos_pk_01c,sizeof(trans_code_ftcos_pk_01c),0,1); }break; default: { r = SC_ERROR_INTERNAL; }break; } SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; } else { 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } /** * We don't really set the security envirment,but cache it.It will be set when * security operation is performed later.Because we may transport partial of * the sign/decipher data within the security envirment 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "internal set security env failed"); sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x86,0x80); apdu.data=p; apdu.lc = size; apdu.datalen = size; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; r = entersafe_transmit_apdu(card, &apdu,0,0,0,0); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Verify rv:%i", r); } else { {/*verify*/ sc_apdu_t apdu; u8 sbuf[0x10]={0}; memcpy(sbuf,data->pin1.data,data->pin1.len); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT,0x20,0x00,data->pin_reference+1); apdu.lc = apdu.datalen = sizeof(sbuf); apdu.data = sbuf; r = entersafe_transmit_apdu(card, &apdu,0,0,0,0); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); } {/*change*/ sc_apdu_t apdu; u8 sbuf[0x12]={0}; sbuf[0] = 0x33; sbuf[1] = 0x00; memcpy(sbuf+2,data->pin2.data,data->pin2.len); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT,0xF4,0x0B,data->pin_reference); apdu.cla = 0x84; apdu.lc = apdu.datalen = sizeof(sbuf); apdu.data = sbuf; r = entersafe_transmit_apdu(card, &apdu,key_maintain,sizeof(key_maintain),1,1); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); } } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); /* invalidate cache */ card->cache.valid = 0; 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: { r = entersafe_transmit_apdu(card, &apdu,trans_code_ftcos_pk_01c,sizeof(trans_code_ftcos_pk_01c),0,1); }break; default: { r = SC_ERROR_INTERNAL; }break; } SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write prkey failed"); } p=sbuff; {/* write pukey */ *p++=0x00; /* EC */ *p++=0x00; /* ver */ entersafe_encode_bignum('E',rsa->exponent,&p); entersafe_encode_bignum('N',rsa->modulus,&p); sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xF4,0x2A,key_id); apdu.cla=0x84; apdu.data=sbuff; apdu.lc=apdu.datalen=p-sbuff; r=entersafe_transmit_apdu(card,&apdu,key_maintain,sizeof(key_maintain),1,1); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write pukey failed"); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); } 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write prkey factor failed(MSE)"); } {/* 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); apdu.data=sbuff; apdu.lc=apdu.datalen=data.len; r = entersafe_transmit_apdu(card,&apdu,0,0,0,0); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write prkey factor failed"); } 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "write p failed"); r = entersafe_write_rsa_key_factor(card,key_id,0x22,0x02,rsa->q); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "write q failed"); r = entersafe_write_rsa_key_factor(card,key_id,0x22,0x03,rsa->dmp1); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "write dmp1 failed"); r = entersafe_write_rsa_key_factor(card,key_id,0x22,0x04,rsa->dmq1); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "write dmq1 failed"); r = entersafe_write_rsa_key_factor(card,key_id,0x22,0x05,rsa->iqmp); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "write iqmp failed"); } {/* write pukey */ u8 sbuff[SC_MAX_APDU_BUFFER_SIZE]; sc_apdu_t apdu; /* first 64(0x40) bytes of N */ sbuff[0]=0x83; sbuff[1]=0x02; sbuff[2]=key_id; sbuff[3]=0xA2; sbuff[4]=0x89; sbuff[5]=0x40; memcpy(sbuff+6,rsa->modulus.data,0x40); sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0x22,0x01,0xB8); apdu.data=sbuff; apdu.lc=apdu.datalen=0x46; r=entersafe_transmit_apdu(card,&apdu,0,0,0,0); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write pukey N(1) failed"); /* left 192(0xC0) bytes of N */ sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0x46,0x0B,0x00); apdu.data=rsa->modulus.data+0x40; apdu.lc=apdu.datalen=0xC0; r=entersafe_transmit_apdu(card,&apdu,0,0,0,0); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write pukey N(2) failed"); /* E */ r = entersafe_write_rsa_key_factor(card,key_id,0x2A,0x0D,rsa->exponent); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "write exponent failed"); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); } 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write prkey failed"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); } 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]; u8 sbuf[4],*p; 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card,apdu.sw1,apdu.sw2),"EnterSafe set MSE failed"); /* 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card,apdu.sw1,apdu.sw2),"EnterSafe generate keypair failed"); /* 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card,apdu.sw1,apdu.sw2),"EnterSafe get pukey failed"); data->modulus = malloc(len); if (!data->modulus) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_OUT_OF_MEMORY); p=rbuf; assert(*p=='E'); p+=2+p[1]; /* N */ assert(*p=='N'); ++p; if(*p++>0x80) { u8 len_bytes=(*(p-1))&0x0f; size_t module_len=0; while(len_bytes!=0) { module_len=module_len<<8; module_len+=*p++; --len_bytes; } } 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card,apdu.sw1,apdu.sw2),"EnterSafe get SN failed"); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Preinstall rsa key failed"); } } {/* key maintain */ /* create key maintain*/ sbuf[0] = 0; /* key len extern */ sbuf[1] = sizeof(key_maintain); /* key len */ sbuf[2] = 0x03; /* USAGE */ sbuf[3] = ENTERSAFE_AC_ALWAYS; /* use AC */ sbuf[4] = ENTERSAFE_AC_ALWAYS; /* CHANGE AC */ sbuf[5] = ENTERSAFE_AC_NEVER; /* UPDATE AC */ sbuf[6] = 0x01; /* ALGO */ sbuf[7] = 0x00; /* EC */ sbuf[8] = 0x00; /* VER */ memcpy(&sbuf[9], key_maintain, sizeof(key_maintain)); sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xF0,0x00,0x00); apdu.cla=0x84; apdu.data=sbuf; apdu.lc=apdu.datalen=0x19; r = entersafe_transmit_apdu(card,&apdu,init_key,sizeof(init_key),0,1); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Preinstall key maintain failed"); } {/* user PIN */ memset(sbuf,0,sizeof(sbuf)); sbuf[0] = 0; /* key len extern */ sbuf[1] = 16; /* key len */ sbuf[2] = 0x0B; /* USAGE */ sbuf[3] = ENTERSAFE_AC_ALWAYS; /* use AC */ sbuf[4] = 0X04; /* CHANGE AC */ sbuf[5] = 0x38; /* UPDATE AC */ sbuf[6] = 0x01; /* ALGO */ sbuf[7] = 0xFF; /* EC */ sbuf[8] = 0x00; /* VER */ sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xF0,0x00,ENTERSAFE_USER_PIN_ID); apdu.cla=0x84; apdu.data=sbuf; apdu.lc=apdu.datalen=0x19; r = entersafe_transmit_apdu(card,&apdu,init_key,sizeof(init_key),0,1); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Preinstall user PIN failed"); } {/* user PUK */ memset(sbuf,0,sizeof(sbuf)); sbuf[0] = 0; /* key len extern */ sbuf[1] = 16; /* key len */ sbuf[2] = 0x0B; /* USAGE */ sbuf[3] = ENTERSAFE_AC_ALWAYS; /* use AC */ sbuf[4] = 0X08; /* CHANGE AC */ sbuf[5] = 0xC0; /* UPDATE AC */ sbuf[6] = 0x01; /* ALGO */ sbuf[7] = 0xFF; /* EC */ sbuf[8] = 0x00; /* VER */ sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xF0,0x00,ENTERSAFE_USER_PIN_ID+1); apdu.cla=0x84; apdu.data=sbuf; apdu.lc=apdu.datalen=0x19; r = entersafe_transmit_apdu(card,&apdu,init_key,sizeof(init_key),0,1); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Preinstall user PUK failed"); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); } 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.13.0/src/libopensc/iasecc-sm.c0000644000015201777760000005367012057406034014615 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" #ifndef ENABLE_OPENSSL #error "Need OpenSSL" #endif #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 = card->ctx; struct sc_card_cache *cache = &card->cache; if (!card || !sm_info) return SC_ERROR_INVALID_ARGUMENTS; 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; } #endif 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; #ifdef ENABLE_SM struct sc_remote_apdu *rapdu = rdata->data; int rv = SC_SUCCESS, 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%X", 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)) { int 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); #else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "built without support of SM and External Authentication"); return SC_ERROR_NOT_SUPPORTED; #endif } /* 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; #ifdef ENABLE_SM 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); #else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "built without support of SM and External Authentication"); return SC_ERROR_NOT_SUPPORTED; #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 "); strncpy(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 } static int iasecc_sm_se_mutual_authentication(struct sc_card *card, unsigned se_num) { struct sc_context *ctx = card->ctx; #ifdef ENABLE_SM 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"); if (se.df) 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); #else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "built without support of Secure-Messaging"); return SC_ERROR_NOT_SUPPORTED; #endif } static int iasecc_sm_get_challenge(struct sc_card *card, unsigned char *out, size_t len) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE]; int rv; sc_log(ctx, "SM get challenge: length %i",len); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x84, 0, 0); apdu.le = len; apdu.resplen = len; apdu.resp = rbuf; rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, rv, "Command failed"); memcpy(out, rbuf, apdu.resplen); LOG_FUNC_RETURN(ctx, apdu.resplen); } 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); strncpy(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 = iasecc_sm_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() trasmit APDUs failed"); rdata.free(&rdata); sc_log(ctx, "MA data(len:%i) '%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 } 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; #ifdef ENABLE_SM 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 %i", 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 %i", apdu->resplen); } 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_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:%i)", 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:%i)", 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(%i) '%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:%i, count:%i", 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:%i, count:%i", 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, 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 *)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.13.0/src/libopensc/reader-ctapi.c0000644000015201777760000003616012057406034015304 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error getting status of terminal: %d, using defaults\n", rv); return SC_ERROR_TRANSMIT_FAILED; } if (rbuf[lr-2] != 0x90) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "SW1/SW2: 0x%x/0x%x\n", 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; 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_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error getting status of terminal: %d/%d/0x%x\n", 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_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Invalid data object returnd on CTBCS_P2_STATUS_ICC: 0x%x\n", rbuf[0]); return SC_ERROR_TRANSMIT_FAILED; } /* Fixme - should not be reached */ sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Returned status for %d slots\n", 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 (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_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error transmitting APDU: %d\n", 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, SC_LOG_DEBUG_NORMAL, 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_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "unable to transmit"); goto out; } sc_apdu_log(reader->ctx, SC_LOG_DEBUG_NORMAL, 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; int r; 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_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error activating card: %d\n", rv); return SC_ERROR_TRANSMIT_FAILED; } if (lr < 2) SC_FUNC_RETURN(reader->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); lr -= 2; if (lr > SC_MAX_ATR_SIZE) return SC_ERROR_INTERNAL; reader->atr.len = lr; memcpy(reader->atr.value, rbuf, lr); r = _sc_parse_atr(reader); #if 0 if (reader->atr_info.Fi > 0) { /* Perform PPS negotiation */ cmd[1] = CTBCS_INS_RESET; cmd[4] = 0x03; cmd[5] = 0xFF; cmd[6] = 0x10; cmd[7] = (reader->atr_info.FI << 4) | reader->atr_info.DI; cmd[8] = 0x00; dad = 1; sad = 2; lr = 256; rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, 9, cmd, &lr, rbuf); if (rv) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error negotiating PPS: %d\n", rv); return SC_ERROR_TRANSMIT_FAILED; } } #endif 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); 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, 0, 0, NULL }; static struct ctapi_module * add_module(struct ctapi_global_private_data *gpriv, const char *name, void *dlhandle) { int i; i = gpriv->module_count; gpriv->modules = (struct ctapi_module *) realloc(gpriv->modules, sizeof(struct ctapi_module) * (i+1)); 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; 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "No ports configured.\n"); return -1; } val = conf->name->data; dlh = sc_dlopen(val); if (!dlh) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Unable to open shared library '%s': %s\n", 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); 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "Port '%s' is not a number.\n", list->data); continue; } rv = funcs.CT_init((unsigned short)mod->ctn_count, (unsigned short)port); if (rv) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "CT_init() failed with %d\n", rv); continue; } reader = calloc(1, sizeof(sc_reader_t)); priv = calloc(1, sizeof(struct ctapi_private_data)); if (!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; 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_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error getting status of terminal: %d, using defaults\n", 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_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Invalid data object returnd on CTBCS_P2_STATUS_TFU: 0x%x\n", rbuf[0]); } NumUnits = rbuf[1]; if (NumUnits + 4 > lr) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Invalid data returnd: %d functional units, size %d\n", 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_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Found slot id 0x%x\n", rbuf[i+2]); break; case CTBCS_P1_DISPLAY: priv->ctapi_functional_units |= CTAPI_FU_DISPLAY; sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Display detected\n"); break; case CTBCS_P1_KEYPAD: priv->ctapi_functional_units |= CTAPI_FU_KEYBOARD; sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Keypad detected\n"); break; case CTBCS_P1_PRINTER: priv->ctapi_functional_units |= CTAPI_FU_PRINTER; sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Printer detected\n"); 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_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Biometric sensor detected\n"); break; default: sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Unknown functional unit 0x%x\n", 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_debug(ctx, SC_LOG_DEBUG_NORMAL, "Unable to resolve CT-API symbols.\n"); 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; for (i = 0; ctx->conf_blocks[i] != NULL; i++) { blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], "reader_driver", "ctapi"); if (blocks && blocks[0]) conf_block = blocks[0]; free(blocks); if (conf_block != NULL) break; } if (conf_block == NULL) return 0; 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.13.0/src/libopensc/card-rutoken.c0000644000015201777760000011675012057406034015346 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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 struct sc_atr_table rutoken_atrs[] = { { "3b:6f:00:ff:00:56:72:75:54:6f:6b:6e:73:30:20:00:00:90:00", NULL, NULL, SC_CARD_TYPE_GENERIC_BASE, 0, NULL }, /* Aktiv Rutoken S */ { "3b:6f:00:ff:00:56:75:61:54:6f:6b:6e:73:30:20:00:00:90:00", NULL, NULL, SC_CARD_TYPE_GENERIC_BASE, 0, NULL }, /* Aktiv uaToken S */ { 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); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "ATR recognized as Rutoken\n"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 1); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 0); } static int token_init(sc_card_t *card, const char *card_name) { SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); 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"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s\n", rutoken_errors[i].errorstr); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "sw1 = %x, sw2 = %x", sw1, sw2); return rutoken_errors[i].errorno; } } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown SWs; SW1=%02X, SW2=%02X\n", sw1, sw2); return SC_ERROR_CARD_CMD_FAILED; } static 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); if (apdu.sw1 == 0x6A && apdu.sw2 == 0x82) break; /* Next file not found */ ret = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, ""); if (apdu.resplen <= 2) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_WRONG_LENGTH); /* save first file(dir) ID */ tag = sc_asn1_find_tag(card->ctx, apdu.resp + 2, apdu.resplen - 2, 0x83, &taglen); if (!tag || taglen != sizeof(previd)) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, ""); } sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0x02); apdu.lc = sizeof(previd); apdu.data = previd; apdu.datalen = sizeof(previd); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, len); } 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, (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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SC_AC_OP_UPDATE %i %i", (int)(*(int8_t*)&file->sec_attr[1 +1]), file->sec_attr[1+7 +1*4]); sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, (int)(*(int8_t*)&file->sec_attr[1 +1]), file->sec_attr[1+7 +1*4]); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SC_AC_OP_WRITE %i %i", (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; u8 t0, t1; 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); default: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, ""); if (apdu.resplen > 0 && apdu.resp[0] != 0x62) /* Tag 0x62 - FCP */ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); file = sc_file_new(); if (file == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); file->path = *in_path; if (card->ops->process_fci == NULL) { sc_file_free(file); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); } if (apdu.resplen > 1 && apdu.resplen >= (size_t)apdu.resp[1] + 2) { ret = card->ops->process_fci(card, file, apdu.resp+2, apdu.resp[1]); if (ret == SC_SUCCESS) { t0 = file->id & 0xFF; t1 = (file->id >> 8) & 0xFF; file->id = (t0 << 8) | t1; t0 = file->size & 0xFF; t1 = (file->size >> 8) & 0xFF; file->size = (t0 << 8) | t1; } } if (file->sec_attr && file->sec_attr_len == sizeof(sc_SecAttrV2_t)) set_acl_from_sec_attr(card, file); else ret = SC_ERROR_UNKNOWN_DATA_RECEIVED; if (ret != SC_SUCCESS) sc_file_free(file); else { assert(file_out); *file_out = file; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); 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: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "AccessMode.%u, attr[0]=0x%x", conv_attr[i].sec_attr_pos, attr[0]); attr[1 + conv_attr[i].sec_attr_pos] = (u8)entry->method; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "method %u", (u8)entry->method); if (entry->method == SC_AC_CHV) { attr[1+7 + conv_attr[i].sec_attr_pos*4] = (u8)entry->key_ref; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "key_ref %u", (u8)entry->key_ref); } } else { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Set sec_attr from ACL failed"); } assert(iso_ops && iso_ops->create_file); ret = iso_ops->create_file(card, file); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File type has to be SC_PATH_TYPE_FILE_ID\n"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); } 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_transmit_apdu(card, &apdu), "APDU transmit failed"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2)); } 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); if (ret == SC_ERROR_PIN_CODE_INCORRECT && tries_left) { sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, ref_qualifier); ret = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); if (ret == SC_ERROR_PIN_CODE_INCORRECT) *tries_left = (int)(apdu.sw2 & 0x0f); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, ret); } 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Select MF failed"); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x40, 0x00, 0x00); apdu.cla = 0x80; ret = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Invalid 'old' pass"); } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, ref_qualifier); apdu.lc = newlen; apdu.datalen = newlen; apdu.data = newref; ret = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Tries left: %i\n", left); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Invalid 'puk' pass"); } #endif sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2c, 0x03, ref_qualifier); ret = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); senv = (auth_senv_t*)card->drv_data; if (!senv) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); if (env->algorithm != SC_ALGORITHM_GOST) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); senv->algorithm = SC_ALGORITHM_GOST; if (env->key_ref_len != 1) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No or invalid key reference\n"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); } 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: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); } /* set SE */ ret = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); 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; 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, ": crgram_len %i; outlen %i", 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "len out cipher %d\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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); if (senv->algorithm == SC_ALGORITHM_GOST) ret = rutoken_compute_mac_gost(card, data, datalen, out, outlen); else ret = SC_ERROR_NOT_SUPPORTED; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, ret); } static int rutoken_get_challenge(sc_card_t *card, u8 *rnd, size_t count) { sc_apdu_t apdu; u8 rbuf[32]; size_t n; int ret = SC_SUCCESS; /* if count == 0 */ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x84, 0x00, 0x00); apdu.le = sizeof(rbuf); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); while (count > 0) { ret = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Get challenge failed"); if (apdu.resplen != sizeof(rbuf)) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN); n = count < sizeof(rbuf) ? count : sizeof(rbuf); memcpy(rnd, rbuf, n); count -= n; rnd += n; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, ret); } static int rutoken_get_serial(sc_card_t *card, sc_serial_number_t *serial) { sc_apdu_t apdu; int ret; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, 0x81); apdu.resp = serial->value; apdu.resplen = sizeof(serial->value); apdu.le = 4; ret = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, 0x89); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = sizeof(rbuf); ret = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, apdu_ins, 0x00, 0x00); apdu.cla = 0x80; ret = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "cmd = %d", cmd); ret = SC_ERROR_NOT_SUPPORTED; break; } } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; /* 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.13.0/src/libopensc/pkcs15.h0000644000015201777760000010001412057406034014045 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _OPENSC_PKCS15_H #define _OPENSC_PKCS15_H #ifdef __cplusplus extern "C" { #endif #include "libopensc/opensc.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; }; 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_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_pubkey_dsa { sc_pkcs15_bignum_t pub; sc_pkcs15_bignum_t p; sc_pkcs15_bignum_t q; sc_pkcs15_bignum_t g; }; struct sc_pkcs15_prkey_dsa { /* public components */ sc_pkcs15_bignum_t pub; sc_pkcs15_bignum_t p; sc_pkcs15_bignum_t q; sc_pkcs15_bignum_t g; /* private key */ sc_pkcs15_bignum_t priv; }; /* * The ecParameters can be presented as * - named curve; * - OID of named curve; * - implicit parameters. */ struct sc_pkcs15_ec_parameters { char *named_curve; struct sc_object_id id; sc_pkcs15_der_t der; size_t field_length; /* in bits */ }; 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_pkcs15_ec_parameters params; sc_pkcs15_der_t ecpointQ; /* note this is der */ }; struct sc_pkcs15_prkey_ec { struct sc_pkcs15_ec_parameters params; sc_pkcs15_bignum_t privateD; /* note this is bignum */ sc_pkcs15_der_t ecpointQ; /* note this is der */ }; 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 { int algorithm; struct sc_algorithm_id * alg_id; /* Decoded key */ union { struct sc_pkcs15_pubkey_rsa rsa; struct sc_pkcs15_pubkey_dsa dsa; struct sc_pkcs15_pubkey_ec ec; struct sc_pkcs15_pubkey_gostr3410 gostr3410; } u; /* DER encoded raw key */ sc_pkcs15_der_t data; }; typedef struct sc_pkcs15_pubkey sc_pkcs15_pubkey_t; struct sc_pkcs15_prkey { unsigned int algorithm; /* TODO do we need: struct sc_algorithm_id * alg_id; */ union { struct sc_pkcs15_prkey_rsa rsa; struct sc_pkcs15_prkey_dsa dsa; struct sc_pkcs15_prkey_ec ec; struct sc_pkcs15_prkey_gostr3410 gostr3410; } 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 *crl; size_t crl_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; sc_pkcs15_der_t value; }; typedef struct sc_pkcs15_cert_info sc_pkcs15_cert_info_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; 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; }; typedef struct sc_pkcs15_data_info sc_pkcs15_data_info_t; #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 /* keyUsageFlags are the same for all key types */ #define SC_PKCS15_KEY_USAGE_ENCRYPT 0x01 #define SC_PKCS15_KEY_USAGE_DECRYPT 0x02 #define SC_PKCS15_KEY_USAGE_SIGN 0x04 #define SC_PKCS15_KEY_USAGE_SIGNRECOVER 0x08 #define SC_PKCS15_KEY_USAGE_WRAP 0x10 #define SC_PKCS15_KEY_USAGE_UNWRAP 0x20 #define SC_PKCS15_KEY_USAGE_VERIFY 0x40 #define SC_PKCS15_KEY_USAGE_VERIFYRECOVER 0x80 #define SC_PKCS15_KEY_USAGE_DERIVE 0x100 #define SC_PKCS15_KEY_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 */ 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; }; 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; }; 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; 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_skey sc_pkcs15_data #define sc_pkcs15_skey_t sc_pkcs15_data_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_DSA 0x102 #define SC_PKCS15_TYPE_PRKEY_GOSTR3410 0x103 #define SC_PKCS15_TYPE_PRKEY_EC 0x104 #define SC_PKCS15_TYPE_PUBKEY 0x200 #define SC_PKCS15_TYPE_PUBKEY_RSA 0x201 #define SC_PKCS15_TYPE_PUBKEY_DSA 0x202 #define SC_PKCS15_TYPE_PUBKEY_GOSTR3410 0x203 #define SC_PKCS15_TYPE_PUBKEY_EC 0x204 #define SC_PKCS15_TYPE_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; /* Used by minidriver and its on-card support */ char *guid; }; 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 *, 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; } opts; unsigned int magic; void *dll_handle; /* shared lib for emulated cards */ 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 sc_pkcs15_card_t */ #define SC_PKCS15_CARD_FLAG_EMULATED 0x02000000 /* 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_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(sc_pkcs15_card_t *, unsigned int, const sc_pkcs15_id_t *, sc_pkcs15_object_t **); struct sc_pkcs15_card * sc_pkcs15_card_new(void); void sc_pkcs15_card_free(struct sc_pkcs15_card *p15card); void sc_pkcs15_card_clear(sc_pkcs15_card_t *p15card); 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); int sc_pkcs15_derive(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_object *prkey_obj, unsigned long flags, const u8 *in, size_t inlen, u8 *out, unsigned long *poutlen); 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); 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_dsa(struct sc_context *, struct sc_pkcs15_pubkey_dsa *, const u8 *, size_t); int sc_pkcs15_encode_pubkey_dsa(struct sc_context *, struct sc_pkcs15_pubkey_dsa *, u8 **, size_t *); int sc_pkcs15_decode_pubkey_gostr3410(sc_context_t *, struct sc_pkcs15_pubkey_gostr3410 *, const u8 *, size_t); int sc_pkcs15_encode_pubkey_gostr3410(sc_context_t *, 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_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 *); 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_pubkey_from_cert(struct sc_context *, struct sc_pkcs15_der *, struct sc_pkcs15_pubkey **); int sc_pkcs15_pubkey_from_spki_filename(struct sc_context *, char *, sc_pkcs15_pubkey_t ** ); int sc_pkcs15_pubkey_from_spki(struct sc_context *, sc_pkcs15_pubkey_t **, 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_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, 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, 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); /* 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_create(struct sc_pkcs15_card *p15card, struct sc_card *card); int sc_pkcs15_find_prkey_by_id(struct sc_pkcs15_card *card, const struct sc_pkcs15_id *id, 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(sc_pkcs15_card_t *, const sc_path_t *, int, sc_pkcs15_object_t **); 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_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_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 sc_pkcs15_object_t *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(sc_context_t *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_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_decode_enveloped_data(struct sc_context *ctx, struct sc_pkcs15_enveloped_data *result, const u8 *buf, size_t buflen); int sc_pkcs15_encode_enveloped_data(struct sc_context *ctx, struct sc_pkcs15_enveloped_data *data, u8 **buf, size_t *buflen); int sc_pkcs15_add_object(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj); void sc_pkcs15_remove_object(struct sc_pkcs15_card *p15card, 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(sc_context_t *ctx, struct sc_pkcs15_card *p15card, u8 **buf, size_t *buflen); /* Deduce private key attributes from cerresponding 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_object(sc_pkcs15_object_t *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); /* 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(sc_pkcs15_der_t *, const sc_pkcs15_der_t *); int sc_pkcs15_get_object_id(const struct sc_pkcs15_object *, struct sc_pkcs15_id *); int sc_pkcs15_get_guid(struct sc_pkcs15_card *, const struct sc_pkcs15_object *, 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); struct sc_supported_algo_info *sc_pkcs15_get_supported_algo(struct sc_pkcs15_card *, unsigned, unsigned); 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_pkcs15_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); /* 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(sc_pkcs15_card_t *, sc_pkcs15_search_key_t *, sc_pkcs15_object_t **, size_t); /* This structure is passed to the new sc_pkcs15emu_*_init functions */ typedef struct sc_pkcs15emu_opt { scconf_block *blk; unsigned int flags; } sc_pkcs15emu_opt_t; #define SC_PKCS15EMU_FLAGS_NO_CHECK 0x00000001 extern int sc_pkcs15_bind_synthetic(sc_pkcs15_card_t *); extern int sc_pkcs15_is_emulation_only(sc_card_t *); int sc_pkcs15emu_object_add(sc_pkcs15_card_t *, unsigned int, const sc_pkcs15_object_t *, const void *); /* some wrapper functions for sc_pkcs15emu_object_add */ int sc_pkcs15emu_add_pin_obj(sc_pkcs15_card_t *, const sc_pkcs15_object_t *, const sc_pkcs15_auth_info_t *); int sc_pkcs15emu_add_rsa_prkey(sc_pkcs15_card_t *, const sc_pkcs15_object_t *, const sc_pkcs15_prkey_info_t *); int sc_pkcs15emu_add_rsa_pubkey(sc_pkcs15_card_t *, const sc_pkcs15_object_t *, const sc_pkcs15_pubkey_info_t *); int sc_pkcs15emu_add_ec_prkey(sc_pkcs15_card_t *, const sc_pkcs15_object_t *, const sc_pkcs15_prkey_info_t *); int sc_pkcs15emu_add_ec_pubkey(sc_pkcs15_card_t *, const sc_pkcs15_object_t *, const sc_pkcs15_pubkey_info_t *); int sc_pkcs15emu_add_x509_cert(sc_pkcs15_card_t *, const sc_pkcs15_object_t *, const sc_pkcs15_cert_info_t *); int sc_pkcs15emu_add_data_object(sc_pkcs15_card_t *, const sc_pkcs15_object_t *, const sc_pkcs15_data_info_t *); #ifdef __cplusplus } #endif #endif opensc-0.13.0/src/libopensc/errors.c0000644000015201777760000001166312057406034014261 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include "errors.h" #define DIM(v) (sizeof(v)/(sizeof((v)[0]))) const char *sc_strerror(int error) { 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 (hotplug device?)", "Reader reattached (hotplug device?)", "Reader in use by another application" }; const 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 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 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" }; const 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 int p15i_base = -SC_ERROR_PKCS15INIT; const int sm_base = -SC_ERROR_SM; 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" }; const char *misc_errors[] = { "Unknown error", "PKCS#15 compatible smart card not found", }; const int misc_base = -SC_ERROR_UNKNOWN; const char *no_errors = "Success"; const char **errors = NULL; int count = 0, err_base = 0; if (!error) return no_errors; if (error < 0) error = -error; if (error >= misc_base) { errors = misc_errors; count = DIM(misc_errors); err_base = misc_base; } else if (error >= sm_base) { errors = sm_errors; count = DIM(sm_errors); err_base = sm_base; } else if (error >= p15i_base) { errors = p15i_errors; count = DIM(p15i_errors); err_base = p15i_base; } else if (error >= int_base) { errors = int_errors; count = DIM(int_errors); err_base = int_base; } else if (error >= arg_base) { errors = arg_errors; count = DIM(arg_errors); err_base = arg_base; } else if (error >= card_base) { errors = card_errors; count = DIM(card_errors); err_base = card_base; } else if (error >= rdr_base) { errors = rdr_errors; count = DIM(rdr_errors); err_base = rdr_base; } error -= err_base; if (error >= count || count == 0) return misc_errors[0]; return errors[error]; } opensc-0.13.0/src/libopensc/log.h0000644000015201777760000000641512057406034013532 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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_RFU2, /* RFU */ SC_LOG_DEBUG_ASN1, /* asn1.c only */ SC_LOG_DEBUG_MATCH, /* card matching only */ }; /* You can't do #ifndef __FUNCTION__ */ #if !defined(__GNUC__) && !defined(__IBMC__) && !(defined(_MSC_VER) && (_MSC_VER >= 1300)) #define __FUNCTION__ NULL #endif #if defined(__GNUC__) #define sc_debug(ctx, level, format, args...) sc_do_log(ctx, level, __FILE__, __LINE__, __FUNCTION__, format , ## args) #define sc_log(ctx, format, args...) sc_do_log(ctx, SC_LOG_DEBUG_NORMAL, __FILE__, __LINE__, __FUNCTION__, format , ## args) #else #define sc_debug _sc_debug #define sc_log _sc_log #endif 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_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_hex_dump(struct sc_context *ctx, int level, const u8 * buf, size_t len, char *out, size_t outlen); char * sc_dump_hex(const u8 * in, size_t count); #define SC_FUNC_CALLED(ctx, level) do { \ sc_do_log(ctx, level, __FILE__, __LINE__, __FUNCTION__, "called\n"); \ } while (0) #define LOG_FUNC_CALLED(ctx) SC_FUNC_CALLED((ctx), SC_LOG_DEBUG_NORMAL) #define SC_FUNC_RETURN(ctx, level, r) do { \ int _ret = r; \ if (_ret <= 0) { \ sc_do_log(ctx, level, __FILE__, __LINE__, __FUNCTION__, \ "returning with: %d (%s)\n", _ret, sc_strerror(_ret)); \ } else { \ sc_do_log(ctx, level, __FILE__, __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(ctx, level, __FILE__, __LINE__, __FUNCTION__, \ "%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)) #ifdef __cplusplus } #endif #endif opensc-0.13.0/src/libopensc/iasecc-sdo.c0000644000015201777760000011425712057406034014762 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 *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, 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) 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; 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 (ops[ii].mask == 0) LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); sc_log(ctx, "OP:%i, mask:0x%X", op, ops[ii].mask); sc_log(ctx, "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) { if (sdo->docp.tries_remaining.value) free(sdo->docp.tries_remaining.value); if (sdo->docp.usage_remaining.value) free(sdo->docp.usage_remaining.value); if (sdo->docp.non_repudiation.value) free(sdo->docp.non_repudiation.value); if (sdo->docp.acls_contact.value) free(sdo->docp.acls_contact.value); if (sdo->docp.size.value) free(sdo->docp.size.value); if (sdo->docp.name.value) free(sdo->docp.name.value); if (sdo->docp.issuer_data.value) free(sdo->docp.issuer_data.value); if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PUBLIC) { if (sdo->data.pub_key.n.value) free(sdo->data.pub_key.n.value); if (sdo->data.pub_key.e.value) free(sdo->data.pub_key.e.value); if (sdo->data.pub_key.compulsory.value) free(sdo->data.pub_key.compulsory.value); if (sdo->data.pub_key.chr.value) free(sdo->data.pub_key.chr.value); if (sdo->data.pub_key.cha.value) free(sdo->data.pub_key.cha.value); } else if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PRIVATE) { if (sdo->data.prv_key.p.value) free(sdo->data.prv_key.p.value); if (sdo->data.prv_key.q.value) free(sdo->data.prv_key.q.value); if (sdo->data.prv_key.iqmp.value) free(sdo->data.prv_key.iqmp.value); if (sdo->data.prv_key.dmp1.value) free(sdo->data.prv_key.dmp1.value); if (sdo->data.prv_key.dmq1.value) free(sdo->data.prv_key.dmq1.value); if (sdo->data.prv_key.compulsory.value) free(sdo->data.prv_key.compulsory.value); } else if (sdo->sdo_class == IASECC_SDO_CLASS_CHV) { if (sdo->data.chv.size_max.value) free(sdo->data.chv.size_max.value); if (sdo->data.chv.size_min.value) free(sdo->data.chv.size_min.value); if (sdo->data.chv.value.value) free(sdo->data.chv.value.value); } } 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, 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); memset(&crt, 0, sizeof(crt)); crt.tag = *(data + 0); len = *(data + 1); for(offs = 2; offs < len + 2; offs += 3) { 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, size_size; int rv; LOG_FUNC_CALLED(ctx); if (*data == IASECC_SDO_TEMPLATE_TAG) { size_size = iasecc_parse_size(data + 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 %i, size_size %i", size, size_size); if (*data != IASECC_SDO_TAG_HEADER) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); if ((*(data + 1) & 0x7F) != IASECC_SDO_CLASS_SE) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); size_size = iasecc_parse_size(data + 3, &size); LOG_TEST_RET(ctx, size_size, "parse error: invalid SDO SE data size"); if (data_len != size + size_size + 3) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalide SDO SE data size"); data += 3 + size_size; data_len = size; sc_log(ctx, "IASECC_SDO_TEMPLATE SE: size %i, size_size %i", size, size_size); } if (*data != IASECC_SDO_CLASS_SE) { sc_log(ctx, "Invalid SE tag 0x%X; data length %i", *data, data_len); LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } size_size = iasecc_parse_size(data + 1, &size); LOG_TEST_RET(ctx, size_size, "parse error: invalid size data"); if (data_len != size + size_size + 1) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalide SE data size"); offs = 1 + size_size; for (; offs < data_len;) { rv = iasecc_crt_parse(card, data + offs, se); LOG_TEST_RET(ctx, rv, "parse error: invalid SE data"); offs += rv; } if (offs != data_len) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: not totaly parsed"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_parse_size(unsigned char *data, size_t *out) { if (*data < 0x80) { *out = *data; return 1; } else if (*data == 0x81) { *out = *(data + 1); return 2; } else if (*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, struct iasecc_extended_tlv *tlv) { struct sc_context *ctx = card->ctx; size_t size_len, tag_len; memset(tlv, 0, sizeof(*tlv)); sc_log(ctx, "iasecc_parse_get_tlv() called for tag 0x%X", *data); if ((*data == 0x7F) || (*data == 0x5F)) { 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, &tlv->size); LOG_TEST_RET(ctx, size_len, "parse error: invalid size 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 %i bytes", tag_len + size_len + tlv->size); return 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, &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 %i", rv, tlv.tag, tlv.size); if (tlv.tag == IASECC_SDO_CHV_TAG_SIZE_MAX) chv->size_max = tlv; else if (tlv.tag == IASECC_SDO_CHV_TAG_SIZE_MIN) chv->size_min = tlv; else if (tlv.tag == IASECC_SDO_CHV_TAG_VALUE) chv->value = tlv; else 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, &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 %i", rv, tlv.tag, tlv.size); if (tlv.tag == IASECC_SDO_PRVKEY_TAG_COMPULSORY) prvkey->compulsory = tlv; else 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, &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 %i", rv, tlv.tag, tlv.size); if (tlv.tag == IASECC_SDO_PUBKEY_TAG_N) pubkey->n = tlv; else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_E) pubkey->e = tlv; else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_CHR) pubkey->chr = tlv; else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_CHA) pubkey->cha = tlv; else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_COMPULSORY) pubkey->compulsory = tlv; else 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, &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 %i", rv, tlv.tag, tlv.size); if (tlv.tag == IASECC_SDO_KEYSET_TAG_COMPULSORY) keyset->compulsory = tlv; else 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, &tlv); LOG_TEST_RET(ctx, rv, "iasecc_parse_get_tlv() get and parse TLV error"); sc_log(ctx, "iasecc_parse_docp() parse_get_tlv retuned %i; tag %X; size %i", 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) { sdo->docp.acls_contact = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_ACLS_CONTACTLESS) { sdo->docp.acls_contactless = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_SIZE) { sdo->docp.size = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_NAME) { sdo->docp.name = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_ISSUER_DATA) { sdo->docp.issuer_data = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_NON_REPUDATION) { sdo->docp.non_repudiation = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_USAGE_REMAINING) { sdo->docp.usage_remaining = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_TRIES_MAXIMUM) { sdo->docp.tries_maximum = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_TRIES_REMAINING) { sdo->docp.tries_remaining = tlv; } else { 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, 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, &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 %i", 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_REPUDATION) { sdo->docp.non_repudiation = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_USAGE_REMAINING) { sdo->docp.usage_remaining = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_TRIES_MAXIMUM) { sdo->docp.tries_maximum = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_TRIES_REMAINING) { sdo->docp.tries_remaining = tlv; } else if (tlv.tag == IASECC_SDO_CHV_TAG) { if (sdo->sdo_class != IASECC_SDO_CLASS_CHV) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: IASECC_SDO_CHV_TAG tag in non User CHV SDO"); rv = iasecc_parse_chv(card, tlv.value, tlv.size, &sdo->data.chv); LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO CHV data"); free(tlv.value); } else if (tlv.tag == IASECC_SDO_PUBKEY_TAG) { if (sdo->sdo_class != IASECC_SDO_CLASS_RSA_PUBLIC) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: SDO_PUBLIC_KEY tag in non PUBLIC_KEY SDO"); rv = iasecc_parse_pubkey(card, tlv.value, tlv.size, &sdo->data.pub_key); LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO PUBLIC KEY data"); free(tlv.value); } else if (tlv.tag == IASECC_SDO_PRVKEY_TAG) { if (sdo->sdo_class != IASECC_SDO_CLASS_RSA_PRIVATE) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: SDO_PRIVATE_KEY tag in non PRIVATE_KEY SDO"); rv = iasecc_parse_prvkey(card, tlv.value, tlv.size, &sdo->data.prv_key); LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO PRIVATE KEY data"); free(tlv.value); } else if (tlv.tag == IASECC_SDO_KEYSET_TAG) { if (sdo->sdo_class != IASECC_SDO_CLASS_KEYSET) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: SDO_KEYSET tag in non KEYSET SDO"); rv = iasecc_parse_keyset(card, tlv.value, tlv.size, &sdo->data.keyset); LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO KEYSET data"); free(tlv.value); } else { sc_log(ctx, "iasecc_sdo_parse_data() non supported tag 0x%X", tlv.tag); 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, size_size; int rv; LOG_FUNC_CALLED(ctx); if (*data == IASECC_SDO_TEMPLATE_TAG) { size_size = iasecc_parse_size(data + 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 %i, size_size %i", 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, &size); LOG_TEST_RET(ctx, size_size, "parse error: invalid size data"); if (data_len != size + size_size + 3) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalide SDO data size"); sc_log(ctx, "sz %i, sz_size %i", size, size_size); offs = 3 + size_size; for (; offs < data_len;) { rv = iasecc_sdo_parse_data(card, data + offs, sdo); LOG_TEST_RET(ctx, rv, "parse error: invalid SDO data"); offs += rv; } if (offs != data_len) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: not totaly parsed"); sc_log(ctx, "docp.acls_contact.size %i, docp.size.size %i", 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, 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_MEMORY_FAILURE; 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) { *out = sdo; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } size_size = iasecc_parse_size(data + 3, &size); LOG_TEST_RET(ctx, size_size, "parse error: invalid size data"); if (data_len != size + size_size + 3) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalide SDO data size"); sc_log(ctx, "sz %i, sz_size %i", size, size_size); offs = 3 + size_size; for (; offs < data_len;) { rv = iasecc_sdo_parse_data(card, data + offs, sdo); LOG_TEST_RET(ctx, rv, "parse error: invalid SDO data"); offs += rv; } if (offs != data_len) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: not totaly parsed"); sc_log(ctx, "docp.acls_contact.size %i; docp.size.size %i", sdo->docp.acls_contact.size, sdo->docp.size.size); *out = sdo; 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; int offs = 0, sz = tlv->size + 2; 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_MEMORY_FAILURE); 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, *tmp_blob, *docp_blob; 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)); st_blob = NULL; blob_size = 0; rv = iasecc_update_blob(ctx, &docp->acls_contact, &st_blob, &blob_size); LOG_TEST_RET(ctx, rv, "ECC: cannot add contact ACLs to blob"); rv = iasecc_update_blob(ctx, &docp->acls_contactless, &st_blob, &blob_size); LOG_TEST_RET(ctx, rv, "ECC: cannot add contactless ACLs to blob"); tlv.tag = IASECC_DOCP_TAG_ACLS; tlv.size = blob_size; tlv.value = st_blob; tmp_blob = NULL; blob_size = 0; rv = iasecc_update_blob(ctx, &tlv, &tmp_blob, &blob_size); LOG_TEST_RET(ctx, rv, "ECC: cannot add ACLs template to blob"); rv = iasecc_update_blob(ctx, &docp->name, &tmp_blob, &blob_size); LOG_TEST_RET(ctx, rv, "ECC: cannot add NAME to blob"); rv = iasecc_update_blob(ctx, &docp->tries_maximum, &tmp_blob, &blob_size); LOG_TEST_RET(ctx, rv, "ECC: cannot add TRIES MAXIMUM to blob"); rv = iasecc_update_blob(ctx, &docp->tries_remaining, &tmp_blob, &blob_size); LOG_TEST_RET(ctx, rv, "ECC: cannot add TRIES REMAINING to blob"); rv = iasecc_update_blob(ctx, &docp->usage_maximum, &tmp_blob, &blob_size); LOG_TEST_RET(ctx, rv, "ECC: cannot add USAGE MAXIMUM to blob"); rv = iasecc_update_blob(ctx, &docp->usage_remaining, &tmp_blob, &blob_size); LOG_TEST_RET(ctx, rv, "ECC: cannot add USAGE REMAINING to blob"); rv = iasecc_update_blob(ctx, &docp->non_repudiation, &tmp_blob, &blob_size); LOG_TEST_RET(ctx, rv, "ECC: cannot add NON REPUDATION to blob"); rv = iasecc_update_blob(ctx, &docp->size, &tmp_blob, &blob_size); LOG_TEST_RET(ctx, rv, "ECC: cannot add SIZE to blob"); rv = iasecc_update_blob(ctx, &docp->issuer_data, &tmp_blob, &blob_size); LOG_TEST_RET(ctx, rv, "ECC: cannot add IDATA to blob"); tlv.tag = IASECC_DOCP_TAG; tlv.size = blob_size; tlv.value = tmp_blob; docp_blob = NULL; blob_size = 0; rv = iasecc_update_blob(ctx, &tlv, &docp_blob, &blob_size); LOG_TEST_RET(ctx, rv, "ECC: cannot add ACLs to blob"); free(tmp_blob); if (out && out_len) { *out = docp_blob; *out_len = blob_size; } 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"); sc_debug(ctx, SC_LOG_DEBUG_ASN1,"Create data: %s", sc_dump_hex(*out, out_len)); LOG_FUNC_RETURN(ctx, 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, 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 %i", 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() unbelivable !!!"); 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); } #endif /* ENABLE_OPENSSL */ opensc-0.13.0/src/libopensc/card-muscle.c0000644000015201777760000005736712057406034015157 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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 struct sc_atr_table muscle_atrs[] = { /* 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; /* Since we send an APDU, the card's logout function may be called... * however it's not always properly nulled out... */ card->ops->logout = NULL; if (msc_select_applet(card, muscleAppletId, 5) == 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 && response[0] == 0x01) { card->type = SC_CARD_TYPE_MUSCLE_V1; 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: acl_entry |= (1 << 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; int 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); int 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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) { int newFileSize = idx + count; u8* buffer = malloc(newFileSize); if(buffer == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); r = msc_read_object(card, objectId, 0, buffer, file->size); /* TODO: RETREIVE 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); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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; if(!file_data->ef) { int x; mscfs_file_t *childFile; /* Delete children */ mscfs_check_cache(fs); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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)) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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; */ } if((0 == memcmp(oid, "\x3F\x00\x00\x00", 4)) || (0 == memcmp(oid, "\x3F\x00\x3F\x00", 4))) { } r = msc_delete_object(card, id, 1); /* Check if its the root... this file generally is virtual * So don't return an error if it fails */ 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); 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; int pathlen = path_in->len; int r = 0; int objectIndex; u8* oid; mscfs_check_cache(fs); r = mscfs_loadFileInfo(fs, path_in->value, path_in->len, &file_data, &objectIndex); if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); /* Check if its the right type */ if(requiredType >= 0 && requiredType != file_data->ef) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); } 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 { 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; card->name = "MuscleApplet"; card->drv_data = malloc(sizeof(muscle_private_t)); if(!card->drv_data) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); } 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); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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 */ _sc_match_atr(card, muscle_atrs, &card->type); 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; } /* 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; int count = 0; mscfs_check_cache(priv->fs); for(x = 0; x < fs->cache.size; x++) { u8* oid= fs->cache.array[x].objectId.id; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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; } } 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; msc_verify_pin_apdu(card, &apdu, buffer, bufferLength, cmd->pin_reference, cmd->pin1.data, cmd->pin1.len); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported authentication method\n"); return SC_ERROR_NOT_SUPPORTED; } case SC_PIN_CMD_CHANGE: switch(cmd->pin_type) { case SC_AC_CHV: { sc_apdu_t apdu; msc_change_pin_apdu(card, &apdu, buffer, bufferLength, cmd->pin_reference, cmd->pin1.data, cmd->pin1.len, cmd->pin2.data, cmd->pin2.len); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported authentication method\n"); return SC_ERROR_NOT_SUPPORTED; } case SC_PIN_CMD_UNBLOCK: switch(cmd->pin_type) { case SC_AC_CHV: { sc_apdu_t apdu; msc_unblock_pin_apdu(card, &apdu, buffer, bufferLength, cmd->pin_reference, cmd->pin1.data, cmd->pin1.len); 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported authentication method\n"); return SC_ERROR_NOT_SUPPORTED; } default: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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 SUPPOT 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 SUPPOT 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid crypto operation supplied.\n"); return SC_ERROR_NOT_SUPPORTED; } if (env->algorithm != SC_ALGORITHM_RSA) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid crypto algorithm supplied.\n"); return SC_ERROR_NOT_SUPPORTED; } /* ADJUST FOR PKCS1 padding support for decryption only */ if ((env->algorithm_flags & SC_ALGORITHM_RSA_PADS) || (env->algorithm_flags & SC_ALGORITHM_RSA_HASHES)) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Card supports only raw RSA.\n"); return SC_ERROR_NOT_SUPPORTED; } if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { if (env->key_ref_len != 1 || (env->key_ref[0] > 0x0F)) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid key reference supplied.\n"); return SC_ERROR_NOT_SUPPORTED; } priv->rsa_key_ref = env->key_ref[0]; } if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Algorithm reference not supported.\n"); return SC_ERROR_NOT_SUPPORTED; } /* if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) if (memcmp(env->file_ref.value, "\x00\x12", 2) != 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File reference is not 0012.\n"); 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; /* saniti check */ if (priv->env.operation != SC_SEC_OPERATION_DECIPHER) return SC_ERROR_INVALID_ARGUMENTS; key_id = priv->rsa_key_ref * 2; /* Private key */ if (out_len < crgram_len) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Output buffer too small"); 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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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 return msc_get_challenge(card, len, 0, NULL, rnd); } 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 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; return &muscle_drv; } struct sc_card_driver * sc_get_muscle_driver(void) { return sc_get_driver(); } opensc-0.13.0/src/libopensc/pkcs15-actalis.c0000644000015201777760000002227712057406034015474 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #ifdef ENABLE_ZLIB #include #endif #include "common/compat_strlcpy.h" #include "libopensc/pkcs15.h" #include "libopensc/log.h" int sc_pkcs15emu_actalis_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); static int (*set_security_env) (sc_card_t *, const sc_security_env_t *, int); static int set_sec_env(sc_card_t * card, const sc_security_env_t *env, int se_num) { 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); } static void set_string(char **strp, const char *value) { if (*strp) free(*strp); *strp = value ? strdup(value) : NULL; } #if 1 /* XXX: temporary copy of the old pkcs15emu functions, * to be removed */ static int sc_pkcs15emu_add_pin(sc_pkcs15_card_t *p15card, const sc_pkcs15_id_t *id, const char *label, const sc_path_t *path, int ref, int type, unsigned int min_length, unsigned int max_length, int flags, int tries_left, const char pad_char, int obj_flags) { sc_pkcs15_auth_info_t info; sc_pkcs15_object_t obj; memset(&info, 0, sizeof(info)); memset(&obj, 0, sizeof(obj)); info.auth_id = *id; info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; info.attrs.pin.min_length = min_length; info.attrs.pin.max_length = max_length; info.attrs.pin.stored_length = max_length; info.attrs.pin.type = type; info.attrs.pin.reference = ref; info.attrs.pin.flags = flags; info.attrs.pin.pad_char = pad_char; info.tries_left = tries_left; 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.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; 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 i = 0, j = 0; const char *certLabel[] = { "User Non-repudiation Certificate", /* "User Non-repudiation Certificate" */ "TSA Certificate", "CA Certificate" }; const char *certPath[] = { "3F00300060006002", "3F00300060006003", "3F00300060006004" }; #endif const char *keyPath = "3F00300040000008"; const char *pinDfName = "05040200"; /* const int prkey_usage = SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; */ const int authprkey_usage = SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER | SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT; const char *authPIN = "Authentication PIN"; /* const char *nonrepPIN = "Non-repudiation PIN"; */ const char *authPRKEY = "Authentication Key"; /* const char *nonrepPRKEY = "Non repudiation Key"; */ p15card->opts.use_file_cache = 1; /* Get Serial number */ sc_format_path("3F0030000001", &path); r = sc_select_file(card, &path, NULL); if (r != SC_SUCCESS) return SC_ERROR_WRONG_CARD; sc_read_binary(card, 0xC3, serial_buf, 12, 0); 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)); sc_read_binary(card, 2, size, 2, 0); 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)); sc_read_binary(card, 4, compCert, compLen, 0); if (uncompress(cert, &len, compCert, compLen) != Z_OK) return SC_ERROR_INTERNAL; cpath.index = 0; cpath.count = 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; sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); } } #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, sc_pkcs15emu_opt_t * opts) { if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) return sc_pkcs15emu_actalis_init(p15card); else { int r = actalis_detect_card(p15card); if (r) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_actalis_init(p15card); } } opensc-0.13.0/src/Makefile.am0000644000015201777760000000031612057406034012650 00000000000000MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak # Order IS important SUBDIRS = common scconf pkcs15init libopensc pkcs11 \ tools tests minidriver if ENABLE_SM SUBDIRS += sm endif opensc-0.13.0/src/minidriver/0000755000015201777760000000000012057406120013040 500000000000000opensc-0.13.0/src/minidriver/minidriver-westcos.reg0000644000015201777760000000050012057406034017313 00000000000000Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\CEV WESTCOS] "80000001"="opensc-minidriver.dll" "ATR"=hex:3f,69,00,00,00,64,01,00,00,00,80,90,00 "ATRMask"=hex:ff,ff,ff,ff,ff,ff,ff,00,00,00,f0,ff,ff "Crypto Provider"="Microsoft Base Smart Card Crypto Provider" opensc-0.13.0/src/minidriver/opensc-minidriver.inf.in0000644000015201777760000000731512057406034017532 00000000000000 [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.13.0/src/minidriver/minidriver.exports0000644000015201777760000000002312057406034016555 00000000000000CardAcquireContext opensc-0.13.0/src/minidriver/Makefile.mak0000644000015201777760000000113412057406034015172 00000000000000TOPDIR = ..\.. TARGET = opensc-minidriver.dll OBJECTS = minidriver.obj !INCLUDE $(TOPDIR)\win32\Make.rules.mak all: $(TARGET) $(TARGET): $(OBJECTS) ..\libopensc\opensc_a.lib ..\pkcs15init\pkcs15init.lib echo LIBRARY $* > $*.def echo EXPORTS >> $*.def type minidriver.exports >> $*.def link /dll $(LINKFLAGS) /def:$*.def /out:$(TARGET) $(OBJECTS) ..\libopensc\opensc_a.lib ..\pkcs15init\pkcs15init.lib $(ZLIB_LIB) $(OPENSSL_LIB) ..\common\libscdl.lib ws2_32.lib gdi32.lib advapi32.lib Crypt32.lib User32.lib if EXIST $(TARGET).manifest mt -manifest $(TARGET).manifest -outputresource:$(TARGET);2 opensc-0.13.0/src/minidriver/opensc-minidriver.inf0000644000015201777760000000722112057406062017122 00000000000000 [Version] Signature="$Windows NT$" Class=SmartCard ClassGuid={990A2BD7-E738-46c7-B26F-1CF8FB9F1391} Provider=%ProviderName% CatalogFile=delta.cat DriverVer=05/02/2010,0,13,0,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.13.0/src/minidriver/minidriver-sc-hsm.reg0000644000015201777760000000266412057406034017033 00000000000000Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\SmartCard-HSM] "80000001"="opensc-minidriver.dll" "Crypto Provider"="Microsoft Base Smart Card Crypto Provider" "ATR"=hex:3b,fe,18,00,00,81,31,fe,45,80,31,81,54,48,53,4d,31,73,80,21,40,81,07,fa "ATRMask"=hex:ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\SmartCard-HSM-CL] "80000001"="opensc-minidriver.dll" "Crypto Provider"="Microsoft Base Smart Card Crypto Provider" "ATR"=hex:3B,8E,80,01,80,31,81,54,48,53,4D,31,73,80,21,40,81,07,18 "ATRMask"=hex:ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff opensc-0.13.0/src/minidriver/minidriver.c0000644000015201777760000033237212057406034015312 00000000000000/* * minidriver.c: OpenSC minidriver * * Copyright (C) 2009,2010 francois.leblanc@cev-sa.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * 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 "cardmod.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 "pkcs15init/pkcs15-init.h" #ifdef ENABLE_OPENSSL #include #if OPENSSL_VERSION_NUMBER >= 0x10000000L #include #endif #endif #if defined(__MINGW32__) /* Part of the build svn project in the include directory */ #include "cardmod-mingw-compat.h" #endif #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 12 #define MD_CARDID_SIZE 16 #define MD_UTC_TIME_LENGTH_MAX 16 #define MD_CARDCF_LENGTH (sizeof(CARD_CACHE_FILE_FORMAT)) #define MD_DATA_APPLICAITON_NAME "CSP" #define MD_DATA_DEFAULT_CONT_LABEL "Default Key Container" #define MD_KEY_USAGE_KEYEXCHANGE \ SC_PKCS15INIT_X509_KEY_ENCIPHERMENT | \ SC_PKCS15INIT_X509_DATA_ENCIPHERMENT | \ 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) /* 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 struct md_directory { unsigned char parent[9]; 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 parent[9]; 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 flags; unsigned size_key_exchange, size_sign; struct sc_pkcs15_object *cert_obj, *prkey_obj, *pubkey_obj; }; typedef struct _VENDOR_SPECIFIC { struct sc_pkcs15_object *obj_user_pin, *obj_sopin; 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; }VENDOR_SPECIFIC; /* * Windows (ex. Vista) may access the card from more the one thread. * The following data type and static data is an attemt to resolve * some of the encountered multi-thread issues of OpenSC * on the minidriver side. * * TODO: resolve multi-thread issues on the OpenSC side */ #define MD_STATIC_FLAG_READ_ONLY 1 #define MD_STATIC_FLAG_SUPPORTS_X509_ENROLLMENT 2 #define MD_STATIC_FLAG_CONTEXT_DELETED 4 #define MD_STATIC_PROCESS_ATTACHED 0xA11AC4EDL struct md_opensc_static_data { unsigned flags, flags_checked; unsigned long attach_check; }; static struct md_opensc_static_data md_static_data; #define C_ASN1_MD_CONTAINER_ATTRS_SIZE 7 static const struct sc_asn1_entry c_asn1_md_container_attrs[C_ASN1_MD_CONTAINER_ATTRS_SIZE] = { { "index", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "id", SC_ASN1_PKCS15_ID, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_EMPTY_ALLOWED, NULL, NULL }, { "guid", SC_ASN1_UTF8STRING, SC_ASN1_TAG_UTF8STRING, SC_ASN1_EMPTY_ALLOWED, NULL, NULL }, { "flags", SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, 0, NULL, NULL }, { "sizeKeyExchange", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "sizeSign", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_MD_CONTAINER_SIZE 2 static const struct sc_asn1_entry c_asn1_md_container[C_ASN1_MD_CONTAINER_SIZE] = { { "mdContainer", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static int associate_card(PCARD_DATA pCardData); static int disassociate_card(PCARD_DATA pCardData); static DWORD md_get_cardcf(PCARD_DATA pCardData, CARD_CACHE_FILE_FORMAT **out); static DWORD md_pkcs15_delete_object(PCARD_DATA pCardData, struct sc_pkcs15_object *obj); static DWORD md_fs_init(PCARD_DATA pCardData); static void logprintf(PCARD_DATA pCardData, int level, const char* format, ...) { va_list arg; VENDOR_SPECIFIC *vs; #define CARDMOD_LOW_LEVEL_DEBUG 1 #ifdef CARDMOD_LOW_LEVEL_DEBUG /* Use a simplied log to get all messages including messages * before opensc is loaded. The file must be modifiable by all * users as we maybe called under lsa or user. Note data from * multiple process and threads may get intermingled. * flush to get last message before ann crash * close so as the file is not left open during any wait. */ { FILE* lldebugfp = NULL; lldebugfp = fopen("C:\\tmp\\md.log","a+"); if (lldebugfp) { va_start(arg, format); vfprintf(lldebugfp, format, arg); va_end(arg); fflush(lldebugfp); fclose(lldebugfp); lldebugfp = NULL; } } #endif va_start(arg, format); if(pCardData != NULL) { vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); if(vs != NULL && vs->ctx != NULL) { #ifdef _MSC_VER sc_do_log_noframe(vs->ctx, level, format, arg); #else /* FIXME: trouble in vsprintf with %S arg under mingw32 */ if(vs->ctx->debug>=level) { vfprintf(vs->ctx->debug_file, format, arg); } #endif } } va_end(arg); } static void loghex(PCARD_DATA pCardData, int level, PBYTE data, int len) { char line[74]; char *c; int i, a; unsigned char * p; logprintf(pCardData, level, "--- %p:%d\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) { sprintf(c,"%02X", *p); 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 void print_werror(PCARD_DATA pCardData, char *str) { void *buf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, (LPTSTR) &buf, 0, NULL); logprintf(pCardData, 0, "%s%s\n", str, buf); LocalFree(buf); } /* * check if the card has been removed, or the * caller has changed the handles. * if so, then free up all previous card info * and reestablish */ static int check_reader_status(PCARD_DATA pCardData) { int r; VENDOR_SPECIFIC *vs = NULL; logprintf(pCardData, 4, "check_reader_status\n"); if(!pCardData) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); if(!vs) return SCARD_E_INVALID_PARAMETER; logprintf(pCardData, 7, "pCardData->hSCardCtx:0x%08X hScard:0x%08X\n", pCardData->hSCardCtx, pCardData->hScard); if (pCardData->hSCardCtx != vs->hSCardCtx || pCardData->hScard != vs->hScard) { logprintf (pCardData, 1, "HANDLES CHANGED from 0x%08X 0x%08X\n", vs->hSCardCtx, vs->hScard); // Basically a mini AcquireContext r = disassociate_card(pCardData); logprintf(pCardData, 1, "disassociate_card r = 0x%08X\n", r); r = associate_card(pCardData); /* need to check return codes */ logprintf(pCardData, 1, "associate_card r = 0x%08X\n", r); // Rebuild 'soft' fs - in case changed r = md_fs_init(pCardData); logprintf(pCardData, 1, "md_fs_init r = 0x%08X\n", r); } else if (vs->reader) { /* This should always work, as BaseCSP should be checking for removal too */ r = sc_detect_card_presence(vs->reader); logprintf(pCardData, 2, "check_reader_status r=%d flags 0x%08X\n", r, vs->reader->flags); } return SCARD_S_SUCCESS; } static DWORD md_get_pin_by_role(PCARD_DATA pCardData, PIN_ID role, struct sc_pkcs15_object **ret_obj) { VENDOR_SPECIFIC *vs; int rv = SC_SUCCESS; if (!pCardData) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); if (!ret_obj) return SCARD_E_INVALID_PARAMETER; *ret_obj = NULL; if (role == ROLE_USER) { if (!vs->obj_user_pin) { /* Get 'global' User PIN; if no, get the 'local' one */ rv = sc_pkcs15_find_pin_by_flags(vs->p15card, SC_PKCS15_PIN_TYPE_FLAGS_PIN_GLOBAL, SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, &vs->obj_user_pin); if (rv) rv = sc_pkcs15_find_pin_by_flags(vs->p15card, SC_PKCS15_PIN_TYPE_FLAGS_PIN_LOCAL, SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, &vs->obj_user_pin); } *ret_obj = vs->obj_user_pin; } else if (role == ROLE_ADMIN) { /* Get SO PIN; if no, get the 'global' PUK; if no get the 'local' one */ if (!vs->obj_sopin) { rv = sc_pkcs15_find_pin_by_flags(vs->p15card, SC_PKCS15_PIN_TYPE_FLAGS_SOPIN, SC_PKCS15_PIN_TYPE_FLAGS_SOPIN, NULL, &vs->obj_sopin); if (rv) rv = sc_pkcs15_find_pin_by_flags(vs->p15card, SC_PKCS15_PIN_TYPE_FLAGS_PUK_GLOBAL, SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, &vs->obj_sopin); if (rv) rv = sc_pkcs15_find_pin_by_flags(vs->p15card, SC_PKCS15_PIN_TYPE_FLAGS_PUK_LOCAL, SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, &vs->obj_sopin); } *ret_obj = vs->obj_sopin; } else { logprintf(pCardData, 2, "cannot get PIN object: unsupported role\n"); return SCARD_E_UNSUPPORTED_FEATURE; } return (rv == SC_SUCCESS) ? SCARD_S_SUCCESS : SCARD_E_UNSUPPORTED_FEATURE; } /* 'Write' mode can be enabled from the OpenSC configuration file*/ static BOOL md_is_read_only(PCARD_DATA pCardData) { VENDOR_SPECIFIC *vs; BOOL ret = TRUE; if (!pCardData) return TRUE; logprintf(pCardData, 2, "Is read-only?\n"); if (md_static_data.flags_checked & MD_STATIC_FLAG_READ_ONLY) { ret = (md_static_data.flags & MD_STATIC_FLAG_READ_ONLY) ? TRUE : FALSE; logprintf(pCardData, 2, "Returns checked flag: %s\n", ret ? "TRUE" : "FALSE"); return ret; } vs = pCardData->pvVendorSpecific; if (vs->ctx && vs->reader) { /* TODO: use atr from pCardData */ scconf_block *atrblock = _sc_match_atr_block(vs->ctx, NULL, &vs->reader->atr); logprintf(pCardData, 2, "Match ATR:\n", atrblock); loghex(pCardData, 3, vs->reader->atr.value, vs->reader->atr.len); if (atrblock) if (scconf_get_bool(atrblock, "md_read_only", 1) == 0) ret = FALSE; } md_static_data.flags_checked |= MD_STATIC_FLAG_READ_ONLY; if (ret == TRUE) md_static_data.flags |= MD_STATIC_FLAG_READ_ONLY; else md_static_data.flags &= ~MD_STATIC_FLAG_READ_ONLY; logprintf(pCardData, 2, "Returns read-only flag '%s', static flags %X/%X\n", ret ? "TRUE" : "FALSE", md_static_data.flags, md_static_data.flags_checked); return ret; } /* 'Write' mode can be enabled from the OpenSC configuration file*/ static BOOL md_is_supports_X509_enrollment(PCARD_DATA pCardData) { VENDOR_SPECIFIC *vs; BOOL ret = FALSE; if (!pCardData) return FALSE; logprintf(pCardData, 2, "Is supports X509 enrollment?\n"); if (md_static_data.flags_checked & MD_STATIC_FLAG_SUPPORTS_X509_ENROLLMENT) { ret = (md_static_data.flags & MD_STATIC_FLAG_SUPPORTS_X509_ENROLLMENT) ? TRUE : FALSE; logprintf(pCardData, 2, "Returns checked flag: %s\n", ret ? "TRUE" : "FALSE"); return ret; } vs = pCardData->pvVendorSpecific; if (vs->ctx && vs->reader) { /* TODO: use atr from pCardData */ scconf_block *atrblock = _sc_match_atr_block(vs->ctx, NULL, &vs->reader->atr); logprintf(pCardData, 2, "Match ATR:\n"); loghex(pCardData, 3, vs->reader->atr.value, vs->reader->atr.len); if (atrblock) if (scconf_get_bool(atrblock, "md_supports_X509_enrollment", 0)) ret = TRUE; } md_static_data.flags_checked |= MD_STATIC_FLAG_SUPPORTS_X509_ENROLLMENT; if (ret == TRUE) md_static_data.flags |= MD_STATIC_FLAG_SUPPORTS_X509_ENROLLMENT; else md_static_data.flags &= ~MD_STATIC_FLAG_SUPPORTS_X509_ENROLLMENT; logprintf(pCardData, 2, "Returns x509-enrollment flag '%s', static flags %X/%X\n", ret ? "TRUE" : "FALSE", md_static_data.flags, md_static_data.flags_checked); return ret; } /* Check if specified PIN has been verified */ static BOOL md_is_pin_set(PCARD_DATA pCardData, DWORD role) { VENDOR_SPECIFIC *vs; CARD_CACHE_FILE_FORMAT *cardcf = NULL; if (!pCardData) return FALSE; vs = pCardData->pvVendorSpecific; if (md_get_cardcf(pCardData, &cardcf) != SCARD_S_SUCCESS) return FALSE; return IS_PIN_SET(cardcf->bPinsFreshness, role); } /* 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 (!parent) parent = &vs->root; if (!name) { dir = parent; } else { dir = parent->subdirs; while(dir) { if (!strcmp(dir->name, 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_FILE_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)); strncpy(new_dir->name, name, sizeof(new_dir->name) - 1); 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) { VENDOR_SPECIFIC *vs; struct md_file *file = NULL; struct md_directory *dir = NULL; DWORD dwret; if (out) *out = NULL; if (!pCardData || !name) return SCARD_E_INVALID_PARAMETER; vs = pCardData->pvVendorSpecific; dwret = md_fs_find_directory(pCardData, NULL, parent, &dir); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 2, "find directory '%s' error: %X\n", parent ? parent : "", 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 (!strcmp(file->name, 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)); strncpy(new_file->name, name, sizeof(new_file->name) - 1); 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; } 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; dwret = md_fs_find_directory(pCardData, NULL, parent, &dir); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 2, "find directory '%s' error: %X\n", parent ? parent : "", 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 (!strcmp(dir->files->name, 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 (!strcmp(file->next->name, 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%X\n", idx, dwret); } } return dwret; } static DWORD md_pkcs15_encode_cardcf(PCARD_DATA pCardData, unsigned char *in, size_t in_size, unsigned char *out, size_t *out_size) { VENDOR_SPECIFIC *vs; char *last_update = NULL; if (!pCardData || !in || in_size < MD_CARDCF_LENGTH || !out || !out_size || *out_size < MD_CARDCF_LENGTH) return SCARD_E_INVALID_PARAMETER; vs = pCardData->pvVendorSpecific; memcpy(out, in, MD_CARDCF_LENGTH); /* write down 'cardcf' with cleared PinsFreshness */ ((CARD_CACHE_FILE_FORMAT *)out)->bPinsFreshness = PIN_SET_NONE; last_update = sc_pkcs15_get_lastupdate(vs->p15card); if (!last_update || (*out_size < MD_CARDCF_LENGTH + MD_UTC_TIME_LENGTH_MAX)) { *out_size = MD_CARDCF_LENGTH; } else { size_t lu_size = strlen(last_update); if (lu_size > MD_UTC_TIME_LENGTH_MAX) lu_size = MD_UTC_TIME_LENGTH_MAX; memcpy(out + MD_CARDCF_LENGTH, last_update, lu_size); if (lu_size < MD_UTC_TIME_LENGTH_MAX) memset(out + MD_CARDCF_LENGTH + lu_size, 0, MD_UTC_TIME_LENGTH_MAX - lu_size); *out_size = MD_CARDCF_LENGTH + MD_UTC_TIME_LENGTH_MAX; } return SCARD_S_SUCCESS; } static DWORD md_pkcs15_encode_cmapfile(PCARD_DATA pCardData, unsigned char **out, size_t *out_len) { VENDOR_SPECIFIC *vs; unsigned char *encoded, *ret; size_t guid_len, encoded_len, flags_len, ret_len; int idx; if (!pCardData || !out || !out_len) return SCARD_E_INVALID_PARAMETER; vs = pCardData->pvVendorSpecific; logprintf(pCardData, 2, "encode P15 'cmapfile'\n"); ret = NULL, ret_len = 0; for (idx=0; idxp15_containers[idx]; int rv; if (!cont.id.len && !strlen(cont.guid)) continue; sc_copy_asn1_entry(c_asn1_md_container_attrs, asn1_md_container_attrs); sc_copy_asn1_entry(c_asn1_md_container, asn1_md_container); guid_len = strlen(cont.guid); flags_len = sizeof(size_t); sc_format_asn1_entry(asn1_md_container_attrs + 0, &cont.index, NULL, 1); sc_format_asn1_entry(asn1_md_container_attrs + 1, &cont.id, NULL, 1); sc_format_asn1_entry(asn1_md_container_attrs + 2, cont.guid, &guid_len, 1); sc_format_asn1_entry(asn1_md_container_attrs + 3, &cont.flags, &flags_len, 1); sc_format_asn1_entry(asn1_md_container_attrs + 4, &cont.size_key_exchange, NULL, 1); sc_format_asn1_entry(asn1_md_container_attrs + 5, &cont.size_sign, NULL, 1); sc_format_asn1_entry(asn1_md_container + 0, asn1_md_container_attrs, NULL, 1); rv = sc_asn1_encode(vs->ctx, asn1_md_container, &encoded, &encoded_len); if (rv < 0) { logprintf(pCardData, 3, "MdEncodeCMapFile(): ASN1 encode error(%i): %s\n", rv, sc_strerror(rv)); return SCARD_F_INTERNAL_ERROR; } ret = realloc(ret, ret_len + encoded_len); if (!ret) { logprintf(pCardData, 3, "MdEncodeCMapFile(): realloc failed\n"); return SCARD_E_NO_MEMORY; } memcpy(ret + ret_len, encoded, encoded_len); free(encoded); ret_len += encoded_len; } logprintf(pCardData, 3, "encoded P15 'cmapfile':\n"); loghex(pCardData, 3, ret, ret_len); *out = ret; *out_len = ret_len; return SCARD_S_SUCCESS; } /* * 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; DWORD dwret = SCARD_F_INTERNAL_ERROR; int nn_records, idx; if (!pCardData || !blob || size < sizeof(CONTAINER_MAP_RECORD)) return SCARD_E_INVALID_PARAMETER; vs = pCardData->pvVendorSpecific; nn_records = 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; count = wcstombs(cont->guid, pp->wszGuid, sizeof(cont->guid)); if (!count) { memset(cont, 0, sizeof(CONTAINER_MAP_RECORD)); } else { 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),cont->guid,cont->flags); } } return SCARD_S_SUCCESS; } static DWORD md_pkcs15_update_container_from_do(PCARD_DATA pCardData, struct sc_pkcs15_object *dobj) { VENDOR_SPECIFIC *vs; DWORD dwret = SCARD_F_INTERNAL_ERROR; struct sc_pkcs15_data *ddata = NULL; struct sc_pkcs15_id id; int rv, offs, idx; unsigned flags; if (!pCardData || !dobj) return SCARD_E_INVALID_PARAMETER; vs = pCardData->pvVendorSpecific; rv = sc_pkcs15_read_data_object(vs->p15card, (struct sc_pkcs15_data_info *)dobj->data, &ddata); if (rv) { logprintf(pCardData, 2, "sc_pkcs15_read_data_object('%s') returned %i\n", dobj->label, rv); return SCARD_F_INTERNAL_ERROR; } offs = 0; if (*(ddata->data + offs++) != 0x01) { sc_pkcs15_free_data_object(ddata); return SCARD_E_INVALID_VALUE; } id.len = *(ddata->data + offs++); memcpy(id.value, ddata->data + offs, id.len); offs += id.len; if (*(ddata->data + offs++) != 0x02) { sc_pkcs15_free_data_object(ddata); return SCARD_E_INVALID_VALUE; } if (*(ddata->data + offs++) != 0x01) { sc_pkcs15_free_data_object(ddata); return SCARD_E_INVALID_VALUE; } flags = *(ddata->data + offs); for (idx=0; idxp15_containers[idx].prkey_obj; idx++) { if (sc_pkcs15_compare_id(&id, &vs->p15_containers[idx].id)) { snprintf(vs->p15_containers[idx].guid, sizeof(vs->p15_containers[idx].guid), "%s", dobj->label); vs->p15_containers[idx].flags = flags; logprintf(pCardData, 2, "Set container's guid to '%s' and flags to 0x%X\n", vs->p15_containers[idx].guid, flags); break; } } sc_pkcs15_free_data_object(ddata); return SCARD_S_SUCCESS; } static DWORD md_pkcs15_default_container_from_do(PCARD_DATA pCardData, struct sc_pkcs15_object *dobj) { VENDOR_SPECIFIC *vs; struct sc_pkcs15_data *ddata = NULL; DWORD dwret = SCARD_F_INTERNAL_ERROR; int rv, idx; char guid[MAX_CONTAINER_NAME_LEN + 1]; if (!pCardData || !dobj) return SCARD_E_INVALID_PARAMETER; vs = pCardData->pvVendorSpecific; rv = sc_pkcs15_read_data_object(vs->p15card, (struct sc_pkcs15_data_info *)dobj->data, &ddata); if (rv) { logprintf(pCardData, 2, "sc_pkcs15_read_data_object('%s') returned %i\n", dobj->label, rv); return SCARD_F_INTERNAL_ERROR; } if (ddata->data_len > MAX_CONTAINER_NAME_LEN || ddata->data_len < 32) { logprintf(pCardData, 2, "Invalid container name length %i\n", ddata->data_len); return SCARD_E_INVALID_VALUE; } memset(guid, 0, sizeof(guid)); memcpy(&guid[0] , ddata->data, ddata->data_len); logprintf(pCardData, 2, "Search container '%s' to set it as default\n", guid); for (idx=0; idxp15_containers[idx].prkey_obj; idx++) { if (strstr(vs->p15_containers[idx].guid, guid)) { vs->p15_containers[idx].flags |= CONTAINER_MAP_DEFAULT_CONTAINER; logprintf(pCardData, 2, "Default container is '%s'\n", vs->p15_containers[idx].guid); break; } } sc_pkcs15_free_data_object(ddata); 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; card = vs->p15card->card; if (!obj) return SCARD_S_SUCCESS; logprintf(pCardData, 3, "MdDeleteObject('%s',type:0x%X) called\n", 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, app_info, &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(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->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); } 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; } dwret = md_fs_set_content(pCardData, file, cardid_bin, MD_CARDID_SIZE); if (dwret != SCARD_S_SUCCESS) return dwret; } logprintf(pCardData, 3, "cardid(%i)\n", file->size); loghex(pCardData, 3, file->blob, file->size); return SCARD_S_SUCCESS; } /* * Return content of the 'soft' file. */ static void 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; vs = pCardData->pvVendorSpecific; dwret = md_fs_find_directory(pCardData, NULL, parent, &dir); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 2, "find directory '%s' error: %X\n", parent ? parent : "", dwret); return; } else if (!dir) { logprintf(pCardData, 2, "directory '%s' not found\n", parent ? parent : ""); return; } if (!strcmp(dir->name, "mscp")) { int idx, rv; if(sscanf(file->name, "ksc%d", &idx) > 0) { } else if(sscanf(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; rv = sc_pkcs15_read_certificate(vs->p15card, cert_info, &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; } file->size = cert->data.len; file->blob = pCardData->pfnCspAlloc(cert->data.len); CopyMemory(file->blob, cert->data.value, cert->data.len); sc_pkcs15_free_certificate(cert); } } else { return; } } /* * 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) { VENDOR_SPECIFIC *vs; char *last_update = NULL; CARD_CACHE_FILE_FORMAT empty; size_t empty_len = sizeof(empty); DWORD dwret; if (!pCardData || !file) return SCARD_E_INVALID_PARAMETER; vs = pCardData->pvVendorSpecific; memset(&empty, 0, sizeof(empty)); empty.bVersion = CARD_CACHE_FILE_CURRENT_VERSION; last_update = sc_pkcs15_get_lastupdate(vs->p15card); if (last_update) { unsigned crc32 = sc_crc32(last_update, strlen(last_update)); logprintf(pCardData, 2, "Set 'cardcf' using lastUpdate '%s'; CRC32 %X\n", last_update, crc32); empty.wContainersFreshness = crc32; empty.wFilesFreshness = crc32; } else { logprintf(pCardData, 2, "Set 'cardcf' using random value\n"); srand((unsigned)time(NULL)); empty.wContainersFreshness = rand()%30000; empty.wFilesFreshness = rand()%30000; } 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(%i)\n", file->size); loghex(pCardData, 3, file->blob, file->size); return SCARD_S_SUCCESS; } /* * Return content of the 'soft' 'cardcf' file */ static DWORD md_get_cardcf(PCARD_DATA pCardData, CARD_CACHE_FILE_FORMAT **out) { struct md_file *file = NULL; if (!pCardData) return SCARD_E_INVALID_PARAMETER; md_fs_find_file(pCardData, NULL, "cardcf", &file); if (!file) { logprintf(pCardData, 2, "file 'cardcf' not found\n"); return SCARD_E_FILE_NOT_FOUND; } if (!file->blob || file->size < MD_CARDCF_LENGTH) return SCARD_E_INVALID_VALUE; if (out) *out = (CARD_CACHE_FILE_FORMAT *)file->blob; 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(%i)\n", file->size); loghex(pCardData, 3, file->blob, file->size); 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. Initalize the content of the 'soft' 'cmapfile' from the inernal p15-containers. */ static DWORD md_set_cmapfile(PCARD_DATA pCardData, struct md_file *file) { VENDOR_SPECIFIC *vs; PCONTAINER_MAP_RECORD p; sc_pkcs15_pubkey_t *pubkey = NULL; 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]; if (!pCardData || !file) return SCARD_E_INVALID_PARAMETER; logprintf(pCardData, 0, "set 'cmapfile'\n"); vs = pCardData->pvVendorSpecific; cmap_len = MD_MAX_KEY_CONTAINERS*sizeof(CONTAINER_MAP_RECORD); cmap_buf = pCardData->pfnCspAlloc(cmap_len); if(!cmap_buf) return SCARD_E_NO_MEMORY; memset(cmap_buf, 0, cmap_len); rv = sc_pkcs15_get_objects(vs->p15card, SC_PKCS15_TYPE_PRKEY_RSA, 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], *cert_obj = NULL; 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) { logprintf(pCardData, 7, "Non 'RSA' key (type:%X) are ignored\n", key_obj->type); continue; } rv = sc_pkcs15_get_guid(vs->p15card, key_obj, 0, cont->guid, sizeof(cont->guid)); if (rv) { logprintf(pCardData, 2, "sc_pkcs15_get_guid() error %d\n", rv); return SCARD_F_INTERNAL_ERROR; } logprintf(pCardData, 7, "Container[%i]'s guid=%s\n", ii, cont->guid); cont->flags = CONTAINER_MAP_VALID_CONTAINER; /* 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 (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; logprintf(pCardData, 7, "Container[%i]'s key-exchange:%i, sign:%i\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: certficate 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", 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", cont->pubkey_obj->label); } if (conts_num) { /* Read 'CMAPFILE' and update the attributes of P15 containers */ 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, MD_DATA_APPLICAITON_NAME)) continue; logprintf(pCardData, 2, "Found 'DATA' object '%s'\n", dobjs[ii]->label); if (!strcmp(dobjs[ii]->label, MD_DATA_DEFAULT_CONT_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; } } /* 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 = vs->p15_containers[ii].size_sign; (p+ii)->wKeyExchangeKeySizeBits = 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((char *)k_name, sizeof(k_name), "kxc%02i", ii); 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((char *)k_name, sizeof(k_name), "ksc%02i", ii); 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_set_content(pCardData, file, cmap_buf, cmap_len); pCardData->pfnCspFree(cmap_buf); if (dwret != SCARD_S_SUCCESS) return dwret; logprintf(pCardData, 3, "cmap(%i)\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) return dwret; dwret = md_fs_add_file(pCardData, &(vs->root.files), "cardcf", EveryoneReadUserWriteAc, NULL, 0, &cardcf); if (dwret != SCARD_S_SUCCESS) return dwret; dwret = md_set_cardcf(pCardData, cardcf); if (dwret != SCARD_S_SUCCESS) return dwret; dwret = md_fs_add_file(pCardData, &(vs->root.files), "cardapps", EveryoneReadAdminWriteAc, NULL, 0, &cardapps); if (dwret != SCARD_S_SUCCESS) return dwret; dwret = md_set_cardapps(pCardData, cardapps); if (dwret != SCARD_S_SUCCESS) return dwret; dwret = md_fs_add_directory(pCardData, &(vs->root.subdirs), "mscp", UserCreateDeleteDirAc, &mscp); if (dwret != SCARD_S_SUCCESS) return dwret; dwret = md_fs_add_file(pCardData, &(mscp->files), "cmapfile", EveryoneReadUserWriteAc, NULL, 0, &cmapfile); if (dwret != SCARD_S_SUCCESS) return dwret; dwret = md_set_cmapfile(pCardData, cmapfile); if (dwret != SCARD_S_SUCCESS) return dwret; logprintf(pCardData, 3, "MD virtual file system initialized\n"); return SCARD_S_SUCCESS; } /* 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_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; pCardCapabilities->fKeyGen = TRUE; 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); /* 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; if (key_type == AT_SIGNATURE || key_type == AT_KEYEXCHANGE) { key_algo = SC_ALGORITHM_RSA; } else { logprintf(pCardData, 3, "Unsupported key type: 0x%X\n", key_type); return SCARD_E_UNSUPPORTED_FEATURE; } vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); if (flags & CARD_CREATE_CONTAINER_KEY_IMPORT) { 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 == 0x31415352 || pub_rsa->magic == 0x32415352) { 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 %i\n", 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%X,size:0x%X)\n", key_type, key_size); return SCARD_E_UNSUPPORTED_FEATURE; } static DWORD md_pkcs15_generate_key(PCARD_DATA pCardData, DWORD idx, DWORD key_type, DWORD key_size) { 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 dw, dwret = SCARD_F_INTERNAL_ERROR; if (!pCardData) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); card = vs->p15card->card; memset(&pub_args, 0, sizeof(pub_args)); memset(&keygen_args, 0, sizeof(keygen_args)); keygen_args.prkey_args.label = keygen_args.pubkey_label = "TODO: key label"; 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 { logprintf(pCardData, 3, "MdGenerateKey(): unsupported key type: 0x%X\n", key_type); return SCARD_E_INVALID_PARAMETER; } keygen_args.prkey_args.access_flags = MD_KEY_ACCESS; dw = md_get_pin_by_role(pCardData, ROLE_USER, &pin_obj); if (dw != SCARD_S_SUCCESS) { logprintf(pCardData, 2, "MdGenerateKey(): cannot get User PIN object"); return dw; } 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, app_info, &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]); if (strlen(cont->guid)) keygen_args.prkey_args.guid = cont->guid; 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; } 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:%i,id:%s,guid:%s)\n", idx, sc_pkcs15_print_id(&cont->id),cont->guid); dwret = SCARD_S_SUCCESS; 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) { #if OPENSSL_VERSION_NUMBER >= 0x10000000L 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 dw, dwret = SCARD_F_INTERNAL_ERROR; if (!pCardData) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); card = vs->p15card->card; pkey = b2i_PrivateKey(&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%X\n", key_type); return SCARD_E_INVALID_PARAMETER; } prkey_args.access_flags = MD_KEY_ACCESS; dw = md_get_pin_by_role(pCardData, ROLE_USER, &pin_obj); if (dw != SCARD_S_SUCCESS) { logprintf(pCardData, 2, "MdStoreKey(): cannot get User PIN object"); return dw; } 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, app_info, &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]); if (strlen(cont->guid)) prkey_args.guid = cont->guid; 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; } 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:%i,id:%s,guid:%s)\n", idx, sc_pkcs15_print_id(&cont->id),cont->guid); dwret = SCARD_S_SUCCESS; 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 } static DWORD md_pkcs15_store_certificate(PCARD_DATA pCardData, char *file_name, unsigned char *blob, size_t len) { VENDOR_SPECIFIC *vs; 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; DWORD dwret = SCARD_F_INTERNAL_ERROR; if (!pCardData) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); card = vs->p15card->card; memset(&args, 0, sizeof(args)); args.der_encoded.value = blob; args.der_encoded.len = len; 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, app_info, &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(CARD_KEY_SIZES *pKeySizes) { if (!pKeySizes) return SCARD_E_INVALID_PARAMETER; if (pKeySizes->dwVersion != CARD_KEY_SIZES_CURRENT_VERSION && pKeySizes->dwVersion != 0) return ERROR_REVISION_MISMATCH; pKeySizes->dwVersion = CARD_KEY_SIZES_CURRENT_VERSION; pKeySizes->dwMinimumBitlen = 1024; pKeySizes->dwDefaultBitlen = 2048; pKeySizes->dwMaximumBitlen = 2048; pKeySizes->dwIncrementalBitlen = 1024; return SCARD_S_SUCCESS; } DWORD WINAPI CardDeleteContext(__inout PCARD_DATA pCardData) { VENDOR_SPECIFIC *vs = NULL; if (md_static_data.attach_check != MD_STATIC_PROCESS_ATTACHED) return SCARD_S_SUCCESS; if(!pCardData) return SCARD_E_INVALID_PARAMETER; logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p hScard=0x%08X hSCardCtx=0x%08X CardDeleteContext\n", GetCurrentProcessId(), GetCurrentThreadId(), pCardData, pCardData->hScard, pCardData->hSCardCtx); vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); if(!vs) return SCARD_E_INVALID_PARAMETER; disassociate_card(pCardData); if(vs->ctx) { logprintf(pCardData, 6, "release context\n"); sc_release_context(vs->ctx); md_static_data.flags |= MD_STATIC_FLAG_CONTEXT_DELETED; vs->ctx = NULL; } logprintf(pCardData, 1, "**********************************************************************\n"); pCardData->pfnCspFree(pCardData->pvVendorSpecific); pCardData->pvVendorSpecific = NULL; return SCARD_S_SUCCESS; } DWORD WINAPI CardQueryCapabilities(__in PCARD_DATA pCardData, __in PCARD_CAPABILITIES pCardCapabilities) { DWORD dwret; logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "pCardCapabilities=%X\n", pCardCapabilities); if (!pCardData || !pCardCapabilities) return SCARD_E_INVALID_PARAMETER; dwret = md_card_capabilities(pCardCapabilities); if (dwret != SCARD_S_SUCCESS) return dwret; check_reader_status(pCardData); return SCARD_S_SUCCESS; } DWORD WINAPI CardDeleteContainer(__in PCARD_DATA pCardData, __in BYTE bContainerIndex, __in DWORD dwReserved) { logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardDeleteContainer(idx:%i)\n", bContainerIndex); logprintf(pCardData, 1, "CardDeleteContainer() not supported\n"); return SCARD_E_UNSUPPORTED_FEATURE; } DWORD WINAPI CardCreateContainer(__in PCARD_DATA pCardData, __in BYTE bContainerIndex, __in DWORD dwFlags, __in DWORD dwKeySpec, __in DWORD dwKeySize, __in PBYTE pbKeyData) { VENDOR_SPECIFIC *vs = NULL; DWORD dwret; if (!pCardData) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardCreateContainer(idx:%i,flags:%X,type:%X,size:%i,data:%p)\n", bContainerIndex, dwFlags, dwKeySpec, dwKeySize, pbKeyData); 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"); return dwret; } if (dwFlags & CARD_CREATE_CONTAINER_KEY_GEN) { dwret = md_pkcs15_generate_key(pCardData, bContainerIndex, dwKeySpec, dwKeySize); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "key generation failed"); return dwret; } logprintf(pCardData, 1, "key generated"); } else if ((dwFlags & CARD_CREATE_CONTAINER_KEY_IMPORT) && (pbKeyData != NULL)) { dwret = md_pkcs15_store_key(pCardData, bContainerIndex, dwKeySpec, pbKeyData, dwKeySize); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "key store failed"); return dwret; } logprintf(pCardData, 1, "key imported"); } else { logprintf(pCardData, 1, "Invalid dwFlags value: 0x%X", dwFlags); return SCARD_E_INVALID_PARAMETER; } return SCARD_S_SUCCESS; } typedef struct { PUBLICKEYSTRUC publickeystruc; RSAPUBKEY rsapubkey; } PUBKEYSTRUCT_BASE; DWORD WINAPI CardGetContainerInfo(__in PCARD_DATA pCardData, __in BYTE bContainerIndex, __in DWORD dwFlags, __in PCONTAINER_INFO pContainerInfo) { VENDOR_SPECIFIC *vs = NULL; DWORD sz = 0, ret; struct md_pkcs15_container *cont = NULL; struct sc_pkcs15_der pubkey_der; int rv; if(!pCardData) return SCARD_E_INVALID_PARAMETER; if (!pContainerInfo) return SCARD_E_INVALID_PARAMETER; logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardGetContainerInfo bContainerIndex=%u, dwFlags=0x%08X, " \ "dwVersion=%u, cbSigPublicKey=%u, cbKeyExPublicKey=%u\n", \ bContainerIndex, dwFlags, pContainerInfo->dwVersion, \ pContainerInfo->cbSigPublicKey, pContainerInfo->cbKeyExPublicKey); if (dwFlags) return SCARD_E_INVALID_PARAMETER; if (bContainerIndex >= MD_MAX_KEY_CONTAINERS) return SCARD_E_NO_KEY_CONTAINER; if (pContainerInfo->dwVersion < 0 || pContainerInfo->dwVersion > CONTAINER_INFO_CURRENT_VERSION) return ERROR_REVISION_MISMATCH; pContainerInfo->dwVersion = CONTAINER_INFO_CURRENT_VERSION; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); cont = &vs->p15_containers[bContainerIndex]; if (!cont->prkey_obj) { logprintf(pCardData, 7, "Container %i is empty\n", bContainerIndex); return SCARD_E_NO_KEY_CONTAINER; } check_reader_status(pCardData); pubkey_der.value = NULL; pubkey_der.len = 0; ret = SCARD_S_SUCCESS; if ((cont->prkey_obj->content.value != NULL) && (cont->prkey_obj->content.len > 0)) sc_der_copy(&pubkey_der, &cont->prkey_obj->content); if (!pubkey_der.value && cont->pubkey_obj) { struct sc_pkcs15_pubkey *pubkey = NULL; logprintf(pCardData, 1, "now read public key '%s'\n", cont->pubkey_obj->label); rv = sc_pkcs15_read_pubkey(vs->p15card, cont->pubkey_obj, &pubkey); if (!rv) { if(pubkey->algorithm == SC_ALGORITHM_RSA) sc_der_copy(&pubkey_der, &pubkey->data); else ret = SCARD_E_UNSUPPORTED_FEATURE; 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; logprintf(pCardData, 1, "now read certificate '%s'\n", cont->cert_obj->label); rv = sc_pkcs15_read_certificate(vs->p15card, (struct sc_pkcs15_cert_info *)(cont->cert_obj->data), &cert); if(!rv) { if(cert->key->algorithm == SC_ALGORITHM_RSA) sc_der_copy(&pubkey_der, &cert->key->data); else ret = SCARD_E_UNSUPPORTED_FEATURE; sc_pkcs15_free_certificate(cert); } else { logprintf(pCardData, 1, "certificate '%d' read error %d\n", 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"); return SCARD_F_INTERNAL_ERROR; } if (ret != SCARD_S_SUCCESS) { logprintf(pCardData, 7, "GetContainerInfo(idx:%i) failed; error %X", bContainerIndex, ret); return ret; } logprintf(pCardData, 7, "SubjectPublicKeyInfo:\n"); loghex(pCardData, 7, pubkey_der.value, pubkey_der.len); 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, pubkey_der.len, 0, NULL, &sz); if (cont->size_sign) { PUBKEYSTRUCT_BASE *oh = (PUBKEYSTRUCT_BASE *)pCardData->pfnCspAlloc(sz); if (!oh) return SCARD_E_NO_MEMORY; CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, pubkey_der.value, pubkey_der.len, 0, oh, &sz); oh->publickeystruc.aiKeyAlg = CALG_RSA_SIGN; pContainerInfo->cbSigPublicKey = sz; pContainerInfo->pbSigPublicKey = (PBYTE)oh; logprintf(pCardData, 3, "return info on SIGN_CONTAINER_INDEX %i\n", bContainerIndex); } if (cont->size_key_exchange) { PUBKEYSTRUCT_BASE *oh = (PUBKEYSTRUCT_BASE*)pCardData->pfnCspAlloc(sz); if (!oh) return SCARD_E_NO_MEMORY; CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, pubkey_der.value, pubkey_der.len, 0, oh, &sz); oh->publickeystruc.aiKeyAlg = CALG_RSA_KEYX; pContainerInfo->cbKeyExPublicKey = sz; pContainerInfo->pbKeyExPublicKey = (PBYTE)oh; logprintf(pCardData, 3, "return info on KEYX_CONTAINER_INDEX %i\n", bContainerIndex); } } logprintf(pCardData, 7, "returns container(idx:%i) info", bContainerIndex); return SCARD_S_SUCCESS; } DWORD WINAPI CardAuthenticatePin(__in PCARD_DATA pCardData, __in LPWSTR pwszUserId, __in PBYTE pbPin, __in DWORD cbPin, __out_opt PDWORD pcAttemptsRemaining) { int r, tries_left; sc_pkcs15_object_t *pin_obj; char type[256]; VENDOR_SPECIFIC *vs; struct md_file *cardcf_file = NULL; CARD_CACHE_FILE_FORMAT *cardcf = NULL; DWORD dwret; if(!pCardData) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardAuthenticatePin '%S':%d\n", NULLWSTR(pwszUserId), cbPin); check_reader_status(pCardData); dwret = md_get_cardcf(pCardData, &cardcf); if (dwret != SCARD_S_SUCCESS) return dwret; if (NULL == pwszUserId) return SCARD_E_INVALID_PARAMETER; if (wcscmp(wszCARD_USER_USER,pwszUserId) != 0 && wcscmp(wszCARD_USER_ADMIN,pwszUserId) != 0) return SCARD_E_INVALID_PARAMETER; if (NULL == pbPin) return SCARD_E_INVALID_PARAMETER; if (cbPin < 4 || cbPin > 12) return SCARD_W_WRONG_CHV; if (wcscmp(wszCARD_USER_ADMIN,pwszUserId) == 0) return SCARD_W_WRONG_CHV; wcstombs(type, pwszUserId, 100); type[10] = 0; logprintf(pCardData, 1, "CardAuthenticatePin %.20s, %d, %d\n", NULLSTR(type), cbPin, (pcAttemptsRemaining==NULL?-2:*pcAttemptsRemaining)); r = md_get_pin_by_role(pCardData, ROLE_USER, &pin_obj); if (r != SCARD_S_SUCCESS) { logprintf(pCardData, 2, "Cannot get User PIN object"); return r; } r = sc_pkcs15_verify_pin(vs->p15card, pin_obj, (const u8 *) pbPin, cbPin); if (r) { logprintf(pCardData, 1, "PIN code verification failed: %s\n", sc_strerror(r)); tries_left = ((struct sc_pkcs15_auth_info *)pin_obj->data)->tries_left; if (r == SC_ERROR_AUTH_METHOD_BLOCKED) return SCARD_W_CHV_BLOCKED; if(pcAttemptsRemaining) (*pcAttemptsRemaining) = tries_left; return SCARD_W_WRONG_CHV; } logprintf(pCardData, 3, "Pin code correct.\n"); SET_PIN(cardcf->bPinsFreshness, ROLE_USER); logprintf(pCardData, 3, "PinsFreshness = %d\n", cardcf->bPinsFreshness); return SCARD_S_SUCCESS; } DWORD WINAPI CardGetChallenge(__in PCARD_DATA pCardData, __deref_out_bcount(*pcbChallengeData) PBYTE *ppbChallengeData, __out PDWORD pcbChallengeData) { logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardGetChallenge - unsupported\n"); return SCARD_E_UNSUPPORTED_FEATURE; } DWORD WINAPI CardAuthenticateChallenge(__in PCARD_DATA pCardData, __in_bcount(cbResponseData) PBYTE pbResponseData, __in DWORD cbResponseData, __out_opt PDWORD pcAttemptsRemaining) { logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardAuthenticateChallenge - unsupported\n"); return 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) { logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardUnblockPin - unsupported\n"); return SCARD_E_UNSUPPORTED_FEATURE; } 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) { logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardChangeAuthenticator - unsupported\n"); return SCARD_E_UNSUPPORTED_FEATURE; } DWORD WINAPI CardDeauthenticate(__in PCARD_DATA pCardData, __in LPWSTR pwszUserId, __in DWORD dwFlags) { VENDOR_SPECIFIC *vs; CARD_CACHE_FILE_FORMAT *cardcf = NULL; struct md_file *cmapfile = NULL; DWORD dwret; logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardDeauthenticate(%S) %d\n", NULLWSTR(pwszUserId), dwFlags); if(!pCardData) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); check_reader_status(pCardData); dwret = md_get_cardcf(pCardData, &cardcf); if (dwret != SCARD_S_SUCCESS) return dwret; logprintf(pCardData, 1, "CardDeauthenticate bPinsFreshness:%d\n", cardcf->bPinsFreshness); if (!wcscmp(pwszUserId, wszCARD_USER_USER)) CLEAR_PIN(cardcf->bPinsFreshness, ROLE_USER); else if (!wcscmp(pwszUserId, wszCARD_USER_ADMIN)) CLEAR_PIN(cardcf->bPinsFreshness, ROLE_ADMIN); else return SCARD_E_INVALID_PARAMETER; logprintf(pCardData, 5, "PinsFreshness = %d\n", cardcf->bPinsFreshness); /* TODO Reset PKCS#15 PIN object 'validated' flag */ return SCARD_S_SUCCESS; } DWORD WINAPI CardCreateDirectory(__in PCARD_DATA pCardData, __in LPSTR pszDirectoryName, __in CARD_DIRECTORY_ACCESS_CONDITION AccessCondition) { logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardCreateDirectory - unsupported\n"); return SCARD_E_UNSUPPORTED_FEATURE; } DWORD WINAPI CardDeleteDirectory(__in PCARD_DATA pCardData, __in LPSTR pszDirectoryName) { logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardDeleteDirectory(%s) - unsupported\n", NULLSTR(pszDirectoryName)); return SCARD_E_UNSUPPORTED_FEATURE; } DWORD WINAPI CardCreateFile(__in PCARD_DATA pCardData, __in LPSTR pszDirectoryName, __in LPSTR pszFileName, __in DWORD cbInitialCreationSize, __in CARD_FILE_ACCESS_CONDITION AccessCondition) { struct md_directory *dir = NULL; DWORD dwret; logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardCreateFile(%s::%s, size %i, acl:0x%X) called\n", NULLSTR(pszDirectoryName), NULLSTR(pszFileName), cbInitialCreationSize, AccessCondition); dwret = md_fs_find_directory(pCardData, NULL, pszDirectoryName, &dir); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "CardCreateFile() cannot find parent directory '%s'", NULLSTR(pszDirectoryName)); return dwret; } dwret = md_fs_add_file(pCardData, &dir->files, pszFileName, AccessCondition, NULL, cbInitialCreationSize, NULL); if (dwret != SCARD_S_SUCCESS) return dwret; return SCARD_S_SUCCESS; } DWORD WINAPI CardReadFile(__in PCARD_DATA pCardData, __in LPSTR pszDirectoryName, __in LPSTR pszFileName, __in DWORD dwFlags, __deref_out_bcount(*pcbData) PBYTE *ppbData, __out PDWORD pcbData) { VENDOR_SPECIFIC *vs; struct md_directory *dir = NULL; struct md_file *file = NULL; logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardReadFile\n"); if(!pCardData) return SCARD_E_INVALID_PARAMETER; if (!ppbData || !pcbData) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); logprintf(pCardData, 2, "pszDirectoryName = %s, pszFileName = %s, dwFlags = %X, pcbData=%d, *ppbData=%X\n", NULLSTR(pszDirectoryName), NULLSTR(pszFileName), dwFlags, *pcbData, *ppbData); if (!pszFileName || !strlen(pszFileName)) return SCARD_E_INVALID_PARAMETER; if (dwFlags) return SCARD_E_INVALID_PARAMETER; check_reader_status(pCardData); md_fs_find_file(pCardData, pszDirectoryName, pszFileName, &file); if (!file) { logprintf(pCardData, 2, "CardReadFile(): file '%s' not found in '%s'\n", NULLSTR(pszFileName), NULLSTR(pszDirectoryName)); return SCARD_E_FILE_NOT_FOUND; } if (!file->blob) md_fs_read_content(pCardData, pszDirectoryName, file); *ppbData = pCardData->pfnCspAlloc(file->size); if(!*ppbData) return SCARD_E_NO_MEMORY; *pcbData = file->size; memcpy(*ppbData, file->blob, file->size); logprintf(pCardData, 7, "returns '%s' content:\n", NULLSTR(pszFileName)); loghex(pCardData, 7, *ppbData, *pcbData); return SCARD_S_SUCCESS; } DWORD WINAPI CardWriteFile(__in PCARD_DATA pCardData, __in LPSTR pszDirectoryName, __in LPSTR pszFileName, __in DWORD dwFlags, __in_bcount(cbData) PBYTE pbData, __in DWORD cbData) { struct md_file *file = NULL; DWORD dwret; if(!pCardData) return SCARD_E_INVALID_PARAMETER; logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardWriteFile() dirName:'%s', fileName:'%s' \n", NULLSTR(pszDirectoryName), NULLSTR(pszFileName)); check_reader_status(pCardData); if (pbData && cbData) { logprintf(pCardData, 1, "CardWriteFile try to write (%i):\n", 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)); return SCARD_E_FILE_NOT_FOUND; } 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: %li\n", dwret); return dwret; } if (pszDirectoryName && !strcmp(pszDirectoryName, "mscp")) { if (strlen(pszFileName) == 5 && (strstr(pszFileName, "kxc") == pszFileName || strstr(pszFileName, "ksc") == pszFileName)) { dwret = md_pkcs15_store_certificate(pCardData, pszFileName, pbData, cbData); if (dwret != SCARD_S_SUCCESS) return dwret; logprintf(pCardData, 2, "md_pkcs15_store_certificate() OK\n"); } } logprintf(pCardData, 2, "write '%s' ok.\n", NULLSTR(pszFileName)); return SCARD_S_SUCCESS; } DWORD WINAPI CardDeleteFile(__in PCARD_DATA pCardData, __in LPSTR pszDirectoryName, __in LPSTR pszFileName, __in DWORD dwFlags) { struct md_file *file = NULL; DWORD dwret; logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardDeleteFile(%s, %s) called\n", NULLSTR(pszDirectoryName), NULLSTR(pszFileName)); if(!pCardData) return SCARD_E_INVALID_PARAMETER; check_reader_status(pCardData); dwret = md_fs_delete_file(pCardData, pszDirectoryName, pszFileName); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 2, "CardDeleteFile(): delete file error: %X\n", dwret); return dwret; } return SCARD_S_SUCCESS; } DWORD WINAPI CardEnumFiles(__in PCARD_DATA pCardData, __in LPSTR pszDirectoryName, __out_ecount(*pdwcbFileName) LPSTR *pmszFileNames, __out LPDWORD pdwcbFileName, __in DWORD dwFlags) { VENDOR_SPECIFIC *vs = NULL; char mstr[0x100]; struct md_directory *dir = NULL; struct md_file *file = NULL; size_t offs; logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardEnumFiles() directory '%s'\n", NULLSTR(pszDirectoryName)); if (!pCardData) return SCARD_E_INVALID_PARAMETER; if (!pmszFileNames || !pdwcbFileName) return SCARD_E_INVALID_PARAMETER; if (dwFlags) { logprintf(pCardData, 1, "CardEnumFiles() dwFlags not 'zero' -- %X\n", dwFlags); return SCARD_E_INVALID_PARAMETER; } vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); 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)); return SCARD_E_FILE_NOT_FOUND; } file = dir->files; for (offs = 0; file != NULL && offs < sizeof(mstr) - 10;) { logprintf(pCardData, 2, "enum files(): file name '%s'\n", file->name); strcpy(mstr+offs, file->name); offs += strlen(file->name) + 1; file = file->next; } offs += 1; *pmszFileNames = (LPSTR)(*pCardData->pfnCspAlloc)(offs); if (*pmszFileNames == NULL) return SCARD_E_NO_MEMORY; CopyMemory(*pmszFileNames, mstr, offs); *pdwcbFileName = offs; return SCARD_S_SUCCESS; } DWORD WINAPI CardGetFileInfo(__in PCARD_DATA pCardData, __in LPSTR pszDirectoryName, __in LPSTR pszFileName, __in PCARD_FILE_INFO pCardFileInfo) { VENDOR_SPECIFIC *vs = NULL; struct md_directory *dir = NULL; struct md_file *file = NULL; logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardGetFileInfo(dirName:'%s',fileName:'%s', out %p)\n", NULLSTR(pszDirectoryName), NULLSTR(pszFileName), pCardFileInfo); vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); md_fs_find_file(pCardData, pszDirectoryName, pszFileName, &file); if (!file) { logprintf(pCardData, 2, "CardWriteFile(): file '%s' not found in '%s'\n", NULLSTR(pszFileName), NULLSTR(pszDirectoryName)); return SCARD_E_FILE_NOT_FOUND; } pCardFileInfo->dwVersion = CARD_FILE_INFO_CURRENT_VERSION; pCardFileInfo->cbFileSize = file->size; pCardFileInfo->AccessCondition = file->acl; return SCARD_S_SUCCESS; } DWORD WINAPI CardQueryFreeSpace(__in PCARD_DATA pCardData, __in DWORD dwFlags, __in PCARD_FREE_SPACE_INFO pCardFreeSpaceInfo) { VENDOR_SPECIFIC *vs; DWORD dwret; logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardQueryFreeSpace %p, dwFlags=%X, version=%X\n", pCardFreeSpaceInfo, dwFlags, pCardFreeSpaceInfo->dwVersion); if (!pCardData) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); check_reader_status(pCardData); dwret = md_free_space(pCardData, pCardFreeSpaceInfo); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "CardQueryFreeSpace() md free space error"); return dwret; } logprintf(pCardData, 7, "FreeSpace:\n"); loghex(pCardData, 7, (BYTE *)pCardFreeSpaceInfo, sizeof(*pCardFreeSpaceInfo)); return SCARD_S_SUCCESS; } DWORD WINAPI CardQueryKeySizes(__in PCARD_DATA pCardData, __in DWORD dwKeySpec, __in DWORD dwFlags, __out PCARD_KEY_SIZES pKeySizes) { DWORD dwret; logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardQueryKeySizes dwKeySpec=%X, dwFlags=%X, version=%X\n", dwKeySpec, dwFlags, pKeySizes->dwVersion); if (!pCardData) return SCARD_E_INVALID_PARAMETER; dwret = md_query_key_sizes(pKeySizes); if (dwret != SCARD_S_SUCCESS) return dwret; logprintf(pCardData, 7, "pKeySizes:\n"); loghex(pCardData, 7, (BYTE *)pKeySizes, sizeof(*pKeySizes)); return SCARD_S_SUCCESS; } DWORD WINAPI CardRSADecrypt(__in PCARD_DATA pCardData, __inout PCARD_RSA_DECRYPT_INFO pInfo) { int r, opt_crypt_flags = 0; unsigned ui; VENDOR_SPECIFIC *vs; struct sc_pkcs15_prkey_info *prkey_info; BYTE *pbuf = NULL, *pbuf2 = NULL; DWORD lg= 0, lg2 = 0; struct sc_pkcs15_object *pkey = NULL; struct sc_algorithm_info *alg_info = NULL; logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardRSADecrypt\n"); if (!pCardData) return SCARD_E_INVALID_PARAMETER; if (!pInfo) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); check_reader_status(pCardData); logprintf(pCardData, 2, "CardRSADecrypt dwVersion=%u, bContainerIndex=%u,dwKeySpec=%u pbData=%p, cbData=%u\n", pInfo->dwVersion,pInfo->bContainerIndex ,pInfo->dwKeySpec, pInfo->pbData, pInfo->cbData); if (pInfo->dwVersion >= CARD_RSA_KEY_DECRYPT_INFO_VERSION_TWO) logprintf(pCardData, 2, " pPaddingInfo=%p dwPaddingType=0x%08X\n", pInfo->pPaddingInfo, pInfo->dwPaddingType); pkey = vs->p15_containers[pInfo->bContainerIndex].prkey_obj; if (!pkey) { logprintf(pCardData, 2, "CardRSADecrypt prkey not found\n"); return SCARD_E_INVALID_PARAMETER; } /* input and output buffers are always the same size */ pbuf = pCardData->pfnCspAlloc(pInfo->cbData); if (!pbuf) return SCARD_E_NO_MEMORY; lg2 = pInfo->cbData; pbuf2 = pCardData->pfnCspAlloc(pInfo->cbData); if (!pbuf2) return SCARD_E_NO_MEMORY; /*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, prkey_info->modulus_length); if (!alg_info) { logprintf(pCardData, 2, "Cannot get appropriate RSA card algorithm for key size %i\n", prkey_info->modulus_length); return SCARD_F_INTERNAL_ERROR; } if (alg_info->flags & SC_ALGORITHM_RSA_RAW) { r = sc_pkcs15_decipher(vs->p15card, pkey, opt_crypt_flags, pbuf, pInfo->cbData, pbuf2, pInfo->cbData); logprintf(pCardData, 2, "sc_pkcs15_decipher returned %d\n", r); // Need to handle padding if (pInfo->dwVersion >= CARD_RSA_KEY_DECRYPT_INFO_VERSION_TWO) { if (pInfo->dwPaddingType == CARD_PADDING_PKCS1) { r = sc_pkcs1_strip_02_padding(pbuf2, pInfo->cbData, pbuf2, &pInfo->cbData); } /* TODO: Handle OAEP padding if present - can call PFN_CSP_UNPAD_DATA */ } } else if (alg_info->flags & SC_ALGORITHM_RSA_PAD_PKCS1) { logprintf(pCardData, 2, "sc_pkcs15_decipher: no oncard RSA RAW mechanism, try to use with PAD_PKCS1\n"); r = sc_pkcs15_decipher(vs->p15card, pkey, opt_crypt_flags | SC_ALGORITHM_RSA_PAD_PKCS1, pbuf, pInfo->cbData, pbuf2, pInfo->cbData); 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"); return SCARD_E_INVALID_PARAMETER; } if ( r < 0) { logprintf(pCardData, 2, "sc_pkcs15_decipher erreur %s\n", sc_strerror(r)); return SCARD_E_INVALID_VALUE; } logprintf(pCardData, 2, "decrypted data(%i):\n", pInfo->cbData); loghex(pCardData, 7, pbuf2, pInfo->cbData); /*inversion donnees */ for(ui = 0; ui < pInfo->cbData; ui++) pInfo->pbData[ui] = pbuf2[pInfo->cbData-ui-1]; pCardData->pfnCspFree(pbuf); pCardData->pfnCspFree(pbuf2); return SCARD_S_SUCCESS; } DWORD WINAPI CardSignData(__in PCARD_DATA pCardData, __in PCARD_SIGNING_INFO pInfo) { VENDOR_SPECIFIC *vs; ALG_ID hashAlg; sc_pkcs15_prkey_info_t *prkey_info; BYTE dataToSign[0x200]; int r, opt_crypt_flags = 0, opt_hash_flags = 0; size_t dataToSignLen = sizeof(dataToSign); sc_pkcs15_object_t *pkey; logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardSignData\n"); if (!pCardData || !pInfo) return SCARD_E_INVALID_PARAMETER; logprintf(pCardData, 2, "CardSignData dwVersion=%u, bContainerIndex=%u, dwKeySpec=%u, dwSigningFlags=0x%08X, aiHashAlg=0x%08X\n", pInfo->dwVersion,pInfo->bContainerIndex ,pInfo->dwKeySpec, pInfo->dwSigningFlags, pInfo->aiHashAlg); logprintf(pCardData, 7, "pInfo->pbData(%i) ", pInfo->cbData); loghex(pCardData, 7, pInfo->pbData, pInfo->cbData); hashAlg = pInfo->aiHashAlg; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); if (pInfo->bContainerIndex >= MD_MAX_KEY_CONTAINERS) return SCARD_E_NO_KEY_CONTAINER; pkey = vs->p15_containers[pInfo->bContainerIndex].prkey_obj; if (!pkey) return SCARD_E_NO_KEY_CONTAINER; prkey_info = (struct sc_pkcs15_prkey_info *)(pkey->data); check_reader_status(pCardData); logprintf(pCardData, 2, "pInfo->dwVersion = %d\n", pInfo->dwVersion); if (dataToSignLen < pInfo->cbData) return SCARD_E_INSUFFICIENT_BUFFER; memcpy(dataToSign, pInfo->pbData, pInfo->cbData); dataToSignLen = pInfo->cbData; if (CARD_PADDING_INFO_PRESENT & pInfo->dwSigningFlags) { BCRYPT_PKCS1_PADDING_INFO *pinf = (BCRYPT_PKCS1_PADDING_INFO *)pInfo->pPaddingInfo; if (CARD_PADDING_PKCS1 != pInfo->dwPaddingType) { logprintf(pCardData, 0, "unsupported paddingtype\n"); return SCARD_E_UNSUPPORTED_FEATURE; } if (!pinf->pszAlgId) { /* hashAlg = CALG_SSL3_SHAMD5; */ logprintf(pCardData, 3, "Using CALG_SSL3_SHAMD5 hashAlg\n"); opt_hash_flags = SC_ALGORITHM_RSA_HASH_MD5_SHA1; } else { if (wcscmp(pinf->pszAlgId, L"MD5") == 0) opt_hash_flags = SC_ALGORITHM_RSA_HASH_MD5; else if (wcscmp(pinf->pszAlgId, L"SHA1") == 0) opt_hash_flags = SC_ALGORITHM_RSA_HASH_SHA1; else if (wcscmp(pinf->pszAlgId, L"SHAMD5") == 0) opt_hash_flags = SC_ALGORITHM_RSA_HASH_MD5_SHA1; else if (wcscmp(pinf->pszAlgId, L"SHA256") == 0) opt_hash_flags = SC_ALGORITHM_RSA_HASH_SHA256; else { logprintf(pCardData, 0,"unknown AlgId %S\n",NULLWSTR(pinf->pszAlgId)); return SCARD_E_UNSUPPORTED_FEATURE; } } } else { logprintf(pCardData, 3, "CARD_PADDING_INFO_PRESENT not set\n"); if (GET_ALG_CLASS(hashAlg) != ALG_CLASS_HASH) { logprintf(pCardData, 0, "bogus aiHashAlg\n"); return SCARD_E_INVALID_PARAMETER; } if (hashAlg == CALG_MD5) opt_hash_flags = SC_ALGORITHM_RSA_HASH_MD5; else if (hashAlg == CALG_SHA1) opt_hash_flags = SC_ALGORITHM_RSA_HASH_SHA1; else if (hashAlg == CALG_SSL3_SHAMD5) opt_hash_flags = SC_ALGORITHM_RSA_HASH_MD5_SHA1; else if (hashAlg == CALG_SHA_256) opt_hash_flags = SC_ALGORITHM_RSA_HASH_SHA256; else if (hashAlg !=0) return SCARD_E_UNSUPPORTED_FEATURE; } /* From sc-minidriver_specs_v7.docx pp.76: * 'The Base CSP/KSP performs the hashing operation on the data before passing it * to CardSignData for signature.' * So, the SC_ALGORITHM_RSA_HASH_* flags should not be passed to pkcs15 library * when calculating the signature . * * From sc-minidriver_specs_v7.docx pp.76: * 'If the aiHashAlg member is nonzero, it specifies the hash algorithm’s object identifier (OID) * that is encoded in the PKCS padding.' * So, the digest info has be included into the data to be signed. * */ if (opt_hash_flags) { logprintf(pCardData, 2, "include digest info of the algorithm 0x%08X\n", opt_hash_flags); dataToSignLen = sizeof(dataToSign); r = sc_pkcs1_encode(vs->ctx, opt_hash_flags, pInfo->pbData, pInfo->cbData, dataToSign, &dataToSignLen, 0); if (r) { logprintf(pCardData, 2, "PKCS#1 encode error %s\n", sc_strerror(r)); return SCARD_E_INVALID_VALUE; } } opt_crypt_flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE; pInfo->cbSignedData = prkey_info->modulus_length / 8; logprintf(pCardData, 3, "pInfo->cbSignedData = %d\n", pInfo->cbSignedData); if(!(pInfo->dwSigningFlags&CARD_BUFFER_SIZE_ONLY)) { int r,i; BYTE *pbuf = NULL; DWORD lg; lg = pInfo->cbSignedData; logprintf(pCardData, 3, "lg = %d\n", lg); pbuf = pCardData->pfnCspAlloc(lg); if (!pbuf) return SCARD_E_NO_MEMORY; logprintf(pCardData, 7, "Data to sign: "); loghex(pCardData, 7, dataToSign, dataToSignLen); pInfo->pbSignedData = pCardData->pfnCspAlloc(pInfo->cbSignedData); if (!pInfo->pbSignedData) { pCardData->pfnCspFree(pbuf); return SCARD_E_NO_MEMORY; } r = sc_pkcs15_compute_signature(vs->p15card, pkey, opt_crypt_flags, dataToSign, dataToSignLen, pbuf, lg); logprintf(pCardData, 2, "sc_pkcs15_compute_signature return %d\n", r); if(r < 0) { logprintf(pCardData, 2, "sc_pkcs15_compute_signature erreur %s\n", sc_strerror(r)); return SCARD_F_INTERNAL_ERROR; } pInfo->cbSignedData = r; /*inversion donnees*/ for(i = 0; i < r; i++) pInfo->pbSignedData[i] = pbuf[r-i-1]; pCardData->pfnCspFree(pbuf); logprintf(pCardData, 7, "Signature (inverted): "); loghex(pCardData, 7, pInfo->pbSignedData, pInfo->cbSignedData); } logprintf(pCardData, 3, "CardSignData, dwVersion=%u, name=%S, hScard=0x%08X, hSCardCtx=0x%08X\n", pCardData->dwVersion, NULLWSTR(pCardData->pwszCardName),pCardData->hScard, pCardData->hSCardCtx); return SCARD_S_SUCCESS; } DWORD WINAPI CardConstructDHAgreement(__in PCARD_DATA pCardData, __in PCARD_DH_AGREEMENT_INFO pAgreementInfo) { logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardConstructDHAgreement - unsupported\n"); return SCARD_E_UNSUPPORTED_FEATURE; } DWORD WINAPI CardDeriveKey(__in PCARD_DATA pCardData, __in PCARD_DERIVE_KEY pAgreementInfo) { logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardDeriveKey - unsupported\n"); return SCARD_E_UNSUPPORTED_FEATURE; } DWORD WINAPI CardDestroyDHAgreement( __in PCARD_DATA pCardData, __in BYTE bSecretAgreementIndex, __in DWORD dwFlags) { logprintf(pCardData, 1, "CardDestroyDHAgreement - unsupported\n"); return SCARD_E_UNSUPPORTED_FEATURE; } DWORD WINAPI CspGetDHAgreement(__in PCARD_DATA pCardData, __in PVOID hSecretAgreement, __out BYTE* pbSecretAgreementIndex, __in DWORD dwFlags) { logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CspGetDHAgreement - unsupported\n"); return SCARD_E_UNSUPPORTED_FEATURE; } DWORD WINAPI CardGetChallengeEx(__in PCARD_DATA pCardData, __in PIN_ID PinId, __deref_out_bcount(*pcbChallengeData) PBYTE *ppbChallengeData, __out PDWORD pcbChallengeData, __in DWORD dwFlags) { logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardGetChallengeEx - unsupported\n"); return SCARD_E_UNSUPPORTED_FEATURE; } DWORD WINAPI CardAuthenticateEx(__in PCARD_DATA pCardData, __in PIN_ID PinId, __in DWORD dwFlags, __in PBYTE pbPinData, __in DWORD cbPinData, __deref_out_bcount_opt(*pcbSessionPin) PBYTE *ppbSessionPin, __out_opt PDWORD pcbSessionPin, __out_opt PDWORD pcAttemptsRemaining) { int r, tries_left; VENDOR_SPECIFIC *vs; CARD_CACHE_FILE_FORMAT *cardcf = NULL; DWORD dwret; sc_pkcs15_object_t *pin_obj = NULL; logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardAuthenticateEx\n"); if (!pCardData) return SCARD_E_INVALID_PARAMETER; logprintf(pCardData, 2, "CardAuthenticateEx: PinId=%u, dwFlags=0x%08X, cbPinData=%u, Attempts %s\n", PinId,dwFlags,cbPinData,pcAttemptsRemaining ? "YES" : "NO"); vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); check_reader_status(pCardData); if (dwFlags == CARD_AUTHENTICATE_GENERATE_SESSION_PIN || dwFlags == CARD_AUTHENTICATE_SESSION_PIN) return SCARD_E_UNSUPPORTED_FEATURE; if (dwFlags && dwFlags != CARD_PIN_SILENT_CONTEXT) return SCARD_E_INVALID_PARAMETER; if (NULL == pbPinData) return SCARD_E_INVALID_PARAMETER; if (PinId != ROLE_USER) return SCARD_E_INVALID_PARAMETER; r = md_get_pin_by_role(pCardData, PinId, &pin_obj); if (r != SCARD_S_SUCCESS) { logprintf(pCardData, 2, "Cannot get User PIN object"); return r; } r = sc_pkcs15_verify_pin(vs->p15card, pin_obj, (const u8 *) pbPinData, cbPinData); if (r) { logprintf(pCardData, 2, "PIN code verification failed: %s\n", sc_strerror(r)); tries_left = ((struct sc_pkcs15_auth_info *)pin_obj->data)->tries_left; logprintf(pCardData, 7, "PIN retries left: %i\n", tries_left); if (r == SC_ERROR_AUTH_METHOD_BLOCKED) return SCARD_W_CHV_BLOCKED; if(pcAttemptsRemaining) (*pcAttemptsRemaining) = tries_left; return SCARD_W_WRONG_CHV; } logprintf(pCardData, 2, "Pin code correct.\n"); dwret = md_get_cardcf(pCardData, &cardcf); if (dwret != SCARD_S_SUCCESS) return dwret; SET_PIN(cardcf->bPinsFreshness, PinId); logprintf(pCardData, 7, "PinsFreshness = %d\n", cardcf->bPinsFreshness); return 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) { logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardChangeAuthenticatorEx - unsupported\n"); return SCARD_E_UNSUPPORTED_FEATURE; } DWORD WINAPI CardDeauthenticateEx(__in PCARD_DATA pCardData, __in PIN_SET PinId, __in DWORD dwFlags) { VENDOR_SPECIFIC *vs; CARD_CACHE_FILE_FORMAT *cardcf = NULL; struct md_file *cmapfile = NULL; DWORD dwret; logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardDeauthenticateEx PinId=%d dwFlags=0x%08X\n",PinId, dwFlags); if (!pCardData) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); check_reader_status(pCardData); dwret = md_get_cardcf(pCardData, &cardcf); if (dwret != SCARD_S_SUCCESS) return dwret; CLEAR_PIN(cardcf->bPinsFreshness, PinId); logprintf(pCardData, 1, "CardDeauthenticateEx bPinsFreshness:%d\n", cardcf->bPinsFreshness); /* TODO Reset PKCS#15 PIN object 'validated' flag */ return SCARD_S_SUCCESS; } 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) { logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardGetContainerProperty\n"); check_reader_status(pCardData); if (!pCardData) return SCARD_E_INVALID_PARAMETER; logprintf(pCardData, 2, "CardGetContainerProperty bContainerIndex=%u, wszProperty=%S," \ "cbData=%u, dwFlags=0x%08X\n",bContainerIndex,NULLWSTR(wszProperty),cbData,dwFlags); if (!wszProperty) return SCARD_E_INVALID_PARAMETER; if (dwFlags) return SCARD_E_INVALID_PARAMETER; if (!pbData || !pdwDataLen) return SCARD_E_INVALID_PARAMETER; 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 ) return ERROR_REVISION_MISMATCH; if (cbData < sizeof(*p)) return ERROR_INSUFFICIENT_BUFFER; return 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)) return ERROR_INSUFFICIENT_BUFFER; *p = ROLE_USER; logprintf(pCardData, 2,"Return Pin id %u\n",*p); return SCARD_S_SUCCESS; } return 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) { logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardSetContainerProperty - unsupported\n"); return 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; logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 2, "CardGetProperty('%S',cbData=%u,dwFlags=%u) called\n", NULLWSTR(wszProperty),cbData,dwFlags); if (!pCardData || !wszProperty) return SCARD_E_INVALID_PARAMETER; if (!pbData || !pdwDataLen) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); check_reader_status(pCardData); if (wcscmp(CP_CARD_FREE_SPACE,wszProperty) == 0) { PCARD_FREE_SPACE_INFO pCardFreeSpaceInfo = (PCARD_FREE_SPACE_INFO )pbData; if (pdwDataLen) *pdwDataLen = sizeof(*pCardFreeSpaceInfo); if (cbData < sizeof(*pCardFreeSpaceInfo)) return SCARD_E_NO_MEMORY; dwret = md_free_space(pCardData, pCardFreeSpaceInfo); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "Get free space error"); return dwret; } } else if (wcscmp(CP_CARD_CAPABILITIES, wszProperty) == 0) { PCARD_CAPABILITIES pCardCapabilities = (PCARD_CAPABILITIES )pbData; if (pdwDataLen) *pdwDataLen = sizeof(*pCardCapabilities); if (cbData < sizeof(*pCardCapabilities)) return ERROR_INSUFFICIENT_BUFFER; dwret = md_card_capabilities(pCardCapabilities); if (dwret != SCARD_S_SUCCESS) return dwret; } else if (wcscmp(CP_CARD_KEYSIZES,wszProperty) == 0) { PCARD_KEY_SIZES pKeySizes = (PCARD_KEY_SIZES )pbData; if (pdwDataLen) *pdwDataLen = sizeof(*pKeySizes); if (cbData < sizeof(*pKeySizes)) return ERROR_INSUFFICIENT_BUFFER; dwret = md_query_key_sizes(pKeySizes); if (dwret != SCARD_S_SUCCESS) return dwret; } else if (wcscmp(CP_CARD_READ_ONLY, wszProperty) == 0) { BOOL *p = (BOOL *)pbData; if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData < sizeof(*p)) return ERROR_INSUFFICIENT_BUFFER; *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)) return 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)) return 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"); return SCARD_E_FILE_NOT_FOUND; } if (pdwDataLen) *pdwDataLen = cardid->size; if (cbData < cardid->size) return 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); size_t sn_len = strlen(vs->p15card->tokeninfo->serial_number)/2; 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 = buf_len; if (cbData < buf_len) return 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)) return ERROR_INSUFFICIENT_BUFFER; if (p->dwVersion != PIN_INFO_CURRENT_VERSION) return ERROR_REVISION_MISMATCH; p->PinType = AlphaNumericPinType; p->dwFlags = 0; switch (dwFlags) { case ROLE_USER: logprintf(pCardData, 2,"returning info on PIN ROLE_USER ( Auth ) [%u]\n",dwFlags); p->PinPurpose = DigitalSignaturePin; p->PinCachePolicy.dwVersion = PIN_CACHE_POLICY_CURRENT_VERSION; p->PinCachePolicy.dwPinCachePolicyInfo = 0; p->PinCachePolicy.PinCachePolicyType = PinCacheNormal; p->dwChangePermission = CREATE_PIN_SET(ROLE_USER); p->dwUnblockPermission = CREATE_PIN_SET(ROLE_ADMIN); break; case ROLE_ADMIN: logprintf(pCardData, 2,"returning info on PIN ROLE_ADMIN ( Unblock ) [%u]\n",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, 0,"Invalid Pin number %u requested\n",dwFlags); return SCARD_E_INVALID_PARAMETER; } } else if (wcscmp(CP_CARD_LIST_PINS,wszProperty) == 0) { PPIN_SET p = (PPIN_SET) pbData; if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData < sizeof(*p)) return ERROR_INSUFFICIENT_BUFFER; SET_PIN(*p, ROLE_USER); } else if (wcscmp(CP_CARD_AUTHENTICATED_STATE,wszProperty) == 0) { PPIN_SET p = (PPIN_SET) pbData; if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData < sizeof(*p)) return ERROR_INSUFFICIENT_BUFFER; logprintf(pCardData, 7, "CARD_AUTHENTICATED_STATE invalid\n"); return SCARD_E_INVALID_PARAMETER; } else if (wcscmp(CP_CARD_PIN_STRENGTH_VERIFY,wszProperty) == 0) { DWORD *p = (DWORD *)pbData; if (dwFlags != ROLE_USER) return SCARD_E_INVALID_PARAMETER; if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData < sizeof(*p)) return ERROR_INSUFFICIENT_BUFFER; *p = CARD_PIN_STRENGTH_PLAINTEXT; } else { logprintf(pCardData, 3, "Unsupported property '%S'\n", wszProperty); return SCARD_E_INVALID_PARAMETER; } logprintf(pCardData, 7, "returns '%S' ", wszProperty); loghex(pCardData, 7, pbData, *pdwDataLen); return SCARD_S_SUCCESS; } DWORD WINAPI CardSetProperty(__in PCARD_DATA pCardData, __in LPCWSTR wszProperty, __in_bcount(cbDataLen) PBYTE pbData, __in DWORD cbDataLen, __in DWORD dwFlags) { logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardSetProperty\n"); if (!pCardData) return SCARD_E_INVALID_PARAMETER; logprintf(pCardData, 2, "CardSetProperty wszProperty=%S, cbDataLen=%u, dwFlags=%u",\ NULLWSTR(wszProperty),cbDataLen,dwFlags); if (!wszProperty) return SCARD_E_INVALID_PARAMETER; if (wcscmp(CP_CARD_PIN_STRENGTH_VERIFY, wszProperty) == 0 || wcscmp(CP_CARD_PIN_INFO, wszProperty) == 0) return SCARD_E_INVALID_PARAMETER; if (dwFlags) return SCARD_E_INVALID_PARAMETER; if (wcscmp(CP_PIN_CONTEXT_STRING, wszProperty) == 0) return SCARD_S_SUCCESS; if (wcscmp(CP_CARD_CACHE_MODE, wszProperty) == 0 || wcscmp(CP_SUPPORTS_WIN_X509_ENROLLMENT, wszProperty) == 0 || wcscmp(CP_CARD_GUID, wszProperty) == 0 || wcscmp(CP_CARD_SERIAL_NO, wszProperty) == 0) { return SCARD_E_INVALID_PARAMETER; } if (!pbData || !cbDataLen) return SCARD_E_INVALID_PARAMETER; if (wcscmp(CP_PARENT_WINDOW, wszProperty) == 0) { if (cbDataLen != sizeof(DWORD)) { return SCARD_E_INVALID_PARAMETER; } else { HWND cp = *((HWND *) pbData); if (cp!=0 && !IsWindow(cp)) return SCARD_E_INVALID_PARAMETER; } return SCARD_S_SUCCESS; } logprintf(pCardData, 3, "INVALID PARAMETER\n"); return SCARD_E_INVALID_PARAMETER; } DWORD WINAPI CardAcquireContext(IN PCARD_DATA pCardData, __in DWORD dwFlags) { VENDOR_SPECIFIC *vs; DWORD dwret, suppliedVersion = 0; if (!pCardData) return SCARD_E_INVALID_PARAMETER; if (dwFlags) return SCARD_E_INVALID_PARAMETER; suppliedVersion = pCardData->dwVersion; /* VENDOR SPECIFIC */ vs = pCardData->pvVendorSpecific = pCardData->pfnCspAlloc(sizeof(VENDOR_SPECIFIC)); memset(vs, 0, sizeof(VENDOR_SPECIFIC)); logprintf(pCardData, 1, "==================================================================\n"); logprintf(pCardData, 1, "\nP:%d T:%d pCardData:%p ",GetCurrentProcessId(), GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardAcquireContext, dwVersion=%u, name=%S,hScard=0x%08X, hSCardCtx=0x%08X\n", pCardData->dwVersion, NULLWSTR(pCardData->pwszCardName),pCardData->hScard, pCardData->hSCardCtx); vs->hScard = pCardData->hScard; vs->hSCardCtx = pCardData->hSCardCtx; /* The lowest supported version is 4. */ if (pCardData->dwVersion < MD_MINIMUM_VERSION_SUPPORTED) return (DWORD) ERROR_REVISION_MISMATCH; if( pCardData->hScard == 0) { logprintf(pCardData, 0, "Invalide handle.\n"); return SCARD_E_INVALID_HANDLE; } logprintf(pCardData, 2, "request version pCardData->dwVersion = %d\n", pCardData->dwVersion); pCardData->dwVersion = min(pCardData->dwVersion, MD_CURRENT_VERSION_SUPPORTED); logprintf(pCardData, 2, "pCardData->dwVersion = %d\n", pCardData->dwVersion); dwret = md_create_context(pCardData, vs); if (dwret != SCARD_S_SUCCESS) return dwret; md_static_data.flags &= ~MD_STATIC_FLAG_CONTEXT_DELETED; 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; /* NULL */ 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) return dwret; dwret = md_fs_init(pCardData); if (dwret != SCARD_S_SUCCESS) return dwret; logprintf(pCardData, 1, "OpenSC init done.\n"); if (suppliedVersion > 4) { pCardData->pfnCardDeriveKey = CardDeriveKey; pCardData->pfnCardDestroyDHAgreement = CardDestroyDHAgreement; pCardData->pfnCspGetDHAgreement = CspGetDHAgreement; if (suppliedVersion > 5 ) { pCardData->pfnCardGetChallengeEx = CardGetChallengeEx; pCardData->pfnCardAuthenticateEx = CardAuthenticateEx; pCardData->pfnCardChangeAuthenticatorEx = CardChangeAuthenticatorEx; pCardData->pfnCardDeauthenticateEx = CardDeauthenticateEx; pCardData->pfnCardGetContainerProperty = CardGetContainerProperty; pCardData->pfnCardSetContainerProperty = CardSetContainerProperty; pCardData->pfnCardGetProperty = CardGetProperty; pCardData->pfnCardSetProperty = CardSetProperty; } } return SCARD_S_SUCCESS; } static int associate_card(PCARD_DATA pCardData) { VENDOR_SPECIFIC *vs; DWORD dw; int r; logprintf(pCardData, 1, "associate_card\n"); if (!pCardData) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); /* * set the addresses of the reader and card handles * Our cardmod 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; /** * Check if a linked context has been deleted - if so, repair shared data. * Multithreaded issue - TODO: proper multithreaded handling */ if (md_static_data.flags & MD_STATIC_FLAG_CONTEXT_DELETED) { r = sc_context_repair(&(vs->ctx)); logprintf(pCardData, 2, "sc_context_repair called - result = %d, %s\n", r, sc_strerror(r)); md_static_data.flags &= ~MD_STATIC_FLAG_CONTEXT_DELETED; } /* set the provided reader and card handles into ctx */ logprintf(pCardData, 5, "cardmod_use_handles %d\n", sc_ctx_use_reader(vs->ctx, &vs->hSCardCtx, &vs->hScard)); /* should be only one reader */ logprintf(pCardData, 5, "sc_ctx_get_reader_count(ctx): %d\n", sc_ctx_get_reader_count(vs->ctx)); vs->reader = sc_ctx_get_reader(vs->ctx, 0); if(vs->reader) { struct sc_app_info *app_generic = NULL; struct sc_aid *aid = NULL; r = sc_connect_card(vs->reader, &(vs->card)); if(r) { logprintf(pCardData, 0, "Cannot connect card in reader '%s'\n", NULLSTR(vs->reader->name)); return SCARD_E_UNKNOWN_CARD; } logprintf(pCardData, 3, "Connected card in '%s'\n", NULLSTR(vs->reader->name)); 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(vs->card == NULL || vs->p15card == NULL) { logprintf(pCardData, 0, "Card unknown.\n"); return SCARD_E_UNKNOWN_CARD; } dw = md_get_pin_by_role(pCardData, ROLE_USER, &vs->obj_user_pin); if (dw != SCARD_S_SUCCESS) { logprintf(pCardData, 2, "Cannot get User PIN object"); return dw; } dw = md_get_pin_by_role(pCardData, ROLE_USER, &vs->obj_sopin); if (dw != SCARD_S_SUCCESS) logprintf(pCardData, 2, "Cannot get ADMIN PIN object -- ignored"); return SCARD_S_SUCCESS; } static int disassociate_card(PCARD_DATA pCardData) { VENDOR_SPECIFIC *vs; logprintf(pCardData, 1, "disassociate_card\n"); if (!pCardData) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); vs->obj_user_pin = NULL; vs->obj_sopin = NULL; 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; return SCARD_S_SUCCESS; } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { #ifdef CARDMOD_LOW_LEVEL_DEBUG CHAR name[MAX_PATH + 1] = "\0"; char *reason = ""; GetModuleFileName(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%X) '%s'; reason='%s'; Reserved=%p; P:%d; T:%d\n", hModule, name, reason, lpReserved, GetCurrentProcessId(), GetCurrentThreadId()); #endif switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: md_static_data.attach_check = MD_STATIC_PROCESS_ATTACHED; break; case DLL_PROCESS_DETACH: md_static_data.attach_check = 0; break; } return TRUE; } #ifdef _MANAGED #pragma managed(pop) #endif #endif opensc-0.13.0/src/minidriver/Makefile.am0000644000015201777760000000174012057406034015022 00000000000000include $(top_srcdir)/win32/ltrc.inc MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak if ENABLE_MINIDRIVER lib_LTLIBRARIES = opensc-minidriver@LIBRARY_BITNESS@.la # Do we need this on bin? Why can't we # put it in dedicated directory dist_sbin_SCRIPTS = opensc-minidriver.inf minidriver-westcos.reg minidriver-sc-hsm.reg else dist_noinst_DATA = opensc-minidriver.inf minidriver-westcos.reg minidriver-sc-hsm.reg endif AM_CPPFLAGS = -I$(top_srcdir)/src opensc_minidriver@LIBRARY_BITNESS@_la_SOURCES = minidriver.c minidriver.exports \ $(top_builddir)/win32/versioninfo.rc opensc_minidriver@LIBRARY_BITNESS@_la_LIBADD = \ $(top_builddir)/src/libopensc/libopensc.la \ -lcrypt32 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)/" endif opensc-0.13.0/src/minidriver/Makefile.in0000644000015201777760000005652612057406055015052 00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program 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. @SET_MAKE@ # Required to build Windows resource file VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ DIST_COMMON = $(am__dist_noinst_DATA_DIST) \ $(am__dist_sbin_SCRIPTS_DIST) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in $(srcdir)/opensc-minidriver.inf.in \ $(top_srcdir)/win32/ltrc.inc subdir = src/minidriver ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = opensc-minidriver.inf CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(sbindir)" LTLIBRARIES = $(lib_LTLIBRARIES) opensc_minidriver@LIBRARY_BITNESS@_la_DEPENDENCIES = \ $(top_builddir)/src/libopensc/libopensc.la am__dirstamp = $(am__leading_dot)dirstamp am_opensc_minidriver@LIBRARY_BITNESS@_la_OBJECTS = minidriver.lo \ $(top_builddir)/win32/versioninfo.lo opensc_minidriver@LIBRARY_BITNESS@_la_OBJECTS = \ $(am_opensc_minidriver@LIBRARY_BITNESS@_la_OBJECTS) AM_V_lt = $(am__v_lt_$(V)) am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) am__v_lt_0 = --silent opensc_minidriver@LIBRARY_BITNESS@_la_LINK = $(LIBTOOL) $(AM_V_lt) \ --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \ $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(opensc_minidriver@LIBRARY_BITNESS@_la_LDFLAGS) $(LDFLAGS) -o \ $@ @ENABLE_MINIDRIVER_TRUE@am_opensc_minidriver@LIBRARY_BITNESS@_la_rpath = \ @ENABLE_MINIDRIVER_TRUE@ -rpath $(libdir) am__dist_sbin_SCRIPTS_DIST = opensc-minidriver.inf \ minidriver-westcos.reg minidriver-sc-hsm.reg SCRIPTS = $(dist_sbin_SCRIPTS) DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_$(V)) am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) am__v_CC_0 = @echo " CC " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_$(V)) am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) am__v_CCLD_0 = @echo " CCLD " $@; AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; SOURCES = $(opensc_minidriver@LIBRARY_BITNESS@_la_SOURCES) DIST_SOURCES = $(opensc_minidriver@LIBRARY_BITNESS@_la_SOURCES) am__dist_noinst_DATA_DIST = opensc-minidriver.inf \ minidriver-westcos.reg minidriver-sc-hsm.reg DATA = $(dist_noinst_DATA) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUG_FILE = @DEBUG_FILE@ DEFAULT_PCSC_PROVIDER = @DEFAULT_PCSC_PROVIDER@ DEFAULT_SM_MODULE = @DEFAULT_SM_MODULE@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GIT = @GIT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBRARY_BITNESS = @LIBRARY_BITNESS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCT_CFLAGS = @OPENCT_CFLAGS@ OPENCT_LIBS = @OPENCT_LIBS@ OPENSC_LT_AGE = @OPENSC_LT_AGE@ OPENSC_LT_CURRENT = @OPENSC_LT_CURRENT@ OPENSC_LT_OLDEST = @OPENSC_LT_OLDEST@ OPENSC_LT_REVISION = @OPENSC_LT_REVISION@ OPENSC_VERSION_FIX = @OPENSC_VERSION_FIX@ OPENSC_VERSION_MAJOR = @OPENSC_VERSION_MAJOR@ OPENSC_VERSION_MINOR = @OPENSC_VERSION_MINOR@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OPTIONAL_OPENCT_CFLAGS = @OPTIONAL_OPENCT_CFLAGS@ OPTIONAL_OPENCT_LIBS = @OPTIONAL_OPENCT_LIBS@ OPTIONAL_OPENSSL_CFLAGS = @OPTIONAL_OPENSSL_CFLAGS@ OPTIONAL_OPENSSL_LIBS = @OPTIONAL_OPENSSL_LIBS@ OPTIONAL_PCSC_CFLAGS = @OPTIONAL_PCSC_CFLAGS@ OPTIONAL_READLINE_CFLAGS = @OPTIONAL_READLINE_CFLAGS@ OPTIONAL_READLINE_LIBS = @OPTIONAL_READLINE_LIBS@ OPTIONAL_ZLIB_CFLAGS = @OPTIONAL_ZLIB_CFLAGS@ OPTIONAL_ZLIB_LIBS = @OPTIONAL_ZLIB_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCSC_CFLAGS = @PCSC_CFLAGS@ PCSC_LIBS = @PCSC_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RC = @RC@ READLINE_CFLAGS = @READLINE_CFLAGS@ READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ WIN_LIBPREFIX = @WIN_LIBPREFIX@ XSLTPROC = @XSLTPROC@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ git = @git@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkcs11dir = @pkcs11dir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ xslstylesheetsdir = @xslstylesheetsdir@ RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE) MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak @ENABLE_MINIDRIVER_TRUE@lib_LTLIBRARIES = opensc-minidriver@LIBRARY_BITNESS@.la # Do we need this on bin? Why can't we # put it in dedicated directory @ENABLE_MINIDRIVER_TRUE@dist_sbin_SCRIPTS = opensc-minidriver.inf minidriver-westcos.reg minidriver-sc-hsm.reg @ENABLE_MINIDRIVER_FALSE@dist_noinst_DATA = opensc-minidriver.inf minidriver-westcos.reg minidriver-sc-hsm.reg AM_CPPFLAGS = -I$(top_srcdir)/src opensc_minidriver@LIBRARY_BITNESS@_la_SOURCES = minidriver.c minidriver.exports \ $(top_builddir)/win32/versioninfo.rc opensc_minidriver@LIBRARY_BITNESS@_la_LIBADD = \ $(top_builddir)/src/libopensc/libopensc.la \ -lcrypt32 opensc_minidriver@LIBRARY_BITNESS@_la_LDFLAGS = $(AM_LDFLAGS) \ -export-symbols "$(srcdir)/minidriver.exports" \ -module -avoid-version -no-undefined all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj .rc $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/win32/ltrc.inc $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/minidriver/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/minidriver/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): opensc-minidriver.inf: $(top_builddir)/config.status $(srcdir)/opensc-minidriver.inf.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ test "$$dir" != "$$p" || dir=.; \ echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done $(top_builddir)/win32/$(am__dirstamp): @$(MKDIR_P) $(top_builddir)/win32 @: > $(top_builddir)/win32/$(am__dirstamp) $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) $(top_builddir)/win32/$(DEPDIR) @: > $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp) $(top_builddir)/win32/versioninfo.lo: \ $(top_builddir)/win32/$(am__dirstamp) \ $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp) opensc-minidriver@LIBRARY_BITNESS@.la: $(opensc_minidriver@LIBRARY_BITNESS@_la_OBJECTS) $(opensc_minidriver@LIBRARY_BITNESS@_la_DEPENDENCIES) $(AM_V_CCLD)$(opensc_minidriver@LIBRARY_BITNESS@_la_LINK) $(am_opensc_minidriver@LIBRARY_BITNESS@_la_rpath) $(opensc_minidriver@LIBRARY_BITNESS@_la_OBJECTS) $(opensc_minidriver@LIBRARY_BITNESS@_la_LIBADD) $(LIBS) install-dist_sbinSCRIPTS: $(dist_sbin_SCRIPTS) @$(NORMAL_INSTALL) test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)" @list='$(dist_sbin_SCRIPTS)'; test -n "$(sbindir)" || list=; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ } \ ; done uninstall-dist_sbinSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(dist_sbin_SCRIPTS)'; test -n "$(sbindir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(sbindir)" && rm -f $$files mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f $(top_builddir)/win32/versioninfo.$(OBJEXT) -rm -f $(top_builddir)/win32/versioninfo.lo distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/minidriver.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf $(top_builddir)/win32/.libs $(top_builddir)/win32/_libs -rm -rf .libs _libs ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: CTAGS CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(SCRIPTS) $(DATA) installdirs: for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(sbindir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -test -z "$(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp)" || rm -f $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp) -test -z "$(top_builddir)/win32/$(am__dirstamp)" || rm -f $(top_builddir)/win32/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) @ENABLE_MINIDRIVER_FALSE@install-exec-hook: clean: clean-am clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-dist_sbinSCRIPTS install-libLTLIBRARIES @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-exec-hook install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dist_sbinSCRIPTS uninstall-libLTLIBRARIES .MAKE: install-am install-exec-am install-strip .PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ clean-libLTLIBRARIES clean-libtool ctags distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am \ install-dist_sbinSCRIPTS install-dvi install-dvi-am \ install-exec install-exec-am install-exec-hook install-html \ install-html-am install-info install-info-am \ install-libLTLIBRARIES install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags uninstall uninstall-am uninstall-dist_sbinSCRIPTS \ uninstall-libLTLIBRARIES .rc.lo: $(LTRCCOMPILE) -i "$<" -o "$@" .rc.o: $(RCCOMPILE) -i "$<" -o "$@" @ENABLE_MINIDRIVER_TRUE@install-exec-hook: @ENABLE_MINIDRIVER_TRUE@ mv "$(DESTDIR)$(libdir)/opensc-minidriver@LIBRARY_BITNESS@.dll" "$(DESTDIR)$(bindir)/" # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: opensc-0.13.0/src/tests/0000755000015201777760000000000012057406120012032 500000000000000opensc-0.13.0/src/tests/sc-test.c0000644000015201777760000000614112057406034013506 00000000000000/* Copyright (C) 2001 Juha Yrjölä * All rights reserved. * * Common functions for test programs */ #include "config.h" #include #include #include #include "common/compat_getopt.h" #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 } }; #if 0 const char * option_help[] = { "Uses reader number [0]", "Forces the use of driver [auto-detect]", "Debug output -- may be supplied several times", }; #endif 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 != 1 && i < (int) sc_ctx_get_reader_count(ctx); i++) rc = sc_detect_card_presence(sc_ctx_get_reader(ctx, opt_reader)); if (rc == 1) opt_reader = i - 1; } if (rc > 0) { printf("Card detected in reader '%s'\n",sc_ctx_get_reader(ctx, opt_reader)->name); break; } if (rc < 0) return rc; printf("Please insert a smart card. Press return to continue"); fflush(stdout); 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.13.0/src/tests/Makefile.mak0000644000015201777760000000111312057406034014161 00000000000000 TOPDIR = ..\.. TARGETS = base64.exe p15dump.exe \ p15dump.exe pintest.exe # prngtest.exe lottery.exe all: print.obj sc-test.obj $(TARGETS) $(TARGETS): $(TOPDIR)\win32\versioninfo.res print.obj sc-test.obj \ ..\common\common.lib ..\libopensc\opensc.lib !INCLUDE $(TOPDIR)\win32\Make.rules.mak .c.obj: cl $(COPTS) /c $< .c.exe: cl $(COPTS) /c $< link $(LINKFLAGS) /pdb:$*.pdb /out:$@ $*.obj sc-test.obj print.obj \ ..\common\common.lib ..\libopensc\opensc.lib $(TOPDIR)\win32\versioninfo.res if EXIST $@.manifest mt -manifest $@.manifest -outputresource:$@;1 opensc-0.13.0/src/tests/p15dump.c0000644000015201777760000000612012057406034013414 00000000000000/* 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); sc_lock(card); 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"); sc_unlock(card); 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); sc_unlock(card); 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 { path = p15card->file_app->path; sc_append_path_id(&path, (const u8 *) "\x50\x33", 2); } path.count = -1; r = sc_pkcs15_read_file(p15card, &path, &buf, &buf_len); 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); sc_lock(card); 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)); 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); sc_unlock(card); sc_test_cleanup(); return 0; } opensc-0.13.0/src/tests/prngtest.c0000644000015201777760000000257212057406034013776 00000000000000/* 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.13.0/src/tests/sc-test.h0000644000015201777760000000066612057406034013521 00000000000000#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.13.0/src/tests/lottery.c0000644000015201777760000000310112057406034013617 00000000000000/* Copyright (C) 2001 Juha Yrjölä * All rights reserved. */ #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[]) { int i, c, r, cnt = 3, freq[39]; struct timeval tv1, tv2; u8 buf[14]; i = 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) gettimeofday(&tv1, NULL); 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 + 1); 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.13.0/src/tests/base64.c0000644000015201777760000000134112057406034013205 00000000000000#include "config.h" #include #include "libopensc/opensc.h" #include "libopensc/asn1.h" int main(int argc, char *argv[]) { int len; FILE *inf; u8 buf[8192]; u8 outbuf[8192]; if (argc != 2) { fprintf(stderr, "Usage: base64 \n"); return 1; } inf = fopen(argv[1], "r"); if (inf == NULL) { perror(argv[1]); return 1; } len = fread(buf, 1, sizeof(buf), inf); if (len < 0) { perror("fread"); return 1; } if (len == 8192) { fprintf(stderr, "Too long input file.\n"); return 1; } len = sc_base64_decode((const char *) buf, outbuf, sizeof(outbuf)); if (len < 0) { fprintf(stderr, "Base64 decoding failed: %s\n", sc_strerror(len)); return 1; } fwrite(outbuf, len, 1, stdout); return 0; } opensc-0.13.0/src/tests/print.c0000644000015201777760000001754512057406034013272 00000000000000/* 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_PRKEY_DSA: printer = print_prkey; kind = "Private DSA key"; break; case SC_PKCS15_TYPE_PUBKEY_DSA: printer = print_pubkey; kind = "Public DSA key"; break; case SC_PKCS15_TYPE_CERT_X509: printer = print_cert_x509; kind = "X.509 Certificate"; 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", 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.13.0/src/tests/Makefile.am0000644000015201777760000000175112057406034014016 00000000000000include $(top_srcdir)/win32/ltrc.inc MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak SUBDIRS = regression noinst_PROGRAMS = base64 lottery p15dump pintest prngtest AM_CPPFLAGS = -I$(top_srcdir)/src 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.13.0/src/tests/regression/0000755000015201777760000000000012057406120014212 500000000000000opensc-0.13.0/src/tests/regression/crypt00040000755000015201777760000000211612057406034015531 00000000000000#!/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.13.0/src/tests/regression/init00040000755000015201777760000000033512057406034015334 00000000000000#!/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.13.0/src/tests/regression/init00050000755000015201777760000000034512057406034015336 00000000000000#!/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.13.0/src/tests/regression/crypt00050000755000015201777760000000360212057406034015533 00000000000000#!/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.13.0/src/tests/regression/crypt00020000755000015201777760000000142212057406034015526 00000000000000#!/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.13.0/src/tests/regression/crypt00060000755000015201777760000000351112057406034015533 00000000000000#!/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.13.0/src/tests/regression/crypt00030000755000015201777760000000221512057406034015530 00000000000000#!/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.13.0/src/tests/regression/init00090000755000015201777760000000122312057406034015336 00000000000000#!/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.13.0/src/tests/regression/init00120000755000015201777760000000042512057406034015333 00000000000000#!/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.13.0/src/tests/regression/README0000644000015201777760000000271112057406034015017 00000000000000 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.13.0/src/tests/regression/init00110000755000015201777760000000132312057406034015330 00000000000000#!/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.13.0/src/tests/regression/crypt00010000755000015201777760000000177512057406034015540 00000000000000#!/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.13.0/src/tests/regression/pin00020000755000015201777760000000027212057406034015155 00000000000000#!/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.13.0/src/tests/regression/test.p120000644000015201777760000000453512057406034015450 000000000000000 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.13.0/src/tests/regression/init00060000755000015201777760000000047512057406034015343 00000000000000#!/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.13.0/src/tests/regression/init00030000755000015201777760000000034112057406034015330 00000000000000#!/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.13.0/src/tests/regression/init00070000755000015201777760000000061212057406034015335 00000000000000#!/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.13.0/src/tests/regression/init00010000755000015201777760000000034512057406034015332 00000000000000#!/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.13.0/src/tests/regression/run-all0000755000015201777760000000147412057406034015444 00000000000000#!/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.13.0/src/tests/regression/crypt00070000755000015201777760000000354012057406034015536 00000000000000#!/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 \ --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 \ --slot-label "OpenSC Test Card" $* < /dev/null success } opensc-0.13.0/src/tests/regression/init00080000755000015201777760000000036612057406034015344 00000000000000#!/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.13.0/src/tests/regression/bintest0000644000015201777760000000100012057406034015520 00000000000000?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.13.0/src/tests/regression/init00100000755000015201777760000000131512057406034015330 00000000000000#!/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.13.0/src/tests/regression/Makefile.am0000644000015201777760000000064212057406034016174 00000000000000MAINTAINERCLEANFILES = $(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.13.0/src/tests/regression/pin00010000755000015201777760000000027112057406034015153 00000000000000#!/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.13.0/src/tests/regression/init00020000755000015201777760000000051312057406034015330 00000000000000#!/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.13.0/src/tests/regression/erase0000755000015201777760000000017312057406034015164 00000000000000#!/bin/bash # # Erase p15 card # # Run this from the regression test directory. . functions p15_erase --secret @01=0000 opensc-0.13.0/src/tests/regression/Makefile.in0000644000015201777760000002730712057406056016220 00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program 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. @SET_MAKE@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/tests/regression DIST_COMMON = README $(dist_check_DATA) $(dist_check_SCRIPTS) \ $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ SOURCES = DIST_SOURCES = DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUG_FILE = @DEBUG_FILE@ DEFAULT_PCSC_PROVIDER = @DEFAULT_PCSC_PROVIDER@ DEFAULT_SM_MODULE = @DEFAULT_SM_MODULE@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GIT = @GIT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBRARY_BITNESS = @LIBRARY_BITNESS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCT_CFLAGS = @OPENCT_CFLAGS@ OPENCT_LIBS = @OPENCT_LIBS@ OPENSC_LT_AGE = @OPENSC_LT_AGE@ OPENSC_LT_CURRENT = @OPENSC_LT_CURRENT@ OPENSC_LT_OLDEST = @OPENSC_LT_OLDEST@ OPENSC_LT_REVISION = @OPENSC_LT_REVISION@ OPENSC_VERSION_FIX = @OPENSC_VERSION_FIX@ OPENSC_VERSION_MAJOR = @OPENSC_VERSION_MAJOR@ OPENSC_VERSION_MINOR = @OPENSC_VERSION_MINOR@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OPTIONAL_OPENCT_CFLAGS = @OPTIONAL_OPENCT_CFLAGS@ OPTIONAL_OPENCT_LIBS = @OPTIONAL_OPENCT_LIBS@ OPTIONAL_OPENSSL_CFLAGS = @OPTIONAL_OPENSSL_CFLAGS@ OPTIONAL_OPENSSL_LIBS = @OPTIONAL_OPENSSL_LIBS@ OPTIONAL_PCSC_CFLAGS = @OPTIONAL_PCSC_CFLAGS@ OPTIONAL_READLINE_CFLAGS = @OPTIONAL_READLINE_CFLAGS@ OPTIONAL_READLINE_LIBS = @OPTIONAL_READLINE_LIBS@ OPTIONAL_ZLIB_CFLAGS = @OPTIONAL_ZLIB_CFLAGS@ OPTIONAL_ZLIB_LIBS = @OPTIONAL_ZLIB_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCSC_CFLAGS = @PCSC_CFLAGS@ PCSC_LIBS = @PCSC_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RC = @RC@ READLINE_CFLAGS = @READLINE_CFLAGS@ READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ WIN_LIBPREFIX = @WIN_LIBPREFIX@ XSLTPROC = @XSLTPROC@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ git = @git@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkcs11dir = @pkcs11dir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ xslstylesheetsdir = @xslstylesheetsdir@ MAINTAINERCLEANFILES = $(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 all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/tests/regression/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/tests/regression/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags: TAGS TAGS: ctags: CTAGS CTAGS: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am $(MAKE) $(AM_MAKEFLAGS) $(dist_check_SCRIPTS) \ $(dist_check_DATA) check: check-am all-am: Makefile installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libtool clean-local mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: check-am install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ clean-local distclean distclean-generic distclean-libtool \ distdir dvi dvi-am html html-am info info-am install \ install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ uninstall uninstall-am # remove log files from regression tests clean-local: -rm -rf out failed # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: opensc-0.13.0/src/tests/Makefile.in0000644000015201777760000006400012057406056014027 00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program 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. @SET_MAKE@ # Required to build Windows resource file VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ $(top_srcdir)/win32/ltrc.inc noinst_PROGRAMS = base64$(EXEEXT) lottery$(EXEEXT) p15dump$(EXEEXT) \ pintest$(EXEEXT) prngtest$(EXEEXT) @WIN32_TRUE@am__append_1 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_2 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_3 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_4 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_5 = $(top_builddir)/win32/versioninfo.rc subdir = src/tests ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = PROGRAMS = $(noinst_PROGRAMS) am__base64_SOURCES_DIST = base64.c sc-test.c sc-test.h \ $(top_builddir)/win32/versioninfo.rc am__objects_1 = sc-test.$(OBJEXT) am__objects_2 = am__dirstamp = $(am__leading_dot)dirstamp @WIN32_TRUE@am__objects_3 = \ @WIN32_TRUE@ $(top_builddir)/win32/versioninfo.$(OBJEXT) am_base64_OBJECTS = base64.$(OBJEXT) $(am__objects_1) $(am__objects_2) \ $(am__objects_3) base64_OBJECTS = $(am_base64_OBJECTS) base64_LDADD = $(LDADD) AM_V_lt = $(am__v_lt_$(V)) am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) am__v_lt_0 = --silent am__lottery_SOURCES_DIST = lottery.c sc-test.c sc-test.h \ $(top_builddir)/win32/versioninfo.rc am_lottery_OBJECTS = lottery.$(OBJEXT) $(am__objects_1) \ $(am__objects_2) $(am__objects_3) lottery_OBJECTS = $(am_lottery_OBJECTS) lottery_LDADD = $(LDADD) am__p15dump_SOURCES_DIST = p15dump.c print.c sc-test.c sc-test.h \ $(top_builddir)/win32/versioninfo.rc am_p15dump_OBJECTS = p15dump.$(OBJEXT) print.$(OBJEXT) \ $(am__objects_1) $(am__objects_2) $(am__objects_3) p15dump_OBJECTS = $(am_p15dump_OBJECTS) p15dump_LDADD = $(LDADD) am__pintest_SOURCES_DIST = pintest.c print.c sc-test.c sc-test.h \ $(top_builddir)/win32/versioninfo.rc am_pintest_OBJECTS = pintest.$(OBJEXT) print.$(OBJEXT) \ $(am__objects_1) $(am__objects_2) $(am__objects_3) pintest_OBJECTS = $(am_pintest_OBJECTS) pintest_LDADD = $(LDADD) am__prngtest_SOURCES_DIST = prngtest.c sc-test.c sc-test.h \ $(top_builddir)/win32/versioninfo.rc am_prngtest_OBJECTS = prngtest.$(OBJEXT) $(am__objects_1) \ $(am__objects_2) $(am__objects_3) prngtest_OBJECTS = $(am_prngtest_OBJECTS) prngtest_LDADD = $(LDADD) DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_$(V)) am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) am__v_CC_0 = @echo " CC " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_$(V)) am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) am__v_CCLD_0 = @echo " CCLD " $@; AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; SOURCES = $(base64_SOURCES) $(lottery_SOURCES) $(p15dump_SOURCES) \ $(pintest_SOURCES) $(prngtest_SOURCES) DIST_SOURCES = $(am__base64_SOURCES_DIST) $(am__lottery_SOURCES_DIST) \ $(am__p15dump_SOURCES_DIST) $(am__pintest_SOURCES_DIST) \ $(am__prngtest_SOURCES_DIST) RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ html-recursive info-recursive install-data-recursive \ install-dvi-recursive install-exec-recursive \ install-html-recursive install-info-recursive \ install-pdf-recursive install-ps-recursive install-recursive \ installcheck-recursive installdirs-recursive pdf-recursive \ ps-recursive uninstall-recursive RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ distdir ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUG_FILE = @DEBUG_FILE@ DEFAULT_PCSC_PROVIDER = @DEFAULT_PCSC_PROVIDER@ DEFAULT_SM_MODULE = @DEFAULT_SM_MODULE@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GIT = @GIT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBRARY_BITNESS = @LIBRARY_BITNESS@ LIBS = \ $(top_builddir)/src/libopensc/libopensc.la \ $(top_builddir)/src/common/libscdl.la \ $(top_builddir)/src/common/libcompat.la LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCT_CFLAGS = @OPENCT_CFLAGS@ OPENCT_LIBS = @OPENCT_LIBS@ OPENSC_LT_AGE = @OPENSC_LT_AGE@ OPENSC_LT_CURRENT = @OPENSC_LT_CURRENT@ OPENSC_LT_OLDEST = @OPENSC_LT_OLDEST@ OPENSC_LT_REVISION = @OPENSC_LT_REVISION@ OPENSC_VERSION_FIX = @OPENSC_VERSION_FIX@ OPENSC_VERSION_MAJOR = @OPENSC_VERSION_MAJOR@ OPENSC_VERSION_MINOR = @OPENSC_VERSION_MINOR@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OPTIONAL_OPENCT_CFLAGS = @OPTIONAL_OPENCT_CFLAGS@ OPTIONAL_OPENCT_LIBS = @OPTIONAL_OPENCT_LIBS@ OPTIONAL_OPENSSL_CFLAGS = @OPTIONAL_OPENSSL_CFLAGS@ OPTIONAL_OPENSSL_LIBS = @OPTIONAL_OPENSSL_LIBS@ OPTIONAL_PCSC_CFLAGS = @OPTIONAL_PCSC_CFLAGS@ OPTIONAL_READLINE_CFLAGS = @OPTIONAL_READLINE_CFLAGS@ OPTIONAL_READLINE_LIBS = @OPTIONAL_READLINE_LIBS@ OPTIONAL_ZLIB_CFLAGS = @OPTIONAL_ZLIB_CFLAGS@ OPTIONAL_ZLIB_LIBS = @OPTIONAL_ZLIB_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCSC_CFLAGS = @PCSC_CFLAGS@ PCSC_LIBS = @PCSC_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RC = @RC@ READLINE_CFLAGS = @READLINE_CFLAGS@ READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ WIN_LIBPREFIX = @WIN_LIBPREFIX@ XSLTPROC = @XSLTPROC@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ git = @git@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkcs11dir = @pkcs11dir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ xslstylesheetsdir = @xslstylesheetsdir@ RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE) MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak SUBDIRS = regression AM_CPPFLAGS = -I$(top_srcdir)/src COMMON_SRC = sc-test.c COMMON_INC = sc-test.h base64_SOURCES = base64.c $(COMMON_SRC) $(COMMON_INC) $(am__append_1) lottery_SOURCES = lottery.c $(COMMON_SRC) $(COMMON_INC) \ $(am__append_2) p15dump_SOURCES = p15dump.c print.c $(COMMON_SRC) $(COMMON_INC) \ $(am__append_3) pintest_SOURCES = pintest.c print.c $(COMMON_SRC) $(COMMON_INC) \ $(am__append_4) prngtest_SOURCES = prngtest.c $(COMMON_SRC) $(COMMON_INC) \ $(am__append_5) all: all-recursive .SUFFIXES: .SUFFIXES: .c .lo .o .obj .rc $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/win32/ltrc.inc $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/tests/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/tests/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list $(top_builddir)/win32/$(am__dirstamp): @$(MKDIR_P) $(top_builddir)/win32 @: > $(top_builddir)/win32/$(am__dirstamp) $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) $(top_builddir)/win32/$(DEPDIR) @: > $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp) $(top_builddir)/win32/versioninfo.$(OBJEXT): \ $(top_builddir)/win32/$(am__dirstamp) \ $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp) base64$(EXEEXT): $(base64_OBJECTS) $(base64_DEPENDENCIES) @rm -f base64$(EXEEXT) $(AM_V_CCLD)$(LINK) $(base64_OBJECTS) $(base64_LDADD) $(LIBS) lottery$(EXEEXT): $(lottery_OBJECTS) $(lottery_DEPENDENCIES) @rm -f lottery$(EXEEXT) $(AM_V_CCLD)$(LINK) $(lottery_OBJECTS) $(lottery_LDADD) $(LIBS) p15dump$(EXEEXT): $(p15dump_OBJECTS) $(p15dump_DEPENDENCIES) @rm -f p15dump$(EXEEXT) $(AM_V_CCLD)$(LINK) $(p15dump_OBJECTS) $(p15dump_LDADD) $(LIBS) pintest$(EXEEXT): $(pintest_OBJECTS) $(pintest_DEPENDENCIES) @rm -f pintest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(pintest_OBJECTS) $(pintest_LDADD) $(LIBS) prngtest$(EXEEXT): $(prngtest_OBJECTS) $(prngtest_DEPENDENCIES) @rm -f prngtest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(prngtest_OBJECTS) $(prngtest_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f $(top_builddir)/win32/versioninfo.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lottery.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p15dump.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pintest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prngtest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sc-test.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run `make' without going through this Makefile. # To change the values of `make' variables: instead of editing Makefiles, # (1) if the variable is set in `config.status', edit `config.status' # (which will cause the Makefiles to be regenerated when you run `make'); # (2) otherwise, pass the desired values on the `make' command line. $(RECURSIVE_TARGETS): @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ *k*) failcom='fail=yes';; \ esac; \ done; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ list='$(SUBDIRS)'; for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" $(RECURSIVE_CLEAN_TARGETS): @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ *k*) failcom='fail=yes';; \ esac; \ done; \ dot_seen=no; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ rev=''; for subdir in $$list; do \ if test "$$subdir" = "."; then :; else \ rev="$$subdir $$rev"; \ fi; \ done; \ rev="$$rev ."; \ target=`echo $@ | sed s/-recursive//`; \ for subdir in $$rev; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done && test -z "$$fail" tags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ done ctags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ done ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: CTAGS CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(PROGRAMS) installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -test -z "$(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp)" || rm -f $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp) -test -z "$(top_builddir)/win32/$(am__dirstamp)" || rm -f $(top_builddir)/win32/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-recursive clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ mostlyclean-am distclean: distclean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \ install-am install-strip tags-recursive .PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ all all-am check check-am clean clean-generic clean-libtool \ clean-noinstPROGRAMS ctags ctags-recursive distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \ uninstall uninstall-am .rc.lo: $(LTRCCOMPILE) -i "$<" -o "$@" .rc.o: $(RCCOMPILE) -i "$<" -o "$@" # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: opensc-0.13.0/src/tests/pintest.c0000644000015201777760000000507712057406034013621 00000000000000/* 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/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)); sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, n); 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[80]; u8 *pass; if (pin_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) { printf("Skipping unblocking pin [%s]\n", pin_obj->label); return 0; } sprintf(prompt, "Please enter PIN code [%s]: ", pin_obj->label); pass = (u8 *) getpass(prompt); sc_lock(card); i = sc_pkcs15_verify_pin(p15card, pin_obj, pass, strlen((char *) pass)); sc_unlock(card); 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); sc_lock(card); i = sc_pkcs15_bind(card, NULL, &p15card); sc_unlock(card); if (i) { fprintf(stderr, "failed: %s\n", sc_strerror(i)); sc_test_cleanup(); return 1; } printf("found.\n"); printf("Enumerating PIN codes...\n"); sc_lock(card); count = enum_pins(&objs); sc_unlock(card); 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.13.0/src/pkcs11/0000755000015201777760000000000012057406120011772 500000000000000opensc-0.13.0/src/pkcs11/pkcs11-display.h0000644000015201777760000000536012057406034014640 00000000000000#ifndef PKCS11_DISPLAY_H #define PKCS11_DISPLAY_H /* * Copyright (C) 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * 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, KEY_T, CRT_T, MEC_T, USR_T, STA_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); extern type_spec ck_attribute_specs[]; extern CK_ULONG ck_attribute_num; extern enum_spec ck_types[]; #ifdef __cplusplus }; #endif #endif opensc-0.13.0/src/pkcs11/debug.c0000644000015201777760000001470212057406034013154 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 NELE(x) (sizeof(x)/sizeof((x)[0])) #define STR(x) #x #define __(x) (x), #x #define _(x) { (x), #x, NULL, NULL } #define ul(x) { (x), #x, sc_pkcs11_print_ulong, NULL } #define ulm(x) { (x), #x, sc_pkcs11_print_ulong, map_##x } #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_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_ECDSA_PARAMS), _(CKA_EC_PARAMS), _(CKA_EC_POINT), _(CKA_SECONDARY_AUTH), ul(CKA_AUTH_PIN_FLAGS), _(CKA_HW_FEATURE_TYPE), _(CKA_RESET_ON_INIT), _(CKA_HAS_RESET), _(CKA_VENDOR_DEFINED), b(CKA_ALWAYS_AUTHENTICATE), _(CKA_GOSTR3410_PARAMS), { 0, NULL, NULL, NULL } }; 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%x = %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.13.0/src/pkcs11/Makefile.mak0000644000015201777760000000246512057406034014134 00000000000000TOPDIR = ..\.. 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 \ $(TOPDIR)\win32\versioninfo.res OBJECTS3 = pkcs11-spy.obj pkcs11-display.obj \ $(TOPDIR)\win32\versioninfo.res all: $(TOPDIR)\win32\versioninfo.res $(TARGET1) $(TARGET3) !INCLUDE $(TOPDIR)\win32\Make.rules.mak $(TARGET1): $(OBJECTS) ..\libopensc\opensc_a.lib ..\pkcs15init\pkcs15init.lib echo LIBRARY $* > $*.def echo EXPORTS >> $*.def type opensc-pkcs11.exports >> $*.def link $(LINKFLAGS) /dll /def:$*.def /implib:$*.lib /out:$(TARGET1) $(OBJECTS) ..\libopensc\opensc_a.lib ..\pkcs15init\pkcs15init.lib $(OPENSSL_LIB) gdi32.lib if EXIST $(TARGET1).manifest mt -manifest $(TARGET1).manifest -outputresource:$(TARGET1);2 $(TARGET3): $(OBJECTS3) ..\libopensc\opensc.lib echo LIBRARY $* > $*.def echo EXPORTS >> $*.def type $*.exports >> $*.def link $(LINKFLAGS) /dll /def:$*.def /implib:$*.lib /out:$(TARGET3) $(OBJECTS3) ..\libopensc\opensc.lib ..\common\libpkcs11.lib ..\common\libscdl.lib $(OPENSSL_LIB) gdi32.lib advapi32.lib if EXIST $(TARGET3).manifest mt -manifest $(TARGET3).manifest -outputresource:$(TARGET3);2 opensc-0.13.0/src/pkcs11/pkcs11-spy.c0000644000015201777760000011330512057406034014000 00000000000000/* * Copyright (C) 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA */ #include "config.h" #include #include #ifdef _WIN32 #include #include #include #else #include #include #endif #define CRYPTOKI_EXPORTS #include "pkcs11-display.h" #define __PASTE(x,y) x##y extern void *C_LoadModule(const char *name, CK_FUNCTION_LIST_PTR_PTR); extern CK_RV C_UnloadModule(void *module); /* Declare all spy_* Cryptoki function */ /* Spy Module Function List */ static CK_FUNCTION_LIST_PTR pkcs11_spy = NULL; /* Real Module Function List */ static CK_FUNCTION_LIST_PTR po = NULL; /* Dynamic Module Handle */ static void *modhandle = NULL; /* Spy module output */ static FILE *spy_output = NULL; /* Inits the spy. If successfull, po != NULL */ static CK_RV init_spy(void) { const char *output, *module; int 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 = malloc(sizeof(CK_FUNCTION_LIST)); if (pkcs11_spy) { /* with our own pkcs11.h we need to maintain this ourself */ pkcs11_spy->version.major = 2; pkcs11_spy->version.major = 11; pkcs11_spy->C_Initialize = C_Initialize; pkcs11_spy->C_Finalize = C_Finalize; pkcs11_spy->C_GetInfo = C_GetInfo; pkcs11_spy->C_GetFunctionList = C_GetFunctionList; pkcs11_spy->C_GetSlotList = C_GetSlotList; pkcs11_spy->C_GetSlotInfo = C_GetSlotInfo; pkcs11_spy->C_GetTokenInfo = C_GetTokenInfo; pkcs11_spy->C_GetMechanismList = C_GetMechanismList; pkcs11_spy->C_GetMechanismInfo = C_GetMechanismInfo; pkcs11_spy->C_InitToken = C_InitToken; pkcs11_spy->C_InitPIN = C_InitPIN; pkcs11_spy->C_SetPIN = C_SetPIN; pkcs11_spy->C_OpenSession = C_OpenSession; pkcs11_spy->C_CloseSession = C_CloseSession; pkcs11_spy->C_CloseAllSessions = C_CloseAllSessions; pkcs11_spy->C_GetSessionInfo = C_GetSessionInfo; pkcs11_spy->C_GetOperationState = C_GetOperationState; pkcs11_spy->C_SetOperationState = C_SetOperationState; pkcs11_spy->C_Login = C_Login; pkcs11_spy->C_Logout = C_Logout; pkcs11_spy->C_CreateObject = C_CreateObject; pkcs11_spy->C_CopyObject = C_CopyObject; pkcs11_spy->C_DestroyObject = C_DestroyObject; pkcs11_spy->C_GetObjectSize = C_GetObjectSize; pkcs11_spy->C_GetAttributeValue = C_GetAttributeValue; pkcs11_spy->C_SetAttributeValue = C_SetAttributeValue; pkcs11_spy->C_FindObjectsInit = C_FindObjectsInit; pkcs11_spy->C_FindObjects = C_FindObjects; pkcs11_spy->C_FindObjectsFinal = C_FindObjectsFinal; pkcs11_spy->C_EncryptInit = C_EncryptInit; pkcs11_spy->C_Encrypt = C_Encrypt; pkcs11_spy->C_EncryptUpdate = C_EncryptUpdate; pkcs11_spy->C_EncryptFinal = C_EncryptFinal; pkcs11_spy->C_DecryptInit = C_DecryptInit; pkcs11_spy->C_Decrypt = C_Decrypt; pkcs11_spy->C_DecryptUpdate = C_DecryptUpdate; pkcs11_spy->C_DecryptFinal = C_DecryptFinal; pkcs11_spy->C_DigestInit = C_DigestInit; pkcs11_spy->C_Digest = C_Digest; pkcs11_spy->C_DigestUpdate = C_DigestUpdate; pkcs11_spy->C_DigestKey = C_DigestKey; pkcs11_spy->C_DigestFinal = C_DigestFinal; pkcs11_spy->C_SignInit = C_SignInit; pkcs11_spy->C_Sign = C_Sign; pkcs11_spy->C_SignUpdate = C_SignUpdate; pkcs11_spy->C_SignFinal = C_SignFinal; pkcs11_spy->C_SignRecoverInit = C_SignRecoverInit; pkcs11_spy->C_SignRecover = C_SignRecover; pkcs11_spy->C_VerifyInit = C_VerifyInit; pkcs11_spy->C_Verify = C_Verify; pkcs11_spy->C_VerifyUpdate = C_VerifyUpdate; pkcs11_spy->C_VerifyFinal = C_VerifyFinal; pkcs11_spy->C_VerifyRecoverInit = C_VerifyRecoverInit; pkcs11_spy->C_VerifyRecover = C_VerifyRecover; pkcs11_spy->C_DigestEncryptUpdate = C_DigestEncryptUpdate; pkcs11_spy->C_DecryptDigestUpdate = C_DecryptDigestUpdate; pkcs11_spy->C_SignEncryptUpdate = C_SignEncryptUpdate; pkcs11_spy->C_DecryptVerifyUpdate = C_DecryptVerifyUpdate; pkcs11_spy->C_GenerateKey = C_GenerateKey; pkcs11_spy->C_GenerateKeyPair = C_GenerateKeyPair; pkcs11_spy->C_WrapKey = C_WrapKey; pkcs11_spy->C_UnwrapKey = C_UnwrapKey; pkcs11_spy->C_DeriveKey = C_DeriveKey; pkcs11_spy->C_SeedRandom = C_SeedRandom; pkcs11_spy->C_GenerateRandom = C_GenerateRandom; pkcs11_spy->C_GetFunctionStatus = C_GetFunctionStatus; pkcs11_spy->C_CancelFunction = C_CancelFunction; pkcs11_spy->C_WaitForSlotEvent = C_WaitForSlotEvent; } else { return CKR_HOST_MEMORY; } /* * 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 runing * 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 runing * 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); 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, "%i-%02i-%02i %02i:%02i:%02i.%03i\n", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); #else gettimeofday (&tv, NULL); tm = localtime (&tv.tv_sec); strftime (time_string, sizeof(time_string), "%F %H:%M:%S", tm); fprintf(spy_output, "%s.%03ld\n", time_string, 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 print_ptr_in(const char *name, CK_VOID_PTR ptr) { fprintf(spy_output, "[in] %s = %p\n", name, ptr); } 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"); *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; const char *name = lookup_enum(MEC_T, type); enter("C_GetMechanismInfo"); spy_dump_ulong_in("slotID", slotID); if (name) fprintf(spy_output, "%30s \n", name); else fprintf(spy_output, " Unknown Mechanism (%08lx) \n", type); 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, "pApplication=%p\n", pApplication); fprintf(spy_output, "Notify=%p\n", (void *)Notify); rv = po->C_OpenSession(slotID, flags, pApplication, Notify, phSession); spy_dump_ulong_out("*phSession", *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(spy_output, "[in] userType = %s\n", lookup_enum(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); fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); 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); 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); 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); fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); 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); 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); 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); fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); 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); fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); 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); 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); 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); fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); 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); 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); fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); 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); fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); 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); fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); 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); fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); 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); fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); 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); 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); fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); 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); fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); 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); rv = po->C_WaitForSlotEvent(flags, pSlot, pRserved); return retne(rv); } opensc-0.13.0/src/pkcs11/pkcs11.h0000644000015201777760000012600312057406034013173 00000000000000/* 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 2 #define CRYPTOKI_VERSION_MINOR 20 #define CRYPTOKI_VERSION_REVISION 6 /* Compatibility interface is default, unless CRYPTOKI_GNU is given. */ #ifndef CRYPTOKI_GNU #ifndef CRYPTOKI_COMPAT #define CRYPTOKI_COMPAT 1 #endif #endif /* System dependencies. */ #if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32) /* There is a matching pop below. */ #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_mechanism _CK_MECHANISM #define parameter pParameter #define parameter_len ulParameterLen #define ck_mechanism_info _CK_MECHANISM_INFO #define min_key_size ulMinKeySize #define max_key_size ulMaxKeySize #define ck_rv_t CK_RV #define ck_notify_t CK_NOTIFY #define ck_function_list _CK_FUNCTION_LIST #define ck_createmutex_t CK_CREATEMUTEX #define ck_destroymutex_t CK_DESTROYMUTEX #define ck_lockmutex_t CK_LOCKMUTEX #define ck_unlockmutex_t CK_UNLOCKMUTEX #define ck_c_initialize_args _CK_C_INITIALIZE_ARGS #define create_mutex CreateMutex #define destroy_mutex DestroyMutex #define lock_mutex LockMutex #define unlock_mutex UnlockMutex #define reserved pReserved #endif /* CRYPTOKI_COMPAT */ typedef unsigned long ck_flags_t; struct ck_version { unsigned char major; unsigned char minor; }; struct ck_info { struct ck_version cryptoki_version; unsigned char manufacturer_id[32]; ck_flags_t flags; unsigned char library_description[32]; struct ck_version library_version; }; typedef unsigned long ck_notification_t; #define CKN_SURRENDER (0UL) typedef unsigned long ck_slot_id_t; struct ck_slot_info { unsigned char slot_description[64]; unsigned char manufacturer_id[32]; ck_flags_t flags; struct ck_version hardware_version; struct ck_version firmware_version; }; #define CKF_TOKEN_PRESENT (1UL << 0) #define CKF_REMOVABLE_DEVICE (1UL << 1) #define CKF_HW_SLOT (1UL << 2) #define CKF_ARRAY_ATTRIBUTE (1UL << 30) struct ck_token_info { unsigned char label[32]; unsigned char manufacturer_id[32]; unsigned char model[16]; unsigned char serial_number[16]; ck_flags_t flags; unsigned long max_session_count; unsigned long session_count; unsigned long max_rw_session_count; unsigned long rw_session_count; unsigned long max_pin_len; unsigned long min_pin_len; unsigned long total_public_memory; unsigned long free_public_memory; unsigned long total_private_memory; unsigned long free_private_memory; struct ck_version hardware_version; struct ck_version firmware_version; unsigned char utc_time[16]; }; #define CKF_RNG (1UL << 0) #define CKF_WRITE_PROTECTED (1UL << 1) #define CKF_LOGIN_REQUIRED (1UL << 2) #define CKF_USER_PIN_INITIALIZED (1UL << 3) #define CKF_RESTORE_KEY_NOT_NEEDED (1UL << 5) #define CKF_CLOCK_ON_TOKEN (1UL << 6) #define CKF_PROTECTED_AUTHENTICATION_PATH (1UL << 8) #define CKF_DUAL_CRYPTO_OPERATIONS (1UL << 9) #define CKF_TOKEN_INITIALIZED (1UL << 10) #define CKF_SECONDARY_AUTHENTICATION (1UL << 11) #define CKF_USER_PIN_COUNT_LOW (1UL << 16) #define CKF_USER_PIN_FINAL_TRY (1UL << 17) #define CKF_USER_PIN_LOCKED (1UL << 18) #define CKF_USER_PIN_TO_BE_CHANGED (1UL << 19) #define CKF_SO_PIN_COUNT_LOW (1UL << 20) #define CKF_SO_PIN_FINAL_TRY (1UL << 21) #define CKF_SO_PIN_LOCKED (1UL << 22) #define CKF_SO_PIN_TO_BE_CHANGED (1UL << 23) #define CK_UNAVAILABLE_INFORMATION ((unsigned long) -1) #define CK_EFFECTIVELY_INFINITE (0UL) typedef unsigned long ck_session_handle_t; #define CK_INVALID_HANDLE (0UL) typedef unsigned long ck_user_type_t; #define CKU_SO (0UL) #define CKU_USER (1UL) #define CKU_CONTEXT_SPECIFIC (2UL) typedef unsigned long ck_state_t; #define CKS_RO_PUBLIC_SESSION (0UL) #define CKS_RO_USER_FUNCTIONS (1UL) #define CKS_RW_PUBLIC_SESSION (2UL) #define CKS_RW_USER_FUNCTIONS (3UL) #define CKS_RW_SO_FUNCTIONS (4UL) struct ck_session_info { ck_slot_id_t slot_id; ck_state_t state; ck_flags_t flags; unsigned long device_error; }; #define CKF_RW_SESSION (1UL << 1) #define CKF_SERIAL_SESSION (1UL << 2) typedef unsigned long ck_object_handle_t; typedef unsigned long ck_object_class_t; #define CKO_DATA (0UL) #define CKO_CERTIFICATE (1UL) #define CKO_PUBLIC_KEY (2UL) #define CKO_PRIVATE_KEY (3UL) #define CKO_SECRET_KEY (4UL) #define CKO_HW_FEATURE (5UL) #define CKO_DOMAIN_PARAMETERS (6UL) #define CKO_MECHANISM (7UL) #define CKO_VENDOR_DEFINED (1UL << 31) typedef unsigned long ck_hw_feature_type_t; #define CKH_MONOTONIC_COUNTER (1UL) #define CKH_CLOCK (2UL) #define CKH_USER_INTERFACE (3UL) #define CKH_VENDOR_DEFINED (1UL << 31) typedef unsigned long ck_key_type_t; #define CKK_RSA (0UL) #define CKK_DSA (1UL) #define CKK_DH (2UL) #define CKK_ECDSA (3UL) #define CKK_EC (3UL) #define CKK_X9_42_DH (4UL) #define CKK_KEA (5UL) #define CKK_GENERIC_SECRET (0x10UL) #define CKK_RC2 (0x11UL) #define CKK_RC4 (0x12UL) #define CKK_DES (0x13UL) #define CKK_DES2 (0x14UL) #define CKK_DES3 (0x15UL) #define CKK_CAST (0x16UL) #define CKK_CAST3 (0x17UL) #define CKK_CAST128 (0x18UL) #define CKK_RC5 (0x19UL) #define CKK_IDEA (0x1aUL) #define CKK_SKIPJACK (0x1bUL) #define CKK_BATON (0x1cUL) #define CKK_JUNIPER (0x1dUL) #define CKK_CDMF (0x1eUL) #define CKK_AES (0x1fUL) #define CKK_BLOWFISH (0x20UL) #define CKK_TWOFISH (0x21UL) #define CKK_GOSTR3410 (0x30UL) #define CKK_VENDOR_DEFINED (1UL << 31) typedef unsigned long ck_certificate_type_t; #define CKC_X_509 (0UL) #define CKC_X_509_ATTR_CERT (1UL) #define CKC_WTLS (2UL) #define CKC_VENDOR_DEFINED (1UL << 31) typedef unsigned long ck_attribute_type_t; #define CKA_CLASS (0UL) #define CKA_TOKEN (1UL) #define CKA_PRIVATE (2UL) #define CKA_LABEL (3UL) #define CKA_APPLICATION (0x10UL) #define CKA_VALUE (0x11UL) #define CKA_OBJECT_ID (0x12UL) #define CKA_CERTIFICATE_TYPE (0x80UL) #define CKA_ISSUER (0x81UL) #define CKA_SERIAL_NUMBER (0x82UL) #define CKA_AC_ISSUER (0x83UL) #define CKA_OWNER (0x84UL) #define CKA_ATTR_TYPES (0x85UL) #define CKA_TRUSTED (0x86UL) #define CKA_CERTIFICATE_CATEGORY (0x87UL) #define CKA_JAVA_MIDP_SECURITY_DOMAIN (0x88UL) #define CKA_URL (0x89UL) #define CKA_HASH_OF_SUBJECT_PUBLIC_KEY (0x8aUL) #define CKA_HASH_OF_ISSUER_PUBLIC_KEY (0x8bUL) #define CKA_CHECK_VALUE (0x90UL) #define CKA_KEY_TYPE (0x100UL) #define CKA_SUBJECT (0x101UL) #define CKA_ID (0x102UL) #define CKA_SENSITIVE (0x103UL) #define CKA_ENCRYPT (0x104UL) #define CKA_DECRYPT (0x105UL) #define CKA_WRAP (0x106UL) #define CKA_UNWRAP (0x107UL) #define CKA_SIGN (0x108UL) #define CKA_SIGN_RECOVER (0x109UL) #define CKA_VERIFY (0x10aUL) #define CKA_VERIFY_RECOVER (0x10bUL) #define CKA_DERIVE (0x10cUL) #define CKA_START_DATE (0x110UL) #define CKA_END_DATE (0x111UL) #define CKA_MODULUS (0x120UL) #define CKA_MODULUS_BITS (0x121UL) #define CKA_PUBLIC_EXPONENT (0x122UL) #define CKA_PRIVATE_EXPONENT (0x123UL) #define CKA_PRIME_1 (0x124UL) #define CKA_PRIME_2 (0x125UL) #define CKA_EXPONENT_1 (0x126UL) #define CKA_EXPONENT_2 (0x127UL) #define CKA_COEFFICIENT (0x128UL) #define CKA_PRIME (0x130UL) #define CKA_SUBPRIME (0x131UL) #define CKA_BASE (0x132UL) #define CKA_PRIME_BITS (0x133UL) #define CKA_SUB_PRIME_BITS (0x134UL) #define CKA_VALUE_BITS (0x160UL) #define CKA_VALUE_LEN (0x161UL) #define CKA_EXTRACTABLE (0x162UL) #define CKA_LOCAL (0x163UL) #define CKA_NEVER_EXTRACTABLE (0x164UL) #define CKA_ALWAYS_SENSITIVE (0x165UL) #define CKA_KEY_GEN_MECHANISM (0x166UL) #define CKA_MODIFIABLE (0x170UL) #define CKA_ECDSA_PARAMS (0x180UL) #define CKA_EC_PARAMS (0x180UL) #define CKA_EC_POINT (0x181UL) #define CKA_SECONDARY_AUTH (0x200UL) #define CKA_AUTH_PIN_FLAGS (0x201UL) #define CKA_ALWAYS_AUTHENTICATE (0x202UL) #define CKA_WRAP_WITH_TRUSTED (0x210UL) #define CKA_GOSTR3410_PARAMS (0x250UL) #define CKA_GOSTR3411_PARAMS (0x251UL) #define CKA_GOST28147_PARAMS (0x252UL) #define CKA_HW_FEATURE_TYPE (0x300UL) #define CKA_RESET_ON_INIT (0x301UL) #define CKA_HAS_RESET (0x302UL) #define CKA_PIXEL_X (0x400UL) #define CKA_PIXEL_Y (0x401UL) #define CKA_RESOLUTION (0x402UL) #define CKA_CHAR_ROWS (0x403UL) #define CKA_CHAR_COLUMNS (0x404UL) #define CKA_COLOR (0x405UL) #define CKA_BITS_PER_PIXEL (0x406UL) #define CKA_CHAR_SETS (0x480UL) #define CKA_ENCODING_METHODS (0x481UL) #define CKA_MIME_TYPES (0x482UL) #define CKA_MECHANISM_TYPE (0x500UL) #define CKA_REQUIRED_CMS_ATTRIBUTES (0x501UL) #define CKA_DEFAULT_CMS_ATTRIBUTES (0x502UL) #define CKA_SUPPORTED_CMS_ATTRIBUTES (0x503UL) #define CKA_WRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x211UL) #define CKA_UNWRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x212UL) #define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE | 0x600UL) #define CKA_VENDOR_DEFINED (1UL << 31) struct ck_attribute { ck_attribute_type_t type; void *value; unsigned long value_len; }; struct ck_date { unsigned char year[4]; unsigned char month[2]; unsigned char day[2]; }; typedef unsigned long ck_mechanism_type_t; #define CKM_RSA_PKCS_KEY_PAIR_GEN (0UL) #define CKM_RSA_PKCS (1UL) #define CKM_RSA_9796 (2UL) #define CKM_RSA_X_509 (3UL) #define CKM_MD2_RSA_PKCS (4UL) #define CKM_MD5_RSA_PKCS (5UL) #define CKM_SHA1_RSA_PKCS (6UL) #define CKM_RIPEMD128_RSA_PKCS (7UL) #define CKM_RIPEMD160_RSA_PKCS (8UL) #define CKM_RSA_PKCS_OAEP (9UL) #define CKM_RSA_X9_31_KEY_PAIR_GEN (0xaUL) #define CKM_RSA_X9_31 (0xbUL) #define CKM_SHA1_RSA_X9_31 (0xcUL) #define CKM_RSA_PKCS_PSS (0xdUL) #define CKM_SHA1_RSA_PKCS_PSS (0xeUL) #define CKM_DSA_KEY_PAIR_GEN (0x10UL) #define CKM_DSA (0x11UL) #define CKM_DSA_SHA1 (0x12UL) #define CKM_DH_PKCS_KEY_PAIR_GEN (0x20UL) #define CKM_DH_PKCS_DERIVE (0x21UL) #define CKM_X9_42_DH_KEY_PAIR_GEN (0x30UL) #define CKM_X9_42_DH_DERIVE (0x31UL) #define CKM_X9_42_DH_HYBRID_DERIVE (0x32UL) #define CKM_X9_42_MQV_DERIVE (0x33UL) #define CKM_SHA256_RSA_PKCS (0x40UL) #define CKM_SHA384_RSA_PKCS (0x41UL) #define CKM_SHA512_RSA_PKCS (0x42UL) #define CKM_SHA256_RSA_PKCS_PSS (0x43UL) #define CKM_SHA384_RSA_PKCS_PSS (0x44UL) #define CKM_SHA512_RSA_PKCS_PSS (0x45UL) #define CKM_RC2_KEY_GEN (0x100UL) #define CKM_RC2_ECB (0x101UL) #define CKM_RC2_CBC (0x102UL) #define CKM_RC2_MAC (0x103UL) #define CKM_RC2_MAC_GENERAL (0x104UL) #define CKM_RC2_CBC_PAD (0x105UL) #define CKM_RC4_KEY_GEN (0x110UL) #define CKM_RC4 (0x111UL) #define CKM_DES_KEY_GEN (0x120UL) #define CKM_DES_ECB (0x121UL) #define CKM_DES_CBC (0x122UL) #define CKM_DES_MAC (0x123UL) #define CKM_DES_MAC_GENERAL (0x124UL) #define CKM_DES_CBC_PAD (0x125UL) #define CKM_DES2_KEY_GEN (0x130UL) #define CKM_DES3_KEY_GEN (0x131UL) #define CKM_DES3_ECB (0x132UL) #define CKM_DES3_CBC (0x133UL) #define CKM_DES3_MAC (0x134UL) #define CKM_DES3_MAC_GENERAL (0x135UL) #define CKM_DES3_CBC_PAD (0x136UL) #define CKM_CDMF_KEY_GEN (0x140UL) #define CKM_CDMF_ECB (0x141UL) #define CKM_CDMF_CBC (0x142UL) #define CKM_CDMF_MAC (0x143UL) #define CKM_CDMF_MAC_GENERAL (0x144UL) #define CKM_CDMF_CBC_PAD (0x145UL) #define CKM_MD2 (0x200UL) #define CKM_MD2_HMAC (0x201UL) #define CKM_MD2_HMAC_GENERAL (0x202UL) #define CKM_MD5 (0x210UL) #define CKM_MD5_HMAC (0x211UL) #define CKM_MD5_HMAC_GENERAL (0x212UL) #define CKM_SHA_1 (0x220UL) #define CKM_SHA_1_HMAC (0x221UL) #define CKM_SHA_1_HMAC_GENERAL (0x222UL) #define CKM_RIPEMD128 (0x230UL) #define CKM_RIPEMD128_HMAC (0x231UL) #define CKM_RIPEMD128_HMAC_GENERAL (0x232UL) #define CKM_RIPEMD160 (0x240UL) #define CKM_RIPEMD160_HMAC (0x241UL) #define CKM_RIPEMD160_HMAC_GENERAL (0x242UL) #define CKM_SHA256 (0x250UL) #define CKM_SHA256_HMAC (0x251UL) #define CKM_SHA256_HMAC_GENERAL (0x252UL) #define CKM_SHA384 (0x260UL) #define CKM_SHA384_HMAC (0x261UL) #define CKM_SHA384_HMAC_GENERAL (0x262UL) #define CKM_SHA512 (0x270UL) #define CKM_SHA512_HMAC (0x271UL) #define CKM_SHA512_HMAC_GENERAL (0x272UL) #define CKM_CAST_KEY_GEN (0x300UL) #define CKM_CAST_ECB (0x301UL) #define CKM_CAST_CBC (0x302UL) #define CKM_CAST_MAC (0x303UL) #define CKM_CAST_MAC_GENERAL (0x304UL) #define CKM_CAST_CBC_PAD (0x305UL) #define CKM_CAST3_KEY_GEN (0x310UL) #define CKM_CAST3_ECB (0x311UL) #define CKM_CAST3_CBC (0x312UL) #define CKM_CAST3_MAC (0x313UL) #define CKM_CAST3_MAC_GENERAL (0x314UL) #define CKM_CAST3_CBC_PAD (0x315UL) #define CKM_CAST5_KEY_GEN (0x320UL) #define CKM_CAST128_KEY_GEN (0x320UL) #define CKM_CAST5_ECB (0x321UL) #define CKM_CAST128_ECB (0x321UL) #define CKM_CAST5_CBC (0x322UL) #define CKM_CAST128_CBC (0x322UL) #define CKM_CAST5_MAC (0x323UL) #define CKM_CAST128_MAC (0x323UL) #define CKM_CAST5_MAC_GENERAL (0x324UL) #define CKM_CAST128_MAC_GENERAL (0x324UL) #define CKM_CAST5_CBC_PAD (0x325UL) #define CKM_CAST128_CBC_PAD (0x325UL) #define CKM_RC5_KEY_GEN (0x330UL) #define CKM_RC5_ECB (0x331UL) #define CKM_RC5_CBC (0x332UL) #define CKM_RC5_MAC (0x333UL) #define CKM_RC5_MAC_GENERAL (0x334UL) #define CKM_RC5_CBC_PAD (0x335UL) #define CKM_IDEA_KEY_GEN (0x340UL) #define CKM_IDEA_ECB (0x341UL) #define CKM_IDEA_CBC (0x342UL) #define CKM_IDEA_MAC (0x343UL) #define CKM_IDEA_MAC_GENERAL (0x344UL) #define CKM_IDEA_CBC_PAD (0x345UL) #define CKM_GENERIC_SECRET_KEY_GEN (0x350UL) #define CKM_CONCATENATE_BASE_AND_KEY (0x360UL) #define CKM_CONCATENATE_BASE_AND_DATA (0x362UL) #define CKM_CONCATENATE_DATA_AND_BASE (0x363UL) #define CKM_XOR_BASE_AND_DATA (0x364UL) #define CKM_EXTRACT_KEY_FROM_KEY (0x365UL) #define CKM_SSL3_PRE_MASTER_KEY_GEN (0x370UL) #define CKM_SSL3_MASTER_KEY_DERIVE (0x371UL) #define CKM_SSL3_KEY_AND_MAC_DERIVE (0x372UL) #define CKM_SSL3_MASTER_KEY_DERIVE_DH (0x373UL) #define CKM_TLS_PRE_MASTER_KEY_GEN (0x374UL) #define CKM_TLS_MASTER_KEY_DERIVE (0x375UL) #define CKM_TLS_KEY_AND_MAC_DERIVE (0x376UL) #define CKM_TLS_MASTER_KEY_DERIVE_DH (0x377UL) #define CKM_SSL3_MD5_MAC (0x380UL) #define CKM_SSL3_SHA1_MAC (0x381UL) #define CKM_MD5_KEY_DERIVATION (0x390UL) #define CKM_MD2_KEY_DERIVATION (0x391UL) #define CKM_SHA1_KEY_DERIVATION (0x392UL) #define CKM_PBE_MD2_DES_CBC (0x3a0UL) #define CKM_PBE_MD5_DES_CBC (0x3a1UL) #define CKM_PBE_MD5_CAST_CBC (0x3a2UL) #define CKM_PBE_MD5_CAST3_CBC (0x3a3UL) #define CKM_PBE_MD5_CAST5_CBC (0x3a4UL) #define CKM_PBE_MD5_CAST128_CBC (0x3a4UL) #define CKM_PBE_SHA1_CAST5_CBC (0x3a5UL) #define CKM_PBE_SHA1_CAST128_CBC (0x3a5UL) #define CKM_PBE_SHA1_RC4_128 (0x3a6UL) #define CKM_PBE_SHA1_RC4_40 (0x3a7UL) #define CKM_PBE_SHA1_DES3_EDE_CBC (0x3a8UL) #define CKM_PBE_SHA1_DES2_EDE_CBC (0x3a9UL) #define CKM_PBE_SHA1_RC2_128_CBC (0x3aaUL) #define CKM_PBE_SHA1_RC2_40_CBC (0x3abUL) #define CKM_PKCS5_PBKD2 (0x3b0UL) #define CKM_PBA_SHA1_WITH_SHA1_HMAC (0x3c0UL) #define CKM_KEY_WRAP_LYNKS (0x400UL) #define CKM_KEY_WRAP_SET_OAEP (0x401UL) #define CKM_SKIPJACK_KEY_GEN (0x1000UL) #define CKM_SKIPJACK_ECB64 (0x1001UL) #define CKM_SKIPJACK_CBC64 (0x1002UL) #define CKM_SKIPJACK_OFB64 (0x1003UL) #define CKM_SKIPJACK_CFB64 (0x1004UL) #define CKM_SKIPJACK_CFB32 (0x1005UL) #define CKM_SKIPJACK_CFB16 (0x1006UL) #define CKM_SKIPJACK_CFB8 (0x1007UL) #define CKM_SKIPJACK_WRAP (0x1008UL) #define CKM_SKIPJACK_PRIVATE_WRAP (0x1009UL) #define CKM_SKIPJACK_RELAYX (0x100aUL) #define CKM_KEA_KEY_PAIR_GEN (0x1010UL) #define CKM_KEA_KEY_DERIVE (0x1011UL) #define CKM_FORTEZZA_TIMESTAMP (0x1020UL) #define CKM_BATON_KEY_GEN (0x1030UL) #define CKM_BATON_ECB128 (0x1031UL) #define CKM_BATON_ECB96 (0x1032UL) #define CKM_BATON_CBC128 (0x1033UL) #define CKM_BATON_COUNTER (0x1034UL) #define CKM_BATON_SHUFFLE (0x1035UL) #define CKM_BATON_WRAP (0x1036UL) #define CKM_ECDSA_KEY_PAIR_GEN (0x1040UL) #define CKM_EC_KEY_PAIR_GEN (0x1040UL) #define CKM_ECDSA (0x1041UL) #define CKM_ECDSA_SHA1 (0x1042UL) #define CKM_ECDH1_DERIVE (0x1050UL) #define CKM_ECDH1_COFACTOR_DERIVE (0x1051UL) #define CKM_ECMQV_DERIVE (0x1052UL) #define CKM_JUNIPER_KEY_GEN (0x1060UL) #define CKM_JUNIPER_ECB128 (0x1061UL) #define CKM_JUNIPER_CBC128 (0x1062UL) #define CKM_JUNIPER_COUNTER (0x1063UL) #define CKM_JUNIPER_SHUFFLE (0x1064UL) #define CKM_JUNIPER_WRAP (0x1065UL) #define CKM_FASTHASH (0x1070UL) #define CKM_AES_KEY_GEN (0x1080UL) #define CKM_AES_ECB (0x1081UL) #define CKM_AES_CBC (0x1082UL) #define CKM_AES_MAC (0x1083UL) #define CKM_AES_MAC_GENERAL (0x1084UL) #define CKM_AES_CBC_PAD (0x1085UL) #define CKM_GOSTR3410_KEY_PAIR_GEN (0x1200UL) #define CKM_GOSTR3410 (0x1201UL) #define CKM_GOSTR3410_WITH_GOSTR3411 (0x1202UL) #define CKM_GOSTR3411 (0x1210UL) #define CKM_DSA_PARAMETER_GEN (0x2000UL) #define CKM_DH_PKCS_PARAMETER_GEN (0x2001UL) #define CKM_X9_42_DH_PARAMETER_GEN (0x2002UL) #define CKM_VENDOR_DEFINED (1UL << 31) struct ck_mechanism { ck_mechanism_type_t mechanism; void *parameter; unsigned long parameter_len; }; struct ck_mechanism_info { unsigned long min_key_size; unsigned long max_key_size; ck_flags_t flags; }; #define CKF_HW (1UL << 0) #define CKF_ENCRYPT (1UL << 8) #define CKF_DECRYPT (1UL << 9) #define CKF_DIGEST (1UL << 10) #define CKF_SIGN (1UL << 11) #define CKF_SIGN_RECOVER (1UL << 12) #define CKF_VERIFY (1UL << 13) #define CKF_VERIFY_RECOVER (1UL << 14) #define CKF_GENERATE (1UL << 15) #define CKF_GENERATE_KEY_PAIR (1UL << 16) #define CKF_WRAP (1UL << 17) #define CKF_UNWRAP (1UL << 18) #define CKF_DERIVE (1UL << 19) #define CKF_EXTENSION (1UL << 31) #define CKF_EC_F_P (1UL << 20) #define CKF_EC_F_2M (1UL << 21) #define CKF_EC_ECPARAMETERS (1UL << 22) #define CKF_EC_NAMEDCURVE (1UL << 23) #define CKF_EC_UNCOMPRESES (1UL << 24) #define CKF_EC_COMPRESS (1UL << 25) /* Flags for C_WaitForSlotEvent. */ #define CKF_DONT_BLOCK (1UL) /* Flags for Key derivation */ #define CKD_NULL (1UL << 0) typedef struct CK_ECDH1_DERIVE_PARAMS { unsigned long kdf; unsigned long ulSharedDataLen; unsigned char * pSharedData; unsigned long ulPublicDataLen; unsigned char * pPublicData; } CK_ECDH1_DERIVE_PARAMS; typedef unsigned long ck_rv_t; typedef ck_rv_t (*ck_notify_t) (ck_session_handle_t session, ck_notification_t event, void *application); /* Forward reference. */ struct ck_function_list; #define _CK_DECLARE_FUNCTION(name, args) \ typedef ck_rv_t (*CK_ ## name) args; \ ck_rv_t CK_SPEC name args _CK_DECLARE_FUNCTION (C_Initialize, (void *init_args)); _CK_DECLARE_FUNCTION (C_Finalize, (void *reserved)); _CK_DECLARE_FUNCTION (C_GetInfo, (struct ck_info *info)); _CK_DECLARE_FUNCTION (C_GetFunctionList, (struct ck_function_list **function_list)); _CK_DECLARE_FUNCTION (C_GetSlotList, (unsigned char token_present, ck_slot_id_t *slot_list, unsigned long *count)); _CK_DECLARE_FUNCTION (C_GetSlotInfo, (ck_slot_id_t slot_id, struct ck_slot_info *info)); _CK_DECLARE_FUNCTION (C_GetTokenInfo, (ck_slot_id_t slot_id, struct ck_token_info *info)); _CK_DECLARE_FUNCTION (C_WaitForSlotEvent, (ck_flags_t flags, ck_slot_id_t *slot, void *reserved)); _CK_DECLARE_FUNCTION (C_GetMechanismList, (ck_slot_id_t slot_id, ck_mechanism_type_t *mechanism_list, unsigned long *count)); _CK_DECLARE_FUNCTION (C_GetMechanismInfo, (ck_slot_id_t slot_id, ck_mechanism_type_t type, struct ck_mechanism_info *info)); _CK_DECLARE_FUNCTION (C_InitToken, (ck_slot_id_t slot_id, unsigned char *pin, unsigned long pin_len, unsigned char *label)); _CK_DECLARE_FUNCTION (C_InitPIN, (ck_session_handle_t session, unsigned char *pin, unsigned long pin_len)); _CK_DECLARE_FUNCTION (C_SetPIN, (ck_session_handle_t session, unsigned char *old_pin, unsigned long old_len, unsigned char *new_pin, unsigned long new_len)); _CK_DECLARE_FUNCTION (C_OpenSession, (ck_slot_id_t slot_id, ck_flags_t flags, void *application, ck_notify_t notify, ck_session_handle_t *session)); _CK_DECLARE_FUNCTION (C_CloseSession, (ck_session_handle_t session)); _CK_DECLARE_FUNCTION (C_CloseAllSessions, (ck_slot_id_t slot_id)); _CK_DECLARE_FUNCTION (C_GetSessionInfo, (ck_session_handle_t session, struct ck_session_info *info)); _CK_DECLARE_FUNCTION (C_GetOperationState, (ck_session_handle_t session, unsigned char *operation_state, unsigned long *operation_state_len)); _CK_DECLARE_FUNCTION (C_SetOperationState, (ck_session_handle_t session, unsigned char *operation_state, unsigned long operation_state_len, ck_object_handle_t encryption_key, ck_object_handle_t authentiation_key)); _CK_DECLARE_FUNCTION (C_Login, (ck_session_handle_t session, ck_user_type_t user_type, unsigned char *pin, unsigned long pin_len)); _CK_DECLARE_FUNCTION (C_Logout, (ck_session_handle_t session)); _CK_DECLARE_FUNCTION (C_CreateObject, (ck_session_handle_t session, struct ck_attribute *templ, unsigned long count, ck_object_handle_t *object)); _CK_DECLARE_FUNCTION (C_CopyObject, (ck_session_handle_t session, ck_object_handle_t object, struct ck_attribute *templ, unsigned long count, ck_object_handle_t *new_object)); _CK_DECLARE_FUNCTION (C_DestroyObject, (ck_session_handle_t session, ck_object_handle_t object)); _CK_DECLARE_FUNCTION (C_GetObjectSize, (ck_session_handle_t session, ck_object_handle_t object, unsigned long *size)); _CK_DECLARE_FUNCTION (C_GetAttributeValue, (ck_session_handle_t session, ck_object_handle_t object, struct ck_attribute *templ, unsigned long count)); _CK_DECLARE_FUNCTION (C_SetAttributeValue, (ck_session_handle_t session, ck_object_handle_t object, struct ck_attribute *templ, unsigned long count)); _CK_DECLARE_FUNCTION (C_FindObjectsInit, (ck_session_handle_t session, struct ck_attribute *templ, unsigned long count)); _CK_DECLARE_FUNCTION (C_FindObjects, (ck_session_handle_t session, ck_object_handle_t *object, unsigned long max_object_count, unsigned long *object_count)); _CK_DECLARE_FUNCTION (C_FindObjectsFinal, (ck_session_handle_t session)); _CK_DECLARE_FUNCTION (C_EncryptInit, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t key)); _CK_DECLARE_FUNCTION (C_Encrypt, (ck_session_handle_t session, unsigned char *data, unsigned long data_len, unsigned char *encrypted_data, unsigned long *encrypted_data_len)); _CK_DECLARE_FUNCTION (C_EncryptUpdate, (ck_session_handle_t session, unsigned char *part, unsigned long part_len, unsigned char *encrypted_part, unsigned long *encrypted_part_len)); _CK_DECLARE_FUNCTION (C_EncryptFinal, (ck_session_handle_t session, unsigned char *last_encrypted_part, unsigned long *last_encrypted_part_len)); _CK_DECLARE_FUNCTION (C_DecryptInit, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t key)); _CK_DECLARE_FUNCTION (C_Decrypt, (ck_session_handle_t session, unsigned char *encrypted_data, unsigned long encrypted_data_len, unsigned char *data, unsigned long *data_len)); _CK_DECLARE_FUNCTION (C_DecryptUpdate, (ck_session_handle_t session, unsigned char *encrypted_part, unsigned long encrypted_part_len, unsigned char *part, unsigned long *part_len)); _CK_DECLARE_FUNCTION (C_DecryptFinal, (ck_session_handle_t session, unsigned char *last_part, unsigned long *last_part_len)); _CK_DECLARE_FUNCTION (C_DigestInit, (ck_session_handle_t session, struct ck_mechanism *mechanism)); _CK_DECLARE_FUNCTION (C_Digest, (ck_session_handle_t session, unsigned char *data, unsigned long data_len, unsigned char *digest, unsigned long *digest_len)); _CK_DECLARE_FUNCTION (C_DigestUpdate, (ck_session_handle_t session, unsigned char *part, unsigned long part_len)); _CK_DECLARE_FUNCTION (C_DigestKey, (ck_session_handle_t session, ck_object_handle_t key)); _CK_DECLARE_FUNCTION (C_DigestFinal, (ck_session_handle_t session, unsigned char *digest, unsigned long *digest_len)); _CK_DECLARE_FUNCTION (C_SignInit, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t key)); _CK_DECLARE_FUNCTION (C_Sign, (ck_session_handle_t session, unsigned char *data, unsigned long data_len, unsigned char *signature, unsigned long *signature_len)); _CK_DECLARE_FUNCTION (C_SignUpdate, (ck_session_handle_t session, unsigned char *part, unsigned long part_len)); _CK_DECLARE_FUNCTION (C_SignFinal, (ck_session_handle_t session, unsigned char *signature, unsigned long *signature_len)); _CK_DECLARE_FUNCTION (C_SignRecoverInit, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t key)); _CK_DECLARE_FUNCTION (C_SignRecover, (ck_session_handle_t session, unsigned char *data, unsigned long data_len, unsigned char *signature, unsigned long *signature_len)); _CK_DECLARE_FUNCTION (C_VerifyInit, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t key)); _CK_DECLARE_FUNCTION (C_Verify, (ck_session_handle_t session, unsigned char *data, unsigned long data_len, unsigned char *signature, unsigned long signature_len)); _CK_DECLARE_FUNCTION (C_VerifyUpdate, (ck_session_handle_t session, unsigned char *part, unsigned long part_len)); _CK_DECLARE_FUNCTION (C_VerifyFinal, (ck_session_handle_t session, unsigned char *signature, unsigned long signature_len)); _CK_DECLARE_FUNCTION (C_VerifyRecoverInit, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t key)); _CK_DECLARE_FUNCTION (C_VerifyRecover, (ck_session_handle_t session, unsigned char *signature, unsigned long signature_len, unsigned char *data, unsigned long *data_len)); _CK_DECLARE_FUNCTION (C_DigestEncryptUpdate, (ck_session_handle_t session, unsigned char *part, unsigned long part_len, unsigned char *encrypted_part, unsigned long *encrypted_part_len)); _CK_DECLARE_FUNCTION (C_DecryptDigestUpdate, (ck_session_handle_t session, unsigned char *encrypted_part, unsigned long encrypted_part_len, unsigned char *part, unsigned long *part_len)); _CK_DECLARE_FUNCTION (C_SignEncryptUpdate, (ck_session_handle_t session, unsigned char *part, unsigned long part_len, unsigned char *encrypted_part, unsigned long *encrypted_part_len)); _CK_DECLARE_FUNCTION (C_DecryptVerifyUpdate, (ck_session_handle_t session, unsigned char *encrypted_part, unsigned long encrypted_part_len, unsigned char *part, unsigned long *part_len)); _CK_DECLARE_FUNCTION (C_GenerateKey, (ck_session_handle_t session, struct ck_mechanism *mechanism, struct ck_attribute *templ, unsigned long count, ck_object_handle_t *key)); _CK_DECLARE_FUNCTION (C_GenerateKeyPair, (ck_session_handle_t session, struct ck_mechanism *mechanism, struct ck_attribute *public_key_template, unsigned long public_key_attribute_count, struct ck_attribute *private_key_template, unsigned long private_key_attribute_count, ck_object_handle_t *public_key, ck_object_handle_t *private_key)); _CK_DECLARE_FUNCTION (C_WrapKey, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t wrapping_key, ck_object_handle_t key, unsigned char *wrapped_key, unsigned long *wrapped_key_len)); _CK_DECLARE_FUNCTION (C_UnwrapKey, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t unwrapping_key, unsigned char *wrapped_key, unsigned long wrapped_key_len, struct ck_attribute *templ, unsigned long attribute_count, ck_object_handle_t *key)); _CK_DECLARE_FUNCTION (C_DeriveKey, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t base_key, struct ck_attribute *templ, unsigned long attribute_count, ck_object_handle_t *key)); _CK_DECLARE_FUNCTION (C_SeedRandom, (ck_session_handle_t session, unsigned char *seed, unsigned long seed_len)); _CK_DECLARE_FUNCTION (C_GenerateRandom, (ck_session_handle_t session, unsigned char *random_data, unsigned long random_len)); _CK_DECLARE_FUNCTION (C_GetFunctionStatus, (ck_session_handle_t session)); _CK_DECLARE_FUNCTION (C_CancelFunction, (ck_session_handle_t session)); struct ck_function_list { struct ck_version version; CK_C_Initialize C_Initialize; CK_C_Finalize C_Finalize; CK_C_GetInfo C_GetInfo; CK_C_GetFunctionList C_GetFunctionList; CK_C_GetSlotList C_GetSlotList; CK_C_GetSlotInfo C_GetSlotInfo; CK_C_GetTokenInfo C_GetTokenInfo; CK_C_GetMechanismList C_GetMechanismList; CK_C_GetMechanismInfo C_GetMechanismInfo; CK_C_InitToken C_InitToken; CK_C_InitPIN C_InitPIN; CK_C_SetPIN C_SetPIN; CK_C_OpenSession C_OpenSession; CK_C_CloseSession C_CloseSession; CK_C_CloseAllSessions C_CloseAllSessions; CK_C_GetSessionInfo C_GetSessionInfo; CK_C_GetOperationState C_GetOperationState; CK_C_SetOperationState C_SetOperationState; CK_C_Login C_Login; CK_C_Logout C_Logout; CK_C_CreateObject C_CreateObject; CK_C_CopyObject C_CopyObject; CK_C_DestroyObject C_DestroyObject; CK_C_GetObjectSize C_GetObjectSize; CK_C_GetAttributeValue C_GetAttributeValue; CK_C_SetAttributeValue C_SetAttributeValue; CK_C_FindObjectsInit C_FindObjectsInit; CK_C_FindObjects C_FindObjects; CK_C_FindObjectsFinal C_FindObjectsFinal; CK_C_EncryptInit C_EncryptInit; CK_C_Encrypt C_Encrypt; CK_C_EncryptUpdate C_EncryptUpdate; CK_C_EncryptFinal C_EncryptFinal; CK_C_DecryptInit C_DecryptInit; CK_C_Decrypt C_Decrypt; CK_C_DecryptUpdate C_DecryptUpdate; CK_C_DecryptFinal C_DecryptFinal; CK_C_DigestInit C_DigestInit; CK_C_Digest C_Digest; CK_C_DigestUpdate C_DigestUpdate; CK_C_DigestKey C_DigestKey; CK_C_DigestFinal C_DigestFinal; CK_C_SignInit C_SignInit; CK_C_Sign C_Sign; CK_C_SignUpdate C_SignUpdate; CK_C_SignFinal C_SignFinal; CK_C_SignRecoverInit C_SignRecoverInit; CK_C_SignRecover C_SignRecover; CK_C_VerifyInit C_VerifyInit; CK_C_Verify C_Verify; CK_C_VerifyUpdate C_VerifyUpdate; CK_C_VerifyFinal C_VerifyFinal; CK_C_VerifyRecoverInit C_VerifyRecoverInit; CK_C_VerifyRecover C_VerifyRecover; CK_C_DigestEncryptUpdate C_DigestEncryptUpdate; CK_C_DecryptDigestUpdate C_DecryptDigestUpdate; CK_C_SignEncryptUpdate C_SignEncryptUpdate; CK_C_DecryptVerifyUpdate C_DecryptVerifyUpdate; CK_C_GenerateKey C_GenerateKey; CK_C_GenerateKeyPair C_GenerateKeyPair; CK_C_WrapKey C_WrapKey; CK_C_UnwrapKey C_UnwrapKey; CK_C_DeriveKey C_DeriveKey; CK_C_SeedRandom C_SeedRandom; CK_C_GenerateRandom C_GenerateRandom; CK_C_GetFunctionStatus C_GetFunctionStatus; CK_C_CancelFunction C_CancelFunction; CK_C_WaitForSlotEvent C_WaitForSlotEvent; }; typedef ck_rv_t (*ck_createmutex_t) (void **mutex); typedef ck_rv_t (*ck_destroymutex_t) (void *mutex); typedef ck_rv_t (*ck_lockmutex_t) (void *mutex); typedef ck_rv_t (*ck_unlockmutex_t) (void *mutex); struct ck_c_initialize_args { ck_createmutex_t create_mutex; ck_destroymutex_t destroy_mutex; ck_lockmutex_t lock_mutex; ck_unlockmutex_t unlock_mutex; ck_flags_t flags; void *reserved; }; #define CKF_LIBRARY_CANT_CREATE_OS_THREADS (1UL << 0) #define CKF_OS_LOCKING_OK (1UL << 1) #define CKR_OK (0UL) #define CKR_CANCEL (1UL) #define CKR_HOST_MEMORY (2UL) #define CKR_SLOT_ID_INVALID (3UL) #define CKR_GENERAL_ERROR (5UL) #define CKR_FUNCTION_FAILED (6UL) #define CKR_ARGUMENTS_BAD (7UL) #define CKR_NO_EVENT (8UL) #define CKR_NEED_TO_CREATE_THREADS (9UL) #define CKR_CANT_LOCK (0xaUL) #define CKR_ATTRIBUTE_READ_ONLY (0x10UL) #define CKR_ATTRIBUTE_SENSITIVE (0x11UL) #define CKR_ATTRIBUTE_TYPE_INVALID (0x12UL) #define CKR_ATTRIBUTE_VALUE_INVALID (0x13UL) #define CKR_DATA_INVALID (0x20UL) #define CKR_DATA_LEN_RANGE (0x21UL) #define CKR_DEVICE_ERROR (0x30UL) #define CKR_DEVICE_MEMORY (0x31UL) #define CKR_DEVICE_REMOVED (0x32UL) #define CKR_ENCRYPTED_DATA_INVALID (0x40UL) #define CKR_ENCRYPTED_DATA_LEN_RANGE (0x41UL) #define CKR_FUNCTION_CANCELED (0x50UL) #define CKR_FUNCTION_NOT_PARALLEL (0x51UL) #define CKR_FUNCTION_NOT_SUPPORTED (0x54UL) #define CKR_KEY_HANDLE_INVALID (0x60UL) #define CKR_KEY_SIZE_RANGE (0x62UL) #define CKR_KEY_TYPE_INCONSISTENT (0x63UL) #define CKR_KEY_NOT_NEEDED (0x64UL) #define CKR_KEY_CHANGED (0x65UL) #define CKR_KEY_NEEDED (0x66UL) #define CKR_KEY_INDIGESTIBLE (0x67UL) #define CKR_KEY_FUNCTION_NOT_PERMITTED (0x68UL) #define CKR_KEY_NOT_WRAPPABLE (0x69UL) #define CKR_KEY_UNEXTRACTABLE (0x6aUL) #define CKR_MECHANISM_INVALID (0x70UL) #define CKR_MECHANISM_PARAM_INVALID (0x71UL) #define CKR_OBJECT_HANDLE_INVALID (0x82UL) #define CKR_OPERATION_ACTIVE (0x90UL) #define CKR_OPERATION_NOT_INITIALIZED (0x91UL) #define CKR_PIN_INCORRECT (0xa0UL) #define CKR_PIN_INVALID (0xa1UL) #define CKR_PIN_LEN_RANGE (0xa2UL) #define CKR_PIN_EXPIRED (0xa3UL) #define CKR_PIN_LOCKED (0xa4UL) #define CKR_SESSION_CLOSED (0xb0UL) #define CKR_SESSION_COUNT (0xb1UL) #define CKR_SESSION_HANDLE_INVALID (0xb3UL) #define CKR_SESSION_PARALLEL_NOT_SUPPORTED (0xb4UL) #define CKR_SESSION_READ_ONLY (0xb5UL) #define CKR_SESSION_EXISTS (0xb6UL) #define CKR_SESSION_READ_ONLY_EXISTS (0xb7UL) #define CKR_SESSION_READ_WRITE_SO_EXISTS (0xb8UL) #define CKR_SIGNATURE_INVALID (0xc0UL) #define CKR_SIGNATURE_LEN_RANGE (0xc1UL) #define CKR_TEMPLATE_INCOMPLETE (0xd0UL) #define CKR_TEMPLATE_INCONSISTENT (0xd1UL) #define CKR_TOKEN_NOT_PRESENT (0xe0UL) #define CKR_TOKEN_NOT_RECOGNIZED (0xe1UL) #define CKR_TOKEN_WRITE_PROTECTED (0xe2UL) #define CKR_UNWRAPPING_KEY_HANDLE_INVALID (0xf0UL) #define CKR_UNWRAPPING_KEY_SIZE_RANGE (0xf1UL) #define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT (0xf2UL) #define CKR_USER_ALREADY_LOGGED_IN (0x100UL) #define CKR_USER_NOT_LOGGED_IN (0x101UL) #define CKR_USER_PIN_NOT_INITIALIZED (0x102UL) #define CKR_USER_TYPE_INVALID (0x103UL) #define CKR_USER_ANOTHER_ALREADY_LOGGED_IN (0x104UL) #define CKR_USER_TOO_MANY_TYPES (0x105UL) #define CKR_WRAPPED_KEY_INVALID (0x110UL) #define CKR_WRAPPED_KEY_LEN_RANGE (0x112UL) #define CKR_WRAPPING_KEY_HANDLE_INVALID (0x113UL) #define CKR_WRAPPING_KEY_SIZE_RANGE (0x114UL) #define CKR_WRAPPING_KEY_TYPE_INCONSISTENT (0x115UL) #define CKR_RANDOM_SEED_NOT_SUPPORTED (0x120UL) #define CKR_RANDOM_NO_RNG (0x121UL) #define CKR_DOMAIN_PARAMS_INVALID (0x130UL) #define CKR_BUFFER_TOO_SMALL (0x150UL) #define CKR_SAVED_STATE_INVALID (0x160UL) #define CKR_INFORMATION_SENSITIVE (0x170UL) #define CKR_STATE_UNSAVEABLE (0x180UL) #define CKR_CRYPTOKI_NOT_INITIALIZED (0x190UL) #define CKR_CRYPTOKI_ALREADY_INITIALIZED (0x191UL) #define CKR_MUTEX_BAD (0x1a0UL) #define CKR_MUTEX_NOT_LOCKED (0x1a1UL) #define CKR_FUNCTION_REJECTED (0x200UL) #define CKR_VENDOR_DEFINED (1UL << 31) /* Compatibility layer. */ #ifdef CRYPTOKI_COMPAT #undef CK_DEFINE_FUNCTION #define CK_DEFINE_FUNCTION(retval, name) retval CK_SPEC name /* For NULL. */ #include typedef unsigned char CK_BYTE; typedef unsigned char CK_CHAR; typedef unsigned char CK_UTF8CHAR; typedef unsigned char CK_BBOOL; typedef unsigned long int CK_ULONG; typedef long int CK_LONG; typedef CK_BYTE *CK_BYTE_PTR; typedef CK_CHAR *CK_CHAR_PTR; typedef CK_UTF8CHAR *CK_UTF8CHAR_PTR; typedef CK_ULONG *CK_ULONG_PTR; typedef void *CK_VOID_PTR; typedef void **CK_VOID_PTR_PTR; #define CK_FALSE 0 #define CK_TRUE 1 #ifndef CK_DISABLE_TRUE_FALSE #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #endif typedef struct ck_version CK_VERSION; typedef struct ck_version *CK_VERSION_PTR; typedef struct ck_info CK_INFO; typedef struct ck_info *CK_INFO_PTR; typedef ck_slot_id_t *CK_SLOT_ID_PTR; typedef struct ck_slot_info CK_SLOT_INFO; typedef struct ck_slot_info *CK_SLOT_INFO_PTR; typedef struct ck_token_info CK_TOKEN_INFO; typedef struct ck_token_info *CK_TOKEN_INFO_PTR; typedef ck_session_handle_t *CK_SESSION_HANDLE_PTR; typedef struct ck_session_info CK_SESSION_INFO; typedef struct ck_session_info *CK_SESSION_INFO_PTR; typedef ck_object_handle_t *CK_OBJECT_HANDLE_PTR; typedef ck_object_class_t *CK_OBJECT_CLASS_PTR; typedef struct ck_attribute CK_ATTRIBUTE; typedef struct ck_attribute *CK_ATTRIBUTE_PTR; typedef struct ck_date CK_DATE; typedef struct ck_date *CK_DATE_PTR; typedef ck_mechanism_type_t *CK_MECHANISM_TYPE_PTR; typedef struct ck_mechanism CK_MECHANISM; typedef struct ck_mechanism *CK_MECHANISM_PTR; typedef struct ck_mechanism_info CK_MECHANISM_INFO; typedef struct ck_mechanism_info *CK_MECHANISM_INFO_PTR; typedef struct ck_function_list CK_FUNCTION_LIST; typedef struct ck_function_list *CK_FUNCTION_LIST_PTR; typedef struct ck_function_list **CK_FUNCTION_LIST_PTR_PTR; typedef struct ck_c_initialize_args CK_C_INITIALIZE_ARGS; typedef struct ck_c_initialize_args *CK_C_INITIALIZE_ARGS_PTR; #define NULL_PTR NULL /* Delete the helper macros defined at the top of the file. */ #undef ck_flags_t #undef ck_version #undef ck_info #undef cryptoki_version #undef manufacturer_id #undef library_description #undef library_version #undef ck_notification_t #undef ck_slot_id_t #undef ck_slot_info #undef slot_description #undef hardware_version #undef firmware_version #undef ck_token_info #undef serial_number #undef max_session_count #undef session_count #undef max_rw_session_count #undef rw_session_count #undef max_pin_len #undef min_pin_len #undef total_public_memory #undef free_public_memory #undef total_private_memory #undef free_private_memory #undef utc_time #undef ck_session_handle_t #undef ck_user_type_t #undef ck_state_t #undef ck_session_info #undef slot_id #undef device_error #undef ck_object_handle_t #undef ck_object_class_t #undef ck_hw_feature_type_t #undef ck_key_type_t #undef ck_certificate_type_t #undef ck_attribute_type_t #undef ck_attribute #undef value #undef value_len #undef ck_date #undef ck_mechanism_type_t #undef ck_mechanism #undef parameter #undef parameter_len #undef ck_mechanism_info #undef min_key_size #undef max_key_size #undef ck_rv_t #undef ck_notify_t #undef ck_function_list #undef ck_createmutex_t #undef ck_destroymutex_t #undef ck_lockmutex_t #undef ck_unlockmutex_t #undef ck_c_initialize_args #undef create_mutex #undef destroy_mutex #undef lock_mutex #undef unlock_mutex #undef reserved #endif /* CRYPTOKI_COMPAT */ /* System dependencies. */ #if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32) #pragma pack(pop, cryptoki) #endif #if defined(__cplusplus) } #endif #endif /* PKCS11_H */ opensc-0.13.0/src/pkcs11/pkcs11-object.c0000644000015201777760000011420612057406034014434 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 */ 0, /* key_type */ 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, /* derive */ NULL /* mech_data */ }; static void sc_find_release(sc_pkcs11_operation_t *operation) { struct sc_pkcs11_find_operation *fop = (struct sc_pkcs11_find_operation *)operation; sc_log(context,"freeing %d handles used %d at %p", fop->allocated_handles, fop->num_handles, fop->handles); if (fop->handles) { free(fop->handles); fop->handles = NULL; } } 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; 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; } #if 0 /* TODO DEE what should we check here */ if (!(session->flags & CKF_RW_SESSION)) { rv = CKR_SESSION_READ_ONLY; goto out; } #endif card = session->slot->card; 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(); LOG_FUNC_RETURN(context, 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_attribure = {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_attribure); if (is_token == TRUE && !(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 int precedence[] = { CKR_OK, CKR_BUFFER_TOO_SMALL, CKR_ATTRIBUTE_TYPE_INVALID, CKR_ATTRIBUTE_SENSITIVE, -1 }; char object_name[64]; int j; CK_RV rv; struct sc_pkcs11_session *session; struct sc_pkcs11_object *object; int res, res_type; unsigned int i; 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] != -1; j++) { if (precedence[j] == res) break; } if (j > res_type) { res_type = j; rv = res; } } out: sc_log(context, "C_GetAttributeValue(hSession=0x%lx, hObject=0x%lx) = %s", hSession, hObject, lookup_enum ( RV_T, 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; 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 = %d)\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, (struct sc_pkcs11_operation **)&operation); 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 != CKU_USER && (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 %d/%d: 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 %d/%d: Attribute 0x%x does NOT match.", slot->id, object->handle, pTemplate[j].type); match = 0; break; } if (context->debug >= 4) { sc_log(context, "Object %d/%d: Attribute 0x%x matches.", slot->id, object->handle, pTemplate[j].type); } } if (match) { sc_log(context, "Object %d/%d 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; break; } } 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; 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, (sc_pkcs11_operation_t **) & operation); 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(context, "C_DigestInit() = %s", lookup_enum ( RV_T, 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; 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; rv = sc_pkcs11_md_update(session, pData, ulDataLen); if (rv == CKR_OK) rv = sc_pkcs11_md_final(session, pDigest, pulDigestLen); out: sc_log(context, "C_Digest() = %s", lookup_enum ( RV_T, 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(context, "C_DigestUpdate() == %s", lookup_enum ( RV_T, 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(context, "C_DigestFinal() = %s", lookup_enum ( RV_T, 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(context, "C_SignInit() = %s", lookup_enum ( RV_T, 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 = sc_pkcs11_sign_final(session, pSignature, pulSignatureLen); out: sc_log(context, "C_Sign() = %s", lookup_enum ( RV_T, 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(context, "C_SignUpdate() = %s", lookup_enum ( RV_T, 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 = sc_pkcs11_sign_final(session, pSignature, pulSignatureLen); } out: sc_log(context, "C_SignFinal() = %s", lookup_enum ( RV_T, 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 */ { CK_RV rv; 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; /* FIXME #47: C_SignRecover is not implemented */ return CKR_FUNCTION_NOT_SUPPORTED; 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; } /* XXX: need to tell the signature algorithm that we want * to recover the signature */ sc_log(context, "SignRecover operation initialized\n"); rv = sc_pkcs11_sign_init(session, pMechanism, object, key_type); out: sc_log(context, "C_SignRecoverInit() = %sn", lookup_enum ( RV_T, rv )); sc_pkcs11_unlock(); return rv; } 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 */ { return CKR_FUNCTION_NOT_SUPPORTED; } 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 */ return CKR_FUNCTION_NOT_SUPPORTED; } 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 */ return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_EncryptFinal(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pLastEncryptedPart, /* receives encrypted last part */ CK_ULONG_PTR pulLastEncryptedPartLen) { /* receives byte count */ return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV 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(context, "C_DecryptInit() = %s", lookup_enum ( RV_T, 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 = sc_pkcs11_decr(session, pEncryptedData, ulEncryptedDataLen, pData, pulDataLen); sc_log(context, "C_Decrypt() = %s", lookup_enum ( RV_T, rv )); sc_pkcs11_unlock(); return rv; } CK_RV C_DecryptUpdate(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pEncryptedPart, /* input encrypted data */ CK_ULONG ulEncryptedPartLen, /* count of bytes of input */ CK_BYTE_PTR pPart, /* receives decrypted output */ CK_ULONG_PTR pulPartLen) { /* receives decrypted byte count */ return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_DecryptFinal(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pLastPart, /* receives decrypted output */ CK_ULONG_PTR pulLastPartLen) { /* receives decrypted byte count */ return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_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->card->framework->gen_keypair == NULL) rv = CKR_FUNCTION_NOT_SUPPORTED; else rv = slot->card->framework->gen_keypair(slot, pMechanism, pPublicKeyTemplate, ulPublicKeyAttributeCount, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, phPublicKey, phPrivateKey); 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 */ return CKR_FUNCTION_NOT_SUPPORTED; } 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 */ return CKR_FUNCTION_NOT_SUPPORTED; } 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: 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 = sc_pkcs11_deri(session, pMechanism, object, key_type, hSession, *phKey, key_object); /* TODO if (rv != CK_OK) need to destroy the object */ 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->card->framework->get_random == NULL) rv = CKR_RANDOM_NO_RNG; else rv = slot->card->framework->get_random(slot, RandomData, ulRandomLen); } 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 #if 0 CK_BBOOL can_verify; CK_ATTRIBUTE verify_attribute = { CKA_VERIFY, &can_verify, sizeof(can_verify) }; #endif 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; } #if 0 rv = object->ops->get_attribute(session, object, &verify_attribute); if (rv != CKR_OK || !can_verify) { rv = CKR_KEY_TYPE_INCONSISTENT; goto out; } #endif 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(context, "C_VerifyInit() = %s", lookup_enum ( RV_T, 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 = sc_pkcs11_verif_final(session, pSignature, ulSignatureLen); out: sc_log(context, "C_Verify() = %s", lookup_enum ( RV_T, 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(context, "C_VerifyUpdate() = %s", lookup_enum ( RV_T, 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 = sc_pkcs11_verif_final(session, pSignature, ulSignatureLen); sc_log(context, "C_VerifyFinal() = %s", lookup_enum ( RV_T, 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; } /* * Helper function to compare attributes on any sort of object */ int sc_pkcs11_any_cmp_attribute(struct sc_pkcs11_session *session, void *ptr, CK_ATTRIBUTE_PTR attr) { int rv; struct sc_pkcs11_object *object; u8 temp1[1024]; u8 *temp2 = NULL; /* dynamic allocation for large attributes */ CK_ATTRIBUTE temp_attr; int res; object = (struct sc_pkcs11_object *)ptr; temp_attr.type = attr->type; 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 = malloc(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) { res = 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 res = temp_attr.ulValueLen == attr->ulValueLen && !memcmp(temp_attr.pValue, attr->pValue, attr->ulValueLen); done: if (temp2 != NULL) free(temp2); return res; } opensc-0.13.0/src/pkcs11/pkcs11-spy.exports0000644000015201777760000000002212057406034015251 00000000000000C_GetFunctionList opensc-0.13.0/src/pkcs11/opensc_pkcs11_install.js0000644000015201777760000001133312057406034016454 00000000000000//////////////////////////////////////////////////////////////////////////////////////// // Crypto Mechanism Flags PKCS11_MECH_RSA_FLAG = 0x1<<0; PKCS11_MECH_DSA_FLAG = 0x1<<1; PKCS11_MECH_RC2_FLAG = 0x1<<2; PKCS11_MECH_RC4_FLAG = 0x1<<3; PKCS11_MECH_DES_FLAG = 0x1<<4; PKCS11_MECH_DH_FLAG = 0x1<<5; //Diffie-Hellman PKCS11_MECH_SKIPJACK_FLAG = 0x1<<6; //SKIPJACK algorithm as in Fortezza cards PKCS11_MECH_RC5_FLAG = 0x1<<7; PKCS11_MECH_SHA1_FLAG = 0x1<<8; PKCS11_MECH_MD5_FLAG = 0x1<<9; PKCS11_MECH_MD2_FLAG = 0x1<<10; PKCS11_MECH_RANDOM_FLAG = 0x1<<27; //Random number generator PKCS11_PUB_READABLE_CERT_FLAG = 0x1<<28; //Stored certs can be read off the token w/o logging in PKCS11_DISABLE_FLAG = 0x1<<30; //tell Navigator to disable this slot by default // Important: // 0x1<<11, 0x1<<12, ... , 0x1<<26, 0x1<<29, and 0x1<<31 are reserved // for internal use in Navigator. // Therefore, these bits should always be set to 0; otherwise, // Navigator might exhibit unpredictable behavior. // These flags indicate which mechanisms should be turned on by var pkcs11MechanismFlags = PKCS11_PUB_READABLE_CERT_FLAG; //////////////////////////////////////////////////////////////////////////////////////// // Ciphers that support SSL or S/MIME PKCS11_CIPHER_FORTEZZA_FLAG = 0x1<<0; // Important: // 0x1<<1, 0x1<<2, ... , 0x1<<31 are reserved // for internal use in Navigator. // Therefore, these bits should ALWAYS be set to 0; otherwise, // Navigator might exhibit unpredictable behavior. // These flags indicate which SSL ciphers are supported var pkcs11CipherFlags = 0; //////////////////////////////////////////////////////////////////////////////////////// // Return values of pkcs11.addmodule() & pkcs11.delmodule() // success codes JS_OK_ADD_MODULE = 3; // Successfully added a module JS_OK_DEL_EXTERNAL_MODULE = 2; // Successfully deleted ext. module JS_OK_DEL_INTERNAL_MODULE = 1; // Successfully deleted int. module // failure codes JS_ERR_OTHER = -1; // Other errors than the followings JS_ERR_USER_CANCEL_ACTION = -2; // User abort an action JS_ERR_INCORRECT_NUM_OF_ARGUMENTS= -3; // Calling a method w/ incorrect # of arguments JS_ERR_DEL_MODULE = -4; // Error deleting a module JS_ERR_ADD_MODULE = -5; // Error adding a module JS_ERR_BAD_MODULE_NAME = -6; // The module name is invalid JS_ERR_BAD_DLL_NAME = -7; // The DLL name is bad JS_ERR_BAD_MECHANISM_FLAGS = -8; // The mechanism flags are invalid JS_ERR_BAD_CIPHER_ENABLE_FLAGS = -9; // The SSL, S/MIME cipher flags are invalid JS_ERR_ADD_MODULE_DULICATE =-10; // Module with the same name already installed var vendor = "opensc"; var plat = navigator.platform; function installFiles() { // Step 1. Create a version object and a software update object. vi = new netscape.softupdate.VersionInfo(0, 5, 0, 0); su = new netscape.softupdate.SoftwareUpdate(this, "OpenSC PKCS#11 Module"); // Step 2. Start the install process. err = su.StartInstall("pkcs11/"+vendor+"/opensc", vi, netscape.softupdate.SoftwareUpdate.FULL_INSTALL); if (err != 0) { return false; } // Step 3. Find out the physical location of the Program dir. Folder = su.GetFolder("Program"); // Step 4. Install the files. Unpack them and list where they go. err = su.AddSubcomponent("OpenSC_PKCS11", //component name (logical) vi, // version info "opensc-pkcs11.so", // source file in JAR (physical) Folder, // target folder (physical) "pkcs11/"+vendor+"/"+plat+"/opensc-pkcs11.so", // target path & filename (physical) true); // forces update if (err != 0) { window.alert("Error adding sub-component: "+"("+err+")"); su.AbortInstall(); return false; } // Step 5. Unless there was a problem, move files to final location // and update the Client Version Registry. err = su.FinalizeInstall(); if (err != 0) { window.alert("Error Finalizing Install: "+"("+err+")"); return false; } return true; } installFiles(); // Step 6: Call pkcs11.addmodule() to register the newly downloaded module result = pkcs11.addmodule("OpenSC PKCS#11 Module " + plat, Folder + "pkcs11/" + vendor + "/" + plat + "/opensc-pkcs11.so", pkcs11MechanismFlags, pkcs11CipherFlags); if (result == -10) { window.alert("New module was copied to destination, \nbut setup failed because a module " +"having the same name has been installed. \nTry deleting the module " + moduleCommonName +" first.") } else if (result < 0) { window.alert("New module was copied to destination, but setup failed. Error code: " + result); opensc-0.13.0/src/pkcs11/framework-pkcs15init.c0000644000015201777760000001265412057406034016057 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 = p11card->card; struct sc_profile *profile; int rc; 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; 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_pkcs11_slot **first_slot) { struct sc_profile *profile; struct sc_pkcs11_slot *slot; int rc; 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_card *p11card, void *ptr, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, CK_UTF8CHAR_PTR pLabel) { struct sc_profile *profile = (struct sc_profile *) p11card->fws_data[0]; struct sc_pkcs15init_initargs args; struct sc_pkcs11_slot *slot; int rc, rv, id; 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->card == p11card) slot->token_info.flags |= CKF_TOKEN_INITIALIZED; if (slot->card->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, /* inti_token */ NULL, /* init_pin */ NULL, /* create_object */ NULL, /* gen_keypair */ NULL /* get_random */ }; #endif opensc-0.13.0/src/pkcs11/misc.c0000644000015201777760000002341012057406034013015 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #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) { size_t c; if (!dst || !src || !dstsize) return; memset((char *)dst, ' ', dstsize); c = strlen(src) > dstsize ? dstsize : strlen(src); memcpy((char *)dst, src, c); } 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: 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; } 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); } /* 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) { unsigned int 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_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_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); } 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->plug_and_play = 1; conf->max_virtual_slots = 16; conf->slots_per_card = 4; conf->hide_empty_tokens = 1; conf->lock_login = 0; conf->pin_unblock_style = SC_PKCS11_PIN_UNBLOCK_NOT_ALLOWED; conf->create_puk_slot = 0; conf->zero_ckaid_for_ca_certs = 0; conf->create_slots_flags = 0; conf_block = sc_get_conf_block(ctx, "pkcs11", NULL, 1); if (!conf_block) return; /* contains the defaults, if there is a "pkcs11" config block */ conf->plug_and_play = scconf_get_bool(conf_block, "plug_and_play", conf->plug_and_play); conf->max_virtual_slots = scconf_get_int(conf_block, "max_virtual_slots", conf->max_virtual_slots); conf->slots_per_card = scconf_get_int(conf_block, "slots_per_card", conf->slots_per_card); conf->hide_empty_tokens = scconf_get_bool(conf_block, "hide_empty_tokens", conf->hide_empty_tokens); conf->lock_login = scconf_get_bool(conf_block, "lock_login", conf->lock_login); 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); conf->zero_ckaid_for_ca_certs = scconf_get_bool(conf_block, "zero_ckaid_for_ca_certs", conf->zero_ckaid_for_ca_certs); create_slots_for_pins = (char *)scconf_get_str(conf_block, "create_slots_for_pins", "all"); 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, "application")) conf->create_slots_flags |= SC_PKCS11_SLOT_FOR_APPLICATION; else if (!strcmp(op, "all")) conf->create_slots_flags |= SC_PKCS11_SLOT_CREATE_ALL; op = strtok(NULL, " ,"); } free(tmp); sc_log(ctx, "PKCS#11 options: plug_and_play=%d max_virtual_slots=%d slots_per_card=%d " "hide_empty_tokens=%d lock_login=%d pin_unblock_style=%d " "zero_ckaid_for_ca_certs=%d create_slots_flags=0x%X", conf->plug_and_play, conf->max_virtual_slots, conf->slots_per_card, conf->hide_empty_tokens, conf->lock_login, conf->pin_unblock_style, conf->zero_ckaid_for_ca_certs, conf->create_slots_flags); } opensc-0.13.0/src/pkcs11/slot.c0000644000015201777760000003044612057406034013052 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include "sc-pkcs11.h" 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_get_slot(sc_reader_t *reader) { unsigned int i; /* Locate a slot related to the reader */ for (i = 0; ireader == reader) { return slot; } } return NULL; } static void init_slot_info(CK_SLOT_INFO_PTR pInfo) { strcpy_bp(pInfo->slotDescription, "Virtual hotplug slot", 64); strcpy_bp(pInfo->manufacturerID, "OpenSC (www.opensc-project.org)", 32); pInfo->flags = CKF_REMOVABLE_DEVICE | CKF_HW_SLOT; pInfo->hardwareVersion.major = 0; pInfo->hardwareVersion.minor = 0; 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) { struct sc_pkcs11_slot *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); slot->login_user = -1; slot->id = (CK_SLOT_ID) list_locate(&virtual_slots, slot); sc_log(context, "Creating slot with id 0x%lx", slot->id); list_init(&slot->objects); list_attributes_seeker(&slot->objects, object_list_seeker); init_slot_info(&slot->slot_info); if (reader != NULL) { slot->reader = reader; strcpy_bp(slot->slot_info.slotDescription, reader->name, 64); } return CKR_OK; } /* create slots associated with a reader, called whenever a reader is seen. */ CK_RV initialize_reader(sc_reader_t *reader) { unsigned int i; CK_RV rv; scconf_block *conf_block = NULL; const scconf_list *list = NULL; conf_block = sc_get_conf_block(context, "pkcs11", NULL, 1); if (conf_block != NULL) { list = scconf_find_list(conf_block, "ignored_readers"); while (list != NULL) { if (strstr(reader->name, list->data) != NULL) { sc_log(context, "Ignoring reader \'%s\' because of \'%s\'\n", reader->name, list->data); return CKR_OK; } list = list->next; } } for (i = 0; i < sc_pkcs11_conf.slots_per_card; i++) { rv = create_slot(reader); if (rv != CKR_OK) return rv; } if (sc_detect_card_presence(reader)) { card_detect(reader); } return CKR_OK; } CK_RV card_removed(sc_reader_t * reader) { unsigned int i; struct sc_pkcs11_card *card = 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->card) card = slot->card; slot_token_removed(slot->id); } } if (card) { card->framework->unbind(card); sc_disconnect_card(card->card); /* FIXME: free mechanisms * spaces allocated by the * sc_pkcs11_register_sign_and_hash_mechanism * and sc_pkcs11_new_fw_mechanism. * but see sc_pkcs11_register_generic_mechanisms for (i=0; i < card->nmechanisms; ++i) { // if 'mech_data' is a pointer earlier returned by the ?alloc free(card->mechanisms[i]->mech_data); // if 'mechanisms[i]' is a pointer earlier returned by the ?alloc free(card->mechanisms[i]); } */ free(card->mechanisms); free(card); } return CKR_OK; } CK_RV card_detect(sc_reader_t *reader) { struct sc_pkcs11_card *p11card = NULL; int rc, rv; unsigned int i, j; rv = CKR_OK; 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->card; 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; 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) return sc_to_cryptoki_error(rc, NULL); } /* Detect the framework */ if (p11card->framework == NULL) { struct sc_app_info *app_generic = sc_pkcs15_get_application_by_type(p11card->card, "generic"); struct sc_pkcs11_slot *first_slot = NULL; 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) return CKR_GENERAL_ERROR; /* Initialize framework */ sc_log(context, "%s: Detected framework %d. Creating tokens.", reader->name, i); /* Bind firstly 'generic' application or (emulated?) card without applications */ if (app_generic || !p11card->card->app_count) { sc_log(context, "%s: Try to bind 'generic' token.", reader->name); rv = frameworks[i]->bind(p11card, app_generic); if (rv != CKR_OK) { sc_log(context, "%s: cannot bind 'generic' token.", reader->name); return rv; } sc_log(context, "%s: Creating 'generic' token.", reader->name); rv = frameworks[i]->create_tokens(p11card, app_generic, &first_slot); if (rv != CKR_OK) { sc_log(context, "%s: cannot create 'generic' token.", reader->name); return rv; } } /* 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: cannot bind %s token.", reader->name, app_name); continue; } sc_log(context, "%s: Creating %s token.", reader->name, app_name); rv = frameworks[i]->create_tokens(p11card, app_info, &first_slot); if (rv != CKR_OK) { sc_log(context, "%s: cannot create %s token.", reader->name, app_name); return rv; } } p11card->framework = frameworks[i]; } sc_log(context, "%s: Detection ended", reader->name); return CKR_OK; } CK_RV card_detect_all(void) { unsigned int i; /* 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_get_slot(reader)) initialize_reader(reader); card_detect(sc_ctx_get_reader(context, i)); } return CKR_OK; } /* Allocates an existing slot to a card */ CK_RV slot_allocate(struct sc_pkcs11_slot ** slot, struct sc_pkcs11_card * card) { 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 == card->reader && tmp_slot->card == 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, card->reader->name); tmp_slot->card = card; 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) { int rv; 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; 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; } return CKR_OK; } CK_RV slot_token_removed(CK_SLOT_ID id) { int rv, 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->card != NULL) { if (slot->fw_data != NULL && slot->card->framework != NULL && slot->card->framework->release_token != NULL) slot->card->framework->release_token(slot->card, slot->fw_data); } /* Reset relevant slot properties */ slot->slot_info.flags &= ~CKF_TOKEN_PRESENT; slot->login_user = -1; slot->card = NULL; if (token_was_present) slot->events = SC_EVENT_CARD_REMOVED; 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.13.0/src/pkcs11/Makefile.am0000644000015201777760000000435412057406034013760 00000000000000include $(top_srcdir)/win32/ltrc.inc MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak dist_noinst_SCRIPTS = opensc_pkcs11_install.js lib_LTLIBRARIES = opensc-pkcs11.la pkcs11-spy.la AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(PTHREAD_CFLAGS) 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 opensc-pkcs11.exports \ pkcs11-display.c pkcs11-display.h OPENSC_PKCS11_LIBS = \ $(top_builddir)/src/libopensc/libopensc.la \ $(top_builddir)/src/common/libscdl.la \ $(top_builddir)/src/common/libcompat.la \ $(OPTIONAL_OPENSSL_LIBS) $(PTHREAD_LIBS) opensc_pkcs11_la_SOURCES = $(OPENSC_PKCS11_SRC) $(OPENSC_PKCS11_INC) opensc_pkcs11_la_LIBADD = $(OPENSC_PKCS11_LIBS) opensc_pkcs11_la_LDFLAGS = $(AM_LDFLAGS) \ -export-symbols "$(srcdir)/opensc-pkcs11.exports" \ -module -shared -avoid-version -no-undefined pkcs11_spy_la_SOURCES = pkcs11-spy.c pkcs11-display.c pkcs11-display.h pkcs11-spy.exports pkcs11_spy_la_LIBADD = \ $(top_builddir)/src/common/libpkcs11.la \ $(top_builddir)/src/common/libscdl.la \ $(OPTIONAL_OPENSSL_LIBS) pkcs11_spy_la_LDFLAGS = $(AM_LDFLAGS) \ -export-symbols "$(srcdir)/pkcs11-spy.exports" \ -module -shared -avoid-version -no-undefined if WIN32 opensc_pkcs11_la_SOURCES += $(top_builddir)/win32/versioninfo.rc pkcs11_spy_la_SOURCES += $(top_builddir)/win32/versioninfo.rc endif jar-dir: if test ! -d jar-dir ; then mkdir jar-dir ; fi pkcs11-jar: jar-dir cp .libs/*.so jar-dir cp opensc_pkcs11_install.js jar-dir signtool -Z"opensc-pkcs11.jar" -i"opensc_pkcs11_install.js" \ -k"testcert" jar-dir 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 else # see http://wiki.cacert.org/wiki/Pkcs11TaskForce if CYGWIN PKCS11_SUFFIX=.dll else PKCS11_SUFFIX=.so endif install-exec-hook: $(MKDIR_P) "$(DESTDIR)$(pkcs11dir)" for l in opensc-pkcs11$(PKCS11_SUFFIX) pkcs11-spy$(PKCS11_SUFFIX); do \ rm -f "$(DESTDIR)$(pkcs11dir)/$$l"; \ $(LN_S) ../$$l "$(DESTDIR)$(pkcs11dir)/$$l"; \ done endif opensc-0.13.0/src/pkcs11/pkcs11-display.c0000644000015201777760000014066112057406034014637 00000000000000/* * Copyright (C) 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA */ #include "config.h" #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 (sizeof(CK_VOID_PTR) == 4) sprintf(ret, "%08lx / %ld", (unsigned long) buf_addr, (CK_LONG) buf_len); else sprintf(ret, "%016lx / %ld", (unsigned long) buf_addr, (CK_LONG) buf_len); 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], 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++; } /* padd */ 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"); } static enum_specs ck_cls_s[] = { { CKO_DATA , "CKO_DATA " }, { CKO_CERTIFICATE , "CKO_CERTIFICATE " }, { CKO_PUBLIC_KEY , "CKO_PUBLIC_KEY " }, { CKO_PRIVATE_KEY , "CKO_PRIVATE_KEY " }, { CKO_SECRET_KEY , "CKO_SECRET_KEY " }, { CKO_HW_FEATURE , "CKO_HW_FEATURE " }, { CKO_DOMAIN_PARAMETERS, "CKO_DOMAIN_PARAMETERS" }, { CKO_NETSCAPE_CRL, "CKO_NETSCAPE_CRL " }, { CKO_NETSCAPE_SMIME , "CKO_NETSCAPE_SMIME " }, { CKO_NETSCAPE_TRUST, "CKO_NETSCAPE_TRUST " }, { CKO_NETSCAPE_BUILTIN_ROOT_LIST, "CKO_NETSCAPE_BUILTIN_ROOT_LIST" }, { CKO_VENDOR_DEFINED , "CKO_VENDOR_DEFINED " } }; 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_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 " } }; static enum_specs ck_mec_s[] = { { CKM_RSA_PKCS_KEY_PAIR_GEN , "CKM_RSA_PKCS_KEY_PAIR_GEN " }, { CKM_RSA_PKCS , "CKM_RSA_PKCS " }, { CKM_RSA_9796 , "CKM_RSA_9796 " }, { CKM_RSA_X_509 , "CKM_RSA_X_509 " }, { CKM_MD2_RSA_PKCS , "CKM_MD2_RSA_PKCS " }, { CKM_MD5_RSA_PKCS , "CKM_MD5_RSA_PKCS " }, { CKM_SHA1_RSA_PKCS , "CKM_SHA1_RSA_PKCS " }, { CKM_SHA256_RSA_PKCS , "CKM_SHA256_RSA_PKCS " }, { CKM_SHA384_RSA_PKCS , "CKM_SHA384_RSA_PKCS " }, { CKM_SHA512_RSA_PKCS , "CKM_SHA512_RSA_PKCS " }, { CKM_RIPEMD128_RSA_PKCS , "CKM_RIPEMD128_RSA_PKCS " }, { CKM_RIPEMD160_RSA_PKCS , "CKM_RIPEMD160_RSA_PKCS " }, { CKM_RSA_PKCS_OAEP , "CKM_RSA_PKCS_OAEP " }, { CKM_RSA_X9_31_KEY_PAIR_GEN , "CKM_RSA_X9_31_KEY_PAIR_GEN " }, { CKM_RSA_X9_31 , "CKM_RSA_X9_31 " }, { CKM_SHA1_RSA_X9_31 , "CKM_SHA1_RSA_X9_31 " }, { CKM_RSA_PKCS_PSS , "CKM_RSA_PKCS_PSS " }, { CKM_SHA1_RSA_PKCS_PSS , "CKM_SHA1_RSA_PKCS_PSS " }, { CKM_SHA256_RSA_PKCS_PSS , "CKM_SHA256_RSA_PKCS_PSS " }, { CKM_SHA384_RSA_PKCS_PSS , "CKM_SHA384_RSA_PKCS_PSS " }, { CKM_SHA512_RSA_PKCS_PSS , "CKM_SHA512_RSA_PKCS_PSS " }, { CKM_DSA_KEY_PAIR_GEN , "CKM_DSA_KEY_PAIR_GEN " }, { CKM_DSA , "CKM_DSA " }, { CKM_DSA_SHA1 , "CKM_DSA_SHA1 " }, { CKM_DH_PKCS_KEY_PAIR_GEN , "CKM_DH_PKCS_KEY_PAIR_GEN " }, { CKM_DH_PKCS_DERIVE , "CKM_DH_PKCS_DERIVE " }, { CKM_X9_42_DH_KEY_PAIR_GEN , "CKM_X9_42_DH_KEY_PAIR_GEN " }, { CKM_X9_42_DH_DERIVE , "CKM_X9_42_DH_DERIVE " }, { CKM_X9_42_DH_HYBRID_DERIVE , "CKM_X9_42_DH_HYBRID_DERIVE " }, { CKM_X9_42_MQV_DERIVE , "CKM_X9_42_MQV_DERIVE " }, { CKM_RC2_KEY_GEN , "CKM_RC2_KEY_GEN " }, { CKM_RC2_ECB , "CKM_RC2_ECB " }, { CKM_RC2_CBC , "CKM_RC2_CBC " }, { CKM_RC2_MAC , "CKM_RC2_MAC " }, { CKM_RC2_MAC_GENERAL , "CKM_RC2_MAC_GENERAL " }, { CKM_RC2_CBC_PAD , "CKM_RC2_CBC_PAD " }, { CKM_RC4_KEY_GEN , "CKM_RC4_KEY_GEN " }, { CKM_RC4 , "CKM_RC4 " }, { CKM_DES_KEY_GEN , "CKM_DES_KEY_GEN " }, { CKM_DES_ECB , "CKM_DES_ECB " }, { CKM_DES_CBC , "CKM_DES_CBC " }, { CKM_DES_MAC , "CKM_DES_MAC " }, { CKM_DES_MAC_GENERAL , "CKM_DES_MAC_GENERAL " }, { CKM_DES_CBC_PAD , "CKM_DES_CBC_PAD " }, { CKM_DES2_KEY_GEN , "CKM_DES2_KEY_GEN " }, { CKM_DES3_KEY_GEN , "CKM_DES3_KEY_GEN " }, { CKM_DES3_ECB , "CKM_DES3_ECB " }, { CKM_DES3_CBC , "CKM_DES3_CBC " }, { CKM_DES3_MAC , "CKM_DES3_MAC " }, { CKM_DES3_MAC_GENERAL , "CKM_DES3_MAC_GENERAL " }, { CKM_DES3_CBC_PAD , "CKM_DES3_CBC_PAD " }, { CKM_CDMF_KEY_GEN , "CKM_CDMF_KEY_GEN " }, { CKM_CDMF_ECB , "CKM_CDMF_ECB " }, { CKM_CDMF_CBC , "CKM_CDMF_CBC " }, { CKM_CDMF_MAC , "CKM_CDMF_MAC " }, { CKM_CDMF_MAC_GENERAL , "CKM_CDMF_MAC_GENERAL " }, { CKM_CDMF_CBC_PAD , "CKM_CDMF_CBC_PAD " }, { CKM_MD2 , "CKM_MD2 " }, { CKM_MD2_HMAC , "CKM_MD2_HMAC " }, { CKM_MD2_HMAC_GENERAL , "CKM_MD2_HMAC_GENERAL " }, { CKM_MD5 , "CKM_MD5 " }, { CKM_MD5_HMAC , "CKM_MD5_HMAC " }, { CKM_MD5_HMAC_GENERAL , "CKM_MD5_HMAC_GENERAL " }, { CKM_SHA_1 , "CKM_SHA_1 " }, { CKM_SHA_1_HMAC , "CKM_SHA_1_HMAC " }, { CKM_SHA_1_HMAC_GENERAL , "CKM_SHA_1_HMAC_GENERAL " }, { CKM_SHA256 , "CKM_SHA256 " }, { CKM_SHA256_HMAC , "CKM_SHA256_HMAC " }, { CKM_SHA256_HMAC_GENERAL , "CKM_SHA256_HMAC_GENERAL " }, { CKM_SHA384 , "CKM_SHA384 " }, { CKM_SHA384_HMAC , "CKM_SHA384_HMAC " }, { CKM_SHA384_HMAC_GENERAL , "CKM_SHA384_HMAC_GENERAL " }, { CKM_SHA512 , "CKM_SHA512 " }, { CKM_SHA512_HMAC , "CKM_SHA512_HMAC " }, { CKM_SHA512_HMAC_GENERAL , "CKM_SHA512_HMAC_GENERAL " }, { CKM_RIPEMD128 , "CKM_RIPEMD128 " }, { CKM_RIPEMD128_HMAC , "CKM_RIPEMD128_HMAC " }, { CKM_RIPEMD128_HMAC_GENERAL , "CKM_RIPEMD128_HMAC_GENERAL " }, { CKM_RIPEMD160 , "CKM_RIPEMD160 " }, { CKM_RIPEMD160_HMAC , "CKM_RIPEMD160_HMAC " }, { CKM_RIPEMD160_HMAC_GENERAL , "CKM_RIPEMD160_HMAC_GENERAL " }, { CKM_SHA256 , "CKM_SHA256 " }, { CKM_SHA256_HMAC , "CKM_SHA256_HMAC " }, { CKM_SHA256_HMAC_GENERAL , "CKM_SHA256_HMAC_GENERAL " }, { CKM_SHA384 , "CKM_SHA384 " }, { CKM_SHA384_HMAC , "CKM_SHA384_HMAC " }, { CKM_SHA384_HMAC_GENERAL , "CKM_SHA384_HMAC_GENERAL " }, { CKM_CAST_KEY_GEN , "CKM_CAST_KEY_GEN " }, { CKM_CAST_ECB , "CKM_CAST_ECB " }, { CKM_CAST_CBC , "CKM_CAST_CBC " }, { CKM_CAST_MAC , "CKM_CAST_MAC " }, { CKM_CAST_MAC_GENERAL , "CKM_CAST_MAC_GENERAL " }, { CKM_CAST_CBC_PAD , "CKM_CAST_CBC_PAD " }, { CKM_CAST3_KEY_GEN , "CKM_CAST3_KEY_GEN " }, { CKM_CAST3_ECB , "CKM_CAST3_ECB " }, { CKM_CAST3_CBC , "CKM_CAST3_CBC " }, { CKM_CAST3_MAC , "CKM_CAST3_MAC " }, { CKM_CAST3_MAC_GENERAL , "CKM_CAST3_MAC_GENERAL " }, { CKM_CAST3_CBC_PAD , "CKM_CAST3_CBC_PAD " }, { CKM_CAST5_KEY_GEN , "CKM_CAST5_KEY_GEN " }, { CKM_CAST128_KEY_GEN , "CKM_CAST128_KEY_GEN " }, { CKM_CAST5_ECB , "CKM_CAST5_ECB " }, { CKM_CAST128_ECB , "CKM_CAST128_ECB " }, { CKM_CAST5_CBC , "CKM_CAST5_CBC " }, { CKM_CAST128_CBC , "CKM_CAST128_CBC " }, { CKM_CAST5_MAC , "CKM_CAST5_MAC " }, { CKM_CAST128_MAC , "CKM_CAST128_MAC " }, { CKM_CAST5_MAC_GENERAL , "CKM_CAST5_MAC_GENERAL " }, { CKM_CAST128_MAC_GENERAL , "CKM_CAST128_MAC_GENERAL " }, { CKM_CAST5_CBC_PAD , "CKM_CAST5_CBC_PAD " }, { CKM_CAST128_CBC_PAD , "CKM_CAST128_CBC_PAD " }, { CKM_RC5_KEY_GEN , "CKM_RC5_KEY_GEN " }, { CKM_RC5_ECB , "CKM_RC5_ECB " }, { CKM_RC5_CBC , "CKM_RC5_CBC " }, { CKM_RC5_MAC , "CKM_RC5_MAC " }, { CKM_RC5_MAC_GENERAL , "CKM_RC5_MAC_GENERAL " }, { CKM_RC5_CBC_PAD , "CKM_RC5_CBC_PAD " }, { CKM_IDEA_KEY_GEN , "CKM_IDEA_KEY_GEN " }, { CKM_IDEA_ECB , "CKM_IDEA_ECB " }, { CKM_IDEA_CBC , "CKM_IDEA_CBC " }, { CKM_IDEA_MAC , "CKM_IDEA_MAC " }, { CKM_IDEA_MAC_GENERAL , "CKM_IDEA_MAC_GENERAL " }, { CKM_IDEA_CBC_PAD , "CKM_IDEA_CBC_PAD " }, { CKM_GENERIC_SECRET_KEY_GEN , "CKM_GENERIC_SECRET_KEY_GEN " }, { CKM_CONCATENATE_BASE_AND_KEY , "CKM_CONCATENATE_BASE_AND_KEY " }, { CKM_CONCATENATE_BASE_AND_DATA, "CKM_CONCATENATE_BASE_AND_DATA" }, { CKM_CONCATENATE_DATA_AND_BASE, "CKM_CONCATENATE_DATA_AND_BASE" }, { CKM_XOR_BASE_AND_DATA , "CKM_XOR_BASE_AND_DATA " }, { CKM_EXTRACT_KEY_FROM_KEY , "CKM_EXTRACT_KEY_FROM_KEY " }, { CKM_SSL3_PRE_MASTER_KEY_GEN , "CKM_SSL3_PRE_MASTER_KEY_GEN " }, { CKM_SSL3_MASTER_KEY_DERIVE , "CKM_SSL3_MASTER_KEY_DERIVE " }, { CKM_SSL3_KEY_AND_MAC_DERIVE , "CKM_SSL3_KEY_AND_MAC_DERIVE " }, { CKM_SSL3_MASTER_KEY_DERIVE_DH, "CKM_SSL3_MASTER_KEY_DERIVE_DH" }, { CKM_TLS_PRE_MASTER_KEY_GEN , "CKM_TLS_PRE_MASTER_KEY_GEN " }, { CKM_TLS_MASTER_KEY_DERIVE , "CKM_TLS_MASTER_KEY_DERIVE " }, { CKM_TLS_KEY_AND_MAC_DERIVE , "CKM_TLS_KEY_AND_MAC_DERIVE " }, { CKM_TLS_MASTER_KEY_DERIVE_DH , "CKM_TLS_MASTER_KEY_DERIVE_DH " }, { CKM_SSL3_MD5_MAC , "CKM_SSL3_MD5_MAC " }, { CKM_SSL3_SHA1_MAC , "CKM_SSL3_SHA1_MAC " }, { CKM_MD5_KEY_DERIVATION , "CKM_MD5_KEY_DERIVATION " }, { CKM_MD2_KEY_DERIVATION , "CKM_MD2_KEY_DERIVATION " }, { CKM_SHA1_KEY_DERIVATION , "CKM_SHA1_KEY_DERIVATION " }, { CKM_PBE_MD2_DES_CBC , "CKM_PBE_MD2_DES_CBC " }, { CKM_PBE_MD5_DES_CBC , "CKM_PBE_MD5_DES_CBC " }, { CKM_PBE_MD5_CAST_CBC , "CKM_PBE_MD5_CAST_CBC " }, { CKM_PBE_MD5_CAST3_CBC , "CKM_PBE_MD5_CAST3_CBC " }, { CKM_PBE_MD5_CAST5_CBC , "CKM_PBE_MD5_CAST5_CBC " }, { CKM_PBE_MD5_CAST128_CBC , "CKM_PBE_MD5_CAST128_CBC " }, { CKM_PBE_SHA1_CAST5_CBC , "CKM_PBE_SHA1_CAST5_CBC " }, { CKM_PBE_SHA1_CAST128_CBC , "CKM_PBE_SHA1_CAST128_CBC " }, { CKM_PBE_SHA1_RC4_128 , "CKM_PBE_SHA1_RC4_128 " }, { CKM_PBE_SHA1_RC4_40 , "CKM_PBE_SHA1_RC4_40 " }, { CKM_PBE_SHA1_DES3_EDE_CBC , "CKM_PBE_SHA1_DES3_EDE_CBC " }, { CKM_PBE_SHA1_DES2_EDE_CBC , "CKM_PBE_SHA1_DES2_EDE_CBC " }, { CKM_PBE_SHA1_RC2_128_CBC , "CKM_PBE_SHA1_RC2_128_CBC " }, { CKM_PBE_SHA1_RC2_40_CBC , "CKM_PBE_SHA1_RC2_40_CBC " }, { CKM_PKCS5_PBKD2 , "CKM_PKCS5_PBKD2 " }, { CKM_PBA_SHA1_WITH_SHA1_HMAC , "CKM_PBA_SHA1_WITH_SHA1_HMAC " }, { CKM_KEY_WRAP_LYNKS , "CKM_KEY_WRAP_LYNKS " }, { CKM_KEY_WRAP_SET_OAEP , "CKM_KEY_WRAP_SET_OAEP " }, { CKM_SKIPJACK_KEY_GEN , "CKM_SKIPJACK_KEY_GEN " }, { CKM_SKIPJACK_ECB64 , "CKM_SKIPJACK_ECB64 " }, { CKM_SKIPJACK_CBC64 , "CKM_SKIPJACK_CBC64 " }, { CKM_SKIPJACK_OFB64 , "CKM_SKIPJACK_OFB64 " }, { CKM_SKIPJACK_CFB64 , "CKM_SKIPJACK_CFB64 " }, { CKM_SKIPJACK_CFB32 , "CKM_SKIPJACK_CFB32 " }, { CKM_SKIPJACK_CFB16 , "CKM_SKIPJACK_CFB16 " }, { CKM_SKIPJACK_CFB8 , "CKM_SKIPJACK_CFB8 " }, { CKM_SKIPJACK_WRAP , "CKM_SKIPJACK_WRAP " }, { CKM_SKIPJACK_PRIVATE_WRAP , "CKM_SKIPJACK_PRIVATE_WRAP " }, { CKM_SKIPJACK_RELAYX , "CKM_SKIPJACK_RELAYX " }, { CKM_KEA_KEY_PAIR_GEN , "CKM_KEA_KEY_PAIR_GEN " }, { CKM_KEA_KEY_DERIVE , "CKM_KEA_KEY_DERIVE " }, { CKM_FORTEZZA_TIMESTAMP , "CKM_FORTEZZA_TIMESTAMP " }, { CKM_BATON_KEY_GEN , "CKM_BATON_KEY_GEN " }, { CKM_BATON_ECB128 , "CKM_BATON_ECB128 " }, { CKM_BATON_ECB96 , "CKM_BATON_ECB96 " }, { CKM_BATON_CBC128 , "CKM_BATON_CBC128 " }, { CKM_BATON_COUNTER , "CKM_BATON_COUNTER " }, { CKM_BATON_SHUFFLE , "CKM_BATON_SHUFFLE " }, { CKM_BATON_WRAP , "CKM_BATON_WRAP " }, { CKM_EC_KEY_PAIR_GEN , "CKM_EC_KEY_PAIR_GEN " }, { CKM_ECDSA , "CKM_ECDSA " }, { CKM_ECDSA_SHA1 , "CKM_ECDSA_SHA1 " }, { CKM_ECDH1_DERIVE , "CKM_ECDH1_DERIVE " }, { CKM_ECDH1_COFACTOR_DERIVE , "CKM_ECDH1_COFACTOR_DERIVE " }, { CKM_ECMQV_DERIVE , "CKM_ECMQV_DERIVE " }, { CKM_JUNIPER_KEY_GEN , "CKM_JUNIPER_KEY_GEN " }, { CKM_JUNIPER_ECB128 , "CKM_JUNIPER_ECB128 " }, { CKM_JUNIPER_CBC128 , "CKM_JUNIPER_CBC128 " }, { CKM_JUNIPER_COUNTER , "CKM_JUNIPER_COUNTER " }, { CKM_JUNIPER_SHUFFLE , "CKM_JUNIPER_SHUFFLE " }, { CKM_JUNIPER_WRAP , "CKM_JUNIPER_WRAP " }, { CKM_FASTHASH , "CKM_FASTHASH " }, { CKM_AES_KEY_GEN , "CKM_AES_KEY_GEN " }, { CKM_AES_ECB , "CKM_AES_ECB " }, { CKM_AES_CBC , "CKM_AES_CBC " }, { CKM_AES_MAC , "CKM_AES_MAC " }, { CKM_AES_MAC_GENERAL , "CKM_AES_MAC_GENERAL " }, { CKM_AES_CBC_PAD , "CKM_AES_CBC_PAD " }, { CKM_DSA_PARAMETER_GEN , "CKM_DSA_PARAMETER_GEN " }, { CKM_DH_PKCS_PARAMETER_GEN , "CKM_DH_PKCS_PARAMETER_GEN " }, { CKM_X9_42_DH_PARAMETER_GEN , "CKM_X9_42_DH_PARAMETER_GEN " }, { CKM_VENDOR_DEFINED , "CKM_VENDOR_DEFINED " } }; 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" } }; #define SZ_SPECS sizeof(enum_specs) enum_spec ck_types[] = { { OBJ_T, ck_cls_s, sizeof(ck_cls_s) / SZ_SPECS, "CK_OBJECT_CLASS" }, { KEY_T, ck_key_s, sizeof(ck_key_s) / SZ_SPECS, "CK_KEY_TYPE" }, { CRT_T, ck_crt_s, sizeof(ck_crt_s) / SZ_SPECS, "CK_CERTIFICATE_TYPE" }, { MEC_T, ck_mec_s, sizeof(ck_mec_s) / SZ_SPECS, "CK_MECHANISM_TYPE" }, { USR_T, ck_usr_s, sizeof(ck_usr_s) / SZ_SPECS, "CK_USER_TYPE" }, { STA_T, ck_sta_s, sizeof(ck_sta_s) / SZ_SPECS, "CK_STATE" }, { RV_T, ck_err_s, sizeof(ck_err_s) / SZ_SPECS, "CK_RV" }, }; 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" } }; 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_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_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_ECDSA_PARAMS , "CKA_ECDSA_PARAMS ", print_generic, NULL }, { CKA_EC_PARAMS , "CKA_EC_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_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_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 }, }; 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; 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)\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 & ~known_flags) ? "Unknown " : ""); } void print_attribute_list(FILE *f, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { CK_ULONG j, k; int found; 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; 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: '%32.32s'\n", 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 ); } opensc-0.13.0/src/pkcs11/framework-pkcs15.c0000644000015201777760000042166012057406034015174 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include "libopensc/log.h" #include "libopensc/asn1.h" #include "libopensc/cardctl.h" #include #include #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 64 struct pkcs15_fw_data { struct sc_pkcs15_card * p15_card; struct pkcs15_any_object * objects[MAX_OBJECTS]; unsigned int num_objects; unsigned int locked; unsigned char user_puk[64]; unsigned 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; }; #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_skey_object { struct pkcs15_any_object base; struct sc_pkcs15_skey_info *info; struct sc_pkcs15_skey *valueXXXX; }; #define skey_flags base.base.flags #define skey_p15obj base.p15_object #define is_skey(obj) ((__p15_type(obj) & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_SKEY_OBJECT) extern struct sc_pkcs11_object_ops pkcs15_cert_ops; extern struct sc_pkcs11_object_ops pkcs15_prkey_ops; extern struct sc_pkcs11_object_ops pkcs15_pubkey_ops; extern struct sc_pkcs11_object_ops pkcs15_dobj_ops; extern struct sc_pkcs11_object_ops pkcs15_skey_ops; #define GOST_PARAMS_OID_SIZE 9 static const struct { const CK_BYTE oid[GOST_PARAMS_OID_SIZE]; unsigned char param; } gostr3410_param_oid [] = { { { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01 }, SC_PKCS15_PARAMSET_GOSTR3410_A }, { { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x02 }, SC_PKCS15_PARAMSET_GOSTR3410_B }, { { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x03 }, SC_PKCS15_PARAMSET_GOSTR3410_C } }; 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 asn1_sequence_wrapper(const u8 *, size_t, CK_ATTRIBUTE_PTR); 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 *); static int reselect_app_df(sc_pkcs15_card_t *p15card); static CK_RV set_gost_params(struct sc_pkcs15init_keyarg_gost_params *, struct sc_pkcs15init_keyarg_gost_params *, 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); /* Returns WF data corresponding to the given application or, * if application info is not supplied, returns first available WF data. */ static struct pkcs15_fw_data * get_fw_data(struct sc_pkcs11_card *p11card, struct sc_app_info *app_info, int *out_idx) { struct pkcs15_fw_data *out = NULL; int idx; for (idx=0; idx < SC_PKCS11_FRAMEWORK_DATA_MAX_NUM; idx++) { 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; if (app_info) { file_app = fw_data->p15_card->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 : ""); 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); } ck_rv = register_mechanisms(p11card); if (ck_rv != CKR_OK) { sc_log(context, "cannot register mechanisms; CKR 0x%X", 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; for (idx=0; 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; strcpy_bp(pToken->manufacturerID, p15card->tokeninfo->manufacturer_id, 32); conf_block = sc_get_conf_block(p15card->card->ctx, "framework", "pkcs15", 1); if (conf_block && p15card->file_app) { scconf_block **blocks = NULL; 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 (p15card->flags & SC_PKCS15_CARD_FLAG_EMULATED) strcpy_bp(pToken->model, "PKCS#15 emulated", sizeof(pToken->model)); else strcpy_bp(pToken->model, "PKCS#15", sizeof(pToken->model)); /* Take the last 16 chars of the serial number (if the are more than 16). * _Assuming_ that the serial number is a Big Endian counter, this * will assure that the serial within each type of card will be * unique in pkcs11 (at least for the first 8^16 cards :-) */ if (p15card->tokeninfo->serial_number != NULL) { int sn_start = strlen(p15card->tokeninfo->serial_number) - 16; if (sn_start < 0) sn_start = 0; 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; int 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 0; } 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 sc_pkcs15_object *auth; struct sc_pkcs15_auth_info *pin_info; struct sc_pin_cmd_data data; int r; 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) 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); if (auth) { pin_info = (struct sc_pkcs15_auth_info*) auth->data; if (pin_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) { rv = CKR_FUNCTION_REJECTED; goto out; } /* Try to update PIN info from card */ memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_GET_INFO; data.pin_type = SC_AC_CHV; data.pin_reference = pin_info->attrs.pin.reference; r = sc_pin_cmd(slot->card->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; } 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: 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 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_cert_object *object = NULL; struct pkcs15_pubkey_object *obj2 = 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, &p15_cert); if (rv < 0) return rv; } /* Certificate object */ rv = __pkcs15_create_object(fw_data, (struct pkcs15_any_object **) &object, cert, &pkcs15_cert_ops, sizeof(struct pkcs15_cert_object)); if (rv < 0) return rv; object->cert_info = p15_info; object->cert_data = p15_cert; /* Corresponding public key */ rv = public_key_created(fw_data, &p15_info->id, (struct pkcs15_any_object **) &obj2); if (rv != SC_SUCCESS) rv = __pkcs15_create_object(fw_data, (struct pkcs15_any_object **) &obj2, NULL, &pkcs15_pubkey_ops, sizeof(struct pkcs15_pubkey_object)); 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; if (cert_object != NULL) *cert_object = (struct pkcs15_any_object *) 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_pubkey_object *object; struct sc_pkcs15_pubkey *p15_key; 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? */ 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)) { p15_key = (struct sc_pkcs15_pubkey *) pubkey->emulated; sc_log(context, "Using emulated pubkey %p", p15_key); } else { 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, (struct pkcs15_any_object **) &object, pubkey, &pkcs15_pubkey_ops, sizeof(struct pkcs15_pubkey_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; } if (pubkey_object != NULL) *pubkey_object = (struct pkcs15_any_object *) 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_prkey_object *object; int rv; rv = __pkcs15_create_object(fw_data, (struct pkcs15_any_object **) &object, prkey, &pkcs15_prkey_ops, sizeof(struct pkcs15_prkey_object)); if (rv >= 0) object->prv_info = (struct sc_pkcs15_prkey_info *) prkey->data; if (prkey_object != NULL) *prkey_object = (struct pkcs15_any_object *) object; return 0; } 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_data_object *dobj = NULL; int rv; rv = __pkcs15_create_object(fw_data, (struct pkcs15_any_object **) &dobj, object, &pkcs15_dobj_ops, sizeof(struct pkcs15_data_object)); if (rv >= 0) { dobj->info = (struct sc_pkcs15_data_info *) object->data; dobj->value = NULL; } if (data_object != NULL) *data_object = (struct pkcs15_any_object *) dobj; return 0; } 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_skey_object *skey = NULL; int rv; rv = __pkcs15_create_object(fw_data, (struct pkcs15_any_object **) &skey, object, &pkcs15_skey_ops, sizeof(struct pkcs15_skey_object)); if (rv >= 0) skey->info = (struct sc_pkcs15_skey_info *) object->data; if (skey_object != NULL) *skey_object = (struct pkcs15_any_object *) skey; return 0; } 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 (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; if (!cert) return SC_ERROR_OBJECT_NOT_FOUND; if (cert->cert_data) return 0; rv = sc_pkcs15_read_certificate(fw_data->p15_card, cert->cert_info, &cert->cert_data); 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); /* now that we have the cert and pub key, lets see if we can bind anything else */ pkcs15_bind_related_objects(fw_data); return 0; } 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; 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 = (CK_OBJECT_HANDLE)obj; /* cast pointer to long */ list_append(&slot->objects, obj); sc_log(context, "Slot:%X Setting object handle of 0x%lx to 0x%lx", slot->id, obj->base.handle, (CK_OBJECT_HANDLE)obj); obj->base.handle = (CK_OBJECT_HANDLE)obj; /* cast pointer to long */ 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: pkcs15_add_object(slot, (struct pkcs15_any_object *) obj->related_pubkey, NULL); card_fw_data = (struct pkcs15_fw_data *) slot->card->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 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; char tmp[64]; 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) 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; slot->fw_data = fw_data = calloc(1, sizeof(*fw_data)); 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 { if (auth->label[0]) snprintf(tmp, sizeof(tmp), "%s (%s)", p15card->tokeninfo->label, auth->label); else snprintf(tmp, sizeof(tmp), "%s", p15card->tokeninfo->label); slot->token_info.flags |= CKF_LOGIN_REQUIRED; } } else { snprintf(tmp, sizeof(tmp), "%s", p15card->tokeninfo->label); } strcpy_bp(slot->token_info.label, tmp, 32); 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; } #if 0 FIXME: configurable option if (p15card->flags & SC_PKCS15_CARD_FLAG_EMULATED) slot->token_info.flags |= CKF_WRITE_PROTECTED; #endif if (app_info) slot->app_info = app_info; sc_log(context, "Initialized token '%s' in slot 0x%lx", tmp, slot->id); } 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; int 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 */ pkcs15_init_slot(fw_data->p15_card, slot, auth, app_info); *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_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; /* 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; } struct sc_pkcs15_object * _get_auth_object_by_name(struct sc_pkcs15_card *p15card, char *name) { struct sc_pkcs15_object *out = NULL; int rv = SC_ERROR_OBJECT_NOT_FOUND; 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; } 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; unsigned i; sc_log(context, "Add objects related to PIN('%s',ID:%s)", 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, 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, 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, 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, pin_obj->label); pkcs15_add_object(slot, obj, NULL); } else { sc_log(context, "Slot:%p Object %d skeeped", 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--; } } } static void _add_public_objects(struct sc_pkcs11_slot *slot, struct pkcs15_fw_data *fw_data, struct pkcs15_fw_data *move_to_fw) { 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 and the ones with 'auth_id' defined */ if (obj->p15_object->flags & SC_PKCS15_CO_FLAG_PRIVATE) continue; if (obj->p15_object->auth_id.len) continue; sc_log(context, "Add public object(%p,%s,%x)", obj, obj->p15_object->label, obj->p15_object->type); pkcs15_add_object(slot, obj, NULL); if (move_to_fw && move_to_fw != fw_data && move_to_fw->num_objects < MAX_OBJECTS) { int tail = fw_data->num_objects - i - 1; sc_log(context, "Move public object(%p) from %p to %p", obj, fw_data, move_to_fw); 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--; } } } static CK_RV pkcs15_create_tokens(struct sc_pkcs11_card *p11card, struct sc_app_info *app_info, struct sc_pkcs11_slot **first_slot) { struct pkcs15_fw_data *fw_data = NULL, *ffda = NULL; struct sc_pkcs15_object *auth_user_pin = NULL, *auth_sign_pin = NULL, *fauo = NULL; struct sc_pkcs11_slot *slot = NULL; int i, rv, idx; 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, "CreateSlotsFlags: 0x%X", sc_pkcs11_conf.create_slots_flags); /* Find out framework data corresponding to the given application */ fw_data = get_fw_data(p11card, app_info, &idx); if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_PKCS15_APP_NOT_FOUND, NULL); sc_log(context, "Use FW data with index %i; fw_data->p15_card %p", idx, fw_data->p15_card); /* Try to identify UserPIN and SignPIN by their symbolic name */ auth_user_pin = _get_auth_object_by_name(fw_data->p15_card, "UserPIN"); if (sc_pkcs11_conf.create_slots_flags & SC_PKCS11_SLOT_FOR_PIN_SIGN) auth_sign_pin = _get_auth_object_by_name(fw_data->p15_card, "SignPIN"); sc_log(context, "Flags:0x%X; Auth User/Sign PINs %p/%p", sc_pkcs11_conf.create_slots_flags, auth_user_pin, auth_sign_pin); /* Add PKCS#15 objects of the known types to the framework data */ rv = _pkcs15_create_typed_objects(fw_data); if (rv < 0) return sc_to_cryptoki_error(rv, NULL); 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 || sc_pkcs11_conf.create_slots_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 */ rv = sc_pkcs15_get_objects(fw_data->p15_card, SC_PKCS15_TYPE_AUTH_PIN, auths, SC_PKCS15_MAX_PINS); if (rv < 0) return sc_to_cryptoki_error(rv, NULL); auth_count = rv; 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; 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'", 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 there is no need to create slot for each PIN or for each application, * the objets from the non-first application and protected by the same (global) PIN * are added to the framework data of the first slot .*/ if (!(sc_pkcs11_conf.create_slots_flags & SC_PKCS11_SLOT_FOR_APPLICATION)) { if (first_slot && *first_slot) { /* Initialize variables related to the first created slot */ fauo = slot_data_auth((*first_slot)->fw_data); ffda = (struct pkcs15_fw_data *) p11card->fws_data[(*first_slot)->fw_data_idx]; sc_log(context, "%i objects in first slot", ffda->num_objects); } } sc_log(context, "User/Sign PINs %p/%p", auth_user_pin, auth_sign_pin); if (fauo && auth_user_pin && !memcmp(fauo->data, auth_user_pin->data, sizeof(struct sc_pkcs15_auth_info))) { /* Add objects from the non-first application to the FW data of the first slot */ sc_log(context, "Add objects to existing slot created for PIN '%s'", fauo->label); _add_pin_related_objects(*first_slot, fauo, fw_data, ffda); slot = *first_slot; } else if (auth_user_pin) { /* For the UserPIN of the first slot create slot */ sc_log(context, "Create slot for User PIN '%s'", 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); } /* Create slot for SignPIN and populate it's FW data with the objects protected by SignPIN*/ if (auth_sign_pin && auth_user_pin) { struct sc_pkcs11_slot *sign_slot = NULL; sc_log(context, "Create slot for Sign PIN '%s'", 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 (first_slot && *first_slot==NULL) *first_slot = slot; if (slot) _add_public_objects(slot, fw_data, ffda); if (ffda) sc_log(context, "Finaly there are %i objects in first slot", ffda->num_objects); sc_log(context, "All tokens created"); return CKR_OK; } static CK_RV pkcs15_release_token(struct sc_pkcs11_card *p11card, void *fw_token) { #if 0 unlock_card((struct pkcs15_fw_data *) p11card->fws_data[0]); free(fw_token); return CKR_OK; #else sc_log(context, "pkcs15_release_token() not implemented"); return CKR_FUNCTION_REJECTED; #endif } static CK_RV pkcs15_login(struct sc_pkcs11_slot *slot, CK_USER_TYPE userType, CK_CHAR_PTR pPin, CK_ULONG ulPinLen) { struct sc_pkcs11_card *p11card = slot->card; 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; 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; 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); /* 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 && ulPinLen < sizeof(fw_data->user_puk)) { memcpy(fw_data->user_puk, pPin, ulPinLen); fw_data->user_puk_len = 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); #if 0 /* TODO: Look for pkcs15 auth object with 'unblockingPin' flag activated. * If exists, do verification of PIN (in fact PUK). */ if (sc_pkcs11_conf.pin_unblock_style == SC_PKCS11_PIN_UNBLOCK_SCONTEXT_SETPIN) { if (ulPinLen && ulPinLen < sizeof(fw_data->user_puk)) { memcpy(fw_data->user_puk, pPin, ulPinLen); fw_data->user_puk_len = ulPinLen; } } #endif 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->card->reader->capabilities & SC_READER_CAP_PIN_PAD) { /* pPin should be NULL in case of a pin pad reader, but * some apps (e.g. older Netscapes) don't know about it. * So we don't require that pPin == NULL, but set it to * NULL ourselves. This way, you can supply an empty (if * possible) or fake PIN if an application asks a PIN). */ /* But we want to be able to specify a PIN on the command * line (e.g. for the test scripts). So we don't do anything * here - this gives the user the choice of entering * an empty pin (which makes us use the pin pad) or * a valid pin (which is processed normally). --okir */ if (ulPinLen == 0) pPin = NULL; } else { /* * If PIN is out of range, * it cannot be correct. */ if (ulPinLen < pin_info->attrs.pin.min_length || ulPinLen > pin_info->attrs.pin.max_length) return CKR_PIN_INCORRECT; } /* 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"); } 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; } static CK_RV pkcs15_logout(struct sc_pkcs11_slot *slot) { struct sc_pkcs11_card *p11card = slot->card; struct pkcs15_fw_data *fw_data = NULL; CK_RV ret = CKR_OK; int rc; 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"); memset(fw_data->user_puk, 0, sizeof(fw_data->user_puk)); fw_data->user_puk_len = 0; sc_pkcs15_pincache_clear(fw_data->p15_card); 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"); } /* 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->card; 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; pin_obj = slot_data_auth(slot->fw_data); if (!pin_obj) return CKR_USER_PIN_NOT_INITIALIZED; auth_info = slot_data_auth_info(slot->fw_data); if (!auth_info) return CKR_USER_PIN_NOT_INITIALIZED; 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"); sc_log(context, "Change '%s' (ref:%i,type:%i)", pin_obj->label, auth_info->attrs.pin.reference, login_user); if (p11card->card->reader->capabilities & SC_READER_CAP_PIN_PAD) { /* pPin should be NULL in case of a pin pad reader, but * some apps (e.g. older Netscapes) don't know about it. * So we don't require that pPin == NULL, but set it to * NULL ourselves. This way, you can supply an empty (if * possible) or fake PIN if an application asks a PIN). */ pOldPin = pNewPin = NULL; ulOldLen = ulNewLen = 0; } else if (ulNewLen < auth_info->attrs.pin.min_length || ulNewLen > auth_info->attrs.pin.max_length) { return CKR_PIN_LEN_RANGE; } if (login_user < 0) { if (sc_pkcs11_conf.pin_unblock_style != SC_PKCS11_PIN_UNBLOCK_UNLOGGED_SETPIN) { sc_log(context, "PIN unlock is not allowed in unlogged session"); return CKR_FUNCTION_NOT_SUPPORTED; } 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 == CKU_USER) { rc = sc_pkcs15_change_pin(fw_data->p15_card, pin_obj, pOldPin, ulOldLen, pNewPin, ulNewLen); } else if (login_user == CKU_SO) { struct sc_pkcs15_object *auths[SC_PKCS15_MAX_PINS]; int i, auth_count; 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, "C_SetPIN"); auth_count = rc; for (i = 0; i < auth_count; i++) { auth_info = (struct sc_pkcs15_auth_info*) auths[i]->data; if ((auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)) break; } if (i == auth_count) { sc_log(context, "Change SoPIN non supported"); return CKR_FUNCTION_NOT_SUPPORTED; } rc = sc_pkcs15_change_pin(fw_data->p15_card, auths[i], 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_card *p11card, void *ptr, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, CK_UTF8CHAR_PTR pLabel) { struct sc_cardctl_pkcs11_init_token args; int rv; memset(&args, 0, sizeof(args)); args.so_pin = pPin; args.so_pin_len = ulPinLen; args.label = (const char *) pLabel; rv = sc_card_ctl(p11card->card, SC_CARDCTL_PKCS11_INIT_TOKEN, &args); if (rv == SC_ERROR_NOT_SUPPORTED) return CKR_FUNCTION_NOT_SUPPORTED; if (rv < 0) return sc_to_cryptoki_error(rv, "C_InitToken"); rv = card_removed(p11card->reader); if (rv != SC_SUCCESS) return rv; rv = card_detect_all(); if (rv != SC_SUCCESS) 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->card; 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; 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:%d; 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) { /* 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->card; 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_ec *ec = NULL; struct sc_pkcs15_prkey_gostr3410 *gost = NULL; int rc, rv; char label[SC_PKCS15_MAX_LABEL_SIZE]; memset(&args, 0, sizeof(args)); 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"); /* 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_gost_params(&args.params.gost, NULL, pTemplate, ulCount, NULL, 0); args.key.algorithm = SC_ALGORITHM_GOSTR3410; gost = &args.key.u.gostr3410; break; case CKK_EC: args.key.algorithm = SC_ALGORITHM_EC; ec = &args.key.u.ec; /* TODO: -DEE Do not have PKCS15 card with EC to test this */ /* 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) 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; 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; 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 (rv) { rv = sc_to_cryptoki_error(rc, "C_CreateObject"); goto out; } } rc = sc_pkcs15init_store_private_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_prkey_object(fw_data, key_obj, &key_any_obj); pkcs15_add_object(slot, key_any_obj, phObject); rv = CKR_OK; out: return rv; } /* TODO Only Session secret key objects are supported for now * Sesison objects have CKA_TOKEN=false * This is used by the C_DeriveKey with ECDH to hold the * key, and the calling application can then retrieve tha attributes as needed. * TODO If a card can support secret key objects on the card, this * code will need to be expanded. */ 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->card; 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_skey_info *skey_info; CK_KEY_TYPE key_type; CK_BBOOL _token = FALSE; int rv; char label[SC_PKCS15_MAX_LABEL_SIZE]; memset(&args, 0, sizeof(args)); 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 0 /* 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; #endif /* 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 */ attr_find(pTemplate, ulCount, CKA_TOKEN, &_token, NULL); switch (key_type) { /* Only support GENERIC_SECRET for now */ case CKK_GENERIC_SECRET: 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) { args.data_value.value = calloc(1,attr->ulValueLen); if (!args.data_value.value) return CKR_HOST_MEMORY; memcpy(args.data_value.value, attr->pValue, attr->ulValueLen); args.data_value.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; 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) { /* 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; } 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 = 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.data_value.value; skey_info->data.len = args.data_value.len; skey_info->value_len = args.value_len; /* callers prefered length */ } else { #if 1 rv = CKR_FUNCTION_NOT_SUPPORTED; goto out; #else /* TODO add support for secret key on the card with something like this: */ rc = sc_pkcs15init_store_secret_key(fw_data->p15_card, profile, &args, &key_obj); if (rc < 0) { rv = sc_to_cryptoki_error(rc, "C_CreateObject"); goto out; } #endif } /* 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: 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->card; 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; int rc, rv; char label[SC_PKCS15_MAX_LABEL_SIZE]; memset(&args, 0, sizeof(args)); 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"); /* 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: /* TODO: -DEE Do not have real pkcs15 card with EC */ /* fall through */ default: return CKR_ATTRIBUTE_VALUE_INVALID; } rv = CKR_OK; 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; 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 (!rsa->modulus.len || !rsa->exponent.len) return CKR_TEMPLATE_INCOMPLETE; rc = sc_pkcs15init_store_public_key(fw_data->p15_card, profile, &args, &key_obj); 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->card; 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, rv; char label[SC_PKCS15_MAX_LABEL_SIZE]; memset(&args, 0, sizeof(args)); 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"); /* 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; rv = CKR_OK; 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: rv = 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->card; 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, rv; char label[SC_PKCS15_MAX_LABEL_SIZE]; memset(&args, 0, sizeof(args)); sc_init_oid(&args.app_oid); 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"); rv = CKR_OK; 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: rv = 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; } } if (args.der_encoded.len == 0) { rv = CKR_TEMPLATE_INCOMPLETE; goto out; } rc = sc_pkcs15init_store_data_object(fw_data->p15_card, profile, &args, &data_obj); if (rc < 0) { rv = sc_to_cryptoki_error(rc, "C_CreateObject"); 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->card; struct pkcs15_fw_data *fw_data = NULL; struct sc_profile *profile = NULL; CK_OBJECT_CLASS _class; CK_BBOOL _token = FALSE; int rv, rc; 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"); 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 compatability, will default to TRUE */ /* Dont need profile id creating session only objects */ if (_token == TRUE) { 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 (_token == TRUE) { 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%0x", 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%0x", typ); return CKR_ATTRIBUTE_TYPE_INVALID; } } return CKR_OK; } static CK_RV set_gost_params(struct sc_pkcs15init_keyarg_gost_params *first_params, struct sc_pkcs15init_keyarg_gost_params *second_params, CK_ATTRIBUTE_PTR pPubTpl, CK_ULONG ulPubCnt, CK_ATTRIBUTE_PTR pPrivTpl, CK_ULONG ulPrivCnt) { CK_BYTE gost_params_oid[GOST_PARAMS_OID_SIZE]; size_t len, i; CK_RV rv; len = GOST_PARAMS_OID_SIZE; if (pPrivTpl && ulPrivCnt) rv = attr_find2(pPubTpl, ulPubCnt, pPrivTpl, ulPrivCnt, CKA_GOSTR3410_PARAMS, &gost_params_oid, &len); else rv = attr_find(pPubTpl, ulPubCnt, CKA_GOSTR3410_PARAMS, &gost_params_oid, &len); if (rv == CKR_OK) { size_t nn = sizeof(gostr3410_param_oid)/sizeof(gostr3410_param_oid[0]); if (len != GOST_PARAMS_OID_SIZE) return CKR_ATTRIBUTE_VALUE_INVALID; for (i = 0; i < nn; ++i) { if (!memcmp(gost_params_oid, gostr3410_param_oid[i].oid, len)) { if (first_params) first_params->gostr3410 = gostr3410_param_oid[i].param; if (second_params) second_params->gostr3410 = gostr3410_param_oid[i].param; break; } } if (i == nn) return CKR_ATTRIBUTE_VALUE_INVALID; } 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->card; 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 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, rv = CKR_OK; sc_log(context, "Keypair generation, mech = 0x%0x", 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) return CKR_MECHANISM_INVALID; 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"); 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_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; set_gost_params(&keygen_args.prkey_args.params.gost, &pub_args.params.gost, pPubTpl, ulPubCnt, pPrivTpl, ulPrivCnt); } 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; } else if (keytype == CKK_EC) { struct sc_pkcs15_der *der = &keygen_args.prkey_args.params.ec.der; der->len = sizeof(struct sc_object_id); rv = attr_find_ptr(pPubTpl, ulPubCnt, CKA_EC_PARAMS, (void **)&der->value, &der->len); if (rv != CKR_OK) { sc_unlock(p11card->card); return sc_to_cryptoki_error(rc, "C_GenerateKeyPair"); } keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_EC; pub_args.key.algorithm = SC_ALGORITHM_EC; } else { /* CKA_KEY_TYPE is set, but keytype isn't correct */ rv = CKR_ATTRIBUTE_VALUE_INVALID; goto kpgen_done; } if (keytype == CKK_GOSTR3410) keybits = SC_PKCS15_GOSTR3410_KEYSIZE; else if (keytype == CKK_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 */ } 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; /* 3.a Try on-card key pair generation */ sc_pkcs15init_set_p15card(profile, fw_data->p15_card); sc_log(context, "Try on-card key pair generation"); rc = sc_pkcs15init_generate_key(fw_data->p15_card, profile, &keygen_args, keybits, &priv_key_obj); 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); ((struct pkcs15_prkey_object *) priv_any_obj)->prv_pubkey = (struct pkcs15_pubkey_object *)pub_any_obj; 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) { struct pkcs15_any_object *any_obj = (struct pkcs15_any_object*) object; struct sc_pkcs11_card *p11card = session->slot->card; struct pkcs15_fw_data *fw_data = NULL; int rv; 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"); /* TODO assuming this is a session only object. */ rv = sc_lock(p11card->card); if (rv < 0) return sc_to_cryptoki_error(rv, "C_DestroyObject"); /* Oppose to pkcs15_add_object */ --any_obj->refcount; /* correct refcont */ 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; } 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->card; struct pkcs15_fw_data *fw_data = NULL; struct sc_aid *aid = NULL; struct sc_profile *profile = NULL; int rv; 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"); 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; /* Delete reference to related certificate of the public key PKCS#11 object */ pubkey->pub_genfrom = NULL; if (ao_pubkey->p15_object == NULL) { /* 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_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 refcont */ 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->card; struct pkcs15_fw_data *fw_data = NULL; int rc; 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"); 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, pkcs15_initialize, #ifdef USE_PKCS15_INIT pkcs15_init_pin, pkcs15_create_object, pkcs15_gen_keypair, #else 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->card; 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; 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"); 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, 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; 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 = session->slot->card; struct pkcs15_cert_object *cert = (struct pkcs15_cert_object*) object; struct pkcs15_fw_data *fw_data = NULL; size_t len; 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"); 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: len = strlen(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: if (cert->cert_info->authority && sc_pkcs11_conf.zero_ckaid_for_ca_certs) { check_attribute_buffer(attr, 1); *(unsigned char*)attr->pValue = 0; } else { 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; } return asn1_sequence_wrapper(cert->cert_data->subject, cert->cert_data->subject_len, attr); case CKA_ISSUER: if (check_cert_data_read(fw_data, cert) != 0) { attr->ulValueLen = 0; return CKR_OK; } return asn1_sequence_wrapper(cert->cert_data->issuer, cert->cert_data->issuer_len, attr); 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 int 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->card; struct pkcs15_fw_data *fw_data = NULL; const unsigned char *data = NULL, *_data = NULL; size_t len, _len; 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"); 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)) 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)) return 1; break; default: return sc_pkcs11_any_cmp_attribute(session, object, attr); } 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, /* derive */ NULL /* can_do */ }; /* * PKCS#15 Private Key Object */ static void pkcs15_prkey_release(void *object) { __pkcs15_release_object((struct pkcs15_any_object *) object); } 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 = session->slot->card; struct pkcs15_fw_data *fw_data = NULL; struct sc_pkcs15_pubkey *key = NULL; unsigned int usage; size_t len; 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"); /* 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_ECDSA_PARAMS)) { /* First see if we have a associated public key */ if (prkey->prv_pubkey && prkey->prv_pubkey->pub_data) { key = prkey->prv_pubkey->pub_data; sc_log(context, "use friend public key data %p", key); } 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: case CKA_LOCAL: case CKA_SENSITIVE: case CKA_ALWAYS_SENSITIVE: case CKA_NEVER_EXTRACTABLE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = TRUE; break; case CKA_ALWAYS_AUTHENTICATE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = prkey->prv_p15obj->user_consent; 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: case CKA_EXTRACTABLE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = FALSE; break; case CKA_LABEL: len = strlen(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_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_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: 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_EC: if (key) *(CK_ULONG *) attr->pValue = key->u.ec.params.field_length; else *(CK_ULONG *) attr->pValue = 384; /* TODO -DEE needs work */ 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_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->card; struct pkcs15_fw_data *fw_data = NULL; int rv, flags = 0, prkey_has_path = 0; 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%x.",pMechanism->mechanism); 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"); /* 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; if (prkey->prv_info->path.len || prkey->prv_info->path.aid.len) prkey_has_path = 1; switch (pMechanism->mechanism) { case CKM_RSA_PKCS: flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE; break; case CKM_MD5_RSA_PKCS: flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_MD5; break; case CKM_SHA1_RSA_PKCS: flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_SHA1; break; case CKM_SHA256_RSA_PKCS: flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_SHA256; break; case CKM_SHA384_RSA_PKCS: flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_SHA384; break; case CKM_SHA512_RSA_PKCS: flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_SHA512; break; case CKM_RIPEMD160_RSA_PKCS: flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_RIPEMD160; break; case CKM_RSA_X_509: flags = SC_ALGORITHM_RSA_RAW; 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_ECDSA: flags = SC_ALGORITHM_ECDSA_HASH_NONE; break; case CKM_ECDSA_SHA1: flags = SC_ALGORITHM_ECDSA_HASH_SHA1; break; #if 0 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; #endif default: sc_log(context, "DEE - need EC for %d",pMechanism->mechanism); return CKR_MECHANISM_INVALID; } rv = sc_lock(p11card->card); if (rv < 0) return sc_to_cryptoki_error(rv, "C_Sign"); sc_log(context, "Selected flags %X. Now computing signature for %d bytes. %d bytes reserved.", flags, ulDataLen, *pulDataLen); rv = sc_pkcs15_compute_signature(fw_data->p15_card, prkey->prv_p15obj, flags, pData, ulDataLen, pSignature, *pulDataLen); if (rv < 0 && !sc_pkcs11_conf.lock_login && !prkey_has_path) { /* If private key PKCS#15 object do not have 'path' attribute, * and if PKCS#11 login session is not locked, * the compute signature could fail because of concurent access to the card * by other application that could change the current DF. * In this particular case try to 'reselect' application DF. */ if (reselect_app_df(fw_data->p15_card) == SC_SUCCESS) rv = sc_pkcs15_compute_signature(fw_data->p15_card, prkey->prv_p15obj, flags, pData, ulDataLen, pSignature, *pulDataLen); } sc_unlock(p11card->card); sc_log(context, "Sign complete. Result %d.", rv); if (rv > 0) { *pulDataLen = rv; return CKR_OK; } return sc_to_cryptoki_error(rv, "C_Sign"); } 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->card; struct pkcs15_fw_data *fw_data = NULL; struct pkcs15_prkey_object *prkey; unsigned char decrypted[256]; /* FIXME: Will not work for keys above 2048 bits */ int buff_too_small, rv, flags = 0, prkey_has_path = 0; sc_log(context, "Initiating decryption."); 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"); /* 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; if (prkey->prv_info->path.len || prkey->prv_info->path.aid.len) prkey_has_path = 1; /* Select the proper padding mechanism */ switch (pMechanism->mechanism) { case CKM_RSA_PKCS: flags |= SC_ALGORITHM_RSA_PAD_PKCS1; 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_Decrypt"); rv = sc_pkcs15_decipher(fw_data->p15_card, prkey->prv_p15obj, flags, pEncryptedData, ulEncryptedDataLen, decrypted, sizeof(decrypted)); if (rv < 0 && !sc_pkcs11_conf.lock_login && !prkey_has_path) if (reselect_app_df(fw_data->p15_card) == SC_SUCCESS) rv = sc_pkcs15_decipher(fw_data->p15_card, prkey->prv_p15obj, flags, pEncryptedData, ulEncryptedDataLen, decrypted, sizeof(decrypted)); sc_unlock(p11card->card); sc_log(context, "Decryption complete. Result %d.", rv); if (rv < 0) return sc_to_cryptoki_error(rv, "C_Decrypt"); buff_too_small = (*pulDataLen < (CK_ULONG)rv); *pulDataLen = rv; if (pData == NULL_PTR) return CKR_OK; if (buff_too_small) return CKR_BUFFER_TOO_SMALL; memcpy(pData, decrypted, *pulDataLen); return CKR_OK; } 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->card; struct pkcs15_fw_data *fw_data = NULL; struct pkcs15_prkey_object *prkey = (struct pkcs15_prkey_object *) obj; int need_unlock = 0, prkey_has_path = 0; int rv, flags = 0; CK_BYTE_PTR pSeedData = NULL; CK_ULONG ulSeedDataLen = 0; sc_log(context, "Initiating derivation"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[session->slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_DeriveKey"); sc_log(context, "derivation %p %p %p %p %d %p %d", session, obj, pMechanism, pParameters, ulParametersLen, pData, *pulDataLen); /* 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 (prkey->prv_info->path.len || prkey->prv_info->path.aid.len) prkey_has_path = 1; if (pData != NULL && *pulDataLen > 0) { /* TODO DEE only test for NULL? */ need_unlock = 1; rv = sc_lock(p11card->card); if (rv < 0) return sc_to_cryptoki_error(rv, "C_DeriveKey"); } /* 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: { CK_ECDH1_DERIVE_PARAMS * ecdh_params = (CK_ECDH1_DERIVE_PARAMS *) pParameters; ulSeedDataLen = ecdh_params->ulPublicDataLen; pSeedData = ecdh_params->pPublicData; } break; } rv = sc_pkcs15_derive(fw_data->p15_card, prkey->prv_p15obj, flags, pSeedData, ulSeedDataLen, pData, pulDataLen); if (rv < 0 && !sc_pkcs11_conf.lock_login && !prkey_has_path && need_unlock) if (reselect_app_df(fw_data->p15_card) == SC_SUCCESS) rv = sc_pkcs15_derive(fw_data->p15_card, prkey->prv_p15obj, flags, pSeedData, ulSeedDataLen, pData, pulDataLen); /* 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->card; 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; if (!prkey || !prkey->prv_info) return CKR_KEY_FUNCTION_NOT_PERMITTED; pkinfo = prkey->prv_info; /* Return in there are no usage algorithms specified for this key. */ if (!pkinfo->algo_refs[0]) return CKR_FUNCTION_NOT_SUPPORTED; fw_data = (struct pkcs15_fw_data *) p11card->fws_data[session->slot->fw_data_idx]; 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) return 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]) return CKR_MECHANISM_INVALID; 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, NULL, /* unwrap */ pkcs15_prkey_decrypt, pkcs15_prkey_derive, pkcs15_prkey_can_do }; /* * 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 = session->slot->card; struct pkcs15_pubkey_object *pubkey = (struct pkcs15_pubkey_object*) object; struct pkcs15_cert_object *cert = pubkey->pub_genfrom; struct pkcs15_fw_data *fw_data = NULL; size_t len; 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"); /* We may need to get these from cert */ switch (attr->type) { case CKA_MODULUS: case CKA_MODULUS_BITS: case CKA_VALUE: case CKA_PUBLIC_EXPONENT: case CKA_EC_PARAMS: case CKA_EC_POINT: if (pubkey->pub_data == NULL) /* FIXME: check the return value? */ check_cert_data_read(fw_data, cert); 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: case CKA_LOCAL: case CKA_SENSITIVE: case CKA_ALWAYS_SENSITIVE: case CKA_NEVER_EXTRACTABLE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(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: case CKA_EXTRACTABLE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = FALSE; break; case CKA_LABEL: if (pubkey->pub_p15obj) { len = strlen(pubkey->pub_p15obj->label); check_attribute_buffer(attr, len); memcpy(attr->pValue, pubkey->pub_p15obj->label, len); } else if (cert && cert->cert_p15obj) { len = strlen(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_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_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 (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); case CKA_VALUE: if (pubkey->pub_data) { /* TODO: -DEE Not all pubkeys have CKA_VALUE attribute. RSA and EC * for example don't. So why is this here? * Why checking for cert in this pkcs15_pubkey_get_attribute? */ check_attribute_buffer(attr, pubkey->pub_data->data.len); memcpy(attr->pValue, pubkey->pub_data->data.value, pubkey->pub_data->data.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); } 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, /* derive */ NULL /* can_do */ }; /* 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 int 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->card; struct pkcs15_fw_data *fw_data = NULL; struct sc_card *card = session->slot->card->card; int rv; if (!out_data) return SC_ERROR_INVALID_ARGUMENTS; 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"); rv = sc_lock(card); if (rv < 0) return sc_to_cryptoki_error(rv, "C_GetAttributeValue"); rv = sc_pkcs15_read_data_object(fw_data->p15_card, dobj->info, out_data); 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; sc_log(context, "data_value_to_attr(): data(%p,len:%i)", data, data->data_len); 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; 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 = strlen(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: rv = pkcs15_dobj_get_value(session, dobj, &data); if (rv == CKR_OK) rv = data_value_to_attr(attr, data); 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, /* derive */ NULL /* can_do */ }; /* PKCS#15 Secret Key Objects */ /* TODO Currently only session objects */ static void pkcs15_skey_release(void *object) { __pkcs15_release_object((struct pkcs15_any_object *) object); } 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) { 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; } #if 0 static int pkcs15_skey_get_value(struct sc_pkcs11_session *session, struct pkcs15_skey_object *skey, struct sc_pkcs15_skey **out_data) { int rv; struct sc_pkcs15_skey * skey_data = NULL; struct pkcs15_fw_data *fw_data = (struct pkcs15_fw_data *) session->slot->card->fw_data; sc_card_t *card = session->slot->card->card; if (!out_data) return SC_ERROR_INVALID_ARGUMENTS; /*TODO could try and read extractable secret keys * but for now we only work with session objects * derived from other keys */ skey_data= malloc(sizeof(struct sc_pkcs15_skey)); if (skey_data == NULL) return SC_ERROR_OUT_OF_MEMORY; memset(skey_data, 0, sizeof(struct sc_pkcs15_skey)); if (skey->value && skey->value->data_len) { skey_data->data = malloc(skey_data->data_len); if (skey_data->data == NULL) { free(skey_data); return SC_ERROR_OUT_OF_MEMORY; } skey_data->data_len = skey->value->data_len; memcpy(skey_data->data, skey->value->data, skey->value->data_len); return 0; } *out_data = skey_data; return 0; } #endif 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; 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: /*TODO DEE change if on card skeys are supported */ check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = 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; break; case CKA_MODIFIABLE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = (skey->base.p15_object->flags & 0x02) != 0; /*TODO Why no definition of the flag */ break; case CKA_LABEL: len = strlen(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_VALUE_LEN: check_attribute_buffer(attr, sizeof(CK_ULONG)); *(CK_ULONG*)attr->pValue = skey->info->data.len; 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; } /* * 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 */ NULL, /* unwrap_key */ NULL, /* decrypt */ NULL, /* derive */ NULL /* can_do */ }; /* * 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_params * ecp; if (key == NULL) return CKR_ATTRIBUTE_TYPE_INVALID; if (key->alg_id == NULL) return CKR_ATTRIBUTE_TYPE_INVALID; ecp = (struct sc_ec_params *) key->alg_id->params; switch (key->algorithm) { case SC_ALGORITHM_EC: check_attribute_buffer(attr, ecp->der_len); memcpy(attr->pValue, ecp->der, 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) { if (key == NULL) return CKR_ATTRIBUTE_TYPE_INVALID; switch (key->algorithm) { case SC_ALGORITHM_EC: check_attribute_buffer(attr, key->u.ec.ecpointQ.len); memcpy(attr->pValue, key->u.ec.ecpointQ.value, key->u.ec.ecpointQ.len); 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].param == ((int*)params)[0]) { check_attribute_buffer(attr, sizeof(gostr3410_param_oid[i].oid)); memcpy(attr->pValue, gostr3410_param_oid[i].oid, sizeof(gostr3410_param_oid[i].oid)); 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 asn1_sequence_wrapper(const u8 *data, size_t len, CK_ATTRIBUTE_PTR attr) { u8 *dest; unsigned int n; size_t len2; size_t lenb = 1; len2 = len; /* calculate the number of bytes needed for the length */ if (len > 127) { unsigned int i; for (i = 0; (len & (0xff << i)) != 0 && (0xff << i) != 0; i++) lenb++; } check_attribute_buffer(attr, 1 + lenb + len); dest = (u8 *) attr->pValue; *dest++ = 0x30; /* SEQUENCE tag */ if (len <= 127) { *dest++ = len; } else { for (n = 4; (len & 0xFF000000) == 0; n--) len <<= 8; *dest++ = 0x80 + n; while (n--) { *dest++ = len >> 24; len <<= 8; } } memcpy(dest, data, len2); attr->ulValueLen = (dest - (u8 *) attr->pValue) + len2; return CKR_OK; } static int register_gost_mechanisms(struct sc_pkcs11_card *p11card, int flags) { CK_MECHANISM_INFO mech_info; sc_pkcs11_mechanism_type_t *mt; int 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); if (!mt) return CKR_HOST_MEMORY; rc = sc_pkcs11_register_mechanism(p11card, 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); if (!mt) return CKR_HOST_MEMORY; rc = sc_pkcs11_register_mechanism(p11card, 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); if (!mt) return CKR_HOST_MEMORY; rc = sc_pkcs11_register_mechanism(p11card, mt); if (rc != CKR_OK) return rc; } return CKR_OK; } static int 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; CK_FLAGS ec_flags = 0; int 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_UNCOMPRESES; if (ext_flags & SC_ALGORITHM_EXT_EC_COMPRESS) ec_flags |= CKF_EC_COMPRESS; mech_info.flags = CKF_HW | CKF_SIGN; /* check for more */ mech_info.flags |= ec_flags; mech_info.ulMinKeySize = min_key_size; mech_info.ulMaxKeySize = max_key_size; mt = sc_pkcs11_new_fw_mechanism(CKM_ECDSA, &mech_info, CKK_EC, NULL); if (!mt) return CKR_HOST_MEMORY; rc = sc_pkcs11_register_mechanism(p11card, mt); if (rc != CKR_OK) return rc; #if ENABLE_OPENSSL mt = sc_pkcs11_new_fw_mechanism(CKM_ECDSA_SHA1, &mech_info, CKK_EC, NULL); if (!mt) return CKR_HOST_MEMORY; rc = sc_pkcs11_register_mechanism(p11card, mt); if (rc != CKR_OK) return rc; #endif /* ADD ECDH mechanisms */ /* The PIV uses curves where CKM_ECDH1_DERIVE and CKM_ECDH1_COFACTOR_DERIVE produce the same results */ mech_info.flags &= ~CKF_SIGN; mech_info.flags |= CKF_DERIVE; mt = sc_pkcs11_new_fw_mechanism(CKM_ECDH1_COFACTOR_DERIVE, &mech_info, CKK_EC, NULL); rc = sc_pkcs11_register_mechanism(p11card, mt); if (rc != CKR_OK) return rc; mt = sc_pkcs11_new_fw_mechanism(CKM_ECDH1_DERIVE, &mech_info, CKK_EC, NULL); rc = sc_pkcs11_register_mechanism(p11card, 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); if (!mt) return CKR_HOST_MEMORY; rc = sc_pkcs11_register_mechanism(p11card, 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; unsigned long ec_ext_flags; sc_pkcs11_mechanism_type_t *mt; unsigned int num; int rc, flags = 0; /* 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 mech_info.ulMinKeySize = ~0; mech_info.ulMaxKeySize = 0; ec_min_key_size = ~0; ec_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 * But we need to do this by type of key as * each has different min/max and different flags. * * TODO: -DEE This code assumed RSA, but the GOST * and EC code was forced in. There should be a * routine for each key type. */ 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; 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; flags |= alg_info->flags; ec_ext_flags |= alg_info->u._ec.ext_flags; break; case SC_ALGORITHM_GOSTR3410: flags |= alg_info->flags; break; } alg_info++; } if (flags & SC_ALGORITHM_ECDSA_RAW) rc = register_ec_mechanisms(p11card, flags, ec_ext_flags, ec_min_key_size, ec_max_key_size); if (flags & (SC_ALGORITHM_GOSTR3410_RAW | SC_ALGORITHM_GOSTR3410_HASH_NONE | SC_ALGORITHM_GOSTR3410_HASH_GOSTR3411)) { if (flags & SC_ALGORITHM_GOSTR3410_RAW) flags |= SC_ALGORITHM_GOSTR3410_HASH_NONE; rc = register_gost_mechanisms(p11card, flags); if (rc != CKR_OK) return rc; } /* Check if we support raw RSA */ if (flags & SC_ALGORITHM_RSA_RAW) { mt = sc_pkcs11_new_fw_mechanism(CKM_RSA_X_509, &mech_info, CKK_RSA, NULL); rc = sc_pkcs11_register_mechanism(p11card, mt); if (rc != CKR_OK) return rc; /* If the card supports RAW, it should by all means * have registered everything else, too. If it didn't * we help it a little * FIXME? This may force us to support these in software */ flags |= SC_ALGORITHM_RSA_PAD_PKCS1; #ifdef ENABLE_OPENSSL /* all our software hashes are in OpenSSL */ flags |= SC_ALGORITHM_RSA_HASHES; #endif } /* Check for PKCS1 */ if (flags & SC_ALGORITHM_RSA_PAD_PKCS1) { mt = sc_pkcs11_new_fw_mechanism(CKM_RSA_PKCS, &mech_info, CKK_RSA, NULL); rc = sc_pkcs11_register_mechanism(p11card, mt); if (rc != CKR_OK) return rc; /* if the driver doesn't say what hashes it supports, * claim we will do all of them */ /* FIXME? This may force us to support these in software */ /* FIXME? and we only do hashes if OpenSSL is enabled */ if (!(flags & (SC_ALGORITHM_RSA_HASHES|SC_ALGORITHM_RSA_HASH_NONE))) flags |= SC_ALGORITHM_RSA_HASHES; #ifdef ENABLE_OPENSSL /* sc_pkcs11_register_sign_and_hash_mechanism expects software hash */ if (flags & SC_ALGORITHM_RSA_HASH_SHA1) { rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, CKM_SHA1_RSA_PKCS, CKM_SHA_1, mt); if (rc != CKR_OK) return rc; } if (flags & SC_ALGORITHM_RSA_HASH_SHA256) { rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, CKM_SHA256_RSA_PKCS, CKM_SHA256, mt); if (rc != CKR_OK) return rc; } if (flags & SC_ALGORITHM_RSA_HASH_MD5) { rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, CKM_MD5_RSA_PKCS, CKM_MD5, mt); if (rc != CKR_OK) return rc; } if (flags & SC_ALGORITHM_RSA_HASH_RIPEMD160) { rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, CKM_RIPEMD160_RSA_PKCS, CKM_RIPEMD160, mt); if (rc != CKR_OK) return rc; } #endif if (flags & SC_ALGORITHM_ONBOARD_KEY_GEN) { mech_info.flags = CKF_GENERATE_KEY_PAIR; mt = sc_pkcs11_new_fw_mechanism(CKM_RSA_PKCS_KEY_PAIR_GEN, &mech_info, CKK_RSA, NULL); if (!mt) return CKR_HOST_MEMORY; rc = sc_pkcs11_register_mechanism(p11card, mt); 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; } static int reselect_app_df(sc_pkcs15_card_t *p15card) { int r = SC_SUCCESS; if (p15card->file_app != NULL) { /* if the application df (of the pkcs15 application) is specified select it */ sc_path_t *tpath = &p15card->file_app->path; sc_log(p15card->card->ctx, "reselect application df"); r = sc_select_file(p15card->card, tpath, NULL); } return r; } opensc-0.13.0/src/pkcs11/pkcs11-global.c0000644000015201777760000004635512057406034014437 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #ifdef HAVE_SYS_TIME_H #include #endif #include "sc-pkcs11.h" 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; #if defined(HAVE_PTHREAD) && defined(PKCS11_THREAD_LOCKING) #include CK_RV mutex_create(void **mutex) { pthread_mutex_t *m = malloc(sizeof(*mutex)); 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 }; #elif defined(_WIN32) && defined (PKCS11_THREAD_LOCKING) CK_RV mutex_create(void **mutex) { CRITICAL_SECTION *m; m = malloc(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 }; #endif static CK_C_INITIALIZE_ARGS_PTR global_locking; static void * global_lock = NULL; #if (defined(HAVE_PTHREAD) || defined(_WIN32)) && defined(PKCS11_THREAD_LOCKING) #define 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; } CK_RV C_Initialize(CK_VOID_PTR pInitArgs) { CK_RV rv; #if !defined(_WIN32) pid_t current_pid = getpid(); #endif int rc; unsigned int i; sc_context_param_t ctx_opts; /* Handle fork() exception */ #if !defined(_WIN32) if (current_pid != initialized_pid) { C_Finalize(NULL_PTR); } initialized_pid = current_pid; in_finalize = 0; #endif if (context != NULL) { sc_log(context, "C_Initialize(): Cryptoki already initialized\n"); 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 = "opensc-pkcs11"; 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 */ list_init(&sessions); list_attributes_seeker(&sessions, session_list_seeker); /* List of slots */ list_init(&virtual_slots); list_attributes_seeker(&virtual_slots, slot_list_seeker); /* Create a slot for a future "PnP" stuff. */ if (sc_pkcs11_conf.plug_and_play) { create_slot(NULL); } /* Create slots for readers found on initialization */ for (i=0; ievents = 0; /* Initially there are no events */ } out: if (context != NULL) sc_log(context, "C_Initialize() = %s", lookup_enum ( RV_T, rv )); if (rv != CKR_OK) { if (context != NULL) { sc_release_context(context); context = NULL; } /* Release and destroy the mutex */ sc_pkcs11_free_lock(); } 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 (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); 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 C_GetInfo(CK_INFO_PTR pInfo) { 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 = 2; pInfo->cryptokiVersion.minor = 20; strcpy_bp(pInfo->manufacturerID, "OpenSC (www.opensc-project.org)", sizeof(pInfo->manufacturerID)); strcpy_bp(pInfo->libraryDescription, "Smart card PKCS#11 API", sizeof(pInfo->libraryDescription)); pInfo->libraryVersion.major = 0; pInfo->libraryVersion.minor = 0; /* FIXME: use 0.116 for 0.11.6 from autoconf */ sc_pkcs11_unlock(); return rv; } 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; if ((rv = sc_pkcs11_lock()) != CKR_OK) { return rv; } sc_log(context, "C_GetSlotList(token=%d, %s)", tokenPresent, (pSlotList==NULL_PTR && sc_pkcs11_conf.plug_and_play)? "plug-n-play":"refresh"); /* Slot list can only change in v2.20 */ if (pSlotList == NULL_PTR && sc_pkcs11_conf.plug_and_play) { /* Trick NSS into updating the slot list by changing the hotplug slot ID */ sc_pkcs11_slot_t *hotplug_slot = list_get_at(&virtual_slots, 0); hotplug_slot->id--; sc_ctx_detect_readers(context); } card_detect_all(); found = malloc(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) || (!tokenPresent && slot->reader != prev_reader) || (slot->slot_info.flags & CKF_TOKEN_PRESENT)) found[numMatches++] = slot->id; prev_reader = slot->reader; } if (pSlotList == NULL_PTR) { sc_log(context, "was only a size inquiry (%d)\n", numMatches); *pulCount = numMatches; rv = CKR_OK; goto out; } if (*pulCount < numMatches) { sc_log(context, "buffer was too small (needed %d)\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 %d slots\n", numMatches); out: if (found != NULL) { free (found); found = NULL; } 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; sc_timestamp_t now; 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); rv = slot_get_slot(slotID, &slot); 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); /* 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(0x%lx) = %s", slotID, lookup_enum ( RV_T, 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->card, 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->card, 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; CK_RV rv; unsigned int i; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = slot_get_token(slotID, &slot); if (rv != CKR_OK) goto out; /* Make sure there's no open session for this token */ for (i=0; islot == slot) { rv = CKR_SESSION_EXISTS; goto out; } } if (slot->card->framework->init_token == NULL) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto out; } rv = slot->card->framework->init_token(slot->card, slot->fw_data, pPin, ulPinLen, pLabel); if (rv == CKR_OK) { /* Now we should re-bind all tokens so they get the * corresponding function vector and flags */ } out: sc_pkcs11_unlock(); 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)); /* Not all pcsc-lite versions implement consistently used functions as they are */ /* FIXME: add proper checking into build to check correct pcsc-lite version for SCardStatusChange/SCardCancel */ if (!(flags & CKF_DONT_BLOCK)) return CKR_FUNCTION_NOT_SUPPORTED; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; mask = SC_EVENT_CARD_EVENTS; /* Detect and add new slots for added readers v2.20 */ if (sc_pkcs11_conf.plug_and_play) { mask |= SC_EVENT_READER_EVENTS; } 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); if (sc_pkcs11_conf.plug_and_play && events & SC_EVENT_READER_ATTACHED) { /* NSS/Firefox Triggers a C_GetSlotList(NULL) only if a slot ID is returned that it does not know yet Change the first hotplug slot id on every call to make this happen. */ sc_pkcs11_slot_t *hotplug_slot = list_get_at(&virtual_slots, 0); *pSlot= hotplug_slot->id -1; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; goto out; } /* Was C_Finalize called ? */ if (in_finalize == 1) return CKR_CRYPTOKI_NOT_INITIALIZED; 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(context, "C_WaitForSlotEvent() = %s, event in 0x%lx", lookup_enum (RV_T, rv), *pSlot); sc_pkcs11_unlock(); return rv; } /* * 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; /* 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 = args; } else if (!applock && oslock) { /* Shall be used in threaded environment, must use operating system locking */ global_locking = default_mutex_funcs; } else if (applock && !oslock) { /* Shall be used in threaded envirnoment, must use app provided locking */ global_locking = args; } 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, 11 }, /* Note: NSS/Firefox ignores this version number and uses C_GetInfo() */ C_Initialize, C_Finalize, C_GetInfo, C_GetFunctionList, C_GetSlotList, C_GetSlotInfo, C_GetTokenInfo, C_GetMechanismList, C_GetMechanismInfo, C_InitToken, C_InitPIN, C_SetPIN, C_OpenSession, C_CloseSession, C_CloseAllSessions, C_GetSessionInfo, C_GetOperationState, C_SetOperationState, C_Login, C_Logout, C_CreateObject, C_CopyObject, C_DestroyObject, C_GetObjectSize, C_GetAttributeValue, C_SetAttributeValue, C_FindObjectsInit, C_FindObjects, C_FindObjectsFinal, C_EncryptInit, C_Encrypt, C_EncryptUpdate, C_EncryptFinal, C_DecryptInit, C_Decrypt, C_DecryptUpdate, C_DecryptFinal, C_DigestInit, C_Digest, C_DigestUpdate, C_DigestKey, C_DigestFinal, C_SignInit, C_Sign, C_SignUpdate, C_SignFinal, C_SignRecoverInit, C_SignRecover, C_VerifyInit, C_Verify, C_VerifyUpdate, C_VerifyFinal, C_VerifyRecoverInit, C_VerifyRecover, C_DigestEncryptUpdate, C_DecryptDigestUpdate, C_SignEncryptUpdate, C_DecryptVerifyUpdate, C_GenerateKey, C_GenerateKeyPair, C_WrapKey, C_UnwrapKey, C_DeriveKey, C_SeedRandom, C_GenerateRandom, C_GetFunctionStatus, C_CancelFunction, C_WaitForSlotEvent }; opensc-0.13.0/src/pkcs11/sc-pkcs11.h0000644000015201777760000003534412057406034013605 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __sc_pkcs11_h__ #define __sc_pkcs11_h__ #include "config.h" #ifdef HAVE_MALLOC_H #include #endif #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 #if defined(_WIN32) || defined(USE_CYGWIN) #define PKCS11_DEFAULT_MODULE_NAME "opensc-pkcs11.dll" #else #define PKCS11_DEFAULT_MODULE_NAME "opensc-pkcs11.so" #endif #define SC_PKCS11_PIN_UNBLOCK_NOT_ALLOWED 0 #define SC_PKCS11_PIN_UNBLOCK_UNLOGGED_SETPIN 1 #define SC_PKCS11_PIN_UNBLOCK_SCONTEXT_SETPIN 2 #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_FOR_APPLICATION 4 #define SC_PKCS11_SLOT_CREATE_ALL 8 extern void *C_LoadModule(const char *name, CK_FUNCTION_LIST_PTR_PTR); extern CK_RV C_UnloadModule(void *module); #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 plug_and_play; unsigned int max_virtual_slots; unsigned int slots_per_card; unsigned char hide_empty_tokens; unsigned char lock_login; unsigned int pin_unblock_style; unsigned int create_puk_slot; unsigned int zero_ckaid_for_ca_certs; unsigned int create_slots_flags; }; /* * 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); int (*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, CK_ATTRIBUTE_PTR, CK_ULONG, void **); 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 (*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); /* 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 *, struct sc_pkcs11_slot **); 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 * (ie. certificates, private keys) */ CK_RV (*init_token)(struct sc_pkcs11_card *, 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; }; 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 *card; /* 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 assosiated to slot */ }; typedef struct sc_pkcs11_slot sc_pkcs11_slot_t; /* 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_DERIVE, SC_PKCS11_OPERATION_MAX }; /* This describes a PKCS11 mechanism */ struct sc_pkcs11_mechanism_type { CK_MECHANISM_TYPE mech; /* algorithm: md5, sha1, ... */ CK_MECHANISM_INFO mech_info; /* mechanism info */ CK_MECHANISM_TYPE key_type; /* for sign/decipher ops */ 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)(sc_pkcs11_operation_t *, CK_BYTE_PTR, CK_ULONG, 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); /* mechanism specific data */ const void * mech_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; 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); #define dump_template(level, info, pTemplate, ulCount) \ sc_pkcs11_print_attrs(level, __FILE__, __LINE__, __FUNCTION__, \ info, pTemplate, ulCount) /* Slot and card handling functions */ CK_RV card_removed(sc_reader_t *reader); CK_RV card_detect_all(void); CK_RV create_slot(sc_reader_t *reader); CK_RV initialize_reader(sc_reader_t *reader); CK_RV card_detect(sc_reader_t *reader); 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); /* 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 */ int 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_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 *); 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_MECHANISM_TYPE); CK_RV sc_pkcs11_sign_update(struct sc_pkcs11_session *, CK_BYTE_PTR, CK_ULONG); CK_RV sc_pkcs11_sign_final(struct sc_pkcs11_session *, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV sc_pkcs11_sign_size(struct sc_pkcs11_session *, CK_ULONG_PTR); #ifdef ENABLE_OPENSSL CK_RV sc_pkcs11_verif_init(struct sc_pkcs11_session *, CK_MECHANISM_PTR, struct sc_pkcs11_object *, CK_MECHANISM_TYPE); CK_RV sc_pkcs11_verif_update(struct sc_pkcs11_session *, CK_BYTE_PTR, CK_ULONG); CK_RV sc_pkcs11_verif_final(struct sc_pkcs11_session *, CK_BYTE_PTR, CK_ULONG); #endif CK_RV sc_pkcs11_decr_init(struct sc_pkcs11_session *, CK_MECHANISM_PTR, struct sc_pkcs11_object *, CK_MECHANISM_TYPE); CK_RV sc_pkcs11_decr(struct sc_pkcs11_session *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV sc_pkcs11_deri(struct sc_pkcs11_session *, CK_MECHANISM_PTR, struct sc_pkcs11_object *, CK_KEY_TYPE, CK_SESSION_HANDLE, CK_OBJECT_HANDLE, struct sc_pkcs11_object *); sc_pkcs11_mechanism_type_t *sc_pkcs11_find_mechanism(struct sc_pkcs11_card *, CK_MECHANISM_TYPE, unsigned int); sc_pkcs11_mechanism_type_t *sc_pkcs11_new_fw_mechanism(CK_MECHANISM_TYPE, CK_MECHANISM_INFO_PTR, CK_KEY_TYPE, void *); 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 unsigned char *pubkey, int pubkey_len, const unsigned char *pubkey_params, int pubkey_params_len, CK_MECHANISM_TYPE mech, sc_pkcs11_operation_t *md, unsigned char *inp, int inp_len, unsigned char *signat, int signat_len); #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.13.0/src/pkcs11/pkcs11-session.c0000644000015201777760000002377212057406034014660 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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 conflictions 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; } session->slot = slot; session->notify_callback = Notify; session->notify_data = pApplication; session->flags = flags; slot->nsessions++; session->handle = (CK_SESSION_HANDLE) session; /* cast a pointer to long */ list_append(&sessions, session); *phSession = session->handle; sc_log(context, "C_OpenSession handle: 0x%lx", session->handle); out: sc_log(context, "C_OpenSession() = %s", lookup_enum(RV_T, 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; slot->card->framework->logout(slot); } 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; 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 ((rv = sc_pkcs11_close_session(session->handle)) != CKR_OK) return rv; } return CKR_OK; } 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; } 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; 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; if (slot->login_user == CKU_SO) { pInfo->state = CKS_RW_SO_FUNCTIONS; } else if (slot->login_user == CKU_USER || (!(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: sc_log(context, "C_GetSessionInfo(0x%lx) = %s", hSession, lookup_enum(RV_T, 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, %d)", hSession, userType); slot = session->slot; if (!(slot->token_info.flags & CKF_USER_PIN_INITIALIZED)) { 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; goto out; } else rv = slot->card->framework->login(slot, userType, pPin, ulPinLen); } else { 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 = slot->card->framework->login(slot, userType, pPin, ulPinLen); if (rv == CKR_OK) slot->login_user = userType; } out:sc_pkcs11_unlock(); return rv; } 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; rv = slot->card->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; 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->card->framework->init_pin == NULL) { rv = CKR_FUNCTION_NOT_SUPPORTED; } else { rv = slot->card->framework->init_pin(slot, pPin, ulPinLen); } 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 = slot->card->framework->change_pin(slot, pOldPin, ulOldLen, pNewPin, ulNewLen); out: sc_pkcs11_unlock(); return rv; } opensc-0.13.0/src/pkcs11/Makefile.in0000644000015201777760000006105712057406056014000 00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program 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. @SET_MAKE@ # Required to build Windows resource file VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ DIST_COMMON = $(dist_noinst_SCRIPTS) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in $(top_srcdir)/win32/ltrc.inc @WIN32_TRUE@am__append_1 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_2 = $(top_builddir)/win32/versioninfo.rc subdir = src/pkcs11 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__installdirs = "$(DESTDIR)$(libdir)" LTLIBRARIES = $(lib_LTLIBRARIES) am__DEPENDENCIES_1 = am__DEPENDENCIES_2 = $(top_builddir)/src/libopensc/libopensc.la \ $(top_builddir)/src/common/libscdl.la \ $(top_builddir)/src/common/libcompat.la $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) opensc_pkcs11_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am__opensc_pkcs11_la_SOURCES_DIST = 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 \ opensc-pkcs11.exports pkcs11-display.c pkcs11-display.h \ sc-pkcs11.h pkcs11.h pkcs11-opensc.h \ $(top_builddir)/win32/versioninfo.rc am__objects_1 = pkcs11-global.lo pkcs11-session.lo pkcs11-object.lo \ misc.lo slot.lo mechanism.lo openssl.lo framework-pkcs15.lo \ framework-pkcs15init.lo debug.lo pkcs11-display.lo am__objects_2 = am__dirstamp = $(am__leading_dot)dirstamp @WIN32_TRUE@am__objects_3 = $(top_builddir)/win32/versioninfo.lo am_opensc_pkcs11_la_OBJECTS = $(am__objects_1) $(am__objects_2) \ $(am__objects_3) opensc_pkcs11_la_OBJECTS = $(am_opensc_pkcs11_la_OBJECTS) AM_V_lt = $(am__v_lt_$(V)) am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) am__v_lt_0 = --silent opensc_pkcs11_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(opensc_pkcs11_la_LDFLAGS) $(LDFLAGS) \ -o $@ pkcs11_spy_la_DEPENDENCIES = $(top_builddir)/src/common/libpkcs11.la \ $(top_builddir)/src/common/libscdl.la $(am__DEPENDENCIES_1) am__pkcs11_spy_la_SOURCES_DIST = pkcs11-spy.c pkcs11-display.c \ pkcs11-display.h pkcs11-spy.exports \ $(top_builddir)/win32/versioninfo.rc am_pkcs11_spy_la_OBJECTS = pkcs11-spy.lo pkcs11-display.lo \ $(am__objects_3) pkcs11_spy_la_OBJECTS = $(am_pkcs11_spy_la_OBJECTS) pkcs11_spy_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(pkcs11_spy_la_LDFLAGS) $(LDFLAGS) -o $@ SCRIPTS = $(dist_noinst_SCRIPTS) DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_$(V)) am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) am__v_CC_0 = @echo " CC " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_$(V)) am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) am__v_CCLD_0 = @echo " CCLD " $@; AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; SOURCES = $(opensc_pkcs11_la_SOURCES) $(pkcs11_spy_la_SOURCES) DIST_SOURCES = $(am__opensc_pkcs11_la_SOURCES_DIST) \ $(am__pkcs11_spy_la_SOURCES_DIST) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUG_FILE = @DEBUG_FILE@ DEFAULT_PCSC_PROVIDER = @DEFAULT_PCSC_PROVIDER@ DEFAULT_SM_MODULE = @DEFAULT_SM_MODULE@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GIT = @GIT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBRARY_BITNESS = @LIBRARY_BITNESS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCT_CFLAGS = @OPENCT_CFLAGS@ OPENCT_LIBS = @OPENCT_LIBS@ OPENSC_LT_AGE = @OPENSC_LT_AGE@ OPENSC_LT_CURRENT = @OPENSC_LT_CURRENT@ OPENSC_LT_OLDEST = @OPENSC_LT_OLDEST@ OPENSC_LT_REVISION = @OPENSC_LT_REVISION@ OPENSC_VERSION_FIX = @OPENSC_VERSION_FIX@ OPENSC_VERSION_MAJOR = @OPENSC_VERSION_MAJOR@ OPENSC_VERSION_MINOR = @OPENSC_VERSION_MINOR@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OPTIONAL_OPENCT_CFLAGS = @OPTIONAL_OPENCT_CFLAGS@ OPTIONAL_OPENCT_LIBS = @OPTIONAL_OPENCT_LIBS@ OPTIONAL_OPENSSL_CFLAGS = @OPTIONAL_OPENSSL_CFLAGS@ OPTIONAL_OPENSSL_LIBS = @OPTIONAL_OPENSSL_LIBS@ OPTIONAL_PCSC_CFLAGS = @OPTIONAL_PCSC_CFLAGS@ OPTIONAL_READLINE_CFLAGS = @OPTIONAL_READLINE_CFLAGS@ OPTIONAL_READLINE_LIBS = @OPTIONAL_READLINE_LIBS@ OPTIONAL_ZLIB_CFLAGS = @OPTIONAL_ZLIB_CFLAGS@ OPTIONAL_ZLIB_LIBS = @OPTIONAL_ZLIB_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCSC_CFLAGS = @PCSC_CFLAGS@ PCSC_LIBS = @PCSC_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RC = @RC@ READLINE_CFLAGS = @READLINE_CFLAGS@ READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ WIN_LIBPREFIX = @WIN_LIBPREFIX@ XSLTPROC = @XSLTPROC@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ git = @git@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkcs11dir = @pkcs11dir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ xslstylesheetsdir = @xslstylesheetsdir@ RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE) MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak dist_noinst_SCRIPTS = opensc_pkcs11_install.js lib_LTLIBRARIES = opensc-pkcs11.la pkcs11-spy.la AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(PTHREAD_CFLAGS) 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 opensc-pkcs11.exports \ pkcs11-display.c pkcs11-display.h OPENSC_PKCS11_LIBS = \ $(top_builddir)/src/libopensc/libopensc.la \ $(top_builddir)/src/common/libscdl.la \ $(top_builddir)/src/common/libcompat.la \ $(OPTIONAL_OPENSSL_LIBS) $(PTHREAD_LIBS) opensc_pkcs11_la_SOURCES = $(OPENSC_PKCS11_SRC) $(OPENSC_PKCS11_INC) \ $(am__append_1) opensc_pkcs11_la_LIBADD = $(OPENSC_PKCS11_LIBS) opensc_pkcs11_la_LDFLAGS = $(AM_LDFLAGS) \ -export-symbols "$(srcdir)/opensc-pkcs11.exports" \ -module -shared -avoid-version -no-undefined pkcs11_spy_la_SOURCES = pkcs11-spy.c pkcs11-display.c pkcs11-display.h \ pkcs11-spy.exports $(am__append_2) pkcs11_spy_la_LIBADD = \ $(top_builddir)/src/common/libpkcs11.la \ $(top_builddir)/src/common/libscdl.la \ $(OPTIONAL_OPENSSL_LIBS) pkcs11_spy_la_LDFLAGS = $(AM_LDFLAGS) \ -export-symbols "$(srcdir)/pkcs11-spy.exports" \ -module -shared -avoid-version -no-undefined @CYGWIN_FALSE@@WIN32_FALSE@PKCS11_SUFFIX = .so # see http://wiki.cacert.org/wiki/Pkcs11TaskForce @CYGWIN_TRUE@@WIN32_FALSE@PKCS11_SUFFIX = .dll all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj .rc $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/win32/ltrc.inc $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/pkcs11/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/pkcs11/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ test "$$dir" != "$$p" || dir=.; \ echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done $(top_builddir)/win32/$(am__dirstamp): @$(MKDIR_P) $(top_builddir)/win32 @: > $(top_builddir)/win32/$(am__dirstamp) $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) $(top_builddir)/win32/$(DEPDIR) @: > $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp) $(top_builddir)/win32/versioninfo.lo: \ $(top_builddir)/win32/$(am__dirstamp) \ $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp) opensc-pkcs11.la: $(opensc_pkcs11_la_OBJECTS) $(opensc_pkcs11_la_DEPENDENCIES) $(AM_V_CCLD)$(opensc_pkcs11_la_LINK) -rpath $(libdir) $(opensc_pkcs11_la_OBJECTS) $(opensc_pkcs11_la_LIBADD) $(LIBS) pkcs11-spy.la: $(pkcs11_spy_la_OBJECTS) $(pkcs11_spy_la_DEPENDENCIES) $(AM_V_CCLD)$(pkcs11_spy_la_LINK) -rpath $(libdir) $(pkcs11_spy_la_OBJECTS) $(pkcs11_spy_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f $(top_builddir)/win32/versioninfo.$(OBJEXT) -rm -f $(top_builddir)/win32/versioninfo.lo distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/framework-pkcs15.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/framework-pkcs15init.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mechanism.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openssl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11-display.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11-global.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11-object.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11-session.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11-spy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/slot.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf $(top_builddir)/win32/.libs $(top_builddir)/win32/_libs -rm -rf .libs _libs ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: CTAGS CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(SCRIPTS) installdirs: for dir in "$(DESTDIR)$(libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -test -z "$(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp)" || rm -f $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp) -test -z "$(top_builddir)/win32/$(am__dirstamp)" || rm -f $(top_builddir)/win32/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-libLTLIBRARIES @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-exec-hook install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-libLTLIBRARIES .MAKE: install-am install-exec-am install-strip .PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ clean-libLTLIBRARIES clean-libtool ctags distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-exec-hook \ install-html install-html-am install-info install-info-am \ install-libLTLIBRARIES install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags uninstall uninstall-am uninstall-libLTLIBRARIES .rc.lo: $(LTRCCOMPILE) -i "$<" -o "$@" .rc.o: $(RCCOMPILE) -i "$<" -o "$@" jar-dir: if test ! -d jar-dir ; then mkdir jar-dir ; fi pkcs11-jar: jar-dir cp .libs/*.so jar-dir cp opensc_pkcs11_install.js jar-dir signtool -Z"opensc-pkcs11.jar" -i"opensc_pkcs11_install.js" \ -k"testcert" jar-dir @WIN32_TRUE@install-exec-hook: @WIN32_TRUE@ $(MKDIR_P) "$(DESTDIR)$(libdir)" @WIN32_TRUE@ for l in opensc-pkcs11.dll pkcs11-spy.dll; do \ @WIN32_TRUE@ mv "$(DESTDIR)$(libdir)/$$l" "$(DESTDIR)$(bindir)/$$l"; \ @WIN32_TRUE@ done @WIN32_FALSE@install-exec-hook: @WIN32_FALSE@ $(MKDIR_P) "$(DESTDIR)$(pkcs11dir)" @WIN32_FALSE@ for l in opensc-pkcs11$(PKCS11_SUFFIX) pkcs11-spy$(PKCS11_SUFFIX); do \ @WIN32_FALSE@ rm -f "$(DESTDIR)$(pkcs11dir)/$$l"; \ @WIN32_FALSE@ $(LN_S) ../$$l "$(DESTDIR)$(pkcs11dir)/$$l"; \ @WIN32_FALSE@ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: opensc-0.13.0/src/pkcs11/pkcs11-opensc.h0000644000015201777760000000055712057406034014465 00000000000000#ifndef PKCS11_OPENSC_H #define PKCS11_OPENSC_H /* OpenSC specific extensions */ /* * In PKCS#11 there is no CKA_ attribute dedicated to the NON-REPUDIATION flag. * We need this flag in PKCS#15/libopensc to make dinstinction between * 'signature' and 'qualified signature' key slots. */ #define CKA_OPENSC_NON_REPUDIATION (CKA_VENDOR_DEFINED | 1UL) #endif opensc-0.13.0/src/pkcs11/openssl.c0000644000015201777760000002711112057406034013547 00000000000000/* * OpenSSL helper functions, e.g. for implementing MD5 support * et al * * Copyright (C) 2002 Olaf Kirch */ #include "config.h" #ifdef ENABLE_OPENSSL /* empty file without openssl */ #include #include #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x10000000L #include #include /* for OPENSSL_NO_* */ #ifndef OPENSSL_NO_EC #include #endif /* OPENSSL_NO_EC */ #ifndef OPENSSL_NO_ENGINE #include #endif /* OPENSSL_NO_ENGINE */ #include #include #endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */ #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 *); static sc_pkcs11_mechanism_type_t openssl_sha1_mech = { CKM_SHA_1, { 0, 0, CKF_DIGEST }, 0, 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, /* decrypt_* */ NULL, /* derive */ NULL /* mech_data */ }; #if OPENSSL_VERSION_NUMBER >= 0x00908000L static sc_pkcs11_mechanism_type_t openssl_sha256_mech = { CKM_SHA256, { 0, 0, CKF_DIGEST }, 0, 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, /* decrypt_* */ NULL, /* derive */ NULL /* mech_data */ }; static sc_pkcs11_mechanism_type_t openssl_sha384_mech = { CKM_SHA384, { 0, 0, CKF_DIGEST }, 0, 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, /* decrypt_* */ NULL, /* derive */ NULL /* mech_data */ }; static sc_pkcs11_mechanism_type_t openssl_sha512_mech = { CKM_SHA512, { 0, 0, CKF_DIGEST }, 0, 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, /* decrypt_* */ NULL, /* derive */ NULL /* mech_data */ }; #endif #if OPENSSL_VERSION_NUMBER >= 0x10000000L static sc_pkcs11_mechanism_type_t openssl_gostr3411_mech = { CKM_GOSTR3411, { 0, 0, CKF_DIGEST }, 0, 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, /* decrypt_* */ NULL, /* derive */ NULL /* mech_data */ }; #endif static sc_pkcs11_mechanism_type_t openssl_md5_mech = { CKM_MD5, { 0, 0, CKF_DIGEST }, 0, 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, /* decrypt_* */ NULL, /* derive */ NULL /* mech_data */ }; static sc_pkcs11_mechanism_type_t openssl_ripemd160_mech = { CKM_RIPEMD160, { 0, 0, CKF_DIGEST }, 0, 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, /* decrypt_* */ NULL, /* derive */ NULL /* mech_data */ }; void sc_pkcs11_register_openssl_mechanisms(struct sc_pkcs11_card *card) { #if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_ENGINE) void (*locking_cb)(int, int, const char *, int); ENGINE *e; locking_cb = CRYPTO_get_locking_callback(); if (locking_cb) CRYPTO_set_locking_callback(NULL); e = ENGINE_by_id("gost"); if (!e) { #if !defined(OPENSSL_NO_STATIC_ENGINE) && !defined(OPENSSL_NO_GOST) ENGINE_load_gost(); 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 */ } if (e) { ENGINE_set_default(e, ENGINE_METHOD_ALL); ENGINE_free(e); } if (locking_cb) CRYPTO_set_locking_callback(locking_cb); #endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_ENGINE) */ openssl_sha1_mech.mech_data = EVP_sha1(); sc_pkcs11_register_mechanism(card, &openssl_sha1_mech); #if OPENSSL_VERSION_NUMBER >= 0x00908000L openssl_sha256_mech.mech_data = EVP_sha256(); sc_pkcs11_register_mechanism(card, &openssl_sha256_mech); openssl_sha384_mech.mech_data = EVP_sha384(); sc_pkcs11_register_mechanism(card, &openssl_sha384_mech); openssl_sha512_mech.mech_data = EVP_sha512(); sc_pkcs11_register_mechanism(card, &openssl_sha512_mech); #endif openssl_md5_mech.mech_data = EVP_md5(); sc_pkcs11_register_mechanism(card, &openssl_md5_mech); openssl_ripemd160_mech.mech_data = EVP_ripemd160(); sc_pkcs11_register_mechanism(card, &openssl_ripemd160_mech); #if OPENSSL_VERSION_NUMBER >= 0x10000000L openssl_gostr3411_mech.mech_data = EVP_get_digestbynid(NID_id_GostR3411_94); sc_pkcs11_register_mechanism(card, &openssl_gostr3411_mech); #endif } /* * Handle OpenSSL digest functions */ #define DIGEST_CTX(op) \ ((EVP_MD_CTX *) (op)->priv_data) 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 = calloc(1, sizeof(*md_ctx)))) return CKR_HOST_MEMORY; EVP_DigestInit(md_ctx, md); 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_DigestUpdate(DIGEST_CTX(op), pData, pDataLen); 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 (*pulDigestLen < (unsigned) EVP_MD_CTX_size(md_ctx)) { sc_log(context, "Provided buffer too small: %ul < %d", *pulDigestLen, EVP_MD_CTX_size(md_ctx)); *pulDigestLen = EVP_MD_CTX_size(md_ctx); return CKR_BUFFER_TOO_SMALL; } EVP_DigestFinal(md_ctx, pDigest, (unsigned *) pulDigestLen); return CKR_OK; } static void sc_pkcs11_openssl_md_release(sc_pkcs11_operation_t *op) { EVP_MD_CTX *md_ctx = DIGEST_CTX(op); if (md_ctx) free(md_ctx); op->priv_data = NULL; } #if OPENSSL_VERSION_NUMBER >= 0x10000000L && !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 unsigned char *pubkey, int pubkey_len, const unsigned char *params, int params_len, unsigned char *data, int data_len, unsigned char *signat, int signat_len) { EVP_PKEY *pkey; EVP_PKEY_CTX *pkey_ctx = NULL; EC_POINT *P; BIGNUM *X, *Y; ASN1_OCTET_STRING *octet; const EC_GROUP *group = NULL; char paramset[2] = "A"; int r = -1, ret_vrf = 0; pkey = EVP_PKEY_new(); if (!pkey) 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) { 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 (r == 1 && EVP_PKEY_get0(pkey) != NULL) group = EC_KEY_get0_group(EVP_PKEY_get0(pkey)); r = -1; if (group) octet = d2i_ASN1_OCTET_STRING(NULL, &pubkey, (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_GFp(group, P, X, Y, NULL); BN_free(X); BN_free(Y); if (r == 1 && EVP_PKEY_get0(pkey) && P) r = EC_KEY_set_public_key(EVP_PKEY_get0(pkey), P); 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 /* OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) */ /* If no hash function was used, finish with RSA_public_decrypt(). * If a hash function was used, we can make a big shortcut by * finishing with EVP_VerifyFinal(). */ CK_RV sc_pkcs11_verify_data(const unsigned char *pubkey, int pubkey_len, const unsigned char *pubkey_params, int pubkey_params_len, CK_MECHANISM_TYPE mech, sc_pkcs11_operation_t *md, unsigned char *data, int data_len, unsigned char *signat, int signat_len) { int res; CK_RV rv = CKR_GENERAL_ERROR; EVP_PKEY *pkey; if (mech == CKM_GOSTR3410) { #if OPENSSL_VERSION_NUMBER >= 0x10000000L && !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 } pkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, &pubkey, pubkey_len); if (pkey == NULL) return CKR_GENERAL_ERROR; if (md != NULL) { EVP_MD_CTX *md_ctx = DIGEST_CTX(md); res = EVP_VerifyFinal(md_ctx, signat, signat_len, pkey); EVP_PKEY_free(pkey); if (res == 1) return CKR_OK; else if (res == 0) return CKR_SIGNATURE_INVALID; else { sc_log(context, "EVP_VerifyFinal() returned %d\n", res); return CKR_GENERAL_ERROR; } } else { RSA *rsa; unsigned char *rsa_out = NULL, pad; int rsa_outlen = 0; switch(mech) { case CKM_RSA_PKCS: pad = RSA_PKCS1_PADDING; break; case CKM_RSA_X_509: pad = RSA_NO_PADDING; break; default: EVP_PKEY_free(pkey); return CKR_ARGUMENTS_BAD; } rsa = EVP_PKEY_get1_RSA(pkey); EVP_PKEY_free(pkey); if (rsa == NULL) return CKR_DEVICE_MEMORY; rsa_out = malloc(RSA_size(rsa)); if (rsa_out == NULL) { RSA_free(rsa); return CKR_DEVICE_MEMORY; } rsa_outlen = RSA_public_decrypt(signat_len, signat, rsa_out, rsa, pad); RSA_free(rsa); if(rsa_outlen <= 0) { free(rsa_out); sc_log(context, "RSA_public_decrypt() returned %d\n", rsa_outlen); return CKR_GENERAL_ERROR; } if (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.13.0/src/pkcs11/mechanism.c0000644000015201777760000006160612057406034014037 00000000000000/* * Generic handling of PKCS11 mechanisms * * Copyright (C) 2002 Olaf Kirch */ #include "config.h" #include #include #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; sc_pkcs11_mechanism_type_t *sign_type; }; /* Also used for verification and decryption data */ struct signature_data { struct sc_pkcs11_object *key; struct hash_signature_info *info; sc_pkcs11_operation_t * md; CK_BYTE buffer[4096/8]; unsigned int buffer_len; }; /* * Register a mechanism */ CK_RV sc_pkcs11_register_mechanism(struct sc_pkcs11_card *p11card, sc_pkcs11_mechanism_type_t *mt) { sc_pkcs11_mechanism_type_t **p; if (mt == NULL) return CKR_HOST_MEMORY; p = (sc_pkcs11_mechanism_type_t **) realloc(p11card->mechanisms, (p11card->nmechanisms + 2) * sizeof(*p)); if (p == NULL) return CKR_HOST_MEMORY; p11card->mechanisms = p; p[p11card->nmechanisms++] = mt; p[p11card->nmechanisms] = NULL; return CKR_OK; } /* * Look up a mechanism */ sc_pkcs11_mechanism_type_t * sc_pkcs11_find_mechanism(struct sc_pkcs11_card *p11card, CK_MECHANISM_TYPE mech, unsigned int flags) { sc_pkcs11_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; int rv; 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; int rv; LOG_FUNC_CALLED(context); if (!session || !session->slot || !(p11card = session->slot->card)) 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, 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, rv); } CK_RV sc_pkcs11_md_update(struct sc_pkcs11_session *session, CK_BYTE_PTR pData, CK_ULONG ulDataLen) { sc_pkcs11_operation_t *op; int 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, 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, 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, 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_MECHANISM_TYPE key_type) { struct sc_pkcs11_card *p11card; sc_pkcs11_operation_t *operation; sc_pkcs11_mechanism_type_t *mt; int rv; LOG_FUNC_CALLED(context); if (!session || !session->slot || !(p11card = session->slot->card)) LOG_FUNC_RETURN(context, CKR_ARGUMENTS_BAD); /* See if we support this mechanism type */ sc_log(context, "mechanism 0x%X, key-type 0x%X", 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 */ if (mt->key_type != key_type) LOG_FUNC_RETURN(context, CKR_KEY_TYPE_INCONSISTENT); rv = session_start_operation(session, SC_PKCS11_OPERATION_SIGN, mt, &operation); if (rv != CKR_OK) LOG_FUNC_RETURN(context, rv); memcpy(&operation->mechanism, pMechanism, sizeof(CK_MECHANISM)); rv = mt->sign_init(operation, key); if (rv != CKR_OK) session_stop_operation(session, SC_PKCS11_OPERATION_SIGN); LOG_FUNC_RETURN(context, rv); } CK_RV sc_pkcs11_sign_update(struct sc_pkcs11_session *session, CK_BYTE_PTR pData, CK_ULONG ulDataLen) { sc_pkcs11_operation_t *op; int rv; LOG_FUNC_CALLED(context); rv = session_get_operation(session, SC_PKCS11_OPERATION_SIGN, &op); if (rv != CKR_OK) LOG_FUNC_RETURN(context, 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, 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; int rv; LOG_FUNC_CALLED(context); rv = session_get_operation(session, SC_PKCS11_OPERATION_SIGN, &op); if (rv != CKR_OK) LOG_FUNC_RETURN(context, 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, rv); } CK_RV sc_pkcs11_sign_size(struct sc_pkcs11_session *session, CK_ULONG_PTR pLength) { sc_pkcs11_operation_t *op; int rv; rv = session_get_operation(session, SC_PKCS11_OPERATION_SIGN, &op); if (rv != CKR_OK) LOG_FUNC_RETURN(context, 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, 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 signature_data *data; CK_RV rv; int can_do_it = 0; LOG_FUNC_CALLED(context); if (!(data = calloc(1, sizeof(*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. */ free(data); LOG_FUNC_RETURN(context, 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); free(data); LOG_FUNC_RETURN(context, 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 signature_data *data; LOG_FUNC_CALLED(context); sc_log(context, "data part length %li", ulPartLen); data = (struct signature_data *) operation->priv_data; if (data->md) { CK_RV rv = data->md->type->md_update(data->md, pPart, ulPartLen); LOG_FUNC_RETURN(context, rv); } /* This signature mechanism operates on the raw data */ if (data->buffer_len + ulPartLen > sizeof(data->buffer)) LOG_FUNC_RETURN(context, CKR_DATA_LEN_RANGE); memcpy(data->buffer + data->buffer_len, pPart, ulPartLen); data->buffer_len += ulPartLen; sc_log(context, "data length %li", data->buffer_len); LOG_FUNC_RETURN(context, CKR_OK); } static CK_RV sc_pkcs11_signature_final(sc_pkcs11_operation_t *operation, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { struct signature_data *data; CK_RV rv; LOG_FUNC_CALLED(context); data = (struct signature_data *) operation->priv_data; sc_log(context, "data length %li", data->buffer_len); if (data->md) { sc_pkcs11_operation_t *md = data->md; CK_ULONG len = sizeof(data->buffer); rv = md->type->md_final(md, data->buffer, &len); if (rv == CKR_BUFFER_TOO_SMALL) rv = CKR_FUNCTION_FAILED; if (rv != CKR_OK) LOG_FUNC_RETURN(context, rv); data->buffer_len = len; } sc_log(context, "%li bytes to sign", data->buffer_len); rv = data->key->ops->sign(operation->session, data->key, &operation->mechanism, data->buffer, data->buffer_len, pSignature, pulSignatureLen); LOG_FUNC_RETURN(context, rv); } 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 signature_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: /* TODO: -DEE we should use something other then CKA_MODULUS_BITS... */ rv = key->ops->get_attribute(operation->session, key, &attr); *pLength = ((*pLength + 7)/8) * 2 ; /* 2*nLen in bytes */ 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, rv); } static void sc_pkcs11_signature_release(sc_pkcs11_operation_t *operation) { struct signature_data *data; data = (struct signature_data *) operation->priv_data; if (!data) return; sc_pkcs11_release_operation(&data->md); memset(data, 0, sizeof(*data)); free(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_MECHANISM_TYPE key_type) { struct sc_pkcs11_card *p11card; sc_pkcs11_operation_t *operation; sc_pkcs11_mechanism_type_t *mt; int rv; if (!session || !session->slot || !(p11card = session->slot->card)) 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 */ if (mt->key_type != key_type) return CKR_KEY_TYPE_INCONSISTENT; rv = session_start_operation(session, SC_PKCS11_OPERATION_VERIFY, mt, &operation); if (rv != CKR_OK) return rv; memcpy(&operation->mechanism, pMechanism, sizeof(CK_MECHANISM)); 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; int 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; int 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 signature operation */ static CK_RV sc_pkcs11_verify_init(sc_pkcs11_operation_t *operation, struct sc_pkcs11_object *key) { struct hash_signature_info *info; struct signature_data *data; int rv; if (!(data = calloc(1, sizeof(*data)))) return CKR_HOST_MEMORY; data->info = NULL; data->key = key; /* 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 signature_data *data; data = (struct signature_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 */ if (data->buffer_len + ulPartLen > sizeof(data->buffer)) return CKR_DATA_LEN_RANGE; memcpy(data->buffer + data->buffer_len, pPart, ulPartLen); data->buffer_len += ulPartLen; return CKR_OK; } static CK_RV sc_pkcs11_verify_final(sc_pkcs11_operation_t *operation, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { struct signature_data *data; struct sc_pkcs11_object *key; unsigned char *pubkey_value; CK_KEY_TYPE key_type; CK_BYTE params[9 /* GOST_PARAMS_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)}; int rv; data = (struct signature_data *) operation->priv_data; if (pSignature == NULL) return CKR_ARGUMENTS_BAD; key = data->key; rv = key->ops->get_attribute(operation->session, key, &attr); if (rv != CKR_OK) return rv; pubkey_value = malloc(attr.ulValueLen); attr.pValue = pubkey_value; rv = key->ops->get_attribute(operation->session, key, &attr); if (rv != CKR_OK) goto done; rv = key->ops->get_attribute(operation->session, key, &attr_key_type); if (rv == CKR_OK && 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.mechanism, data->md, data->buffer, data->buffer_len, pSignature, ulSignatureLen); done: free(pubkey_value); return rv; } #endif /* * 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_MECHANISM_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->card)) 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 */ if (mt->key_type != key_type) return CKR_KEY_TYPE_INCONSISTENT; rv = session_start_operation(session, SC_PKCS11_OPERATION_DECRYPT, mt, &operation); if (rv != CKR_OK) return rv; memcpy(&operation->mechanism, pMechanism, sizeof(CK_MECHANISM)); rv = mt->decrypt_init(operation, key); 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; int 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); if (rv != CKR_BUFFER_TOO_SMALL && pData != NULL) session_stop_operation(session, SC_PKCS11_OPERATION_DECRYPT); 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->card)) 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 */ if (mt->key_type != key_type) return CKR_KEY_TYPE_INCONSISTENT; 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 sercet 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 actuall 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 signature operation */ static CK_RV sc_pkcs11_decrypt_init(sc_pkcs11_operation_t *operation, struct sc_pkcs11_object *key) { struct signature_data *data; if (!(data = calloc(1, sizeof(*data)))) return CKR_HOST_MEMORY; data->key = key; operation->priv_data = data; return CKR_OK; } 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 signature_data *data; struct sc_pkcs11_object *key; data = (struct signature_data*) operation->priv_data; key = data->key; return key->ops->decrypt(operation->session, key, &operation->mechanism, pEncryptedData, ulEncryptedDataLen, pData, pulDataLen); } 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); } /* * 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, void *priv_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_type = key_type; mt->mech_data = priv_data; mt->obj_size = sizeof(sc_pkcs11_operation_t); mt->release = sc_pkcs11_signature_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_UNWRAP) { /* TODO */ } 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; } return mt; } /* * 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; } /* * 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 = sign_type->mech_info; if (!(hash_type = sc_pkcs11_find_mechanism(p11card, hash_mech, CKF_DIGEST))) return 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)); info->mech = mech; info->sign_type = sign_type; info->hash_type = hash_type; info->sign_mech = sign_type->mech; info->hash_mech = hash_mech; new_type = sc_pkcs11_new_fw_mechanism(mech, &mech_info, sign_type->key_type, info); if (!new_type) return CKR_HOST_MEMORY; return sc_pkcs11_register_mechanism(p11card, new_type); } opensc-0.13.0/src/pkcs11/opensc-pkcs11.exports0000644000015201777760000000002212057406034015725 00000000000000C_GetFunctionList opensc-0.13.0/src/Makefile.in0000644000015201777760000004467312057406055012702 00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program 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. @SET_MAKE@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @ENABLE_SM_TRUE@am__append_1 = sm subdir = src DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ html-recursive info-recursive install-data-recursive \ install-dvi-recursive install-exec-recursive \ install-html-recursive install-info-recursive \ install-pdf-recursive install-ps-recursive install-recursive \ installcheck-recursive installdirs-recursive pdf-recursive \ ps-recursive uninstall-recursive RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ distdir ETAGS = etags CTAGS = ctags DIST_SUBDIRS = common scconf pkcs15init libopensc pkcs11 tools tests \ minidriver sm DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUG_FILE = @DEBUG_FILE@ DEFAULT_PCSC_PROVIDER = @DEFAULT_PCSC_PROVIDER@ DEFAULT_SM_MODULE = @DEFAULT_SM_MODULE@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GIT = @GIT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBRARY_BITNESS = @LIBRARY_BITNESS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCT_CFLAGS = @OPENCT_CFLAGS@ OPENCT_LIBS = @OPENCT_LIBS@ OPENSC_LT_AGE = @OPENSC_LT_AGE@ OPENSC_LT_CURRENT = @OPENSC_LT_CURRENT@ OPENSC_LT_OLDEST = @OPENSC_LT_OLDEST@ OPENSC_LT_REVISION = @OPENSC_LT_REVISION@ OPENSC_VERSION_FIX = @OPENSC_VERSION_FIX@ OPENSC_VERSION_MAJOR = @OPENSC_VERSION_MAJOR@ OPENSC_VERSION_MINOR = @OPENSC_VERSION_MINOR@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OPTIONAL_OPENCT_CFLAGS = @OPTIONAL_OPENCT_CFLAGS@ OPTIONAL_OPENCT_LIBS = @OPTIONAL_OPENCT_LIBS@ OPTIONAL_OPENSSL_CFLAGS = @OPTIONAL_OPENSSL_CFLAGS@ OPTIONAL_OPENSSL_LIBS = @OPTIONAL_OPENSSL_LIBS@ OPTIONAL_PCSC_CFLAGS = @OPTIONAL_PCSC_CFLAGS@ OPTIONAL_READLINE_CFLAGS = @OPTIONAL_READLINE_CFLAGS@ OPTIONAL_READLINE_LIBS = @OPTIONAL_READLINE_LIBS@ OPTIONAL_ZLIB_CFLAGS = @OPTIONAL_ZLIB_CFLAGS@ OPTIONAL_ZLIB_LIBS = @OPTIONAL_ZLIB_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCSC_CFLAGS = @PCSC_CFLAGS@ PCSC_LIBS = @PCSC_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RC = @RC@ READLINE_CFLAGS = @READLINE_CFLAGS@ READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ WIN_LIBPREFIX = @WIN_LIBPREFIX@ XSLTPROC = @XSLTPROC@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ git = @git@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkcs11dir = @pkcs11dir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ xslstylesheetsdir = @xslstylesheetsdir@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak # Order IS important SUBDIRS = common scconf pkcs15init libopensc pkcs11 tools tests \ minidriver $(am__append_1) all: all-recursive .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run `make' without going through this Makefile. # To change the values of `make' variables: instead of editing Makefiles, # (1) if the variable is set in `config.status', edit `config.status' # (which will cause the Makefiles to be regenerated when you run `make'); # (2) otherwise, pass the desired values on the `make' command line. $(RECURSIVE_TARGETS): @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ *k*) failcom='fail=yes';; \ esac; \ done; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ list='$(SUBDIRS)'; for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" $(RECURSIVE_CLEAN_TARGETS): @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ *k*) failcom='fail=yes';; \ esac; \ done; \ dot_seen=no; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ rev=''; for subdir in $$list; do \ if test "$$subdir" = "."; then :; else \ rev="$$subdir $$rev"; \ fi; \ done; \ rev="$$rev ."; \ target=`echo $@ | sed s/-recursive//`; \ for subdir in $$rev; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done && test -z "$$fail" tags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ done ctags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ done ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: CTAGS CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \ install-am install-strip tags-recursive .PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ all all-am check check-am clean clean-generic clean-libtool \ ctags ctags-recursive distclean distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \ uninstall uninstall-am # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: opensc-0.13.0/src/tools/0000755000015201777760000000000012057406120012030 500000000000000opensc-0.13.0/src/tools/iasecc-tool.c0000644000015201777760000001652412057406034014332 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. Use several times to enable debug output.", 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 sdo_class = 0; int rv, ii, jj; if (!sdo_tag) goto usage; if (*sdo_tag == 'x' || *sdo_tag == 'X') sdo_class = strtol(sdo_tag + 1, NULL, 16); else if ((strlen(sdo_tag) > 2) && (*(sdo_tag + 1) == 'x' || *(sdo_tag + 1) == 'X')) sdo_class = strtol(sdo_tag + 2, NULL, 16); else sdo_class = strtol(sdo_tag, NULL, 10); sdo_class &= 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 * const 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; setbuf(stderr, NULL); setbuf(stdout, NULL); 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; r = sc_context_create(&ctx, &ctx_param); if (r != SC_SUCCESS) { fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); return 1; } /* Only change if not in opensc.conf */ if (verbose > 1 && ctx->debug == 0) { ctx->debug = verbose; sc_ctx_log_to_file(ctx, "stderr"); } if (action_count <= 0) goto end; err = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose); 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 (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); } if (ctx) sc_release_context(ctx); return err; } opensc-0.13.0/src/tools/cryptoflex-tool.c0000644000015201777760000005547712057406034015314 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #include "libopensc/pkcs15.h" #include "common/compat_strlcpy.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. Use several times to enable debug output.", }; 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) strcat(str, opt_appdf); 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, 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, RSA *rsa) { const u8 *p = key; BIGNUM *n, *e; int base; 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) 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); rsa->n = n; rsa->e = e; return 0; } static int gen_d(RSA *rsa) { 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 = BN_mod_inverse(NULL, rsa->e, r0, bnctx)) == NULL) { 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, RSA *rsa) { const u8 *p = key; BIGNUM *bn_p, *q, *dmp1, *dmq1, *iqmp; int base; 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) return -1; cf2bn(p, base, bn_p); p += base; q = BN_new(); if (q == NULL) return -1; cf2bn(p, base, q); p += base; iqmp = BN_new(); if (iqmp == NULL) return -1; cf2bn(p, base, iqmp); p += base; dmp1 = BN_new(); if (dmp1 == NULL) return -1; cf2bn(p, base, dmp1); p += base; dmq1 = BN_new(); if (dmq1 == NULL) return -1; cf2bn(p, base, dmq1); p += base; rsa->p = bn_p; rsa->q = q; rsa->dmp1 = dmp1; rsa->dmq1 = dmq1; rsa->iqmp = iqmp; if (gen_d(rsa)) return -1; return 0; } static int read_public_key(RSA *rsa) { 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 = file->size; 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, rsa); } static int read_private_key(RSA *rsa) { 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 = file->size; 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, rsa); } static int read_key(void) { RSA *rsa = RSA_new(); u8 buf[1024], *p = buf; u8 b64buf[2048]; int r; if (rsa == NULL) return -1; r = read_public_key(rsa); if (r) return r; r = i2d_RSA_PUBKEY(rsa, &p); if (r <= 0) { 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(rsa); if (r == 10) return 0; else if (r) return r; p = buf; r = i2d_RSAPrivateKey(rsa, &p); if (r <= 0) { 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()) 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(RSA **rsa_out) { RSA *rsa = NULL; BIO *in = NULL; int r; in = BIO_new(BIO_s_file()); if (opt_prkeyf == NULL) { fprintf(stderr, "Private key file must be set.\n"); return 2; } r = BIO_read_filename(in, opt_prkeyf); if (r <= 0) { perror(opt_prkeyf); return 2; } rsa = PEM_read_bio_RSAPrivateKey(in, NULL, NULL, NULL); if (rsa == NULL) { fprintf(stderr, "Unable to load private key.\n"); return 2; } BIO_free(in); *rsa_out = rsa; return 0; } static int encode_private_key(RSA *rsa, u8 *key, size_t *keysize) { u8 buf[512], *p = buf; u8 bnbuf[256]; int base = 0; int r; switch (BN_num_bits(rsa->n)) { 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; r = bn2cf(rsa->p, bnbuf); if (r != base) { fprintf(stderr, "Invalid private key.\n"); return 2; } memcpy(p, bnbuf, base); p += base; r = bn2cf(rsa->q, bnbuf); if (r != base) { fprintf(stderr, "Invalid private key.\n"); return 2; } memcpy(p, bnbuf, base); p += base; r = bn2cf(rsa->iqmp, bnbuf); if (r != base) { fprintf(stderr, "Invalid private key.\n"); return 2; } memcpy(p, bnbuf, base); p += base; r = bn2cf(rsa->dmp1, bnbuf); if (r != base) { fprintf(stderr, "Invalid private key.\n"); return 2; } memcpy(p, bnbuf, base); p += base; r = bn2cf(rsa->dmq1, bnbuf); if (r != base) { fprintf(stderr, "Invalid private key.\n"); return 2; } memcpy(p, bnbuf, base); p += base; memcpy(key, buf, p - buf); *keysize = p - buf; return 0; } static int encode_public_key(RSA *rsa, u8 *key, size_t *keysize) { u8 buf[512], *p = buf; u8 bnbuf[256]; int base = 0; int r; switch (BN_num_bits(rsa->n)) { 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; r = bn2cf(rsa->n, bnbuf); if (r != 2*base) { fprintf(stderr, "Invalid public key.\n"); return 2; } 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); memcpy(p, bnbuf, 4); p += 4; memcpy(key, buf, p - buf); *keysize = p - buf; return 0; } 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 = 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 = 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, pubsize; int r; RSA *rsa; r = read_rsa_privkey(&rsa); if (r) return r; r = encode_private_key(rsa, prv, &prvsize); if (r) return r; r = encode_public_key(rsa, 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) strcat(buf, opt_appdf); sc_format_path(buf, &path); return create_pin_file(&path, opt_pin_num, ""); } int main(int argc, char * const 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; r = sc_context_create(&ctx, &ctx_param); if (r) { fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); return 1; } if (verbose > 1) { ctx->debug = verbose; sc_ctx_log_to_file(ctx, "stderr"); } err = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose); 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); } if (ctx) sc_release_context(ctx); return err; } opensc-0.13.0/src/tools/openpgp-tool.c0000644000015201777760000003141312057406034014545 00000000000000/* * openpgp-tool.c: OpenPGP card utility * * Copyright (C) 2012 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #ifndef _WIN32 #include #endif #include #include #include "common/compat_getopt.h" #include "libopensc/opensc.h" #include "libopensc/asn1.h" #include "libopensc/cards.h" #include "libopensc/cardctl.h" #include "util.h" #define OPT_RAW 256 #define OPT_PRETTY 257 #define OPT_VERIFY 258 #define OPT_PIN 259 /* define structures */ struct ef_name_map { const char *name; const char *env_name; const char *ef; char *(*prettify_value)(char *); }; /* declare functions */ static void show_version(void); static char *prettify_name(char *str); static char *prettify_language(char *str); static char *prettify_gender(char *str); static void display_data(const struct ef_name_map *mapping, char *value); static int decode_options(int argc, char **argv); static int do_userinfo(sc_card_t *card); static int read_transp(sc_card_t *card, const char *pathstring, unsigned char *buf, int buflen); static void bintohex(char *buf, int len); /* 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 char *exec_program = NULL; static int opt_genkey = 0; static int opt_keylen = 0; static u8 key_id = 0; static unsigned int key_len = 2048; static int opt_verify = 0; static char *verifytype = NULL; static int opt_pin = 0; static char *pin = NULL; 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' }, { "gen-key", required_argument, NULL, 'G' }, { "key-length",required_argument, NULL, 'L' }, { "help", no_argument, NULL, 'h' }, { "verbose", no_argument, NULL, 'v' }, { "version", no_argument, NULL, 'V' }, { "verify", required_argument, NULL, OPT_VERIFY }, { "pin", required_argument, NULL, OPT_PIN }, { 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 */ NULL, /* U */ "Show card holder information", /* G */ "Generate key", /* L */ "Key length (default 2048)", /* h */ "Print this help message", /* v */ "Verbose operation. Use several times to enable debug output.", /* V */ "Show version number", "Verify PIN (CHV1, CHV2, CHV3...)", "PIN string" }; static const struct ef_name_map openpgp_data[] = { { "Account", "OPENGPG_ACCOUNT", "3F00:005E", NULL }, { "URL", "OPENPGP_URL", "3F00:5F50", NULL }, { "Name", "OPENPGP_NAME", "3F00:0065:005B", prettify_name }, { "Language", "OPENPGP_LANG", "3F00:0065:5F2D", prettify_language }, { "Gender", "OPENPGP_GENDER", "3F00:0065:5F35", prettify_gender }, { "DO 0101", "OPENPGP_DO0101", "3F00:0101", NULL }, { "DO 0102", "OPENPGP_DO0102", "3F00:0102", NULL }, // { "DO 0103", "OPENPGP_DO0103", "3F00:0103", NULL }, // { "DO 0104", "OPENPGP_DO0104", "3F00:0104", NULL }, { NULL, NULL, NULL, NULL } }; static void show_version(void) { fprintf(stderr, "openpgp-tool - OpenPGP card utility version " PACKAGE_VERSION "\n" "\n" "Copyright (c) 2012 Peter Marschall \n" "Licensed under LGPL v2\n"); } /* prettify card holder's name */ static char *prettify_name(char *str) { if (str != NULL) { char *src = str; char *dst = str; while (*src != '\0') { *dst = *src++; if (*dst == '<') { if (*src == '<') src++; *dst = ' '; } dst++; } *dst = '\0'; } return str; } /* prettify language */ static char *prettify_language(char *str) { if (str != NULL) { switch (strlen(str)) { case 8: memmove(str+7, str+6, 1+strlen(str+6)); str[6] = ','; /* fall through */ case 6: memmove(str+5, str+4, 1+strlen(str+4)); str[4] = ','; /* fall through */ case 4: memmove(str+3, str+2, 1+strlen(str+2)); str[2] = ','; /* fall through */ case 2: return str; } } return NULL; } /* convert the raw ISO-5218 SEX value to an english word */ static char *prettify_gender(char *str) { if (str != NULL) { switch (*str) { case '0': return "unknown"; case '1': return "male"; case '2': return "female"; case '9': return "not applicable"; } } return NULL; } static void display_data(const struct ef_name_map *mapping, char *value) { if (mapping != NULL && value != NULL) { if (mapping->prettify_value != NULL && !opt_raw) value = mapping->prettify_value(value); if (value != NULL) { if (exec_program) { char *envvar; envvar = malloc(strlen(mapping->env_name) + strlen(value) + 2); if (envvar != NULL) { strcpy(envvar, mapping->env_name); strcat(envvar, "="); strcat(envvar, value); putenv(envvar); } } else { const char *label = mapping->name; printf("%s:%*s%s\n", label, 10-strlen(label), "", value); } } } } static int decode_options(int argc, char **argv) { int c; while ((c = getopt_long(argc, argv,"r:x:CUG:L:hwvV", options, (int *) 0)) != EOF) { 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++; if (pin) free(pin); pin = strdup(optarg); break; case 'C': opt_cardinfo++; actions++;; break; case 'U': opt_userinfo++; actions++;; break; case 'G': opt_genkey++; key_id = optarg[0] - '0'; actions++; break; case 'L': opt_keylen++; key_len = atoi(optarg); actions++; 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; default: util_print_usage_and_die(app_name, options, option_help, NULL); } } return optind; } static int do_userinfo(sc_card_t *card) { int i; unsigned char buf[2048]; for (i = 0; openpgp_data[i].ef != NULL; i++) { sc_path_t path; sc_file_t *file; size_t count; size_t offset = 0; int r; sc_format_path(openpgp_data[i].ef, &path); r = sc_select_file(card, &path, &file); if (r) { fprintf(stderr, "Failed to select EF %s: %s\n", openpgp_data[i].ef, sc_strerror(r)); return EXIT_FAILURE; } count = file->size; while (count > 0) { int c = count > sizeof(buf) ? sizeof(buf) : count; r = sc_read_binary(card, offset, buf+offset, c, 0); if (r < 0) { fprintf(stderr, "%s: read failed - %s\n", openpgp_data[i].ef, sc_strerror(r)); return EXIT_FAILURE; } if (r != c) { fprintf(stderr, "%s: expecting %d, got only %d bytes\n", openpgp_data[i].ef, c, r); return EXIT_FAILURE; } offset += r; count -= r; } buf[file->size] = '\0'; if (file->size > 0) { display_data(openpgp_data + i, buf); } } return EXIT_SUCCESS; } /* 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 void bintohex(char *buf, int len) { static const char hextable[] = "0123456789ABCDEF"; int i; for (i = len - 1; i >= 0; i--) { unsigned char c = (unsigned char) buf[i]; buf[2 * i + 1] = hextable[c % 16]; buf[2 * i] = hextable[c / 16]; } } int do_genkey(sc_card_t *card, u8 key_id, unsigned int key_len) { int r; sc_cardctl_openpgp_keygen_info_t key_info; u8 fingerprints[60]; sc_path_t path; sc_file_t *file; if (key_id < 1 || key_id > 3) { printf("Unknown key ID %d.\n", key_id); return 1; } memset(&key_info, 0, sizeof(sc_cardctl_openpgp_keygen_info_t)); key_info.keytype = key_id; key_info.modulus_len = key_len; key_info.modulus = malloc(key_len/8); r = sc_card_ctl(card, SC_CARDCTL_OPENPGP_GENERATE_KEY, &key_info); free(key_info.modulus); if (r < 0) { printf("Failed to generate key. Error %s.\n", sc_strerror(r)); return 1; } sc_format_path("006E007300C5", &path); r = sc_select_file(card, &path, &file); r = sc_read_binary(card, 0, fingerprints, 60, 0); if (r < 0) { printf("Failed to retrieve fingerprints. Error %s.\n", sc_strerror(r)); return 1; } printf("Fingerprint:\n%s\n", (char *)sc_dump_hex(fingerprints + 20*(key_id - 1), 20)); return 0; } int do_verify(sc_card_t *card, u8 *type, u8* pin) { struct sc_pin_cmd_data data; int tries_left; int r; if (!type || !pin) return SC_ERROR_INVALID_ARGUMENTS; if (strncasecmp("CHV", type, 3) != 0) { printf("Invalid PIN type. Please use CHV1, CHV2 or CHV3.\n"); return SC_ERROR_INVALID_ARGUMENTS; } if (type[3] < '1' || type[3] > '3' || type[4] != '\0') { printf("Invalid PIN reference. Please use CHV1, CHV2 or CHV3.\n"); 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 = pin; data.pin1.len = strlen(pin); r = sc_pin_cmd(card, &data, &tries_left); return r; } 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_FAILURE; /* 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; r = sc_context_create(&ctx, &ctx_param); if (r) { util_fatal("failed to establish context: %s\n", sc_strerror(r)); return EXIT_FAILURE; } if (verbose > 1) { ctx->debug = verbose; sc_ctx_log_to_file(ctx, "stderr"); } r = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose); if (r) { util_fatal("failed to connect to card: %s\n", sc_strerror(r)); return EXIT_FAILURE; } /* check card type */ if ((card->type != SC_CARD_TYPE_OPENPGP_V1) && (card->type != SC_CARD_TYPE_OPENPGP_V2)) { util_error("not an OpenPGP card"); 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_userinfo) exit_status |= do_userinfo(card); if (opt_verify && opt_pin) { exit_status |= do_verify(card, verifytype, pin); } if (opt_genkey) exit_status |= do_genkey(card, key_id, key_len); 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(EXIT_FAILURE); } out: sc_unlock(card); sc_disconnect_card(card); sc_release_context(ctx); exit(exit_status); } opensc-0.13.0/src/tools/cardos-tool.c0000644000015201777760000007745412057406034014367 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #ifdef ENABLE_OPENSSL #include #include #endif #include "libopensc/opensc.h" #include "util.h" 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'}, {"card-driver", 1, NULL, 'c'}, {"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]", "Forces the use of driver [auto-detect]", "Wait for a card to be inserted", "Verbose operation. Use several times to enable debug output.", }; static sc_context_t *ctx = NULL; static sc_card_t *card = NULL; static int cardos_info(void) { sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 1; } printf("Info : %s\n", apdu.resp); 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 1; } 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 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 { 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 (apdu.sw1 != 0x90 || apdu.sw2 != 0x00 || verbose) { fprintf(stderr, "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 1; } printf("Current life cycle: "); if (rbuf[0] == 0x34) { printf("%d (manufacturing)\n", rbuf[0]); } else if (rbuf[0] == 0x26) { printf("%d (initialization)\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 { 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 1; } if (rbuf[0] == 0x00) { printf("ATR Status: 0x%d ROM-ATR\n",rbuf[0]); } else if (rbuf[0] == 0x90) { 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 1; } 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 1; } printf("Free eeprom memory: %d\n", rbuf[0]<<8|rbuf[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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "Unable to determine current DF:\n"); fprintf(stderr, "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 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; DES_key_schedule ks_a, ks_b; DES_cblock des_in,des_out; unsigned int i,j; if (keylen != 16) { printf("key has wrong size, need 16 bytes, got %zd. 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 key using first 8 bytes of key */ DES_set_key((const_DES_cblock*) &key[0], &ks_a); /* prepare des key using second 8 bytes of key */ DES_set_key((const_DES_cblock*) &key[8], &ks_b); /* first block: XOR with IV and encrypt with key A IV is 8 bytes 00 */ for (i=0; i < 8; i++) des_in[i] = mac_input[i]^00; DES_ecb_encrypt(&des_in, &des_out, &ks_a, 1); /* all other blocks: XOR with prev. result and encrypt with key A */ for (j=1; j < (mac_input_len / 8); j++) { for (i=0; i < 8; i++) des_in[i] = mac_input[i+j*8]^des_out[i]; DES_ecb_encrypt(&des_in, &des_out, &ks_a, 1); } /* 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 */ for (i=0; i < 8; i++) des_in[i] = des_out[i]; DES_ecb_encrypt(&des_in, &des_out, &ks_b, 0); for (i=0; i < 8; i++) des_in[i] = des_out[i]; DES_ecb_encrypt(&des_in, &des_out, &ks_a, 1); /* 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 cleard 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 */ /* xor data and IV (8 bytes 00) to get input data */ for (i=0; i < 8; i++) des_in[i] = enc_input[i] ^ 00; /* encrypt with des2 (tripple des, but using keys A-B-A) */ DES_ecb2_encrypt(&des_in, &des_out, &ks_a, &ks_b, 1); /* copy encrypted bytes into output */ for (i=0; i < 8; i++) out[5+i] = des_out[i]; /* encrypt other blocks (usualy one) */ for (j=1; j < (enc_input_len / 8); j++) { /* xor data and prev. result to get input data */ for (i=0; i < 8; i++) des_in[i] = enc_input[i+j*8] ^ des_out[i]; /* encrypt with des2 (tripple des, but using keys A-B-A) */ DES_ecb2_encrypt(&des_in, &des_out, &ks_a, &ks_b, 1); /* copy encrypted bytes into output */ for (i=0; i < 8; i++) out[5+8*j+i] = des_out[i]; } 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 1; } if (apdu.resplen < 0x04) { printf("expected 4-6 bytes form GET DATA for startkey data, but got only %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 INITIALITATION, 23 PERSINALIZATION 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 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 secreat 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 CONTORL 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 1; } /* use GET DATA for lifecacle one 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 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 (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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 1; } if (apdu.resplen < 0x04) { printf("expected 4-6 bytes form GET DATA for startkey data, but got only %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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 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 (apdu.sw1 != 0x90 || apdu.sw2 != 00 || verbose) { fprintf(stderr, "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 1; } if (apdu.resplen < 0x04) { printf("expected 4-6 bytes form GET DATA for startkey data, but got only %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 *const argv[]) { int err = 0, r, c, long_optind = 0; int do_info = 0; int do_format = 0; int do_change_startkey = 0; int action_count = 0; const char *opt_driver = NULL; const char *opt_startkey = NULL; const char *opt_change_startkey = NULL; sc_context_param_t ctx_param; while (1) { c = getopt_long(argc, argv, "hifs:r:vdc:wS:", options, &long_optind); if (c == -1) break; 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 'c': opt_driver = optarg; break; case 'w': opt_wait = 1; break; } } /* create sc_context_t object */ 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; } if (verbose > 1) { ctx->debug = verbose; sc_ctx_log_to_file(ctx, "stderr"); } if (opt_driver != NULL) { err = sc_set_card_driver(ctx, opt_driver); if (err) { fprintf(stderr, "Driver '%s' not found!\n", opt_driver); err = 1; goto end; } } err = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose); if (err) 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); } if (ctx) sc_release_context(ctx); return err; } opensc-0.13.0/src/tools/Makefile.mak0000644000015201777760000000127712057406034014172 00000000000000TOPDIR = ..\.. !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 sc-hsm-tool.exe \ $(PROGRAMS_OPENSSL) $(TARGETS): $(TOPDIR)\win32\versioninfo.res util.obj all: $(TARGETS) .c.obj: cl $(COPTS) /c $< .c.exe: cl $(COPTS) /c $< link $(LINKFLAGS) /pdb:$*.pdb /out:$@ $*.obj util.obj \ ..\common\common.lib ..\scconf\scconf.lib ..\libopensc\opensc.lib \ ..\pkcs15init\pkcs15init.lib ..\common\libpkcs11.lib ..\common\libscdl.lib \ $(TOPDIR)\win32\versioninfo.res $(OPENSSL_LIB) gdi32.lib if EXIST $@.manifest mt -manifest $@.manifest -outputresource:$@;1 opensc-0.13.0/src/tools/util.c0000644000015201777760000002173712057406034013107 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #ifndef _WIN32 #include #else #include #endif #include #include "util.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_card(sc_context_t *ctx, sc_card_t **cardp, const char *reader_id, int do_wait, int verbose) { struct sc_reader *reader = NULL, *found = NULL; struct sc_card *card = NULL; int r; if (do_wait) { unsigned int event; if (sc_ctx_get_reader_count(ctx) == 0) { fprintf(stderr, "Waiting for a reader to be attached...\n"); r = sc_wait_for_event(ctx, SC_EVENT_READER_ATTACHED, &found, &event, -1, NULL); if (r < 0) { fprintf(stderr, "Error while waiting for a reader: %s\n", sc_strerror(r)); return 3; } r = sc_ctx_detect_readers(ctx); if (r < 0) { fprintf(stderr, "Error while refreshing readers: %s\n", sc_strerror(r)); return 3; } } fprintf(stderr, "Waiting for a card to be inserted...\n"); r = sc_wait_for_event(ctx, SC_EVENT_CARD_INSERTED, &found, &event, -1, NULL); if (r < 0) { fprintf(stderr, "Error while waiting for a card: %s\n", sc_strerror(r)); return 3; } reader = found; } else if (sc_ctx_get_reader_count(ctx) == 0) { fprintf(stderr, "No smart card readers found.\n"); return 1; } 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 * 3]; 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; unsigned int num; errno = 0; num = strtol(reader_id, &endptr, 0); if (!errno && endptr && *endptr == '\0') reader = sc_ctx_get_reader(ctx, 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 1; } if (sc_detect_card_presence(reader) <= 0) { fprintf(stderr, "Card not present.\n"); return 3; } } if (verbose) printf("Connecting to card in reader %s...\n", reader->name); r = sc_connect_card(reader, &card); if (r < 0) { fprintf(stderr, "Failed to connect to card: %s\n", sc_strerror(r)); return 1; } if (verbose) printf("Using card driver %s.\n", card->driver->name); r = sc_lock(card); if (r < 0) { fprintf(stderr, "Failed to lock card: %s\n", sc_strerror(r)); sc_disconnect_card(card); return 1; } *cardp = card; return 0; } void util_print_binary(FILE *f, const u8 *buf, int count) { int 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, int len, const char *sep) { int 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_and_die(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]); } exit(2); } const char * util_acl_to_str(const sc_acl_entry_t *e) { static char line[80], buf[10]; 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; default: strcpy(buf, "????"); break; } strcat(line, buf); strcat(line, " "); e = e->next; } line[strlen(line)-1] = 0; /* get rid of trailing space */ return line; } 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); 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; unsigned int i; #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 buf[i] = getchar(); #else buf[i] = _getch(); #endif if (buf[i] == 0 || buf[i] == 3) break; if (buf[i] == '\n' || buf[i] == '\r') break; } #ifndef _WIN32 tcsetattr (fileno (stdout), TCSAFLUSH, &old); fputs("\n", stdout); #endif if (buf[i] == 0 || buf[i] == 3) { free(buf); return -1; } buf[i] = 0; 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 i; } opensc-0.13.0/src/tools/pkcs15-crypt.c0000644000015201777760000002610012057406034014364 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #ifdef ENABLE_OPENSSL #include #include #include #endif #include "common/compat_getpass.h" #include "libopensc/opensc.h" #include "libopensc/pkcs15.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 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, }; static const struct option options[] = { { "sign", 0, NULL, 's' }, { "decipher", 0, NULL, 'c' }, { "key", 1, NULL, 'k' }, { "reader", 1, NULL, 'r' }, { "input", 1, NULL, 'i' }, { "output", 1, NULL, 'o' }, { "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[] = { "Performs digital signature operation", "Decipher operation", "Selects the private key ID to use", "Uses reader number ", "Selects the input file to use", "Outputs to file ", "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. Use several times to enable debug output.", }; 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[80]; char *pincode; struct sc_pkcs15_auth_info *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); } sprintf(buf, "Enter PIN [%s]: ", 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 int read_input(u8 *buf, int buflen) { FILE *inf; int c; inf = fopen(opt_input, "rb"); if (inf == NULL) { fprintf(stderr, "Unable to open '%s' for reading.\n", opt_input); return -1; } c = fread(buf, 1, buflen, inf); fclose(inf); if (c < 0) { perror("read"); return -1; } return c; } static int write_output(const u8 *buf, int 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, c, len; if (opt_input == NULL) { fprintf(stderr, "No input file specified.\n"); return 2; } 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) && (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; } r = sc_pkcs15_compute_signature(p15card, obj, opt_crypt_flags, buf, c, out, len); if (r < 0) { fprintf(stderr, "Compute signature failed: %s\n", sc_strerror(r)); return 1; } r = write_output(out, r); return 0; } static int decipher(struct sc_pkcs15_object *obj) { u8 buf[1024], out[1024]; int r, c, len; if (opt_input == NULL) { fprintf(stderr, "No input file specified.\n"); return 2; } 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; } r = sc_pkcs15_decipher(p15card, obj, opt_crypt_flags & SC_ALGORITHM_RSA_PAD_PKCS1, buf, c, out, len); if (r < 0) { fprintf(stderr, "Decrypt failed: %s\n", sc_strerror(r)); return 1; } r = write_output(out, r); return 0; } static int get_key(unsigned int usage, sc_pkcs15_object_t **result) { sc_pkcs15_object_t *key, *pin; 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) return 0; pincode = get_pin(pin); if (((pincode == NULL || *pincode == '\0')) && !(p15card->card->reader->capabilities & SC_READER_CAP_PIN_PAD)) return 5; r = sc_pkcs15_verify_pin(p15card, pin, (const u8 *)pincode, pincode ? strlen(pincode) : 0); if (r) { fprintf(stderr, "PIN code verification failed: %s\n", sc_strerror(r)); return 5; } free(pincode); if (verbose) fprintf(stderr, "PIN code correct.\n"); prev_pin = pin; } return 0; } int main(int argc, char * const argv[]) { int err = 0, r, c, long_optind = 0; int do_decipher = 0; int do_sign = 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:Rp:vw", options, &long_optind); if (c == -1) break; if (c == '?') util_print_usage_and_die(app_name, options, option_help, NULL); switch (c) { 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 '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_and_die(app_name, options, option_help, NULL); 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; r = sc_context_create(&ctx, &ctx_param); if (r) { fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); return 1; } if (verbose > 1) { ctx->debug = verbose; sc_ctx_log_to_file(ctx, "stderr"); } err = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose); 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); return 1; } 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, &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_unlock(card); sc_disconnect_card(card); } if (ctx) sc_release_context(ctx); return err; } opensc-0.13.0/src/tools/piv-tool.c0000644000015201777760000003434412057406034013701 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 #if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_EC) #include #endif #include #include #include #include #include #include #include #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/asn1.h" #include "util.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' }, { "card-driver", 1, NULL, 'c' }, { "wait", 0, NULL, 'w' }, { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } }; static const char *option_help[] = { "Prints 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,9B,9C or 9D", "Load a cert that has been gziped ", "Output file for cert or key", "Inout file for cert", "Sends an APDU in format AA:BB:CC:DD:EE:FF...", "Uses reader number [0]", "Forces the use of driver [auto-detect]", "Wait for a card to be inserted", "Verbose operation. Use several times to enable debug output.", }; 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; sc_path_t path; size_t derlen; u8 *der = NULL; u8 *body; size_t bodylen; int r; struct stat stat_buf; if((fp=fopen(object_file, "r"))==NULL){ printf("Cannot open object file, %s %s\n", (object_file)?object_file:"", strerror(errno)); return -1; } stat(object_file, &stat_buf); derlen = stat_buf.st_size; der = malloc(derlen); if (der == NULL) { printf("file %s is too big, %lu\n", object_file, (unsigned long)derlen); return-1 ; } if (1 != fread(der, derlen, 1, fp)) { printf("unable to read file %s\n",object_file); return -1; } /* 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"); return -1; } sc_format_path(object_id, &path); r = sc_select_file(card, &path, NULL); if (r < 0) { fprintf(stderr, "select file failed\n"); return -1; } /* leave 8 bits for flags, and pass in total length */ r = sc_write_binary(card, 0, der, derlen, derlen<<8); return r; } static int load_cert(const char * cert_id, const char * cert_file, int compress) { X509 * cert = NULL; FILE *fp; u8 buf[1]; size_t buflen = 1; sc_path_t path; u8 *der = NULL; u8 *p; size_t derlen; int r; if((fp=fopen(cert_file, "r"))==NULL){ printf("Cannot open cert file, %s %s\n", cert_file?cert_file:"", strerror(errno)); return -1; } if (compress) { /* file is gziped already */ struct stat stat_buf; stat(cert_file, &stat_buf); derlen = stat_buf.st_size; der = malloc(derlen); if (der == NULL) { printf("file %s is too big, %lu\n", cert_file, (unsigned long)derlen); return-1 ; } if (1 != fread(der, derlen, 1, fp)) { printf("unable to read file %s\n",cert_file); return -1; } } else { cert = PEM_read_X509(fp, &cert, NULL, NULL); if(cert == NULL){ printf("file %s does not conatin PEM-encoded certificate\n", cert_file); return -1 ; } derlen = i2d_X509(cert, NULL); der = malloc(derlen); p = der; i2d_X509(cert, &p); } fclose(fp); sc_hex_to_bin(cert_id, buf,&buflen); switch (buf[0]) { case 0x9a: sc_format_path("0101",&path); break; case 0x9b: sc_format_path("0500",&path); break; case 0x9c: sc_format_path("0100",&path); break; case 0x9d: sc_format_path("0102",&path); break; default: fprintf(stderr,"cert must be 9A, 9B, 9C or 9D\n"); return 2; } r = sc_select_file(card, &path, NULL); if (r < 0) { fprintf(stderr, "select file failed\n"); return -1; } /* 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); return r; } static int admin_mode(const char* admin_info) { int r; u8 opts[3]; size_t buflen = 2; if (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, 0, NULL, 0, NULL, 0, NULL, 0}; unsigned long expl; u8 expc[4]; #if OPENSSL_VERSION_NUMBER >= 0x00908000L && !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 0x9b: case 0x9c: case 0x9d: keydata.key_num = buf[0]; break; default: fprintf(stderr, ": must be 9A, 9B, 9C or 9D\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 OPENSSL_VERSION_NUMBER >= 0x00908000L && !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; } evpkey = EVP_PKEY_new(); if (keydata.key_bits > 0) { /* RSA key */ RSA * newkey = NULL; newkey = RSA_new(); if (newkey == NULL) { fprintf(stderr, "gen_key RSA_new failed %d\n",r); return -1; } newkey->n = BN_bin2bn(keydata.pubkey, keydata.pubkey_len, newkey->n); expl = keydata.exponent; expc[3] = (u8) expl & 0xff; expc[2] = (u8) (expl >>8) & 0xff; expc[1] = (u8) (expl >>16) & 0xff; expc[0] = (u8) (expl >>24) & 0xff; newkey->e = BN_bin2bn(expc, 4, newkey->e); if (verbose) RSA_print_fp(stdout, newkey,0); EVP_PKEY_assign_RSA(evpkey, newkey); } else { /* EC key */ #if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_EC) int i; BIGNUM *x; BIGNUM *y; EC_KEY * eckey = NULL; EC_GROUP * ecgroup = NULL; EC_POINT * ecpoint = NULL; ecgroup = EC_GROUP_new_by_curve_name(nid); EC_GROUP_set_asn1_flag(ecgroup, OPENSSL_EC_NAMED_CURVE); ecpoint = EC_POINT_new(ecgroup); /* PIV returns 04||x||y and x and y are the same size */ i = (keydata.ecpoint_len - 1)/2; x = BN_bin2bn(keydata.ecpoint + 1, i, NULL); y = BN_bin2bn(keydata.ecpoint + 1 + i, i, NULL) ; r = EC_POINT_set_affine_coordinates_GFp(ecgroup, ecpoint, x, y, NULL); eckey = EC_KEY_new(); r = EC_KEY_set_group(eckey, ecgroup); r = EC_KEY_set_public_key(eckey, ecpoint); if (verbose) EC_KEY_print_fp(stdout, eckey, 0); EVP_PKEY_assign_EC_KEY(evpkey, eckey); #else 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, 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 (r = 0; r < len0; r++) printf("%02X ", buf[r]); printf("\n"); r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); 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 * const argv[]) { int err = 0, r, c, long_optind = 0; 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 *opt_driver = NULL; 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; setbuf(stderr, NULL); setbuf(stdout, NULL); while (1) { c = getopt_long(argc, argv, "nA:G:O:Z:C:i:o:fvs:c:w", options, &long_optind); if (c == -1) break; if (c == '?') util_print_usage_and_die(app_name, options, option_help, NULL); switch (c) { case OPT_SERIAL: do_print_serial = 1; action_count++; break; case 's': opt_apdus = (char **) realloc(opt_apdus, (opt_apdu_count + 1) * sizeof(char *)); 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; 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 'c': opt_driver = optarg; break; case 'w': opt_wait = 1; break; } } if (action_count == 0) util_print_usage_and_die(app_name, options, option_help, NULL); CRYPTO_malloc_init(); ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); if (out_file) { bp = BIO_new(BIO_s_file()); BIO_write_filename(bp, (char *)out_file); } 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; r = sc_context_create(&ctx, &ctx_param); if (r != SC_SUCCESS) { fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); return 1; } /* Only change if not in opensc.conf */ if (verbose > 1 && ctx->debug == 0) { ctx->debug = verbose; sc_ctx_log_to_file(ctx, "stderr"); } if (action_count <= 0) goto end; if (opt_driver != NULL) { err = sc_set_card_driver(ctx, opt_driver); if (err) { fprintf(stderr, "Driver '%s' not found!\n", opt_driver); err = 1; goto end; } } err = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose); if (err) 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 (ctx) sc_release_context(ctx); ERR_print_errors_fp(stderr); return err; } opensc-0.13.0/src/tools/opensc-tool.c0000644000015201777760000004713412057406034014373 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 }; static const struct option options[] = { { "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' }, { "card-driver", 1, NULL, 'c' }, { "list-algorithms", 0, NULL, OPT_LIST_ALG }, { "wait", 0, NULL, 'w' }, { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } }; static const char *option_help[] = { "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 key, format: section:name:key", "Set configuration key, format: section:name:key:value", "Lists readers", "Lists all installed card drivers", "Recursively lists files stored on card", "Sends an APDU in format AA:BB:CC:DD:EE:FF...", "Uses reader number [0]", "Forces the use of driver [auto-detect]", "Lists algorithms supported by card", "Wait for a card to be inserted", "Verbose operation. Use several times to enable debug output.", }; 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 = section == NULL ? NULL : 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[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 = section == NULL ? NULL : 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[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 *card; 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, &card)) != SC_SUCCESS) { fprintf(stderr, " failed: %s\n", sc_strerror(r)); } else { printf(" %s %s %s\n", tmp, card->name, state & SC_READER_CARD_INUSE ? "[IN USE]" : ""); sc_disconnect_card(card); } } } } return 0; } static int list_drivers(void) { int i; if (ctx->card_drivers[0] == NULL) { printf("No card drivers installed!\n"); return 0; } printf("Configured card drivers:\n"); for (i = 0; ctx->card_drivers[i] != NULL; i++) { printf(" %-16s %s\n", ctx->card_drivers[i]->short_name, ctx->card_drivers[i]->name); } return 0; } static int print_file(sc_card_t *in_card, const sc_file_t *file, const sc_path_t *path, int depth) { 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_read_binary(in_card, 0, buf, file->size, 0); if (r > 0) util_hex_dump_asc(stdout, buf, r, 0); free(buf); } else { unsigned char buf[256]; int i; for (i=0; i < file->record_count; i++) { printf("Record %d\n", i); r = sc_read_record(in_card, i, buf, 256, 0); 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_APDU_BUFFER_SIZE]; r = sc_select_file(card, &path, &file); 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_list_files(card, files, sizeof(files)); 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_APDU_BUFFER_SIZE], rbuf[SC_MAX_APDU_BUFFER_SIZE]; size_t len0, 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 (r = 0; r < len0; r++) printf("%02X ", buf[r]); printf("\n"); r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); 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) 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"; const id2str_t alg_type_names[] = { { SC_ALGORITHM_RSA, "rsa" }, { SC_ALGORITHM_DSA, "dsa" }, { SC_ALGORITHM_EC, "ec" }, { 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" }, { 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, "pkcs1" }, { SC_ALGORITHM_RSA_PAD_ANSI, "ansi" }, { SC_ALGORITHM_RSA_PAD_ISO9796, "iso9796" }, { SC_ALGORITHM_RSA_HASH_SHA1, "sha1" }, { SC_ALGORITHM_RSA_HASH_MD5, "MD5" }, { SC_ALGORITHM_RSA_HASH_MD5_SHA1, "md5-sha1" }, { SC_ALGORITHM_RSA_HASH_RIPEMD160, "ripemd160" }, { SC_ALGORITHM_RSA_HASH_SHA256, "sha256" }, { SC_ALGORITHM_RSA_HASH_SHA384, "sha384" }, { SC_ALGORITHM_RSA_HASH_SHA512, "sha512" }, { SC_ALGORITHM_RSA_HASH_SHA224, "sha224" }, { 0, NULL } }; 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: %d\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 spcific 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; } int main(int argc, char * const 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_serial = 0; int do_print_name = 0; int do_list_algorithms = 0; int action_count = 0; const char *opt_driver = NULL; const char *opt_conf_entry = NULL; sc_context_param_t ctx_param; setbuf(stderr, NULL); setbuf(stdout, NULL); while (1) { c = getopt_long(argc, argv, "inlG:S:fr:vs:Dc:aw", options, &long_optind); if (c == -1) 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': opt_apdus = (char **) realloc(opt_apdus, (opt_apdu_count + 1) * sizeof(char *)); 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 'c': opt_driver = optarg; 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; } } if (action_count == 0) util_print_usage_and_die(app_name, options, option_help, NULL); if (do_info) { opensc_info(); action_count--; } 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; } if (verbose > 1) { ctx->debug = verbose; sc_ctx_log_to_file(ctx, "stderr"); } if (do_get_conf_entry) { if ((err = opensc_get_conf_entry (opt_conf_entry))) goto end; 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 = list_drivers())) goto end; 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; } } err = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose); if (err) goto end; if (do_print_atr) { if (verbose) { printf("Card ATR:\n"); util_hex_dump_asc(stdout, card->atr.value, card->atr.len, -1); } else { char tmp[SC_MAX_ATR_SIZE*3]; sc_bin_to_hex(card->atr.value, card->atr.len, tmp, sizeof(tmp) - 1, ':'); fprintf(stdout,"%s\n",tmp); } action_count--; } 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--; } end: if (card) { sc_unlock(card); sc_disconnect_card(card); } if (ctx) sc_release_context(ctx); return err; } opensc-0.13.0/src/tools/pkcs11-tool.c0000644000015201777760000041526212057406034014207 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #ifdef ENABLE_OPENSSL #include #if OPENSSL_VERSION_NUMBER >= 0x10000000L #include #endif #if OPENSSL_VERSION_NUMBER >= 0x00907000L #include #endif #include #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x00908000L && !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 "util.h" extern void *C_LoadModule(const char *name, CK_FUNCTION_LIST_PTR_PTR); extern CK_RV C_UnloadModule(void *module); #define NEED_SESSION_RO 0x01 #define NEED_SESSION_RW 0x02 static struct ec_curve_info { const char *name; const char *oid; const char *oid_encoded; size_t size; } ec_curve_infos[] = { {"secp192r1", "1.2.840.10045.3.1.1", "06082A8648CE3D030101", 192}, {"prime192r1", "1.2.840.10045.3.1.1", "06082A8648CE3D030101", 192}, {"ansiX9p192r1", "1.2.840.10045.3.1.1", "06082A8648CE3D030101", 192}, {"prime256v1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256}, {"secp256r1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256}, {"ansiX9p256r1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256}, {"secp384r1", "1.3.132.0.34", "06052B81040022", 384}, {"prime384v1", "1.3.132.0.34", "06052B81040022", 384}, {"ansiX9p384r1", "1.3.132.0.34", "06052B81040022", 384}, {"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}, {NULL, NULL, NULL, 0}, }; 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_NONREPUDIATION, OPT_PRIVATE, OPT_TEST_HOTPLUG, OPT_UNLOCK_PIN, OPT_PUK, OPT_NEW_PIN, OPT_LOGIN_TYPE, OPT_TEST_EC, OPT_DERIVE }; 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' }, { "sign", 0, NULL, 's' }, { "hash", 0, NULL, 'h' }, { "derive", 0, NULL, OPT_DERIVE }, { "mechanism", 1, NULL, 'm' }, { "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' }, { "key-type", 1, NULL, OPT_KEY_TYPE }, { "usage-sign", 0, NULL, OPT_KEY_USAGE_SIGN }, { "usage-decrypt", 0, NULL, OPT_KEY_USAGE_DECRYPT }, { "usage-nonrepudiation",0, NULL, OPT_KEY_USAGE_NONREPUDIATION }, { "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 }, { "token-label", 1, NULL, OPT_TOKEN_LABEL }, { "set-id", 1, NULL, 'e' }, { "attr-from", 1, NULL, OPT_ATTR_FROM }, { "input-file", 1, NULL, 'i' }, { "output-file", 1, NULL, 'o' }, { "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 }, { "test-ec", 0, NULL, OPT_TEST_EC }, { NULL, 0, NULL, 0 } }; static const char *option_help[] = { "Specify the module to load (mandatory)", "Show global token information", "List available slots", "List slots with tokens", "List mechanisms supported by the token", "Show objects on token", "Sign some data", "Hash some data", "Derive a secret key using another key and some data", "Specify mechanism (use -M for a list of supported mechanisms)", "Log into the token first", "Specify login type ('so', 'user', 'context-specific'; default:'user')", "Supply User PIN on the command line (if used in scripts: careful!)", "Supply User PUK on the command line", "Supply new User PIN on the command line", "Supply SO PIN on the command line (if used in scripts: careful!)", "Initialize the token, its label and its SO PIN (use with --label and --so-pin)", "Initialize the User PIN (use with --pin and --login)", "Change User PIN", "Unlock User PIN (without '--login' unlock in logged in session; otherwise '--login-type' has to be 'context-specific')", "Key pair generation", "Specify the type and length of the key to create, for example rsa:1024 or EC:prime256v1", "Specify 'sign' key usage flag", "Specify 'decrypt' key usage flag", "Specify 'nonrepudiation' key usage flag", "Write an object (key, cert, data) to the card", "Get object's CKA_VALUE attribute (use with --type)", "Delete an object", "Specify the application label of the data object (use with --type data)", "Specify the application ID of the data object (use with --type data)", "Specify the issuer in hexadecimal format (use with --type cert)", "Specify the subject in hexadecimal format (use with --type cert/privkey/pubkey)", "Specify the type of object (e.g. cert, privkey, pubkey, data)", "Specify the ID of the object", "Specify the label of the object", "Specify the ID of the slot to use", "Specify the description of the slot to use", "Specify the index of the slot to use", "Specify the token label of the slot to use", "Set the CKA_ID of an object, = the (new) CKA_ID", "Use to create some attributes when writing an object", "Specify the input file", "Specify the output file", "Test (best used with the --login or --pin option)", "Test hotplug capabilities (C_GetSlotList + C_WaitForSlotEvent)", "Test Mozilla-like keypair gen and cert req, =certfile", "Verbose operation. (Set OPENSC_DEBUG to enable OpenSC specific debugging)", "Set the CKA_PRIVATE attribute (object is only viewable after a login)", "Test EC (best used with the --login or --pin option)" }; static const char * app_name = "pkcs11-tool"; /* for utils.c */ static int verbose = 0; static const char * opt_input = NULL; static const char * opt_output = NULL; static const char * opt_module = NULL; static int opt_slot_set = 0; static CK_SLOT_ID opt_slot = 0; static const char * opt_slot_description = NULL; static const char * opt_token_label = NULL; static CK_ULONG opt_slot_index = 0; static int opt_slot_index_set = 0; static CK_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 char * opt_pin = NULL; static char * opt_so_pin = NULL; static char * opt_puk = NULL; static 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 int opt_is_private = 0; 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_nonrepudiation = 0; static void *module = NULL; static CK_FUNCTION_LIST_PTR p11 = NULL; static CK_SLOT_ID_PTR p11_slots = NULL; static CK_ULONG p11_num_slots = 0; static int suppress_warn = 0; struct flag_info { CK_FLAGS value; const char * name; }; struct mech_info { CK_MECHANISM_TYPE mech; const char * name; const char * short_name; }; struct x509cert_info { unsigned char subject[256]; int subject_len; unsigned char issuer[256]; 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, CK_OBJECT_CLASS); 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 sign_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_SESSION_HANDLE, CK_OBJECT_HANDLE *, CK_OBJECT_HANDLE *, const char *); static int 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(CK_SESSION_HANDLE, CK_OBJECT_CLASS, CK_OBJECT_HANDLE_PTR, const unsigned char *, size_t id_len, int obj_index); static int find_mechanism(CK_SLOT_ID, CK_FLAGS, CK_MECHANISM_TYPE_PTR, size_t, CK_MECHANISM_TYPE_PTR); 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 void p11_perror(const char *, CK_RV); static const char * CKR2Str(CK_ULONG res); static int p11_test(CK_SESSION_HANDLE session); static int test_card_detection(int); static int hex_to_bin(const char *in, CK_BYTE *out, size_t *outlen); static void test_kpgen_certwrite(CK_SLOT_ID slot, CK_SESSION_HANDLE session); static void test_ec(CK_SLOT_ID slot, 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); /* win32 needs this in open(2) */ #ifndef O_BINARY # define O_BINARY 0 #endif 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_sign = 0; int do_hash = 0; int do_derive = 0; int do_gen_keypair = 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; 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; CK_RV rv; #ifdef _WIN32 if (_set_fmode(_O_BINARY) == EINVAL) util_fatal("Cannot set FMODE to O_BINARY"); #endif #ifdef ENABLE_OPENSSL #if OPENSSL_VERSION_NUMBER >= 0x00907000L OPENSSL_config(NULL); #endif /* OpenSSL magic */ SSLeay_add_all_algorithms(); CRYPTO_malloc_init(); #endif while (1) { c = getopt_long(argc, argv, "ILMOTa:bd:e:hi:klm:o:p:scvty: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 '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 (!hex_to_bin(optarg, new_object_id, &new_object_id_len)) { printf("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, "pubkey") == 0) opt_object_class = CKO_PUBLIC_KEY; else if (strcmp(optarg, "data") == 0) opt_object_class = CKO_DATA; else { printf("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 (!hex_to_bin(optarg, opt_object_id, &opt_object_id_len)) { printf("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 'l': need_session |= NEED_SESSION_RW; opt_login = 1; break; case 'm': opt_mechanism_used = 1; opt_mechanism = p11_name_to_mechanism(optarg); break; case 'o': opt_output = optarg; break; case 'p': opt_pin = optarg; 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_RW; do_sign = 1; action_count++; break; case 't': need_session |= NEED_SESSION_RO; 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-label, --slot-index or --token-label can be used\n"); util_print_usage_and_die(app_name, options, option_help, NULL); } opt_slot_description = optarg; break; case OPT_SLOT_INDEX: if (opt_slot_set || opt_slot_description) { fprintf(stderr, "Error: Only one of --slot, --slot-label, --slot-index or --token-label can be used\n"); 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_TOKEN_LABEL: if (opt_slot_set || opt_slot_description || opt_slot_index_set) { fprintf(stderr, "Error: Only one of --slot, --slot-label, --slot-index or --token-label can be used\n"); 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: opt_new_pin = optarg; break; case OPT_PUK: opt_puk = optarg; 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 { printf("Unsupported login type \"%s\"\n", optarg); util_print_usage_and_die(app_name, options, option_help, NULL); } break; case OPT_SO_PIN: opt_so_pin = optarg; 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; break; case OPT_KEY_USAGE_DECRYPT: opt_key_usage_decrypt = 1; break; case OPT_KEY_USAGE_NONREPUDIATION: opt_key_usage_nonrepudiation = 1; break; case OPT_PRIVATE: opt_is_private = 1; 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: need_session |= NEED_SESSION_RW; do_derive = 1; action_count++; break; default: util_print_usage_and_die(app_name, options, option_help, NULL); } } if (opt_module == NULL) 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); module = C_LoadModule(opt_module, &p11); if (module == NULL) util_fatal("Failed to load pkcs11 module"); rv = p11->C_Initialize(NULL); if (rv != CKR_OK) p11_fatal("C_Initialize", rv); 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-index or --slot-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) { CK_TOKEN_INFO info; get_token_info(opt_slot, &info); if (!(info.flags & CKF_TOKEN_INITIALIZED)) util_fatal("Token not initialized\n"); 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) { printf("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; } if (do_sign || do_derive) { if (!find_object(session, CKO_PRIVATE_KEY, &object, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, 0)) util_fatal("Private key not found"); } /* before list objects, so we can see a derived key */ if (do_derive) derive_key(opt_slot, session, object); if (do_list_objects) list_objects(session, opt_object_class); if (do_sign) sign_data(opt_slot, session, object); if (do_hash) hash_data(opt_slot, session); if (do_gen_keypair) { CK_OBJECT_HANDLE hPublicKey, hPrivateKey; gen_keypair(session, &hPublicKey, &hPrivateKey, opt_key_type); } if (do_write_object) { if (opt_object_class_str == NULL) util_fatal("You should specify the object type with the -y option\n"); 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\n"); 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) util_fatal("You should specify at least one of the " "object ID, object label, application label or application ID\n"); 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\n"); if (opt_object_id_len == 0) util_fatal("You should specify the current ID with the -d option\n"); set_id_attr(session); } if (do_test) p11_test(session); if (do_test_kpgen_certwrite) test_kpgen_certwrite(opt_slot, session); if (do_test_ec) test_ec(opt_slot, session); end: if (session != CK_INVALID_HANDLE) { rv = p11->C_CloseSession(session); if (rv != CKR_OK) p11_fatal("C_CloseSession", rv); } 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_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); p11_slots = calloc(p11_num_slots, sizeof(CK_SLOT_ID)); if (p11_slots == NULL) { perror("calloc failed"); return; } 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 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; } 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))); } 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) 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; get_token_info(opt_slot, &info); /* Identify which pin to enter */ if (login_type == CKU_SO) pin = opt_so_pin; else if (login_type == CKU_USER) pin = opt_pin; else if (login_type == CKU_CONTEXT_SPECIFIC) pin = opt_pin ? opt_pin : 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) printf("Please enter SO PIN: "); else if (login_type == CKU_USER) 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("No PIN entered, exiting!\n"); 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) 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\n"); snprintf((char *) token_label, sizeof (token_label), "%-32.32s", opt_object_label); get_token_info(slot, &info); if (!(info.flags & CKF_PROTECTED_AUTHENTICATION_PATH)) { if (opt_so_pin == NULL) { printf("Please enter the new SO PIN: "); r = util_getpass(&new_pin, &len, stdin); if (r < 0) util_fatal("No PIN entered, exiting\n"); if (!new_pin || !*new_pin || strlen(new_pin) > 20) util_fatal("Invalid SO PIN\n"); strcpy(new_buf, new_pin); 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, exiting\n"); if (!new_pin || !*new_pin || strcmp(new_buf, new_pin) != 0) util_fatal("Different new SO PINs, exiting\n"); pin_allocated = 1; } else { new_pin = opt_so_pin; } if (!new_pin || !*new_pin) util_fatal("Invalid SO PIN\n"); } 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, aborting.\n"); if (!new_pin1 || !*new_pin1 || strlen(new_pin1) > 20) util_fatal("Invalid User PIN\n"); printf("Please enter the new PIN again: "); r = util_getpass(&new_pin2, &len2, stdin); if (r < 0) util_fatal("No PIN entered, aborting.\n"); if (!new_pin2 || !*new_pin2 || strcmp(new_pin1, new_pin2) != 0) util_fatal("Different new User PINs, exiting\n"); } } pin = opt_pin; if (!pin) pin = 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 = 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 (!(info.flags & CKF_PROTECTED_AUTHENTICATION_PATH)) { if (!opt_pin && !opt_so_pin) { printf("Please enter the current PIN: "); r = util_getpass(&old_pin, &len, stdin); if (r < 0) return 1; if (!old_pin || !*old_pin || strlen(old_pin) > 20) return 1; strcpy(old_buf, old_pin); old_pin = old_buf; } else { if (opt_so_pin) old_pin = opt_so_pin; else old_pin = opt_pin; } if (!opt_new_pin) { printf("Please enter the new PIN: "); r = util_getpass(&new_pin, &len, stdin); if (r < 0) return 1; if (!new_pin || !*new_pin || strlen(new_pin) > 20) return 1; strcpy(new_buf, new_pin); 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) return 1; } else { new_pin = opt_new_pin; } } 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 ? opt_pin : opt_puk; else if (login_type == -1) unlock_code = 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 = 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; 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) { printf(" different new PINs, exiting\n"); return -1; } if (!new_pin || !*new_pin || strlen(new_pin) > 20) 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; } 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_RV rv; CK_ULONG sig_len; int fd, r; if (!opt_mechanism_used) if (!find_mechanism(slot, CKF_SIGN|CKF_HW, NULL, 0, &opt_mechanism)) util_fatal("Sign mechanism not supported\n"); printf("Using signature algorithm %s\n", p11_mechanism_to_name(opt_mechanism)); memset(&mech, 0, sizeof(mech)); mech.mechanism = opt_mechanism; 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); r = read(fd, in_buffer, sizeof(in_buffer)); if (r < 0) util_fatal("Cannot read from %s: %m", opt_input); rv = CKR_CANCEL; if (r < (int) sizeof(in_buffer)) { rv = p11->C_SignInit(session, &mech, key); if (rv != CKR_OK) p11_fatal("C_SignInit", rv); sig_len = sizeof(sig_buffer); rv = p11->C_Sign(session, in_buffer, r, sig_buffer, &sig_len); } if (rv != CKR_OK) { rv = p11->C_SignInit(session, &mech, key); if (rv != CKR_OK) p11_fatal("C_SignInit", rv); do { rv = p11->C_SignUpdate(session, in_buffer, r); if (rv != CKR_OK) p11_fatal("C_SignUpdate", rv); r = read(fd, in_buffer, sizeof(in_buffer)); } while (r > 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 defined(ENABLE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_ECDSA) /* * PKCS11 implies the ECDSA sig is 2nLen, * OpenSSL expects sequence of {integer, integer} * so we will write it for OpenSSL if built with OpenSSL */ if (opt_mechanism == CKM_ECDSA) { int nLen; ECDSA_SIG * ecsig = NULL; unsigned char *p = NULL; int der_len; nLen = sig_len/2; ecsig = ECDSA_SIG_new(); ecsig->r = BN_bin2bn(sig_buffer, nLen, ecsig->r); ecsig->s = BN_bin2bn(sig_buffer + nLen, nLen, ecsig->s); der_len = i2d_ECDSA_SIG(ecsig, &p); printf("Writing OpenSSL ECDSA_SIG\n"); r = write(fd, p, der_len); free(p); ECDSA_SIG_free(ecsig); } else #endif /* ENABLE_OPENSSL && !OPENSSL_NO_EC && !OPENSSL_NO_ECDSA */ r = write(fd, sig_buffer, sig_len); if (r < 0) util_fatal("Failed to write to %s: %m", opt_output); if (fd != 1) close(fd); } 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, r; if (!opt_mechanism_used) if (!find_mechanism(slot, CKF_DIGEST, NULL, 0, &opt_mechanism)) util_fatal("Digest mechanism is not supported\n"); printf("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)) < 0) util_fatal("Cannot open %s: %m", opt_input); while ((r = read(fd, buffer, sizeof(buffer))) > 0) { rv = p11->C_DigestUpdate(session, buffer, r); 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); r = write(fd, buffer, hash_len); if (r < 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;} static int gen_keypair(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_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)}, {CKA_ENCRYPT, &_true, sizeof(_true)}, {CKA_VERIFY, &_true, sizeof(_true)}, {CKA_WRAP, &_true, sizeof(_true)}, }; int n_pubkey_attr = 5; 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)}, {CKA_DECRYPT, &_true, sizeof(_true)}, {CKA_SIGN, &_true, sizeof(_true)}, {CKA_UNWRAP, &_true, sizeof(_true)} }; int n_privkey_attr = 7; unsigned char *ecparams = NULL; size_t ecparams_size; CK_RV rv; if (type != NULL) { if (strncmp(type, "RSA:", strlen("RSA:")) == 0 || strncmp(type, "rsa:", strlen("rsa:")) == 0) { CK_ULONG key_length; const char *size = type + strlen("RSA:"); if (size == NULL) util_fatal("Unknown key type %s", type); key_length = (unsigned long)atol(size); if (key_length != 0) 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++; mechanism.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; } else if (!strncmp(type, "EC:", 3)) { int ii; 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) util_fatal("Unknown EC key params '%s'", type + 3); ecparams_size = strlen(ec_curve_infos[ii].oid_encoded) / 2; ecparams = malloc(ecparams_size); if (!ecparams) util_fatal("Allocation error", 0); if (!hex_to_bin(ec_curve_infos[ii].oid_encoded, ecparams, &ecparams_size)) { printf("Cannot convert \"%s\"\n", ec_curve_infos[ii].oid_encoded); util_print_usage_and_die(app_name, options, option_help, NULL); } FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_EC_PARAMS, ecparams, ecparams_size); n_pubkey_attr++; mechanism.mechanism = CKM_EC_KEY_PAIR_GEN; } else { util_fatal("Unknown key type %s", type); } } 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++; } 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; } #ifdef ENABLE_OPENSSL static void parse_certificate(struct x509cert_info *cert, unsigned char *data, int len) { X509 *x; unsigned char *p; const unsigned char *pp; int n; pp = data; x = d2i_X509(NULL, &pp, len); if (!x) { util_fatal("OpenSSL error during X509 certificate parsing"); } p = cert->subject; n = i2d_X509_NAME(x->cert_info->subject, &p); if (n < 0) util_fatal("OpenSSL error while encoding subject name"); if (n > (int)sizeof (cert->subject)) util_fatal("subject name too long"); cert->subject_len = n; p = cert->issuer; n = i2d_X509_NAME(x->cert_info->issuer, &p); if (n < 0) util_fatal("OpenSSL error while encoding issuer name"); if (n > (int)sizeof (cert->issuer)) util_fatal("issuer name too long"); cert->issuer_len = n; p = cert->serialnum; n = i2d_ASN1_INTEGER(x->cert_info->serialNumber, &p); if (n < 0) util_fatal("OpenSSL error while encoding serial number"); if (n > (int)sizeof (cert->serialnum)) util_fatal("serial number too long"); cert->serialnum_len = n; } static int do_read_private_key(unsigned char *data, size_t data_len, EVP_PKEY **key) { BIO *mem; BUF_MEM buf_mem; if (!key) return -1; buf_mem.data = malloc(data_len); if (!buf_mem.data) return -1; memcpy(buf_mem.data, data, data_len); buf_mem.max = buf_mem.length = data_len; mem = BIO_new(BIO_s_mem()); BIO_set_mem_buf(mem, &buf_mem, BIO_NOCLOSE); if (!strstr((char *)data, "-----BEGIN PRIVATE KEY-----")) *key = d2i_PrivateKey_bio(mem, NULL); else *key = PEM_read_bio_PrivateKey(mem, NULL, NULL, NULL); BIO_free(mem); if (*key == NULL) return -1; return 0; } #define RSA_GET_BN(LOCALNAME, BNVALUE) \ do { \ rsa->LOCALNAME = malloc(BN_num_bytes(BNVALUE)); \ if (!rsa->LOCALNAME) \ util_fatal("malloc() failure\n"); \ rsa->LOCALNAME##_len = BN_bn2bin(BNVALUE, rsa->LOCALNAME); \ } while (0) static int parse_rsa_private_key(struct rsakey_info *rsa, unsigned char *data, int len) { RSA *r = NULL; const unsigned char *p; p = data; r = d2i_RSAPrivateKey(NULL, &p, len); if (!r) { util_fatal("OpenSSL error during RSA private key parsing"); } RSA_GET_BN(modulus, r->n); RSA_GET_BN(public_exponent, r->e); RSA_GET_BN(private_exponent, r->d); RSA_GET_BN(prime_1, r->p); RSA_GET_BN(prime_2, r->q); RSA_GET_BN(exponent_1, r->dmp1); RSA_GET_BN(exponent_2, r->dmq1); RSA_GET_BN(coefficient, r->iqmp); return 0; } static void parse_rsa_public_key(struct rsakey_info *rsa, unsigned char *data, int len) { RSA *r = NULL; const unsigned char *p; p = data; r = d2i_RSA_PUBKEY(NULL, &p, len); if (!r) { r = d2i_RSAPublicKey(NULL, &p, len); } if (!r) { util_fatal("OpenSSL error during RSA public key parsing"); } RSA_GET_BN(modulus, r->n); RSA_GET_BN(public_exponent, r->e); } #if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) static int parse_gost_private_key(EVP_PKEY *evp_key, struct gostkey_info *gost) { EC_KEY *src = EVP_PKEY_get0(evp_key); unsigned char *pder; const BIGNUM *bignum; int nid, rv; if (!src) return -1; nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(EVP_PKEY_get0(evp_key))); 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; bignum = EC_KEY_get0_private_key(EVP_PKEY_get0(evp_key)); gost->private.len = BN_num_bytes(bignum); gost->private.value = malloc(gost->private.len); if (!gost->private.value) return -1; BN_bn2bin(bignum, gost->private.value); return 0; } #endif #endif #define MAX_OBJECT_SIZE 5000 /* Currently for certificates (-type cert), private keys (-type privkey), public keys (-type pubkey) and data objects (-type data). Note: only RSA private keys are supported. */ static int write_object(CK_SESSION_HANDLE session) { CK_BBOOL _true = TRUE; unsigned char contents[MAX_OBJECT_SIZE + 1]; int contents_len = 0; unsigned char certdata[MAX_OBJECT_SIZE]; int certdata_len = 0; FILE *f; CK_OBJECT_HANDLE cert_obj, privkey_obj, pubkey_obj, data_obj; CK_ATTRIBUTE cert_templ[20], privkey_templ[20], pubkey_templ[20], data_templ[20]; int n_cert_attr = 0, n_privkey_attr = 0, n_pubkey_attr = 0, n_data_attr = 0; struct sc_object_id oid; CK_RV rv; int need_to_parse_certdata = 0; unsigned char *oid_buf = NULL; #ifdef ENABLE_OPENSSL struct x509cert_info cert; struct rsakey_info rsa; struct gostkey_info gost; EVP_PKEY *evp_key = NULL; memset(&cert, 0, sizeof(cert)); memset(&rsa, 0, sizeof(rsa)); memset(&gost, 0, sizeof(gost)); #endif memset(contents, 0, sizeof(contents)); memset(certdata, 0, sizeof(certdata)); f = fopen(opt_file_to_write, "rb"); if (f == NULL) util_fatal("Couldn't open file \"%s\"\n", opt_file_to_write); contents_len = fread(contents, 1, sizeof(contents) - 1, f); if (contents_len < 0) util_fatal("Couldn't read from file \"%s\"\n", opt_file_to_write); fclose(f); contents[contents_len] = '\0'; if (opt_attr_from_file) { if (!(f = fopen(opt_attr_from_file, "rb"))) util_fatal("Couldn't open file \"%s\"\n", opt_attr_from_file); certdata_len = fread(certdata, 1, sizeof(certdata), f); if (certdata_len < 0) util_fatal("Couldn't read from file \"%s\"\n", opt_attr_from_file); fclose(f); need_to_parse_certdata = 1; } if (opt_object_class == CKO_CERTIFICATE && !opt_attr_from_file) { memcpy(certdata, contents, MAX_OBJECT_SIZE); certdata_len = contents_len; need_to_parse_certdata = 1; } if (need_to_parse_certdata) { #ifdef ENABLE_OPENSSL parse_certificate(&cert, certdata, certdata_len); #else util_fatal("No OpenSSL support, cannot parse certificate\n"); #endif } if (opt_object_class == CKO_PRIVATE_KEY) { #ifdef ENABLE_OPENSSL int rv; rv = do_read_private_key(contents, contents_len, &evp_key); if (rv) util_fatal("Cannot read private key\n"); if (evp_key->type == EVP_PKEY_RSA) { rv = parse_rsa_private_key(&rsa, contents, contents_len); } #if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) else if (evp_key->type == NID_id_GostR3410_2001) { rv = parse_gost_private_key(evp_key, &gost); } #endif else { util_fatal("Unsupported key type: 0x%X\n", evp_key->type); } if (rv) util_fatal("Cannot parse private key\n"); #else util_fatal("No OpenSSL support, cannot parse private key\n"); #endif } if (opt_object_class == CKO_PUBLIC_KEY) { #ifdef ENABLE_OPENSSL parse_rsa_public_key(&rsa, contents, contents_len); #else util_fatal("No OpenSSL support, cannot parse RSA public key\n"); #endif } if (opt_object_class == CKO_CERTIFICATE) { CK_OBJECT_CLASS clazz = CKO_CERTIFICATE; CK_CERTIFICATE_TYPE 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)); n_cert_attr = 4; 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++; } #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 } else if (opt_object_class == CKO_PRIVATE_KEY) { CK_OBJECT_CLASS 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++; } #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++; } if (evp_key->type == EVP_PKEY_RSA) { CK_KEY_TYPE 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 OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) else if (evp_key->type == NID_id_GostR3410_2001) { CK_KEY_TYPE 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 } else if (opt_object_class == CKO_PUBLIC_KEY) { CK_OBJECT_CLASS clazz = CKO_PUBLIC_KEY; CK_KEY_TYPE type = CKK_RSA; FILL_ATTR(pubkey_templ[0], CKA_CLASS, &clazz, sizeof(clazz)); FILL_ATTR(pubkey_templ[1], CKA_KEY_TYPE, &type, sizeof(type)); FILL_ATTR(pubkey_templ[2], CKA_TOKEN, &_true, sizeof(_true)); n_pubkey_attr = 3; if (opt_is_private != 0) { FILL_ATTR(data_templ[n_data_attr], CKA_PRIVATE, &_true, sizeof(_true)); n_data_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++; } #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++; } 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++; #endif } else if (opt_object_class == CKO_DATA) { CK_OBJECT_CLASS 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++; } 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\"\n", opt_application_id); if (sc_asn1_encode_object_id(&oid_buf, &len, &oid)) util_fatal("Cannot encode OID \"%s\"\n", 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++; } } else util_fatal("Writing of a \"%s\" type not (yet) supported\n", opt_object_class_str); 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 (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(session, opt_object_class, &obj, opt_object_id, opt_object_id_len, 0)) { printf("set_id(): coudn't find the object\n"); 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(CK_SESSION_HANDLE sess, CK_OBJECT_CLASS cls, CK_OBJECT_HANDLE_PTR ret, const unsigned char *id, size_t id_len, int obj_index) { CK_ATTRIBUTE attrs[2]; 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) { attrs[nattrs].type = CKA_ID; attrs[nattrs].pValue = (void *) id; attrs[nattrs].ulValueLen = id_len; 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 count; } 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 int 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) { unsigned ii, jj; for (jj=0; jjC_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; \ \ rv = p11->C_GetAttributeValue(sess, obj, &attr, 1); \ if (rv == CKR_OK) { \ 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 (pulCount) \ *pulCount = attr.ulValueLen / sizeof(TYPE); \ } else {\ p11_warn("C_GetAttributeValue(" #ATTR ")", rv); \ } \ return (TYPE *) attr.pValue; \ } /* * Define attribute accessors */ ATTR_METHOD(CLASS, CK_OBJECT_CLASS); #if 0 ATTR_METHOD(TOKEN, CK_BBOOL); ATTR_METHOD(LOCAL, CK_BBOOL); ATTR_METHOD(SENSITIVE, CK_BBOOL); ATTR_METHOD(ALWAYS_SENSITIVE, CK_BBOOL); ATTR_METHOD(NEVER_EXTRACTABLE, CK_BBOOL); #endif ATTR_METHOD(ALWAYS_AUTHENTICATE, CK_BBOOL); ATTR_METHOD(PRIVATE, CK_BBOOL); ATTR_METHOD(MODIFIABLE, CK_BBOOL); ATTR_METHOD(ENCRYPT, CK_BBOOL); ATTR_METHOD(DECRYPT, CK_BBOOL); ATTR_METHOD(SIGN, CK_BBOOL); #if 0 ATTR_METHOD(SIGN_RECOVER, CK_BBOOL); #endif ATTR_METHOD(VERIFY, CK_BBOOL); #if 0 ATTR_METHOD(VERIFY_RECOVER, CK_BBOOL); #endif ATTR_METHOD(WRAP, CK_BBOOL); ATTR_METHOD(UNWRAP, CK_BBOOL); ATTR_METHOD(DERIVE, CK_BBOOL); ATTR_METHOD(OPENSC_NON_REPUDIATION, CK_BBOOL); #if 0 ATTR_METHOD(EXTRACTABLE, CK_BBOOL); #endif ATTR_METHOD(KEY_TYPE, CK_KEY_TYPE); ATTR_METHOD(CERTIFICATE_TYPE, CK_CERTIFICATE_TYPE); ATTR_METHOD(MODULUS_BITS, CK_ULONG); VARATTR_METHOD(LABEL, char); VARATTR_METHOD(APPLICATION, char); VARATTR_METHOD(ID, unsigned char); VARATTR_METHOD(OBJECT_ID, unsigned char); VARATTR_METHOD(MODULUS, CK_BYTE); #ifdef ENABLE_OPENSSL VARATTR_METHOD(PUBLIC_EXPONENT, CK_BYTE); #endif VARATTR_METHOD(VALUE, unsigned char); VARATTR_METHOD(GOSTR3410_PARAMS, unsigned char); VARATTR_METHOD(EC_POINT, unsigned char); VARATTR_METHOD(EC_PARAMS, unsigned char); static void list_objects(CK_SESSION_HANDLE sess, CK_OBJECT_CLASS object_class) { CK_OBJECT_HANDLE object; CK_ULONG count; CK_RV rv; rv = p11->C_FindObjectsInit(sess, NULL, 0); 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; if ((int) object_class == -1 || object_class == getCLASS(sess, object)) 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; default: printf("Object %u, type %u\n", (unsigned int) obj, (unsigned int) cls); } } static void derive_key(CK_SLOT_ID slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) { unsigned char *value = NULL; CK_ULONG value_len = 0; 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_ECDH1_DERIVE_PARAMS ecdh_parms; CK_RV rv; int fd, r; CK_ATTRIBUTE newkey_template[] = { {CKA_TOKEN, &false, sizeof(false)}, /* session only object */ {CKA_CLASS, &newkey_class, sizeof(newkey_class)}, {CKA_KEY_TYPE, &newkey_type, sizeof(newkey_type)}, {CKA_ENCRYPT, &true, sizeof(true)}, {CKA_DECRYPT, &true, sizeof(true)} }; if (!opt_mechanism_used) if (!find_mechanism(slot, CKF_DERIVE|CKF_HW, NULL, 0, &opt_mechanism)) util_fatal("Derive mechanism not supported\n"); printf("Using derive algorithm 0x%8.8lx %s\n", opt_mechanism, p11_mechanism_to_name(opt_mechanism)); memset(&mech, 0, sizeof(mech)); mech.mechanism = opt_mechanism; switch(opt_mechanism) { #if defined(ENABLE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_ECDSA) case CKM_ECDH1_COFACTOR_DERIVE: case CKM_ECDH1_DERIVE: /* Use OpenSSL to read the other public key, and get the raw verion */ { unsigned char buf[512]; int len; BIO *bio_in = NULL; const EC_KEY *eckey = NULL; const EC_GROUP *ecgroup = NULL; const EC_POINT * ecpoint = NULL; bio_in = BIO_new(BIO_s_file()); if (BIO_read_filename(bio_in, opt_input) <= 0) util_fatal("Cannot open %s: %m", opt_input); eckey = d2i_EC_PUBKEY_bio(bio_in, NULL); if (!eckey) util_fatal("Cannot read EC key from %s", opt_input); ecpoint = EC_KEY_get0_public_key(eckey); ecgroup = EC_KEY_get0_group(eckey); if (!ecpoint || !ecgroup) util_fatal("Failed to parse other EC kry from %s", opt_input); len = EC_POINT_point2oct(ecgroup, ecpoint, POINT_CONVERSION_UNCOMPRESSED, buf, sizeof(buf),NULL); memset(&ecdh_parms, 0, sizeof(ecdh_parms)); ecdh_parms.kdf = CKD_NULL; ecdh_parms.ulSharedDataLen = 0; ecdh_parms.pSharedData = NULL; ecdh_parms.ulPublicDataLen = len; /* TODO drop header */ ecdh_parms.pPublicData = buf; /* Cheat to test */ mech.pParameter = &ecdh_parms; mech.ulParameterLen = sizeof(ecdh_parms); } break; #endif /* ENABLE_OPENSSL && !OPENSSL_NO_EC && !OPENSSL_NO_ECDSA */ /* TODO add RSA but do not have card to test */ default: util_fatal("mechanisum not supported for derive\n"); break; } rv = p11->C_DeriveKey(session, &mech, key, newkey_template, 5, &newkey); if (rv != CKR_OK) p11_fatal("C_DeriveKey", rv); /*TODO get the key value and write to stdout or file */ value = getVALUE(session, newkey, &value_len); if (value && value_len > 0) { if (opt_output == NULL) { fd = 1; } else { fd = open(opt_output, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, S_IRUSR|S_IWUSR); if (fd < 0) util_fatal("failed to open %s: %m", opt_output); } r = write(fd, value, value_len); if (r < 0) util_fatal("Failed to write to %s: %m", opt_output); if (fd != 1) close(fd); } } static void show_key(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) { CK_KEY_TYPE key_type = getKEY_TYPE(sess, obj); CK_ULONG size = 0; unsigned char *id, *oid, *value; const char *sepa; char *label; int pub = 1; int sec = 0; 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 (pub) printf("; RSA %lu bits\n", (unsigned long) getMODULUS_BITS(sess, obj)); else printf("; RSA \n"); break; case CKK_GOSTR3410: printf("; GOSTR3410 \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; break; case CKK_EC: printf("; EC"); if (pub) { unsigned char *bytes = NULL; unsigned int n; int ksize; bytes = getEC_POINT(sess, obj, &size); /* * (We only support uncompressed for now) * Uncompresed EC_POINT is DER OCTET STRING of "04||x||y" * So a "256" bit key has x and y of 32 bytes each * something like: "04 41 04||x||y" * Do simple size calculation based on DER encoding */ if ((size - 2) <= 127) ksize = (size - 3) * 4; else if ((size - 3) <= 255) ksize = (size - 4) * 4; else ksize = (size - 5) * 4; printf(" EC_POINT %d bits\n", ksize); if (bytes) { if ((CK_LONG)size > 0) { /* Will print the point here */ printf(" EC_POINT: "); for (n = 0; n < size; n++) printf("%02x", bytes[n]); printf("\n"); } free(bytes); } bytes = NULL; bytes = getEC_PARAMS(sess, obj, &size); if (bytes){ if ((CK_LONG)size > 0) { printf(" EC_PARAMS: "); for (n = 0; n < size; n++) printf("%02x", bytes[n]); printf("\n"); } free(bytes); } } else printf("\n"); break; case CKK_GENERIC_SECRET: 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); free(label); } 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"); free(id); } 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 && getSIGN(sess, obj)) { printf("%ssign", 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 || sec) && getWRAP(sess, obj)) { printf("%swrap", sepa); sepa = ", "; } if ((!pub || sec) && getUNWRAP(sess, obj)) { printf("%sunwrap", sepa); sepa = ", "; } if ((!pub || sec) && getDERIVE(sess, obj)) { printf("%sderive", sepa); sepa = ", "; } if (!*sepa) printf("none"); printf("\n"); if (!pub && getALWAYS_AUTHENTICATE(sess, obj)) printf(" Access: always authenticate\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; unsigned char *id; char *label; 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"); break; default: printf("; unknown cert type\n"); break; } if ((label = getLABEL(sess, obj, NULL)) != NULL) { printf(" label: %s\n", label); free(label); } if ((id = getID(sess, obj, &size)) != NULL && size) { unsigned int n; printf(" ID: "); for (n = 0; n < size; n++) printf("%02x", id[n]); printf("\n"); free(id); } } static void show_dobj(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) { unsigned char *oid_buf; char *label; CK_ULONG size = 0; suppress_warn = 1; printf("Data object %u\n", (unsigned int) obj); printf(" label: "); if ((label = getLABEL(sess, obj, NULL)) != NULL) { printf("'%s'\n", label); free(label); } else { printf("\n"); } printf(" application: "); if ((label = getAPPLICATION(sess, obj, NULL)) != NULL) { printf("'%s'\n", label); free(label); } 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: "); if (getMODIFIABLE(sess, obj)) printf(" modifiable"); if (getPRIVATE(sess, obj)) printf(" private"); if (!getMODIFIABLE(sess, obj) && !getPRIVATE(sess, obj)) printf("\n"); printf ("\n"); suppress_warn = 0; } 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); *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; } /* * 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; 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\"\n", opt_application_id); if (sc_asn1_encode_object_id(&oid_buf, &oid_buf_len, &oid)) util_fatal("Cannot encode OID \"%s\"\n", 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, 0); if (rv != CKR_OK) p11_fatal("find_object_with_attributes()", rv); else if (obj==CK_INVALID_HANDLE) util_fatal("object not found\n"); /* 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. */ value = getVALUE(session, obj, &len); if (value == NULL) util_fatal("get CKA_VALUE failed\n"); if (opt_output) { out = fopen(opt_output, "wb"); if (out==NULL) util_fatal("cannot open '%s'\n", opt_output); } else out = stdout; if (fwrite(value, 1, len, out) != len) util_fatal("cannot write to '%s'\n", opt_output); if (opt_output) fclose(out); 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'\n", opt_application_id); if (sc_asn1_encode_object_id(&oid_buf, &oid_buf_len, &oid)) util_fatal("Cannot encode OID \"%s\"\n", 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, 0); if (rv != CKR_OK) p11_fatal("find_object_with_attributes()", rv); else if (obj==CK_INVALID_HANDLE) util_fatal("object not found\n"); 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) { printf("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); printf("coudn'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_SHA_1, CKM_RIPEMD160, 0xffffff }; unsigned char *digests[] = { (unsigned char *) "\x7a\x08\xb0\x7e\x84\x64\x17\x03\xe5\xf2\xc8\x36\xaa\x59\xa1\x70", (unsigned char *) "\x29\xb0\xe7\x87\x82\x71\x64\x5f\xff\xb7\xee\xc7\xdb\x4a\x74\x73\xa1\xc0\x0b\xc1", (unsigned char *) "\xda\x79\xa5\x8f\xb8\x83\x3d\x61\xf6\x32\x16\x17\xe3\xfd\xf0\x56\x26\x5f\xb7\xcd" }; CK_ULONG digestLens[] = { 16, 20, 20 }; 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)) { printf("Digests: not implemented\n"); return errors; } else { printf("Digests:\n"); } /* 1st test */ 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); for (i = 0; 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; } #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; RSA *rsa; id = NULL; id = getID(session, privKeyObject, &idLen); if (id == NULL) { printf("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); printf("coudn't find the corresponding pubkey for validation\n"); return NULL; } free(id); switch(getKEY_TYPE(session, pubkeyObject)) { case CKK_RSA: pkey = EVP_PKEY_new(); rsa = RSA_new(); mod = getMODULUS(session, pubkeyObject, &modLen); exp = getPUBLIC_EXPONENT(session, pubkeyObject, &expLen); if ( !pkey || !rsa || !mod || !exp) { printf("public key not extractable\n"); if (pkey) free(pkey); if (rsa) free(rsa); if (mod) free(mod); if (exp) free(exp); return NULL; } rsa->n = BN_bin2bn(mod, modLen, NULL); rsa->e = BN_bin2bn(exp, expLen, NULL); EVP_PKEY_assign_RSA(pkey, rsa); free(mod); free(exp); return pkey; case CKK_DSA: case CKK_ECDSA: case CKK_GOSTR3410: break; default: printf("public key of unsupported type\n"); return NULL; } pubkey = getVALUE(session, pubkeyObject, &pubkeyLen); if (pubkey == NULL) { printf("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) { printf(" 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(), EVP_ripemd160(), EVP_sha256(), }; #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 (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 printf("unable to verify signature (compile with ENABLE_OPENSSL)\n"); #else if (!(pkey = get_public_key(session, privKeyObject))) return errors; EVP_VerifyInit(&md_ctx, evp_mds[evp_md_index]); EVP_VerifyUpdate(&md_ctx, verifyData, verifyDataLen); err = EVP_VerifyFinal(&md_ctx, sig1, sigLen1, 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 privKeyObject; CK_MECHANISM ck_mech = { CKM_MD5, NULL, 0 }; CK_MECHANISM_TYPE firstMechType; CK_SESSION_INFO sessionInfo; CK_ULONG i, j; unsigned char data[256]; 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, CKM_RIPEMD160_RSA_PKCS, 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_RO_USER_FUNCTIONS) == 0) { printf("Signatures: not logged in, skipping signature tests\n"); return errors; } if (!find_mechanism(sessionInfo.slotID, CKF_SIGN | CKF_HW, mechTypes, mechTypes_num, &firstMechType)) { printf("Signatures: not implemented\n"); return errors; } printf("Signatures (currently only RSA signatures)\n"); for (j = 0; find_object(sess, CKO_PRIVATE_KEY, &privKeyObject, NULL, 0, j); j++) { printf(" testing key %ld ", j); if ((label = getLABEL(sess, privKeyObject, NULL)) != NULL) { printf("(%s) ", label); free(label); } 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) { printf("Signatures: no private key found in this slot\n"); return 0; } data[0] = 0; data[1] = 1; /* 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; break; default: dataLen = sizeof(data); /* let's hope it's OK */ break; } ck_mech.mechanism = firstMechType; rv = p11->C_SignInit(sess, &ck_mech, privKeyObject); if (rv != CKR_OK) p11_fatal("C_SignInit", rv); 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 (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); 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 (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[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; 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); } /* 4rd test: the other signature keys */ for (i = 0; mechTypes[i] != 0xffffff; i++) if (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) { CK_ULONG modLenBits; label = getLABEL(sess, privKeyObject, NULL); modLenBits = get_private_key_length(sess, privKeyObject); modLenBytes = (modLenBits + 7) / 8; printf(" testing key %d (%u bits%s%s) with 1 signature mechanism", (int) (j-1), (int) modLenBits, label? ", label=" : "", label? label : ""); if (label) free(label); if (!getSIGN(sess, privKeyObject)) { printf(" -- can't be used to sign/verify, skipping\n"); continue; } else if (!modLenBytes) { printf(" -- can't be used to sign/verify, skipping: can't obtain modulus\n"); continue; } else { printf("\n"); } 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; } printf(" %s: ", p11_mechanism_to_name(*mech_type)); if (getALWAYS_AUTHENTICATE(session, priv_key)) login(session,CKU_CONTEXT_SPECIFIC); 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 key_len, i, errors = 0; 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_RO_USER_FUNCTIONS) == 0) { printf("Verify: not logged in, 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"); printf("\n"); 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) { printf(" -- can't get the modulus length, skipping\n"); continue; } errors += sign_verify(sess, priv_key, key_len, pub_key, i != 0); } if (i == 0) printf(" No private key found for testing\n"); return errors; } #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))); EVP_SealInit(&seal_ctx, algo, &key, &key_len, iv, &pkey, 1); /* Encrypt something */ len = sizeof(ciphered); EVP_SealUpdate(&seal_ctx, ciphered, &len, (const unsigned char *) "hello world", 11); ciphered_len = len; len = sizeof(ciphered) - ciphered_len; EVP_SealFinal(&seal_ctx, ciphered + ciphered_len, &len); 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 = key_len_ul; if (key == NULL) { printf("Could not get unwrapped key\n"); return 1; } if (key_len != EVP_CIPHER_key_length(algo)) { printf("Key length mismatch (%d != %d)\n", key_len, EVP_CIPHER_key_length(algo)); return 1; } EVP_DecryptInit(&seal_ctx, algo, key, iv); len = sizeof(cleartext); EVP_DecryptUpdate(&seal_ctx, cleartext, &len, ciphered, ciphered_len); cleartext_len = len; len = sizeof(cleartext) - len; EVP_DecryptFinal(&seal_ctx, cleartext + cleartext_len, &len); cleartext_len += len; if (cleartext_len != 11 || memcmp(cleartext, "hello world", 11)) { printf("resulting cleartext doesn't match input\n"); return 1; } printf("OK\n"); return 0; } #endif /* * Test unwrap functions */ static int test_unwrap(CK_SESSION_HANDLE sess) { int errors = 0; CK_RV rv; CK_OBJECT_HANDLE privKeyObject; CK_MECHANISM_TYPE firstMechType; CK_SESSION_INFO sessionInfo; CK_ULONG 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 | CKF_HW, NULL, 0, &firstMechType)) { printf("Unwrap: not implemented\n"); return errors; } printf("Key unwrap (RSA)\n"); for (j = 0; find_object(sess, CKO_PRIVATE_KEY, &privKeyObject, NULL, 0, j); j++) { printf(" testing key %ld ", j); if ((label = getLABEL(sess, privKeyObject, NULL)) != NULL) { printf("(%s) ", label); free(label); } 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); errors += wrap_unwrap(sess, EVP_cast5_cfb(), privKeyObject); #endif } return errors; } #ifdef ENABLE_OPENSSL static int encrypt_decrypt(CK_SESSION_HANDLE session, CK_MECHANISM_TYPE mech_type, CK_OBJECT_HANDLE privKeyObject) { EVP_PKEY *pkey; unsigned char orig_data[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', '\0'}; unsigned char encrypted[512], data[512]; CK_MECHANISM mech; CK_ULONG encrypted_len, data_len; int failed; CK_RV rv; printf(" %s: ", p11_mechanism_to_name(mech_type)); pkey = get_public_key(session, privKeyObject); if (pkey == NULL) return 0; if (EVP_PKEY_size(pkey) > (int)sizeof(encrypted)) { printf("Ciphertext buffer too small\n"); EVP_PKEY_free(pkey); return 0; } #if OPENSSL_VERSION_NUMBER >= 0x00909000L encrypted_len = EVP_PKEY_encrypt_old(encrypted, orig_data, sizeof(orig_data), pkey); #else encrypted_len = EVP_PKEY_encrypt(encrypted, orig_data, sizeof(orig_data), pkey); #endif EVP_PKEY_free(pkey); if (encrypted_len <= 0) { printf("Encryption failed, returning\n"); return 0; } mech.mechanism = mech_type; rv = p11->C_DecryptInit(session, &mech, privKeyObject); if (rv == CKR_MECHANISM_INVALID) { printf("Mechanism not supported\n"); return 0; } if (rv != CKR_OK) p11_fatal("C_DecryptInit", rv); data_len = encrypted_len; rv = p11->C_Decrypt(session, encrypted, encrypted_len, data, &data_len); if (rv != CKR_OK) p11_fatal("C_Decrypt", rv); if (mech_type == CKM_RSA_X_509) failed = (data[0] != 0) || (data[1] != 2) || (data_len <= sizeof(orig_data) - 2) || memcmp(orig_data, data + data_len - sizeof(orig_data), sizeof(orig_data)); else failed = data_len != sizeof(orig_data) || memcmp(orig_data, data, data_len); if (failed) { CK_ULONG n; printf("resulting cleartext doesn't match input\n"); printf(" Original:"); for (n = 0; n < sizeof(orig_data); n++) printf(" %02x", orig_data[n]); printf("\n"); 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; CK_OBJECT_HANDLE privKeyObject; CK_MECHANISM_TYPE *mechs = NULL; CK_SESSION_INFO sessionInfo; CK_ULONG j, n, num_mechs = 0; char *label; rv = p11->C_GetSessionInfo(sess, &sessionInfo); if (rv != CKR_OK) p11_fatal("C_OpenSession", rv); if ((sessionInfo.state & CKS_RO_USER_FUNCTIONS) == 0) { printf("Decryption: not logged in, 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 (RSA)\n"); for (j = 0; find_object(sess, CKO_PRIVATE_KEY, &privKeyObject, NULL, 0, j); j++) { printf(" testing key %ld ", j); if ((label = getLABEL(sess, privKeyObject, NULL)) != NULL) { printf("(%s) ", label); free(label); } if (!getDECRYPT(sess, privKeyObject)) { printf(" -- can't be used to decrypt, skipping\n"); continue; } printf("\n"); #ifndef ENABLE_OPENSSL printf("No OpenSSL support, unable to validate decryption\n"); n = 0; #else for (n = 0; n < num_mechs; n++) { errors += encrypt_decrypt(sess, mechs[n], privKeyObject); } #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); return 1; } rv = p11->C_GenerateRandom(session, buf1, 10); if (rv != CKR_OK) { p11_perror("C_GenerateRandom", rv); return 1; } rv = p11->C_GenerateRandom(session, buf1, 100); if (rv != CKR_OK) { p11_perror("C_GenerateRandom(buf1,100)", rv); return 1; } rv = p11->C_GenerateRandom(session, buf1, 0); if (rv != CKR_OK) { p11_perror("C_GenerateRandom(buf1,0)", rv); return 1; } rv = p11->C_GenerateRandom(session, buf2, 100); if (rv != CKR_OK) { p11_perror("C_GenerateRandom(buf2,100)", rv); return 1; } if (memcmp(buf1, buf2, 100) == 0) { printf(" ERR: C_GenerateRandom returned twice the same value!!!\n"); errors++; } printf(" seems to be OK\n"); return 0; } 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_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 void 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, *mod; 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; 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) { printf("ERR: no \"CKM_RSA_PKCS_KEY_PAIR_GEN\" found in the mechanism list\n"); return; } f = fopen(opt_file_to_write, "rb"); if (f == NULL) util_fatal("Couldn't open file \"%s\"\n", 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(session, &pub_key, &priv_key, opt_key_type)) return; tmp = getID(session, priv_key, (CK_ULONG *) &opt_object_id_len); if (opt_object_id == NULL || opt_object_id_len == 0) { printf("ERR: newly generated private key has no (or an empty) CKA_ID\n"); return; } memcpy(opt_object_id, tmp, opt_object_id_len); /* This is done in NSS */ mod = getMODULUS(session, priv_key, &mod_len); if (mod_len < 5 || mod_len > 10000) { /* should be resonable limits */ printf("ERR: GetAttribute(privkey, CKA_MODULUS) doesn't seem to work\n"); return; } 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); 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("ERR: C_Sign() didn't return CKR_BUFFER_TO_SMALL but %s\n", CKR2Str(rv)); return; } 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); rv = p11->C_Sign(session, data, data_len, sig, &sig_len); if (rv != CKR_OK) p11_fatal("C_SignInit", rv); 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("\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 allready 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); if (module == NULL) util_fatal("Failed to load pkcs11 module"); rv = p11->C_Initialize(NULL); 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)) return; printf("\n==> OK, successfull! Should work with Mozilla\n"); } 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, *ec_params, *ec_point; 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} }; 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("ERR: no 'CKM_EC_KEY_PAIR_GEN' found in the mechanism list\n"); return; } printf("*** Generating EC key pair ***\n"); if (!gen_keypair(session, &pub_key, &priv_key, opt_key_type)) return; tmp = getID(session, priv_key, (CK_ULONG *) &opt_object_id_len); if (opt_object_id == NULL || opt_object_id_len == 0) { printf("ERR: newly generated private key has no (or an empty) CKA_ID\n"); return; } memcpy(opt_object_id, tmp, opt_object_id_len); /* This is done in NSS */ ec_params = 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; } ec_point = 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_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("*** Do a signature ***\n"); data = data_to_sign; data_len = sizeof(data_to_sign); rv = p11->C_SignInit(session, &mech, priv_key); if (rv != CKR_OK) p11_fatal("C_SignInit", rv); 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("ERR: C_Sign() didn't return CKR_BUFFER_TO_SMALL but %s\n", CKR2Str(rv)); return; } 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_fatal("C_SetAttributeValue", rv); printf("==> OK\n"); } 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) { strcat(buffer, sepa); strcat(buffer, list->name); 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_RNG, "rng" }, { CKF_WRITE_PROTECTED, "readonly" }, { CKF_LOGIN_REQUIRED, "login required" }, { CKF_USER_PIN_INITIALIZED, "PIN initialized" }, { CKF_PROTECTED_AUTHENTICATION_PATH, "PIN pad present" }, { 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_LOCKED, "user PIN locked" }, { 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 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)\n", 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); } static int hex_to_bin(const char *in, unsigned char *out, size_t *outlen) { size_t left, count = 0; if (in == NULL || *in == '\0') { *outlen = 0; return 1; } left = *outlen; while (*in != '\0') { int byte = 0, nybbles = 2; while (nybbles-- && *in && *in != ':') { char c; byte <<= 4; c = *in++; if ('0' <= c && c <= '9') c -= '0'; else if ('a' <= c && c <= 'f') c = c - 'a' + 10; else if ('A' <= c && c <= 'F') c = c - 'A' + 10; else { printf("hex_to_bin(): invalid char '%c' in hex string\n", c); *outlen = 0; return 0; } byte |= c; } if (*in == ':') in++; if (left <= 0) { printf("hex_to_bin(): hex string too long"); *outlen = 0; return 0; } out[count++] = (unsigned char) byte; left--; } *outlen = count; return 1; } static struct mech_info p11_mechanisms[] = { { CKM_RSA_PKCS_KEY_PAIR_GEN, "RSA-PKCS-KEY-PAIR-GEN", NULL }, { CKM_RSA_PKCS, "RSA-PKCS", NULL }, { CKM_RSA_9796, "RSA-9796", NULL }, { CKM_RSA_X_509, "RSA-X-509", NULL }, { CKM_MD2_RSA_PKCS, "MD2-RSA-PKCS", NULL }, { CKM_MD5_RSA_PKCS, "MD5-RSA-PKCS", "rsa-md5" }, { CKM_SHA1_RSA_PKCS, "SHA1-RSA-PKCS", "rsa-sha1" }, { CKM_SHA256_RSA_PKCS, "SHA256-RSA-PKCS", "rsa-sha256" }, { CKM_SHA384_RSA_PKCS, "SHA384-RSA-PKCS", "rsa-sha384" }, { CKM_SHA512_RSA_PKCS, "SHA512-RSA-PKCS", "rsa-sha512" }, { CKM_RIPEMD128_RSA_PKCS, "RIPEMD128-RSA-PKCS", NULL }, { CKM_RIPEMD160_RSA_PKCS, "RIPEMD160-RSA-PKCS", "rsa-ripemd160" }, { CKM_RSA_PKCS_OAEP, "RSA-PKCS-OAEP", NULL }, { CKM_RSA_X9_31_KEY_PAIR_GEN,"RSA-X9-31-KEY-PAIR-GEN", NULL }, { CKM_RSA_X9_31, "RSA-X9-31", NULL }, { CKM_SHA1_RSA_X9_31, "SHA1-RSA-X9-31", NULL }, { CKM_RSA_PKCS_PSS, "RSA-PKCS-PSS", NULL }, { CKM_SHA1_RSA_PKCS_PSS, "SHA1-RSA-PKCS-PSS", NULL }, { CKM_SHA256_RSA_PKCS, "SHA256-RSA-PKCS-PSS", NULL }, { CKM_SHA384_RSA_PKCS, "SHA384-RSA-PKCS-PSS", NULL }, { CKM_SHA512_RSA_PKCS, "SHA512-RSA-PKCS-PSS", NULL }, { CKM_DSA_KEY_PAIR_GEN, "DSA-KEY-PAIR-GEN", NULL }, { CKM_DSA, "DSA", NULL }, { CKM_DSA_SHA1, "DSA-SHA1", NULL }, { CKM_DH_PKCS_KEY_PAIR_GEN,"DH-PKCS-KEY-PAIR-GEN", NULL }, { CKM_DH_PKCS_DERIVE, "DH-PKCS-DERIVE", NULL }, { CKM_X9_42_DH_KEY_PAIR_GEN,"X9-42-DH-KEY-PAIR-GEN", NULL }, { CKM_X9_42_DH_DERIVE, "X9-42-DH-DERIVE", NULL }, { CKM_X9_42_DH_HYBRID_DERIVE,"X9-42-DH-HYBRID-DERIVE", NULL }, { CKM_X9_42_MQV_DERIVE, "X9-42-MQV-DERIVE", NULL }, { CKM_RC2_KEY_GEN, "RC2-KEY-GEN", NULL }, { CKM_RC2_ECB, "RC2-ECB", NULL }, { CKM_RC2_CBC, "RC2-CBC", NULL }, { CKM_RC2_MAC, "RC2-MAC", NULL }, { CKM_RC2_MAC_GENERAL, "RC2-MAC-GENERAL", NULL }, { CKM_RC2_CBC_PAD, "RC2-CBC-PAD", NULL }, { CKM_RC4_KEY_GEN, "RC4-KEY-GEN", NULL }, { CKM_RC4, "RC4", NULL }, { CKM_DES_KEY_GEN, "DES-KEY-GEN", NULL }, { CKM_DES_ECB, "DES-ECB", NULL }, { CKM_DES_CBC, "DES-CBC", NULL }, { CKM_DES_MAC, "DES-MAC", NULL }, { CKM_DES_MAC_GENERAL, "DES-MAC-GENERAL", NULL }, { CKM_DES_CBC_PAD, "DES-CBC-PAD", NULL }, { CKM_DES2_KEY_GEN, "DES2-KEY-GEN", NULL }, { CKM_DES3_KEY_GEN, "DES3-KEY-GEN", NULL }, { CKM_DES3_ECB, "DES3-ECB", NULL }, { CKM_DES3_CBC, "DES3-CBC", NULL }, { CKM_DES3_MAC, "DES3-MAC", NULL }, { CKM_DES3_MAC_GENERAL, "DES3-MAC-GENERAL", NULL }, { CKM_DES3_CBC_PAD, "DES3-CBC-PAD", NULL }, { CKM_CDMF_KEY_GEN, "CDMF-KEY-GEN", NULL }, { CKM_CDMF_ECB, "CDMF-ECB", NULL }, { CKM_CDMF_CBC, "CDMF-CBC", NULL }, { CKM_CDMF_MAC, "CDMF-MAC", NULL }, { CKM_CDMF_MAC_GENERAL, "CDMF-MAC-GENERAL", NULL }, { CKM_CDMF_CBC_PAD, "CDMF-CBC-PAD", NULL }, { CKM_MD2, "MD2", NULL }, { CKM_MD2_HMAC, "MD2-HMAC", NULL }, { CKM_MD2_HMAC_GENERAL, "MD2-HMAC-GENERAL", NULL }, { CKM_MD5, "MD5", NULL }, { CKM_MD5_HMAC, "MD5-HMAC", NULL }, { CKM_MD5_HMAC_GENERAL, "MD5-HMAC-GENERAL", NULL }, { CKM_SHA_1, "SHA-1", NULL }, { CKM_SHA_1_HMAC, "SHA-1-HMAC", NULL }, { CKM_SHA_1_HMAC_GENERAL, "SHA-1-HMAC-GENERAL", NULL }, { CKM_SHA256, "SHA256", NULL }, { CKM_SHA384, "SHA384", NULL }, { CKM_SHA512, "SHA512", NULL }, { CKM_RIPEMD128, "RIPEMD128", NULL }, { CKM_RIPEMD128_HMAC, "RIPEMD128-HMAC", NULL }, { CKM_RIPEMD128_HMAC_GENERAL,"RIPEMD128-HMAC-GENERAL", NULL }, { CKM_RIPEMD160, "RIPEMD160", NULL }, { CKM_RIPEMD160_HMAC, "RIPEMD160-HMAC", NULL }, { CKM_RIPEMD160_HMAC_GENERAL,"RIPEMD160-HMAC-GENERAL", NULL }, { CKM_CAST_KEY_GEN, "CAST-KEY-GEN", NULL }, { CKM_CAST_ECB, "CAST-ECB", NULL }, { CKM_CAST_CBC, "CAST-CBC", NULL }, { CKM_CAST_MAC, "CAST-MAC", NULL }, { CKM_CAST_MAC_GENERAL, "CAST-MAC-GENERAL", NULL }, { CKM_CAST_CBC_PAD, "CAST-CBC-PAD", NULL }, { CKM_CAST3_KEY_GEN, "CAST3-KEY-GEN", NULL }, { CKM_CAST3_ECB, "CAST3-ECB", NULL }, { CKM_CAST3_CBC, "CAST3-CBC", NULL }, { CKM_CAST3_MAC, "CAST3-MAC", NULL }, { CKM_CAST3_MAC_GENERAL, "CAST3-MAC-GENERAL", NULL }, { CKM_CAST3_CBC_PAD, "CAST3-CBC-PAD", NULL }, { CKM_CAST5_KEY_GEN, "CAST5-KEY-GEN", NULL }, { CKM_CAST5_ECB, "CAST5-ECB", NULL }, { CKM_CAST5_CBC, "CAST5-CBC", NULL }, { CKM_CAST5_MAC, "CAST5-MAC", NULL }, { CKM_CAST5_MAC_GENERAL, "CAST5-MAC-GENERAL", NULL }, { CKM_CAST5_CBC_PAD, "CAST5-CBC-PAD", NULL }, { CKM_RC5_KEY_GEN, "RC5-KEY-GEN", NULL }, { CKM_RC5_ECB, "RC5-ECB", NULL }, { CKM_RC5_CBC, "RC5-CBC", NULL }, { CKM_RC5_MAC, "RC5-MAC", NULL }, { CKM_RC5_MAC_GENERAL, "RC5-MAC-GENERAL", NULL }, { CKM_RC5_CBC_PAD, "RC5-CBC-PAD", NULL }, { CKM_IDEA_KEY_GEN, "IDEA-KEY-GEN", NULL }, { CKM_IDEA_ECB, "IDEA-ECB", NULL }, { CKM_IDEA_CBC, "IDEA-CBC", NULL }, { CKM_IDEA_MAC, "IDEA-MAC", NULL }, { CKM_IDEA_MAC_GENERAL, "IDEA-MAC-GENERAL", NULL }, { CKM_IDEA_CBC_PAD, "IDEA-CBC-PAD", NULL }, { CKM_GENERIC_SECRET_KEY_GEN,"GENERIC-SECRET-KEY-GEN", NULL }, { CKM_CONCATENATE_BASE_AND_KEY,"CONCATENATE-BASE-AND-KEY", NULL }, { CKM_CONCATENATE_BASE_AND_DATA,"CONCATENATE-BASE-AND-DATA", NULL }, { CKM_CONCATENATE_DATA_AND_BASE,"CONCATENATE-DATA-AND-BASE", NULL }, { CKM_XOR_BASE_AND_DATA, "XOR-BASE-AND-DATA", NULL }, { CKM_EXTRACT_KEY_FROM_KEY,"EXTRACT-KEY-FROM-KEY", NULL }, { CKM_SSL3_PRE_MASTER_KEY_GEN,"SSL3-PRE-MASTER-KEY-GEN", NULL }, { CKM_SSL3_MASTER_KEY_DERIVE,"SSL3-MASTER-KEY-DERIVE", NULL }, { CKM_SSL3_KEY_AND_MAC_DERIVE,"SSL3-KEY-AND-MAC-DERIVE", NULL }, { CKM_SSL3_MASTER_KEY_DERIVE_DH,"SSL3-MASTER-KEY-DERIVE-DH", NULL }, { CKM_TLS_PRE_MASTER_KEY_GEN,"TLS-PRE-MASTER-KEY-GEN", NULL }, { CKM_TLS_MASTER_KEY_DERIVE,"TLS-MASTER-KEY-DERIVE", NULL }, { CKM_TLS_KEY_AND_MAC_DERIVE,"TLS-KEY-AND-MAC-DERIVE", NULL }, { CKM_TLS_MASTER_KEY_DERIVE_DH,"TLS-MASTER-KEY-DERIVE-DH", NULL }, { CKM_SSL3_MD5_MAC, "SSL3-MD5-MAC", NULL }, { CKM_SSL3_SHA1_MAC, "SSL3-SHA1-MAC", NULL }, { CKM_MD5_KEY_DERIVATION, "MD5-KEY-DERIVATION", NULL }, { CKM_MD2_KEY_DERIVATION, "MD2-KEY-DERIVATION", NULL }, { CKM_SHA1_KEY_DERIVATION,"SHA1-KEY-DERIVATION", NULL }, { CKM_PBE_MD2_DES_CBC, "PBE-MD2-DES-CBC", NULL }, { CKM_PBE_MD5_DES_CBC, "PBE-MD5-DES-CBC", NULL }, { CKM_PBE_MD5_CAST_CBC, "PBE-MD5-CAST-CBC", NULL }, { CKM_PBE_MD5_CAST3_CBC, "PBE-MD5-CAST3-CBC", NULL }, { CKM_PBE_MD5_CAST5_CBC, "PBE-MD5-CAST5-CBC", NULL }, { CKM_PBE_SHA1_CAST5_CBC, "PBE-SHA1-CAST5-CBC", NULL }, { CKM_PBE_SHA1_RC4_128, "PBE-SHA1-RC4-128", NULL }, { CKM_PBE_SHA1_RC4_40, "PBE-SHA1-RC4-40", NULL }, { CKM_PBE_SHA1_DES3_EDE_CBC,"PBE-SHA1-DES3-EDE-CBC", NULL }, { CKM_PBE_SHA1_DES2_EDE_CBC,"PBE-SHA1-DES2-EDE-CBC", NULL }, { CKM_PBE_SHA1_RC2_128_CBC,"PBE-SHA1-RC2-128-CBC", NULL }, { CKM_PBE_SHA1_RC2_40_CBC,"PBE-SHA1-RC2-40-CBC", NULL }, { CKM_PKCS5_PBKD2, "PKCS5-PBKD2", NULL }, { CKM_PBA_SHA1_WITH_SHA1_HMAC,"PBA-SHA1-WITH-SHA1-HMAC", NULL }, { CKM_KEY_WRAP_LYNKS, "KEY-WRAP-LYNKS", NULL }, { CKM_KEY_WRAP_SET_OAEP, "KEY-WRAP-SET-OAEP", NULL }, { CKM_SKIPJACK_KEY_GEN, "SKIPJACK-KEY-GEN", NULL }, { CKM_SKIPJACK_ECB64, "SKIPJACK-ECB64", NULL }, { CKM_SKIPJACK_CBC64, "SKIPJACK-CBC64", NULL }, { CKM_SKIPJACK_OFB64, "SKIPJACK-OFB64", NULL }, { CKM_SKIPJACK_CFB64, "SKIPJACK-CFB64", NULL }, { CKM_SKIPJACK_CFB32, "SKIPJACK-CFB32", NULL }, { CKM_SKIPJACK_CFB16, "SKIPJACK-CFB16", NULL }, { CKM_SKIPJACK_CFB8, "SKIPJACK-CFB8", NULL }, { CKM_SKIPJACK_WRAP, "SKIPJACK-WRAP", NULL }, { CKM_SKIPJACK_PRIVATE_WRAP,"SKIPJACK-PRIVATE-WRAP", NULL }, { CKM_SKIPJACK_RELAYX, "SKIPJACK-RELAYX", NULL }, { CKM_KEA_KEY_PAIR_GEN, "KEA-KEY-PAIR-GEN", NULL }, { CKM_KEA_KEY_DERIVE, "KEA-KEY-DERIVE", NULL }, { CKM_FORTEZZA_TIMESTAMP, "FORTEZZA-TIMESTAMP", NULL }, { CKM_BATON_KEY_GEN, "BATON-KEY-GEN", NULL }, { CKM_BATON_ECB128, "BATON-ECB128", NULL }, { CKM_BATON_ECB96, "BATON-ECB96", NULL }, { CKM_BATON_CBC128, "BATON-CBC128", NULL }, { CKM_BATON_COUNTER, "BATON-COUNTER", NULL }, { CKM_BATON_SHUFFLE, "BATON-SHUFFLE", NULL }, { CKM_BATON_WRAP, "BATON-WRAP", NULL }, { CKM_ECDSA_KEY_PAIR_GEN, "ECDSA-KEY-PAIR-GEN", NULL }, { CKM_ECDSA, "ECDSA", NULL }, { CKM_ECDSA_SHA1, "ECDSA-SHA1", NULL }, { CKM_ECDH1_DERIVE, "ECDH1-DERIVE", NULL }, { CKM_ECDH1_COFACTOR_DERIVE,"ECDH1-COFACTOR-DERIVE", NULL }, { CKM_ECMQV_DERIVE, "ECMQV-DERIVE", NULL }, { CKM_JUNIPER_KEY_GEN, "JUNIPER-KEY-GEN", NULL }, { CKM_JUNIPER_ECB128, "JUNIPER-ECB128", NULL }, { CKM_JUNIPER_CBC128, "JUNIPER-CBC128", NULL }, { CKM_JUNIPER_COUNTER, "JUNIPER-COUNTER", NULL }, { CKM_JUNIPER_SHUFFLE, "JUNIPER-SHUFFLE", NULL }, { CKM_JUNIPER_WRAP, "JUNIPER-WRAP", NULL }, { CKM_FASTHASH, "FASTHASH", NULL }, { CKM_AES_KEY_GEN, "AES-KEY-GEN", NULL }, { CKM_AES_ECB, "AES-ECB", NULL }, { CKM_AES_CBC, "AES-CBC", NULL }, { CKM_AES_MAC, "AES-MAC", NULL }, { CKM_AES_MAC_GENERAL, "AES-MAC-GENERAL", NULL }, { CKM_AES_CBC_PAD, "AES-CBC-PAD", NULL }, { CKM_GOSTR3410_KEY_PAIR_GEN,"GOSTR3410-KEY-PAIR-GEN", NULL }, { CKM_GOSTR3410, "GOSTR3410", NULL }, { CKM_GOSTR3410_WITH_GOSTR3411,"GOSTR3410-WITH-GOSTR3411", NULL }, { CKM_GOSTR3411, "GOSTR3411", NULL }, { CKM_DSA_PARAMETER_GEN, "DSA-PARAMETER-GEN", NULL }, { CKM_DH_PKCS_PARAMETER_GEN,"DH-PKCS-PARAMETER-GEN", NULL }, { CKM_X9_42_DH_PARAMETER_GEN,"X9-42-DH-PARAMETER-GEN", NULL }, { 0, NULL, NULL } }; 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-%lu", (unsigned long) mech); return temp; } static CK_MECHANISM_TYPE p11_name_to_mechanism(const char *name) { struct mech_info *mi; 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\"\n", name); return 0; /* gcc food */ } 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"; } opensc-0.13.0/src/tools/eidenv.c0000644000015201777760000003150612057406034013377 00000000000000/* * eidenv.c: EstEID 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #ifndef _WIN32 #include #endif #include #include #include "common/compat_getopt.h" #include "libopensc/opensc.h" #include "libopensc/asn1.h" #include "libopensc/cards.h" #include "libopensc/esteid.h" #include "util.h" static char *opt_reader = NULL; 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 struct { const char *name; const char *env_name; int recno; } esteid_data[] = { {"Surname", "ESTEID_SURNAME", 1}, {"Given names 1", "ESTEID_GIVEN_NAMES1", 2}, {"Given names 2", "ESTEID_GIVEN_NAMES2", 3}, {"Sex", "ESTEID_SEX", 4}, {"Citizenship", "ESTEID_CITIZENSHIP", 5}, {"Date of birth", "ESTEID_DATE_OF_BIRTH", 6}, {"Personal ID code", "ESTEID_PERSONAL_ID", 7}, {"Document number", "ESTEID_DOCUMENT_NR", 8}, {"Expiry date", "ESTEID_EXPIRY_DATE", 9}, {"Place of birth", "ESTEID_PLACE_OF_BIRTH", 10}, {"Issuing date", "ESTEID_ISSUING_DATE", 11}, {"Permit type", "ESTEID_PERMIT_TYPE", 12}, {"Remark 1", "ESTEID_REMARK1", 13}, {"Remark 2", "ESTEID_REMARK2", 14}, {"Remark 3", "ESTEID_REMARK3", 15}, {"Remark 4", "ESTEID_REMARK4", 16}, {NULL, NULL, 0} }; static void show_version(void) { fprintf(stderr, "eidenv - EstEID utility version " PACKAGE_VERSION "\n" "\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)) != EOF) { 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); } } } static void do_esteid(sc_card_t *card) { sc_path_t path; int r, i; unsigned char buff[512]; if (stats) { int key_used[4]; sc_format_path("3f00eeee0013", &path); r = sc_select_file(card, &path, NULL); if (r) { fprintf(stderr, "Failed to select key counters: %s\n", sc_strerror(r)); goto out; } /* print the counters */ for (i = 1; i <= 4; i++) { r = sc_read_record(card, i, buff, 128, SC_RECORD_BY_REC_NR); key_used[i - 1] = 0xffffff - ((unsigned char) buff[0xc] * 65536 + (unsigned char) buff[0xd] * 256 + (unsigned char) buff[0xe]); } for (i = 0; i < 2; i++) { printf("Key generation #%d usage:\n\tsign: %d\n\tauth: %d\n", i, key_used[i], key_used[i + 2]); } exit_status = EXIT_SUCCESS; goto out; } /* Or just read the datafile */ sc_format_path("3f00eeee5044", &path); r = sc_select_file(card, &path, NULL); if (r) { fprintf(stderr, "Failed to select DF: %s\n", sc_strerror(r)); goto out; } for (i = 0; esteid_data[i].recno != 0; i++) { r = sc_read_record(card, esteid_data[i].recno, buff, 50, SC_RECORD_BY_REC_NR); if (r < 0) { fprintf (stderr, "Failed to read record %d from card: %s\n", esteid_data[i].recno, sc_strerror (r)); goto out; } buff[r] = '\0'; if (exec_program) { unsigned char * cp; cp = malloc(strlen(esteid_data[i].env_name) + strlen((char *) buff) + 2); if (cp) { strcpy((char *) cp,esteid_data[i].env_name); strcat((char *) cp,"="); strcat((char *) cp,(char *) buff); putenv((char *) cp); } } else { printf("%s: %s\n", esteid_data[i].name, buff); } } exit_status = EXIT_SUCCESS; out: return; } /* Select and read a transparent EF */ static int read_transp(sc_card_t *card, const char *pathstring, unsigned char *buf, int buflen) { 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); } } 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, 0); if (r) { fprintf(stderr, "Failed to connect to card: %s\n", sc_strerror(r)); return 1; } /* Check card type */ if (card->type == SC_CARD_TYPE_MCRD_ESTEID_V10 || card->type == SC_CARD_TYPE_MCRD_ESTEID_V11 || card->type == SC_CARD_TYPE_MCRD_ESTEID_V30) do_esteid(card); else if (card->type == SC_CARD_TYPE_BELPIC_EID) do_belpic(card); else { fprintf(stderr, "Not an EstEID or 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.13.0/src/tools/Makefile.am0000644000015201777760000000552112057406034014013 00000000000000include $(top_srcdir)/win32/ltrc.inc MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak noinst_HEADERS = util.h bin_PROGRAMS = opensc-tool opensc-explorer pkcs15-tool pkcs15-crypt \ pkcs11-tool cardos-tool eidenv openpgp-tool iasecc-tool if ENABLE_OPENSSL bin_PROGRAMS += cryptoflex-tool pkcs15-init netkey-tool piv-tool westcos-tool sc-hsm-tool endif # compile with $(PTHREAD_CFLAGS) to allow debugging with gdb AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_READLINE_CFLAGS) $(PTHREAD_CFLAGS) AM_CPPFLAGS = -I$(top_srcdir)/src LIBS = \ $(top_builddir)/src/libopensc/libopensc.la \ $(top_builddir)/src/common/libscdl.la \ $(top_builddir)/src/common/libcompat.la opensc_tool_SOURCES = opensc-tool.c util.c piv_tool_SOURCES = piv-tool.c util.c piv_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) opensc_explorer_SOURCES = opensc-explorer.c util.c opensc_explorer_LDADD = $(OPTIONAL_READLINE_LIBS) pkcs15_tool_SOURCES = pkcs15-tool.c util.c pkcs15_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) pkcs11_tool_SOURCES = pkcs11-tool.c util.c pkcs11_tool_LDADD = \ $(top_builddir)/src/common/libpkcs11.la \ $(OPTIONAL_OPENSSL_LIBS) 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_LDADD = $(OPTIONAL_OPENSSL_LIBS) iasecc_tool_SOURCES = iasecc-tool.c util.c iasecc_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) sc_hsm_tool_SOURCES = sc-hsm-tool.c util.c sc_hsm_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) if WIN32 opensc_tool_SOURCES += $(top_builddir)/win32/versioninfo.rc piv_tool_SOURCES += $(top_builddir)/win32/versioninfo.rc opensc_explorer_SOURCES += $(top_builddir)/win32/versioninfo.rc pkcs15_tool_SOURCES += $(top_builddir)/win32/versioninfo.rc pkcs11_tool_SOURCES += $(top_builddir)/win32/versioninfo.rc pkcs15_crypt_SOURCES += $(top_builddir)/win32/versioninfo.rc cryptoflex_tool_SOURCES += $(top_builddir)/win32/versioninfo.rc pkcs15_init_SOURCES += $(top_builddir)/win32/versioninfo.rc cardos_tool_SOURCES += $(top_builddir)/win32/versioninfo.rc eidenv_SOURCES += $(top_builddir)/win32/versioninfo.rc netkey_tool_SOURCES += $(top_builddir)/win32/versioninfo.rc westcos_tool_SOURCES += $(top_builddir)/win32/versioninfo.rc openpgp_tool_SOURCES += $(top_builddir)/win32/versioninfo.rc iasecc_tool_SOURCES += $(top_builddir)/win32/versioninfo.rc sc_hsm_tool_SOURCES += $(top_builddir)/win32/versioninfo.rc endif opensc-0.13.0/src/tools/sc-hsm-tool.c0000644000015201777760000005460512057406034014277 00000000000000/* * sc-hsm-tool.c: SmartCard-HSM Management Tool * * Copyright (C) 2001 Juha Yrjölä * Copyright (C) 2012 www.CardContact.de, Andreas Schwier, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/asn1.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; static int verbose = 0; // Some reasonable maximums #define MAX_CERT 4096 #define MAX_PRKD 256 #define MAX_KEY 512 #define MAX_WRAPPED_KEY (MAX_CERT + MAX_PRKD + MAX_KEY) enum { OPT_SO_PIN = 0x100, OPT_PIN, OPT_RETRY, OPT_PASSWORD }; static const struct option options[] = { { "initialize", 0, NULL, 'X' }, { "create-dkek-share", 1, NULL, 'C' }, { "import-dkek-share", 1, NULL, 'I' }, { "wrap-key", 1, NULL, 'W' }, { "unwrap-key", 1, NULL, 'U' }, { "dkek-shares", 1, NULL, 's' }, { "so-pin", 1, NULL, OPT_SO_PIN }, { "pin", 1, NULL, OPT_PIN }, { "pin-retry", 1, NULL, OPT_RETRY }, { "password", 1, NULL, OPT_PASSWORD }, { "key-reference", 1, NULL, 'i' }, { "force", 0, NULL, 'f' }, { "reader", 1, NULL, 'r' }, { "wait", 0, NULL, 'w' }, { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } }; static const char *option_help[] = { "Initialize token", "Create DKEK key share and save to ", "Import DKEK key share ", "Wrap key and save to ", "Unwrap key read from ", "Number of DKEK shares [No DKEK]", "Define security officer PIN (SO-PIN)", "Define user PIN", "Define user PIN retry counter", "Define password for DKEK share", "Key reference for key wrap/unwrap", "Force replacement of key and certificate", "Uses reader number [0]", "Wait for a card to be inserted", "Verbose operation. Use several times to enable debug output.", }; static sc_context_t *ctx = NULL; static sc_card_t *card = NULL; 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; 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); /* 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 void initialize(sc_card_t *card, const char *so_pin, const char *user_pin, int retry_counter, int dkek_shares) { sc_cardctl_sc_hsm_init_param_t param; size_t len; char *_so_pin = NULL, *_user_pin = NULL; int r; 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; } if (len != 8) { fprintf(stderr, "SO-PIN must be a hexadecimal string of 16 characters\n"); return; } 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; } if (param.user_pin_len > 16) { fprintf(stderr, "PIN must not be longer than 16 characters\n"); return; } 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; } 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; } if (retry_counter > 10) { fprintf(stderr, "Retry counter must not exceed 10\n"); return; } param.user_pin = (u8 *)_user_pin; param.user_pin_retry_counter = (u8)retry_counter; param.options[0] = 0x00; param.options[1] = 0x01; param.dkek_shares = (char)dkek_shares; 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)); } } static void import_dkek_share(sc_card_t *card, const char *inf, int iter, char *password) { sc_cardctl_sc_hsm_dkek_t dkekinfo; EVP_CIPHER_CTX ctx; FILE *in = NULL; u8 filebuff[64],key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH],outbuff[64]; char *pwd = NULL; int r, outlen; in = fopen(inf, "rb"); if (in == NULL) { perror(inf); return; } if (fread(filebuff, 1, sizeof(filebuff), in) != sizeof(filebuff)) { perror(inf); return; } fclose(in); if (memcmp(filebuff, magic, sizeof(magic) - 1)) { printf("File %s is not a DKEK share\n", inf); return; } if (password == NULL) { printf("Enter password to decrypt DKEK share : "); util_getpass(&pwd, NULL, stdin); printf("\n"); } else { pwd = password; } printf("Deciphering DKEK share, please wait...\n"); EVP_BytesToKey(EVP_aes_256_cbc(), EVP_md5(), filebuff + 8, (u8 *)pwd, strlen(pwd), iter, key, iv); OPENSSL_cleanse(pwd, strlen(pwd)); if (password == NULL) { free(pwd); } EVP_CIPHER_CTX_init(&ctx); EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, key, iv); if (!EVP_DecryptUpdate(&ctx, outbuff, &outlen, filebuff + 16, sizeof(filebuff) - 16)) { printf("Error decrypting DKEK share. Password correct ?\n"); return; } if (!EVP_DecryptFinal_ex(&ctx, outbuff + outlen, &r)) { printf("Error decrypting DKEK share. Password correct ?\n"); return; } 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)); EVP_CIPHER_CTX_cleanup(&ctx); 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)); return; } printf("DKEK share imported\n"); print_dkek_info(&dkekinfo); } static void create_dkek_share(sc_card_t *card, const char *outf, int iter, char *password) { EVP_CIPHER_CTX ctx; FILE *out = NULL; u8 filebuff[64],key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH],outbuff[64]; u8 dkek_share[32]; char *pwd = NULL; int r, outlen; if (password == NULL) { 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 save 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; } break; } OPENSSL_cleanse(refpwd, strlen(refpwd)); free(refpwd); } else { pwd = password; } memcpy(filebuff, magic, sizeof(magic) - 1); r = sc_get_challenge(card, filebuff + 8, 8); if (r < 0) { printf("Error generating random number failed with ", sc_strerror(r)); return; } printf("Enciphering DKEK share, please wait...\n"); EVP_BytesToKey(EVP_aes_256_cbc(), EVP_md5(), filebuff + 8, (u8 *)pwd, strlen(pwd), iter, key, iv); if (password == NULL) { OPENSSL_cleanse(pwd, strlen(pwd)); free(pwd); } r = sc_get_challenge(card, dkek_share, sizeof(dkek_share)); if (r < 0) { printf("Error generating random number failed with ", sc_strerror(r)); return; } EVP_CIPHER_CTX_init(&ctx); EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, key, iv); if (!EVP_EncryptUpdate(&ctx, filebuff + 16, &outlen, dkek_share, sizeof(dkek_share))) { printf("Error encrypting DKEK share\n"); return; } if (!EVP_EncryptFinal_ex(&ctx, filebuff + 16 + outlen, &r)) { printf("Error encrypting DKEK share\n"); return; } out = fopen(outf, "wb"); if (out == NULL) { perror(outf); return; } if (fwrite(filebuff, 1, sizeof(filebuff), out) != sizeof(filebuff)) { perror(outf); return; } fclose(out); OPENSSL_cleanse(filebuff, sizeof(filebuff)); EVP_CIPHER_CTX_cleanup(&ctx); printf("DKEK share created and saved to %s\n", outf); } 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) { return 0; } return len + (ptr - tlv); } static void wrap_key(sc_card_t *card, u8 keyid, const char *outf, const char *pin) { sc_cardctl_sc_hsm_wrapped_key_t wrapped_key; struct sc_pin_cmd_data data; sc_file_t *file = NULL; sc_path_t path; FILE *out = NULL; u8 fid[2]; u8 ef_prkd[MAX_PRKD]; u8 ef_cert[MAX_CERT]; 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 (pin == NULL) { printf("Enter User PIN : "); util_getpass(&lpin, NULL, stdin); printf("\n"); } else { lpin = (u8 *)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 = 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; } if (pin == NULL) { free(lpin); } wrapped_key.key_id = keyid; 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 return; } if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_WRAP_KEY, *) failed with %s\n", sc_strerror(r)); return; } fid[0] = PRKD_PREFIX; fid[1] = 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 = determineLength(ef_prkd, ef_prkd_len); } } fid[0] = EE_CERTIFICATE_PREFIX; fid[1] = 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 = determineLength(ef_cert, ef_cert_len); } } ptr = keyblob; // Encode key in octet string object sc_asn1_write_element(card->ctx, SC_ASN1_OCTET_STRING, wrapped_key.wrapped_key, wrapped_key.wrapped_key_length, &key, &key_len); memcpy(ptr, key, key_len); ptr += key_len; free(key); // 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 in octet string object sc_asn1_write_element(card->ctx, SC_ASN1_SEQUENCE|SC_ASN1_CONS, keyblob, ptr - keyblob, &key, &key_len); out = fopen(outf, "wb"); if (out == NULL) { perror(outf); free(key); return; } if (fwrite(key, 1, key_len, out) != key_len) { perror(outf); free(key); return; } free(key); fclose(out); } 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_file_t newfile; 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); 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 void unwrap_key(sc_card_t *card, u8 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, keybloblen; size_t len, olen, prkd_len, cert_len; in = fopen(inf, "rb"); if (in == NULL) { perror(inf); return; } if ((keybloblen = fread(keyblob, 1, sizeof(keyblob), in)) < 0) { perror(inf); return; } fclose(in); ptr = keyblob; if ((sc_asn1_read_tag(&ptr, keybloblen, &cla, &tag, &len) != SC_SUCCESS) || ((cla & SC_ASN1_TAG_CONSTRUCTED) != SC_ASN1_TAG_CONSTRUCTED) || ((tag != SC_ASN1_TAG_SEQUENCE)) ){ fprintf(stderr, "Invalid wrapped key format (Outer sequence).\n"); return; } if ((sc_asn1_read_tag(&ptr, len, &cla, &tag, &olen) != SC_SUCCESS) || (cla & SC_ASN1_TAG_CONSTRUCTED) || ((tag != SC_ASN1_TAG_OCTET_STRING)) ){ fprintf(stderr, "Invalid wrapped key format (Key binary).\n"); return; } 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] = 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; } } if ((cert_len > 0) && !force) { fid[0] = EE_CERTIFICATE_PREFIX; fid[1] = 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; } } if (pin == NULL) { printf("Enter User PIN : "); util_getpass(&lpin, NULL, stdin); printf("\n"); } else { lpin = (u8 *)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 = 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; } 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 return; } if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_UNWRAP_KEY, *) failed with %s\n", sc_strerror(r)); return; } 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; } } 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; } } printf("Key successfully imported\n"); } int main(int argc, char * const 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_create_dkek_share = 0; int do_wrap_key = 0; int do_unwrap_key = 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; char *opt_password = NULL; int opt_retry_counter = 3; int opt_dkek_shares = -1; int opt_key_reference = -1; int opt_force = 0; int opt_iter = 10000000; sc_context_param_t ctx_param; setbuf(stderr, NULL); setbuf(stdout, NULL); while (1) { c = getopt_long(argc, argv, "XC:I:W:U:s: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 'W': do_wrap_key = 1; opt_filename = optarg; action_count++; break; case 'U': do_unwrap_key = 1; opt_filename = optarg; action_count++; break; case OPT_PASSWORD: opt_password = optarg; break; case OPT_SO_PIN: opt_so_pin = optarg; break; case OPT_PIN: opt_pin = optarg; break; case OPT_RETRY: opt_retry_counter = atol(optarg); break; case 's': opt_dkek_shares = atol(optarg); break; case 'f': opt_force = 1; break; case 'i': opt_key_reference = atol(optarg); break; case 'r': opt_reader = optarg; break; case 'v': verbose++; break; case 'w': opt_wait = 1; break; } } CRYPTO_malloc_init(); ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); memset(&ctx_param, 0, sizeof(sc_context_param_t)); ctx_param.app_name = app_name; r = sc_context_create(&ctx, &ctx_param); if (r != SC_SUCCESS) { fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); return 1; } /* Only change if not in opensc.conf */ if (verbose > 1 && ctx->debug == 0) { ctx->debug = verbose; sc_ctx_log_to_file(ctx, "stderr"); } err = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose); if (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 (do_initialize) { initialize(card, opt_so_pin, opt_pin, opt_retry_counter, opt_dkek_shares); } if (do_create_dkek_share) { create_dkek_share(card, opt_filename, opt_iter, opt_password); } if (do_import_dkek_share) { import_dkek_share(card, opt_filename, opt_iter, opt_password); } if (do_wrap_key) { wrap_key(card, opt_key_reference, opt_filename, opt_pin); } if (do_unwrap_key) { unwrap_key(card, opt_key_reference, opt_filename, opt_pin, opt_force); } if (action_count == 0) { print_info(card, file); } end: if (card) { sc_unlock(card); sc_disconnect_card(card); } if (ctx) sc_release_context(ctx); ERR_print_errors_fp(stderr); return err; } opensc-0.13.0/src/tools/Makefile.in0000644000015201777760000007513312057406056014036 00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program 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. @SET_MAKE@ # Required to build Windows resource file VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in $(top_srcdir)/win32/ltrc.inc bin_PROGRAMS = opensc-tool$(EXEEXT) opensc-explorer$(EXEEXT) \ pkcs15-tool$(EXEEXT) pkcs15-crypt$(EXEEXT) \ pkcs11-tool$(EXEEXT) cardos-tool$(EXEEXT) eidenv$(EXEEXT) \ openpgp-tool$(EXEEXT) iasecc-tool$(EXEEXT) $(am__EXEEXT_1) @ENABLE_OPENSSL_TRUE@am__append_1 = cryptoflex-tool pkcs15-init netkey-tool piv-tool westcos-tool sc-hsm-tool @WIN32_TRUE@am__append_2 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_3 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_4 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_5 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_6 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_7 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_8 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_9 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_10 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_11 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_12 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_13 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_14 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_15 = $(top_builddir)/win32/versioninfo.rc @WIN32_TRUE@am__append_16 = $(top_builddir)/win32/versioninfo.rc subdir = src/tools ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = @ENABLE_OPENSSL_TRUE@am__EXEEXT_1 = cryptoflex-tool$(EXEEXT) \ @ENABLE_OPENSSL_TRUE@ pkcs15-init$(EXEEXT) netkey-tool$(EXEEXT) \ @ENABLE_OPENSSL_TRUE@ piv-tool$(EXEEXT) westcos-tool$(EXEEXT) \ @ENABLE_OPENSSL_TRUE@ sc-hsm-tool$(EXEEXT) am__installdirs = "$(DESTDIR)$(bindir)" PROGRAMS = $(bin_PROGRAMS) am__cardos_tool_SOURCES_DIST = cardos-tool.c util.c \ $(top_builddir)/win32/versioninfo.rc am__dirstamp = $(am__leading_dot)dirstamp @WIN32_TRUE@am__objects_1 = \ @WIN32_TRUE@ $(top_builddir)/win32/versioninfo.$(OBJEXT) am_cardos_tool_OBJECTS = cardos-tool.$(OBJEXT) util.$(OBJEXT) \ $(am__objects_1) cardos_tool_OBJECTS = $(am_cardos_tool_OBJECTS) am__DEPENDENCIES_1 = cardos_tool_DEPENDENCIES = $(am__DEPENDENCIES_1) AM_V_lt = $(am__v_lt_$(V)) am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) am__v_lt_0 = --silent am__cryptoflex_tool_SOURCES_DIST = cryptoflex-tool.c util.c \ $(top_builddir)/win32/versioninfo.rc am_cryptoflex_tool_OBJECTS = cryptoflex-tool.$(OBJEXT) util.$(OBJEXT) \ $(am__objects_1) cryptoflex_tool_OBJECTS = $(am_cryptoflex_tool_OBJECTS) cryptoflex_tool_DEPENDENCIES = $(am__DEPENDENCIES_1) am__eidenv_SOURCES_DIST = eidenv.c util.c \ $(top_builddir)/win32/versioninfo.rc am_eidenv_OBJECTS = eidenv.$(OBJEXT) util.$(OBJEXT) $(am__objects_1) eidenv_OBJECTS = $(am_eidenv_OBJECTS) eidenv_LDADD = $(LDADD) am__iasecc_tool_SOURCES_DIST = iasecc-tool.c util.c \ $(top_builddir)/win32/versioninfo.rc am_iasecc_tool_OBJECTS = iasecc-tool.$(OBJEXT) util.$(OBJEXT) \ $(am__objects_1) iasecc_tool_OBJECTS = $(am_iasecc_tool_OBJECTS) iasecc_tool_DEPENDENCIES = $(am__DEPENDENCIES_1) am__netkey_tool_SOURCES_DIST = netkey-tool.c \ $(top_builddir)/win32/versioninfo.rc am_netkey_tool_OBJECTS = netkey-tool.$(OBJEXT) $(am__objects_1) netkey_tool_OBJECTS = $(am_netkey_tool_OBJECTS) netkey_tool_DEPENDENCIES = $(am__DEPENDENCIES_1) am__openpgp_tool_SOURCES_DIST = openpgp-tool.c util.c \ $(top_builddir)/win32/versioninfo.rc am_openpgp_tool_OBJECTS = openpgp-tool.$(OBJEXT) util.$(OBJEXT) \ $(am__objects_1) openpgp_tool_OBJECTS = $(am_openpgp_tool_OBJECTS) openpgp_tool_DEPENDENCIES = $(am__DEPENDENCIES_1) am__opensc_explorer_SOURCES_DIST = opensc-explorer.c util.c \ $(top_builddir)/win32/versioninfo.rc am_opensc_explorer_OBJECTS = opensc-explorer.$(OBJEXT) util.$(OBJEXT) \ $(am__objects_1) opensc_explorer_OBJECTS = $(am_opensc_explorer_OBJECTS) opensc_explorer_DEPENDENCIES = $(am__DEPENDENCIES_1) am__opensc_tool_SOURCES_DIST = opensc-tool.c util.c \ $(top_builddir)/win32/versioninfo.rc am_opensc_tool_OBJECTS = opensc-tool.$(OBJEXT) util.$(OBJEXT) \ $(am__objects_1) opensc_tool_OBJECTS = $(am_opensc_tool_OBJECTS) opensc_tool_LDADD = $(LDADD) am__piv_tool_SOURCES_DIST = piv-tool.c util.c \ $(top_builddir)/win32/versioninfo.rc am_piv_tool_OBJECTS = piv-tool.$(OBJEXT) util.$(OBJEXT) \ $(am__objects_1) piv_tool_OBJECTS = $(am_piv_tool_OBJECTS) piv_tool_DEPENDENCIES = $(am__DEPENDENCIES_1) am__pkcs11_tool_SOURCES_DIST = pkcs11-tool.c util.c \ $(top_builddir)/win32/versioninfo.rc am_pkcs11_tool_OBJECTS = pkcs11-tool.$(OBJEXT) util.$(OBJEXT) \ $(am__objects_1) pkcs11_tool_OBJECTS = $(am_pkcs11_tool_OBJECTS) pkcs11_tool_DEPENDENCIES = $(top_builddir)/src/common/libpkcs11.la \ $(am__DEPENDENCIES_1) am__pkcs15_crypt_SOURCES_DIST = pkcs15-crypt.c util.c \ $(top_builddir)/win32/versioninfo.rc am_pkcs15_crypt_OBJECTS = pkcs15-crypt.$(OBJEXT) util.$(OBJEXT) \ $(am__objects_1) pkcs15_crypt_OBJECTS = $(am_pkcs15_crypt_OBJECTS) pkcs15_crypt_DEPENDENCIES = $(am__DEPENDENCIES_1) am__pkcs15_init_SOURCES_DIST = pkcs15-init.c util.c \ $(top_builddir)/win32/versioninfo.rc am_pkcs15_init_OBJECTS = pkcs15-init.$(OBJEXT) util.$(OBJEXT) \ $(am__objects_1) pkcs15_init_OBJECTS = $(am_pkcs15_init_OBJECTS) pkcs15_init_DEPENDENCIES = $(am__DEPENDENCIES_1) am__pkcs15_tool_SOURCES_DIST = pkcs15-tool.c util.c \ $(top_builddir)/win32/versioninfo.rc am_pkcs15_tool_OBJECTS = pkcs15-tool.$(OBJEXT) util.$(OBJEXT) \ $(am__objects_1) pkcs15_tool_OBJECTS = $(am_pkcs15_tool_OBJECTS) pkcs15_tool_DEPENDENCIES = $(am__DEPENDENCIES_1) am__sc_hsm_tool_SOURCES_DIST = sc-hsm-tool.c util.c \ $(top_builddir)/win32/versioninfo.rc am_sc_hsm_tool_OBJECTS = sc-hsm-tool.$(OBJEXT) util.$(OBJEXT) \ $(am__objects_1) sc_hsm_tool_OBJECTS = $(am_sc_hsm_tool_OBJECTS) sc_hsm_tool_DEPENDENCIES = $(am__DEPENDENCIES_1) am__westcos_tool_SOURCES_DIST = westcos-tool.c util.c \ $(top_builddir)/win32/versioninfo.rc am_westcos_tool_OBJECTS = westcos-tool.$(OBJEXT) util.$(OBJEXT) \ $(am__objects_1) westcos_tool_OBJECTS = $(am_westcos_tool_OBJECTS) westcos_tool_DEPENDENCIES = $(am__DEPENDENCIES_1) DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_$(V)) am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) am__v_CC_0 = @echo " CC " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_$(V)) am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) am__v_CCLD_0 = @echo " CCLD " $@; AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; SOURCES = $(cardos_tool_SOURCES) $(cryptoflex_tool_SOURCES) \ $(eidenv_SOURCES) $(iasecc_tool_SOURCES) \ $(netkey_tool_SOURCES) $(openpgp_tool_SOURCES) \ $(opensc_explorer_SOURCES) $(opensc_tool_SOURCES) \ $(piv_tool_SOURCES) $(pkcs11_tool_SOURCES) \ $(pkcs15_crypt_SOURCES) $(pkcs15_init_SOURCES) \ $(pkcs15_tool_SOURCES) $(sc_hsm_tool_SOURCES) \ $(westcos_tool_SOURCES) DIST_SOURCES = $(am__cardos_tool_SOURCES_DIST) \ $(am__cryptoflex_tool_SOURCES_DIST) $(am__eidenv_SOURCES_DIST) \ $(am__iasecc_tool_SOURCES_DIST) \ $(am__netkey_tool_SOURCES_DIST) \ $(am__openpgp_tool_SOURCES_DIST) \ $(am__opensc_explorer_SOURCES_DIST) \ $(am__opensc_tool_SOURCES_DIST) $(am__piv_tool_SOURCES_DIST) \ $(am__pkcs11_tool_SOURCES_DIST) \ $(am__pkcs15_crypt_SOURCES_DIST) \ $(am__pkcs15_init_SOURCES_DIST) \ $(am__pkcs15_tool_SOURCES_DIST) \ $(am__sc_hsm_tool_SOURCES_DIST) \ $(am__westcos_tool_SOURCES_DIST) HEADERS = $(noinst_HEADERS) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUG_FILE = @DEBUG_FILE@ DEFAULT_PCSC_PROVIDER = @DEFAULT_PCSC_PROVIDER@ DEFAULT_SM_MODULE = @DEFAULT_SM_MODULE@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GIT = @GIT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBRARY_BITNESS = @LIBRARY_BITNESS@ LIBS = \ $(top_builddir)/src/libopensc/libopensc.la \ $(top_builddir)/src/common/libscdl.la \ $(top_builddir)/src/common/libcompat.la LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCT_CFLAGS = @OPENCT_CFLAGS@ OPENCT_LIBS = @OPENCT_LIBS@ OPENSC_LT_AGE = @OPENSC_LT_AGE@ OPENSC_LT_CURRENT = @OPENSC_LT_CURRENT@ OPENSC_LT_OLDEST = @OPENSC_LT_OLDEST@ OPENSC_LT_REVISION = @OPENSC_LT_REVISION@ OPENSC_VERSION_FIX = @OPENSC_VERSION_FIX@ OPENSC_VERSION_MAJOR = @OPENSC_VERSION_MAJOR@ OPENSC_VERSION_MINOR = @OPENSC_VERSION_MINOR@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OPTIONAL_OPENCT_CFLAGS = @OPTIONAL_OPENCT_CFLAGS@ OPTIONAL_OPENCT_LIBS = @OPTIONAL_OPENCT_LIBS@ OPTIONAL_OPENSSL_CFLAGS = @OPTIONAL_OPENSSL_CFLAGS@ OPTIONAL_OPENSSL_LIBS = @OPTIONAL_OPENSSL_LIBS@ OPTIONAL_PCSC_CFLAGS = @OPTIONAL_PCSC_CFLAGS@ OPTIONAL_READLINE_CFLAGS = @OPTIONAL_READLINE_CFLAGS@ OPTIONAL_READLINE_LIBS = @OPTIONAL_READLINE_LIBS@ OPTIONAL_ZLIB_CFLAGS = @OPTIONAL_ZLIB_CFLAGS@ OPTIONAL_ZLIB_LIBS = @OPTIONAL_ZLIB_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCSC_CFLAGS = @PCSC_CFLAGS@ PCSC_LIBS = @PCSC_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RC = @RC@ READLINE_CFLAGS = @READLINE_CFLAGS@ READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ WIN_LIBPREFIX = @WIN_LIBPREFIX@ XSLTPROC = @XSLTPROC@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ git = @git@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkcs11dir = @pkcs11dir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ xslstylesheetsdir = @xslstylesheetsdir@ RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE) MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak noinst_HEADERS = util.h # compile with $(PTHREAD_CFLAGS) to allow debugging with gdb AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_READLINE_CFLAGS) $(PTHREAD_CFLAGS) AM_CPPFLAGS = -I$(top_srcdir)/src opensc_tool_SOURCES = opensc-tool.c util.c $(am__append_2) piv_tool_SOURCES = piv-tool.c util.c $(am__append_3) piv_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) opensc_explorer_SOURCES = opensc-explorer.c util.c $(am__append_4) opensc_explorer_LDADD = $(OPTIONAL_READLINE_LIBS) pkcs15_tool_SOURCES = pkcs15-tool.c util.c $(am__append_5) pkcs15_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) pkcs11_tool_SOURCES = pkcs11-tool.c util.c $(am__append_6) pkcs11_tool_LDADD = \ $(top_builddir)/src/common/libpkcs11.la \ $(OPTIONAL_OPENSSL_LIBS) pkcs15_crypt_SOURCES = pkcs15-crypt.c util.c $(am__append_7) pkcs15_crypt_LDADD = $(OPTIONAL_OPENSSL_LIBS) cryptoflex_tool_SOURCES = cryptoflex-tool.c util.c $(am__append_8) cryptoflex_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) pkcs15_init_SOURCES = pkcs15-init.c util.c $(am__append_9) pkcs15_init_LDADD = $(OPTIONAL_OPENSSL_LIBS) cardos_tool_SOURCES = cardos-tool.c util.c $(am__append_10) cardos_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) eidenv_SOURCES = eidenv.c util.c $(am__append_11) netkey_tool_SOURCES = netkey-tool.c $(am__append_12) netkey_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) westcos_tool_SOURCES = westcos-tool.c util.c $(am__append_13) westcos_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) openpgp_tool_SOURCES = openpgp-tool.c util.c $(am__append_14) openpgp_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) iasecc_tool_SOURCES = iasecc-tool.c util.c $(am__append_15) iasecc_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) sc_hsm_tool_SOURCES = sc-hsm-tool.c util.c $(am__append_16) sc_hsm_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj .rc $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/win32/ltrc.inc $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/tools/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/tools/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p || test -f $$p1; \ then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list $(top_builddir)/win32/$(am__dirstamp): @$(MKDIR_P) $(top_builddir)/win32 @: > $(top_builddir)/win32/$(am__dirstamp) $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) $(top_builddir)/win32/$(DEPDIR) @: > $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp) $(top_builddir)/win32/versioninfo.$(OBJEXT): \ $(top_builddir)/win32/$(am__dirstamp) \ $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp) cardos-tool$(EXEEXT): $(cardos_tool_OBJECTS) $(cardos_tool_DEPENDENCIES) @rm -f cardos-tool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(cardos_tool_OBJECTS) $(cardos_tool_LDADD) $(LIBS) cryptoflex-tool$(EXEEXT): $(cryptoflex_tool_OBJECTS) $(cryptoflex_tool_DEPENDENCIES) @rm -f cryptoflex-tool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(cryptoflex_tool_OBJECTS) $(cryptoflex_tool_LDADD) $(LIBS) eidenv$(EXEEXT): $(eidenv_OBJECTS) $(eidenv_DEPENDENCIES) @rm -f eidenv$(EXEEXT) $(AM_V_CCLD)$(LINK) $(eidenv_OBJECTS) $(eidenv_LDADD) $(LIBS) iasecc-tool$(EXEEXT): $(iasecc_tool_OBJECTS) $(iasecc_tool_DEPENDENCIES) @rm -f iasecc-tool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(iasecc_tool_OBJECTS) $(iasecc_tool_LDADD) $(LIBS) netkey-tool$(EXEEXT): $(netkey_tool_OBJECTS) $(netkey_tool_DEPENDENCIES) @rm -f netkey-tool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(netkey_tool_OBJECTS) $(netkey_tool_LDADD) $(LIBS) openpgp-tool$(EXEEXT): $(openpgp_tool_OBJECTS) $(openpgp_tool_DEPENDENCIES) @rm -f openpgp-tool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(openpgp_tool_OBJECTS) $(openpgp_tool_LDADD) $(LIBS) opensc-explorer$(EXEEXT): $(opensc_explorer_OBJECTS) $(opensc_explorer_DEPENDENCIES) @rm -f opensc-explorer$(EXEEXT) $(AM_V_CCLD)$(LINK) $(opensc_explorer_OBJECTS) $(opensc_explorer_LDADD) $(LIBS) opensc-tool$(EXEEXT): $(opensc_tool_OBJECTS) $(opensc_tool_DEPENDENCIES) @rm -f opensc-tool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(opensc_tool_OBJECTS) $(opensc_tool_LDADD) $(LIBS) piv-tool$(EXEEXT): $(piv_tool_OBJECTS) $(piv_tool_DEPENDENCIES) @rm -f piv-tool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(piv_tool_OBJECTS) $(piv_tool_LDADD) $(LIBS) pkcs11-tool$(EXEEXT): $(pkcs11_tool_OBJECTS) $(pkcs11_tool_DEPENDENCIES) @rm -f pkcs11-tool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(pkcs11_tool_OBJECTS) $(pkcs11_tool_LDADD) $(LIBS) pkcs15-crypt$(EXEEXT): $(pkcs15_crypt_OBJECTS) $(pkcs15_crypt_DEPENDENCIES) @rm -f pkcs15-crypt$(EXEEXT) $(AM_V_CCLD)$(LINK) $(pkcs15_crypt_OBJECTS) $(pkcs15_crypt_LDADD) $(LIBS) pkcs15-init$(EXEEXT): $(pkcs15_init_OBJECTS) $(pkcs15_init_DEPENDENCIES) @rm -f pkcs15-init$(EXEEXT) $(AM_V_CCLD)$(LINK) $(pkcs15_init_OBJECTS) $(pkcs15_init_LDADD) $(LIBS) pkcs15-tool$(EXEEXT): $(pkcs15_tool_OBJECTS) $(pkcs15_tool_DEPENDENCIES) @rm -f pkcs15-tool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(pkcs15_tool_OBJECTS) $(pkcs15_tool_LDADD) $(LIBS) sc-hsm-tool$(EXEEXT): $(sc_hsm_tool_OBJECTS) $(sc_hsm_tool_DEPENDENCIES) @rm -f sc-hsm-tool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(sc_hsm_tool_OBJECTS) $(sc_hsm_tool_LDADD) $(LIBS) westcos-tool$(EXEEXT): $(westcos_tool_OBJECTS) $(westcos_tool_DEPENDENCIES) @rm -f westcos-tool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(westcos_tool_OBJECTS) $(westcos_tool_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f $(top_builddir)/win32/versioninfo.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cardos-tool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cryptoflex-tool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eidenv.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iasecc-tool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netkey-tool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpgp-tool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/opensc-explorer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/opensc-tool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/piv-tool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11-tool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-crypt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-init.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-tool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sc-hsm-tool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/westcos-tool.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: CTAGS CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(bindir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -test -z "$(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp)" || rm -f $(top_builddir)/win32/$(DEPDIR)/$(am__dirstamp) -test -z "$(top_builddir)/win32/$(am__dirstamp)" || rm -f $(top_builddir)/win32/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ clean-generic clean-libtool ctags distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-binPROGRAMS install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags uninstall uninstall-am \ uninstall-binPROGRAMS .rc.lo: $(LTRCCOMPILE) -i "$<" -o "$@" .rc.o: $(RCCOMPILE) -i "$<" -o "$@" # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: opensc-0.13.0/src/tools/pkcs15-tool.c0000644000015201777760000015335312057406034014213 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #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" static const char *app_name = "pkcs15-tool"; static int opt_wait = 0; static int opt_no_cache = 0; static char * opt_auth_id; static char * opt_reader = NULL; static char * opt_cert = NULL; static char * opt_data = NULL; static char * opt_pubkey = NULL; static char * opt_outfile = NULL; static char * opt_bind_to_aid = NULL; static u8 * opt_newpin = NULL; static u8 * opt_pin = NULL; static u8 * opt_puk = NULL; static int verbose = 0; enum { OPT_CHANGE_PIN = 0x100, OPT_LIST_PINS, OPT_READER, OPT_PIN_ID, OPT_NO_CACHE, OPT_LIST_PUB, OPT_READ_PUB, #if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H)) OPT_READ_SSH, #endif OPT_PIN, OPT_NEWPIN, OPT_PUK, OPT_VERIFY_PIN, OPT_BIND_TO_AID, OPT_LIST_APPLICATIONS, OPT_LIST_SKEYS }; #define NELEMENTS(x) (sizeof(x)/sizeof((x)[0])) static int authenticate(sc_pkcs15_object_t *obj); static int pubkey_pem_encode(sc_pkcs15_pubkey_t *, sc_pkcs15_der_t *, sc_pkcs15_der_t *); static const struct option options[] = { { "learn-card", no_argument, NULL, 'L' }, { "list-applications", no_argument, NULL, OPT_LIST_APPLICATIONS }, { "read-certificate", required_argument, NULL, 'r' }, { "list-certificates", no_argument, NULL, 'c' }, { "read-data-object", required_argument, NULL, 'R' }, { "list-data-objects", no_argument, NULL, 'C' }, { "list-pins", no_argument, NULL, OPT_LIST_PINS }, { "list-secret-keys", no_argument, NULL, OPT_LIST_SKEYS }, { "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 }, #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 }, { "output", required_argument, NULL, 'o' }, { "no-cache", no_argument, NULL, OPT_NO_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' }, { NULL, 0, NULL, 0 } }; static const char *option_help[] = { "Stores card info to cache", "List the on-card PKCS#15 applications", "Reads certificate with ID ", "Lists certificates", "Reads data object with OID, applicationName or label ", "Lists data objects", "Lists PIN codes", "Lists secret keys", "Dump card objects", "Unblock PIN code", "Change PIN or PUK code", "Lists private keys", "Lists 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", #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)", "Outputs to file ", "Disable 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. Use several times to enable debug output.", }; 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 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%X]", 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; char guid[39]; int rv; printf("X.509 Certificate [%s]\n", 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)); rv = sc_pkcs15_get_guid(p15card, obj, 0, guid, sizeof(guid)); if (!rv) printf("\tGUID : %s\n", guid); print_access_rules(obj->access_rules, SC_PKCS15_MAX_ACCESS_RULES); rv = sc_pkcs15_read_certificate(p15card, cert_info, &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, ""); 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 (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 int 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"); return 0; } 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, "w"); 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]); printf("Dumping (%lu bytes) to file <%s>: <", (unsigned long) data_len, opt_outfile); for (i=0; i < data_len; i++) printf(" %02X", data[i]); printf(" >\n"); fclose(outf); } 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; if (sc_pkcs15_compare_id(&id, &cinfo->id) != 1) continue; if (verbose) printf("Reading certificate with ID '%s'\n", opt_cert); r = sc_pkcs15_read_certificate(p15card, cinfo, &cert); 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; if (!sc_format_oid(&oid, opt_data)) { if (!sc_compare_oid(&oid, &cinfo->app_oid)) continue; } else { if (strcmp(opt_data, cinfo->app_label) && strcmp(opt_data, objs[i]->label)) continue; } if (verbose) printf("Reading data object with label '%s'\n", opt_data); r = authenticate(objs[i]); if (r >= 0) { r = sc_pkcs15_read_data_object(p15card, cinfo, &data_object); 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; for (i = 0; i < count; i++) { int idx; struct sc_pkcs15_data_info *cinfo = (struct sc_pkcs15_data_info *) objs[i]->data; if (objs[i]->label) printf("Data object '%s'\n", 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; r = sc_pkcs15_read_data_object(p15card, cinfo, &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 = 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)); } } return 0; } static void print_prkey_info(const struct sc_pkcs15_object *obj) { unsigned int i; struct sc_pkcs15_prkey_info *prkey = (struct sc_pkcs15_prkey_info *) obj->data; const char *types[] = { "", "RSA", "DSA", "GOSTR3410", "EC", "", "", "" }; const char *usages[] = { "encrypt", "decrypt", "sign", "signRecover", "wrap", "unwrap", "verify", "verifyRecover", "derive", "nonRepudiation" }; const size_t usage_count = NELEMENTS(usages); const char *access_flags[] = { "sensitive", "extract", "alwaysSensitive", "neverExtract", "local" }; const unsigned int af_count = NELEMENTS(access_flags); char guid[39]; printf("Private %s Key [%s]\n", types[7 & obj->type], obj->label); print_common_flags(obj); 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"); 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%X)\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)); if (!sc_pkcs15_get_guid(p15card, obj, 0, guid, sizeof(guid))) printf("\tGUID : %s\n", guid); } 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 (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) { unsigned int i; const struct sc_pkcs15_pubkey_info *pubkey = (const struct sc_pkcs15_pubkey_info *) obj->data; const char *types[] = { "", "RSA", "DSA", "GOSTR3410", "EC", "", "", "" }; const char *usages[] = { "encrypt", "decrypt", "sign", "signRecover", "wrap", "unwrap", "verify", "verifyRecover", "derive", "nonRepudiation" }; const unsigned int usage_count = NELEMENTS(usages); const char *access_flags[] = { "sensitive", "extract", "alwaysSensitive", "neverExtract", "local" }; const unsigned int af_count = NELEMENTS(access_flags); printf("Public %s Key [%s]\n", types[7 & obj->type], obj->label); print_common_flags(obj); 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"); 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 printf("\tFieldLength : %lu\n", (unsigned long)pubkey->field_length); printf("\tKey ref : %d (0x%X)\n", pubkey->key_reference, pubkey->key_reference); printf("\tNative : %s\n", pubkey->native ? "yes" : "no"); if (pubkey->path.len || pubkey->path.aid.len) 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)); 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 (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; 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 = 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 */ r = sc_pkcs15_find_cert_by_id(p15card, &id, &obj); if (r >= 0) { if (verbose) printf("Reading certificate with ID '%s'\n", opt_pubkey); r = sc_pkcs15_read_certificate(p15card, (sc_pkcs15_cert_info_t *) obj->data, &cert); } 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); return 2; } if (r < 0) { fprintf(stderr, "Public key enumeration failed: %s\n", sc_strerror(r)); return 1; } if (!pubkey) { fprintf(stderr, "Public key not available\n"); return 1; } r = pubkey_pem_encode(pubkey, &pubkey->data, &pem_key); 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); } if (cert) sc_pkcs15_free_certificate(cert); else if (pubkey) sc_pkcs15_free_pubkey(pubkey); return r; } static void print_skey_info(const struct sc_pkcs15_object *obj) { unsigned int i; struct sc_pkcs15_skey_info *skey = (struct sc_pkcs15_skey_info *) obj->data; const char *types[] = { "generic", "DES", "2DES", "3DES"}; const char *usages[] = { "encrypt", "decrypt", "sign", "signRecover", "wrap", "unwrap", "verify", "verifyRecover", "derive" }; const size_t usage_count = NELEMENTS(usages); const char *access_flags[] = { "sensitive", "extract", "alwaysSensitive", "neverExtract", "local" }; const unsigned int af_count = NELEMENTS(access_flags); char guid[39]; printf("Secret %s Key [%s]\n", types[3 & obj->type], obj->label); print_common_flags(obj); printf("\tUsage : [0x%X]", skey->usage); for (i = 0; i < usage_count; i++) if (skey->usage & (1 << i)) printf(", %s", usages[i]); printf("\n"); printf("\tAccess Flags : [0x%X]", skey->access_flags); for (i = 0; i < af_count; i++) if (skey->access_flags & (1 << i)) printf(", %s", access_flags[i]); 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%X)\n", skey->key_reference, skey->key_reference); if (skey->path.len || skey->path.aid.len) printf("\tPath : %s\n", sc_print_path(&skey->path)); if (!sc_pkcs15_get_guid(p15card, obj, 0, guid, sizeof(guid))) printf("\tGUID : %s\n", 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 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 */ r = sc_pkcs15_find_cert_by_id(p15card, &id, &obj); if (r >= 0) { if (verbose) fprintf(stderr,"Reading certificate with ID '%s'\n", opt_pubkey); r = sc_pkcs15_read_certificate(p15card, (sc_pkcs15_cert_info_t *) obj->data, &cert); } if (r >= 0) pubkey = cert->key; } if (r == SC_ERROR_OBJECT_NOT_FOUND) { if (outf != stdout) fclose(outf); fprintf(stderr, "Public key with ID '%s' not found.\n", opt_pubkey); return 2; } if (r < 0) { if (outf != stdout) fclose(outf); fprintf(stderr, "Public key enumeration failed: %s\n", sc_strerror(r)); return 1; } /* rsa1 keys */ # if 0 if (pubkey->algorithm == SC_ALGORITHM_RSA) { int bits; BIGNUM *bn; char *exp,*mod; bn = BN_new(); BN_bin2bn((unsigned char*)pubkey->u.rsa.modulus.data, pubkey->u.rsa.modulus.len, bn); bits = BN_num_bits(bn); exp = BN_bn2dec(bn); BN_free(bn); bn = BN_new(); BN_bin2bn((unsigned char*)pubkey->u.rsa.exponent.data, pubkey->u.rsa.exponent.len, bn); mod = BN_bn2dec(bn); BN_free(bn); if (bits && exp && mod) fprintf(outf, "%u %s %s\n", bits,mod,exp); else fprintf(stderr, "decoding rsa key failed!\n"); OPENSSL_free(exp); OPENSSL_free(mod); } #endif /* rsa and des keys - ssh2 */ /* key_to_blob */ if (pubkey->algorithm == SC_ALGORITHM_RSA) { unsigned char buf[2048]; unsigned char *uu; uint32_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; uu = malloc(len*2); r = sc_base64_encode(buf, len, uu, 2*len, 2*len); fprintf(outf,"ssh-rsa %s", uu); free(uu); } if (pubkey->algorithm == SC_ALGORITHM_DSA) { unsigned char buf[2048]; unsigned char *uu; uint32_t len; uint32_t n; if (!pubkey->u.dsa.p.data || !pubkey->u.dsa.p.len || !pubkey->u.dsa.q.data || !pubkey->u.dsa.q.len || !pubkey->u.dsa.g.data || !pubkey->u.dsa.g.len || !pubkey->u.dsa.pub.data || !pubkey->u.dsa.pub.len) { fprintf(stderr, "Failed to decode DSA key.\n"); goto fail2; } buf[0]=0; buf[1]=0; buf[2]=0; buf[3]=7; len = sprintf((char *) buf+4,"ssh-dss"); len+=4; if (sizeof(buf)-len < 5+pubkey->u.dsa.p.len) goto fail; n = pubkey->u.dsa.p.len; if (pubkey->u.dsa.p.data[0] & 0x80) n++; buf[len++]=(n >>24) & 0xff; buf[len++]=(n >>16) & 0xff; buf[len++]=(n >>8) & 0xff; buf[len++]=(n) & 0xff; if (pubkey->u.dsa.p.data[0] & 0x80) buf[len++]= 0; memcpy(buf+len,pubkey->u.dsa.p.data, pubkey->u.dsa.p.len); len += pubkey->u.dsa.p.len; if (sizeof(buf)-len < 5+pubkey->u.dsa.q.len) goto fail; n = pubkey->u.dsa.q.len; if (pubkey->u.dsa.q.data[0] & 0x80) n++; buf[len++]=(n >>24) & 0xff; buf[len++]=(n >>16) & 0xff; buf[len++]=(n >>8) & 0xff; buf[len++]=(n) & 0xff; if (pubkey->u.dsa.q.data[0] & 0x80) buf[len++]= 0; memcpy(buf+len,pubkey->u.dsa.q.data, pubkey->u.dsa.q.len); len += pubkey->u.dsa.q.len; if (sizeof(buf)-len < 5+pubkey->u.dsa.g.len) goto fail; n = pubkey->u.dsa.g.len; if (pubkey->u.dsa.g.data[0] & 0x80) n++; buf[len++]=(n >>24) & 0xff; buf[len++]=(n >>16) & 0xff; buf[len++]=(n >>8) & 0xff; buf[len++]=(n) & 0xff; if (pubkey->u.dsa.g.data[0] & 0x80) buf[len++]= 0; memcpy(buf+len,pubkey->u.dsa.g.data, pubkey->u.dsa.g.len); len += pubkey->u.dsa.g.len; if (sizeof(buf)-len < 5+pubkey->u.dsa.pub.len) goto fail; n = pubkey->u.dsa.pub.len; if (pubkey->u.dsa.pub.data[0] & 0x80) n++; buf[len++]=(n >>24) & 0xff; buf[len++]=(n >>16) & 0xff; buf[len++]=(n >>8) & 0xff; buf[len++]=(n) & 0xff; if (pubkey->u.dsa.pub.data[0] & 0x80) buf[len++]= 0; memcpy(buf+len,pubkey->u.dsa.pub.data, pubkey->u.dsa.pub.len); len += pubkey->u.dsa.pub.len; uu = malloc(len*2); r = sc_base64_encode(buf, len, uu, 2*len, 2*len); fprintf(outf,"ssh-dss %s", uu); free(uu); } if (outf != stdout) fclose(outf); if (cert) sc_pkcs15_free_certificate(cert); else if (pubkey) sc_pkcs15_free_pubkey(pubkey); return 0; fail: printf("can't convert key: buffer too small\n"); fail2: if (outf != stdout) fclose(outf); if (cert) sc_pkcs15_free_certificate(cert); else if (pubkey) 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; printf("%s [%s]: ", prompt, 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) 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 *) strdup(pincode); } } 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 = 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 (r < 0) { fprintf(stderr, "Operation failed: %s\n", sc_strerror(r)); return -1; } return 0; } static int authenticate(sc_pkcs15_object_t *obj) { sc_pkcs15_object_t *pin_obj; u8 *pin; 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 = opt_pin; else pin = get_pin("Please enter PIN", pin_obj); return sc_pkcs15_verify_pin(p15card, pin_obj, pin, pin? strlen((char *) pin) : 0); } 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; if (obj->type == SC_PKCS15_TYPE_AUTH_PIN) printf("PIN [%s]\n", obj->label); else if (obj->type == SC_PKCS15_TYPE_AUTH_AUTHKEY) printf("AuthKey [%s]\n", 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 (verbose) printf("Card has %d Authentication objects.\n", r); for (i = 0; i < r; 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 int dump(void) { const char *flags[] = { "Read-only", "Login required", "PRN generation", "EID compliant" }; char *last_update = sc_pkcs15_get_lastupdate(p15card); int i, count = 0; 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\n"); list_pins(); list_private_keys(); list_public_keys(); 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; 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 = 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) 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_TYPE_AUTH_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"); pin = opt_pin ? opt_pin : 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) return 2; pin2 = get_pin("Enter new PIN again", pin_obj); if (pin2 == NULL || strlen((char *) pin2) == 0) 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 (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; 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 = 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"); return 2; } if (pincode == NULL && verbose) printf("Old PIN value will be prompted with pinpad.\n"); newpin = 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"); 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"); return 0; } static int read_and_cache_file(const sc_path_t *path) { sc_file_t *tfile; const sc_acl_entry_t *e; u8 *buf; int r; if (verbose) { printf("Reading file "); util_hex_dump(stdout, path->value, path->len, ""); printf("...\n"); } r = sc_select_file(card, path, &tfile); if (r != 0) { fprintf(stderr, "sc_select_file() failed: %s\n", sc_strerror(r)); return -1; } e = sc_file_get_acl_entry(tfile, SC_AC_OP_READ); if (e != NULL && e->method != SC_AC_NONE) { if (verbose) printf("Skipping; ACL for read operation is not NONE.\n"); return -1; } buf = malloc(tfile->size); if (!buf) { printf("out of memory!"); return -1; } r = sc_read_binary(card, 0, buf, tfile->size, 0); if (r < 0) { fprintf(stderr, "sc_read_binary() failed: %s\n", sc_strerror(r)); free(buf); return -1; } r = sc_pkcs15_cache_file(p15card, path, buf, r); if (r) { fprintf(stderr, "Unable to cache file: %s\n", sc_strerror(r)); free(buf); return -1; } sc_file_free(tfile); free(buf); return 0; } static int learn_card(void) { char dir[PATH_MAX]; int r, i, cert_count; struct sc_pkcs15_object *certs[32]; struct sc_pkcs15_df *df; r = sc_get_cache_dir(ctx, dir, sizeof(dir)); if (r) { fprintf(stderr, "Unable to find cache directory: %s\n", sc_strerror(r)); return 1; } printf("Using cache directory '%s'.\n", dir); r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_CERT_X509, certs, 32); if (r < 0) { fprintf(stderr, "Certificate enumeration failed: %s\n", sc_strerror(r)); return 1; } cert_count = r; r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PRKEY, NULL, 0); if (r < 0) { fprintf(stderr, "Private key enumeration failed: %s\n", sc_strerror(r)); return 1; } r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH, NULL, 0); if (r < 0) { fprintf(stderr, "Authentication objects enumeration failed: %s\n", sc_strerror(r)); return 1; } /* Cache all relevant DF files. The cache * directory is created automatically. */ for (df = p15card->df_list; df != NULL; df = df->next) read_and_cache_file(&df->path); printf("Caching %d certificate(s)...\n", cert_count); for (i = 0; i < cert_count; i++) { sc_path_t tpath; struct sc_pkcs15_cert_info *cinfo = (struct sc_pkcs15_cert_info *) certs[i]->data; printf("[%s]\n", certs[i]->label); memset(&tpath, 0, sizeof(tpath)); tpath = cinfo->path; if (tpath.type == SC_PATH_TYPE_FILE_ID) { /* prepend application DF path in case of a file id */ r = sc_concatenate_path(&tpath, &p15card->file_app->path, &tpath); if (r != SC_SUCCESS) return r; } read_and_cache_file(&tpath); } 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 }; 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 (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: /* 0 = card ok, 1 = card vulnerable, 2 = problem! */ return rc; bad_fci: 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_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: return 0; } int main(int argc, char * const 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_learn_card = 0; int do_test_update = 0; int do_update = 0; int action_count = 0; sc_context_param_t ctx_param; while (1) { c = getopt_long(argc, argv, "r:cuko:va:LR:CwDTU", options, &long_optind); if (c == -1) break; if (c == '?') util_print_usage_and_die(app_name, options, option_help, NULL); switch (c) { case 'r': 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 'C': do_list_data_objects = 1; action_count++; break; case OPT_VERIFY_PIN: do_verify_pin = 1; 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; #endif case 'L': do_learn_card = 1; action_count++; break; case 'T': do_test_update = 1; action_count++; break; case 'U': do_update = 1; action_count++; break; case OPT_READER: opt_reader = optarg; break; case OPT_PIN: opt_pin = (u8 *) optarg; break; case OPT_NEWPIN: opt_newpin = (u8 *) optarg; break; case OPT_PUK: opt_puk = (u8 *) optarg; break; case 'o': opt_outfile = optarg; 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 'w': opt_wait = 1; 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; r = sc_context_create(&ctx, &ctx_param); if (r) { fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); return 1; } if (verbose > 1) { ctx->debug = verbose; sc_ctx_log_to_file(ctx, "stderr"); } err = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose); 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); return 1; } 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_verify_pin) if ((err = verify_pin())) goto end; if (do_learn_card) { if ((err = learn_card())) goto end; action_count--; } 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; } } end: if (p15card) sc_pkcs15_unbind(p15card); if (card) { sc_unlock(card); sc_disconnect_card(card); } if (ctx) sc_release_context(ctx); return err; } /* * Helper function for PEM encoding public key */ static const struct sc_asn1_entry c_asn1_pem_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_pem_key[] = { { "publicKey", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_TAG_SEQUENCE, 0, NULL, NULL}, { NULL, 0, 0, 0, NULL, NULL } }; static int pubkey_pem_encode(sc_pkcs15_pubkey_t *pubkey, sc_pkcs15_der_t *key, sc_pkcs15_der_t *out) { struct sc_asn1_entry asn1_pem_key[2], asn1_pem_key_items[3]; struct sc_algorithm_id algorithm; size_t key_len; memset(&algorithm, 0, sizeof(algorithm)); sc_init_oid(&algorithm.oid); algorithm.algorithm = pubkey->algorithm; if (algorithm.algorithm == SC_ALGORITHM_GOSTR3410) algorithm.params = &pubkey->u.gostr3410.params; sc_copy_asn1_entry(c_asn1_pem_key, asn1_pem_key); sc_copy_asn1_entry(c_asn1_pem_key_items, asn1_pem_key_items); sc_format_asn1_entry(asn1_pem_key + 0, asn1_pem_key_items, NULL, 1); sc_format_asn1_entry(asn1_pem_key_items + 0, &algorithm, NULL, 1); key_len = 8 * key->len; sc_format_asn1_entry(asn1_pem_key_items + 1, key->value, &key_len, 1); return sc_asn1_encode(ctx, asn1_pem_key, &out->value, &out->len); } opensc-0.13.0/src/tools/pkcs15-init.c0000644000015201777760000022042712057406034014176 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #ifdef HAVE_STRING_H #include #endif #include #if OPENSSL_VERSION_NUMBER >= 0x00907000L #include #endif #include #include #include #include #include #include #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x10000000L #include /* for OPENSSL_NO_EC */ #ifndef OPENSSL_NO_EC #include #endif /* OPENSSL_NO_EC */ #endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */ #include "common/compat_strlcpy.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_store_private_key(struct sc_profile *); static int do_store_public_key(struct sc_profile *, EVP_PKEY *); 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); static int do_store_data_object(struct sc_profile *profile); static int do_sanity_check(struct sc_profile *profile); static int init_keyargs(struct sc_pkcs15init_prkeyargs *); 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 void read_options_file(const char *); static void ossl_print_errors(void); static int verify_pin(struct sc_pkcs15_card *, char *); enum { OPT_OPTIONS = 0x100, OPT_PASSPHRASE, OPT_PUBKEY, OPT_EXTRACTABLE, OPT_INSECURE, OPT_AUTHORITY, OPT_ASSERT_PRISTINE, OPT_SECRET, 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_PIN1 = 0x10000, /* don't touch these values */ OPT_PUK1 = 0x10001, OPT_PIN2 = 0x10002, OPT_PUK2 = 0x10003, OPT_SERIAL = 0x10004, OPT_NO_SOPIN = 0x10005, OPT_NO_PROMPT= 0x10006 }; const struct option options[] = { { "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-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 }, { "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}, { "extractable", no_argument, NULL, OPT_EXTRACTABLE }, { "insecure", no_argument, NULL, OPT_INSECURE }, { "use-default-transport-keys", no_argument, NULL, 'T' }, { "no-prompt", no_argument, NULL, OPT_NO_PROMPT }, { "profile", required_argument, NULL, 'p' }, { "card-profile", required_argument, NULL, 'c' }, { "options-file", required_argument, NULL, OPT_OPTIONS }, { "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[] = { "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 an X.509 certificate", "Update an X.509 certificate (carefull 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 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' attribut of tokenInfo", "When storing PKCS#12 ignore CA certificates", "Private key stored as an extractable key", "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", "Specify the general profile to use", "Specify the card profile to use", "Read additional command line options from file", "Wait for card insertion", "Display this message", "Verbose operation. Use several times to enable debug output.", 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_CERT, ACTION_UPDATE_CERT, ACTION_STORE_DATA, ACTION_FINALIZE_CARD, ACTION_CHANGE_ATTRIBUTES, ACTION_SANITY_CHECK, ACTION_UPDATE_LAST_UPDATE, ACTION_ERASE_APPLICATION, ACTION_MAX }; static const char *action_names[] = { "do nothing", "verify that card is pristine", "erase card", "create PKCS #15 meta structure", "store PIN", "generate key", "store private key", "store public key", "store certificate", "update certificate", "store data object", "finalizing card", "delete object(s)", "change attribute(s)", "check card's sanity", }; #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 static sc_context_t * ctx = NULL; static sc_card_t * card = NULL; static struct sc_pkcs15_card * p15card = NULL; static char * opt_reader = NULL; static unsigned int opt_actions; static int opt_extractable = 0, opt_insecure = 0, opt_authority = 0, opt_no_prompt = 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_cert_label = NULL; static char * opt_pins[4]; static char * opt_serial = NULL; static 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 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 verbose = 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; #if OPENSSL_VERSION_NUMBER >= 0x00907000L OPENSSL_config(NULL); #endif /* OpenSSL magic */ SSLeay_add_all_algorithms(); CRYPTO_malloc_init(); #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(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 < ACTION_MAX; n++) { unsigned int action = n; if (!(opt_actions & (1 << action))) continue; if (action != ACTION_ERASE && action != ACTION_INIT && action != ACTION_ASSERT_PRISTINE && 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(card, profile, &aid); if (r < 0) { fprintf(stderr, "Finalize profile error %s\n", sc_strerror(r)); break; } 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)); break; } /* XXX: should compare card to profile here to make * sure we're not messing things up */ if (verbose) printf("Found %s\n", p15card->tokeninfo->label); sc_pkcs15init_set_p15card(profile, p15card); if (opt_verify_pin) { r = verify_pin(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_ASSERT_PRISTINE: /* skip printing error message */ if ((r = do_assert_pristine(card)) < 0) goto out; continue; case ACTION_ERASE: r = do_erase(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_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); break; case ACTION_FINALIZE_CARD: r = do_finalize_card(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(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; } } out: if (profile) { sc_pkcs15init_unbind(profile); } if (p15card) { sc_pkcs15_unbind(p15card); } if (card) { sc_unlock(card); sc_disconnect_card(card); } sc_release_context(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; r = sc_context_create(&ctx, &ctx_param); if (r) { util_error("Failed to establish context: %s\n", sc_strerror(r)); return 0; } if (verbose > 1) { ctx->debug = verbose; sc_ctx_log_to_file(ctx, "stderr"); } if (util_connect_card(ctx, &card, reader, opt_wait, verbose)) 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; 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: 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; } /* * Erase card */ static int do_erase(sc_card_t *in_card, struct sc_profile *profile) { int r; struct sc_pkcs15_card *p15card; p15card = sc_pkcs15_card_new(); p15card->card = in_card; ignore_cmdline_pins++; 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_erase_card(p15card, profile, &aid); } else { r = sc_pkcs15init_erase_card(p15card, profile, NULL); } ignore_cmdline_pins--; 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) { return sc_pkcs15init_finalize_card(in_card, profile); } /* * 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 = 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_no_prompt && !opt_no_sopin) { r = get_new_pin(&hints, role, "pin", &opt_pins[2]); if (r < 0) goto failed; } if (!so_puk_disabled && opt_pins[2] && !opt_pins[3] && !opt_no_prompt) { 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", &opt_pins[3]); if (r < 0) goto failed; } 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; return sc_pkcs15init_add_app(card, profile, &args); 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 = card; hints.p15card = 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", &opt_pins[0])) < 0) goto failed; 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", &opt_pins[1])) < 0) goto failed; } 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; return sc_pkcs15init_store_pin(p15card, profile, &args); failed: fprintf(stderr, "Failed to read PIN: %s\n", sc_strerror(r)); return SC_ERROR_PKCS15INIT; } /* * 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_keyargs(&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(cert[i]->cert_info->subject, namebuf, sizeof(namebuf))); } r = sc_pkcs15_convert_prkey(&args.key, pkey); if (r < 0) 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 = cert[0]->ex_kusage; /* No certificate usage? Assume ordinary * user cert */ if (usage == 0) usage = 0x1F; /* 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 | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE | SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE; r = sc_pkcs15init_store_private_key(p15card, profile, &args, NULL); if (r < 0) 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]; 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) return r; X509_check_purpose(cert[i], -1, -1); cargs.x509_usage = cert[i]->ex_kusage; cargs.label = cert_common_name(cert[i]); if (!cargs.label) cargs.label = X509_NAME_oneline(cert[i]->cert_info->subject, namebuf, sizeof(namebuf)); /* 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) cargs.label = opt_cert_label; } else { if (is_cacert_already_present(&cargs)) { printf("Certificate #%d already present, not stored.\n", i); goto next_cert; } cargs.authority = 1; } r = sc_pkcs15init_store_certificate(p15card, profile, &cargs, NULL); next_cert: free(cargs.der_encoded.value); } /* No certificates - store the public key */ if (ncerts == 0) r = do_store_public_key(profile, pkey); 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(p15card, SC_PKCS15_TYPE_CERT_X509, objs, 32); if (r <= 0) return 0; count = r; for (i = 0; i < count; i++) { cinfo = (sc_pkcs15_cert_info_t *) objs[i]->data; if (!cinfo->authority) continue; if (args->label && objs[i]->label && strcmp(args->label, objs[i]->label)) continue; /* XXX we should also match the usage field here */ /* Compare the DER representation of the certificates */ r = sc_pkcs15_read_certificate(p15card, cinfo, &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); if (r >= 0) init_gost_params(&args.params.gost, pkey); } if (r >= 0) r = sc_pkcs15init_store_public_key(p15card, profile, &args, &dummy); 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_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); if (r >= 0) r = sc_pkcs15init_store_certificate(p15card, profile, &args, NULL); 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, *newpk; 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) 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); /* Compare the public keys, there's no high level openssl function for this(?) */ r = SC_ERROR_INVALID_ARGUMENTS; if (oldpk->type == newpk->type) { if ((oldpk->type == EVP_PKEY_DSA) && !BN_cmp(oldpk->pkey.dsa->p, newpk->pkey.dsa->p) && !BN_cmp(oldpk->pkey.dsa->q, newpk->pkey.dsa->q) && !BN_cmp(oldpk->pkey.dsa->g, newpk->pkey.dsa->g)) r = 0; else if ((oldpk->type == EVP_PKEY_RSA) && !BN_cmp(oldpk->pkey.rsa->n, newpk->pkey.rsa->n) && !BN_cmp(oldpk->pkey.rsa->e, newpk->pkey.rsa->e)) 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; 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(p15card, &id, &obj) != 0) { util_error("Couldn't find the cert with ID %s\n", opt_objectid); return SC_ERROR_OBJECT_NOT_FOUND; } certinfo = (sc_pkcs15_cert_info_t *) obj->data; r = sc_pkcs15_read_certificate(p15card, certinfo, &oldcert); if (r < 0) return r; newcert_raw.value = NULL; r = do_read_check_certificate(oldcert, opt_infile, opt_format, &newcert_raw); sc_pkcs15_free_certificate(oldcert); if (r < 0) return r; r = sc_pkcs15init_update_certificate(p15card, profile, obj, newcert_raw.value, newcert_raw.len); if (newcert_raw.value) free(newcert_raw.value); 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); r = do_read_data_object(opt_infile, &data, &datalen); if (r >= 0) { /* der_encoded contains the plain data, nothing DER encoded */ args.der_encoded.value = data; args.der_encoded.len = datalen; r = sc_pkcs15init_store_data_object(p15card, profile, &args, NULL); } if (data) free(data); return r; } /* * Run card specific sanity check procedure */ static int do_sanity_check(struct sc_profile *profile) { return sc_pkcs15init_sanity_check(p15card, profile); } 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 effcicient 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; *issuercert = NULL; *has_sibling = 0; *stop = 0; r = sc_pkcs15_read_certificate(myp15card, (sc_pkcs15_cert_info_t *) certobj->data, &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; } r = sc_pkcs15_read_certificate(myp15card, (sc_pkcs15_cert_info_t *) otherobj->data, &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: if (cert) 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 untill 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 (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; if (myopt_delete_flags & SC_PKCS15INIT_TYPE_DATA) { struct sc_object_id app_oid; sc_pkcs15_object_t *obj; if (opt_application_id != NULL) { sc_format_oid(&app_oid, opt_application_id); r = sc_pkcs15_find_data_object_by_app_oid(p15card, &app_oid, &obj); } else if (opt_application_name != NULL && opt_label != NULL) { r = sc_pkcs15_find_data_object_by_name(p15card, opt_application_name, opt_label, &obj); } else { util_fatal("Specify the --application-id or --application-name and --label for the data object to be deleted\n"); } if (r >= 0) { r = sc_pkcs15init_delete_object(p15card, profile, obj); if (r >= 0) count++; } } if (myopt_delete_flags & (SC_PKCS15INIT_TYPE_PRKEY | SC_PKCS15INIT_TYPE_PUBKEY | SC_PKCS15INIT_TYPE_CHAIN)) { 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(p15card, profile, id, myopt_delete_flags); if (r >= 0) count += r; } 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(p15card, &id, &obj)) != 0) return r; break; case SC_PKCS15INIT_TYPE_PUBKEY: if ((r = sc_pkcs15_find_pubkey_by_id(p15card, &id, &obj)) != 0) return r; break; case SC_PKCS15INIT_TYPE_CERT: if ((r = sc_pkcs15_find_cert_by_id(p15card, &id, &obj)) != 0) return r; break; case SC_PKCS15INIT_TYPE_DATA: if ((r = sc_pkcs15_find_data_object_by_id(p15card, &id, &obj)) != 0) 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_pkcs15init_update_any_df(p15card, profile, obj->df, 0); 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 = 1024; int r; memset(&keygen_args, 0, sizeof(keygen_args)); keygen_args.pubkey_label = opt_pubkey_label; if ((r = init_keyargs(&keygen_args.prkey_args)) < 0) return r; 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; /* Parse the key spec given on the command line */ if (!strncasecmp(spec, "rsa", 3)) { keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_RSA; spec += 3; } else if (!strncasecmp(spec, "dsa", 3)) { keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_DSA; spec += 3; } else if (!strncasecmp(spec, "gost2001", strlen("gost2001"))) { keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_GOSTR3410; keybits = SC_PKCS15_GOSTR3410_KEYSIZE; /* FIXME: now only SC_PKCS15_PARAMSET_GOSTR3410_A */ keygen_args.prkey_args.params.gost.gostr3410 = SC_PKCS15_PARAMSET_GOSTR3410_A; spec += strlen("gost2001"); } else if (!strncasecmp(spec, "ec", 2)) { keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_EC; spec += 2; } else { util_error("Unknown algorithm \"%s\"", spec); return SC_ERROR_INVALID_ARGUMENTS; } if (*spec == '/' || *spec == '-' || *spec == ':') spec++; if (*spec) { if (isalpha(*spec) && keygen_args.prkey_args.key.algorithm == SC_ALGORITHM_EC) { keygen_args.prkey_args.params.ec.named_curve = strdup(spec); keybits = 0; } else { char *end; keybits = strtoul(spec, &end, 10); if (*end) { util_error("Invalid number of key bits \"%s\"", spec); return SC_ERROR_INVALID_ARGUMENTS; } } } r = sc_pkcs15init_generate_key(p15card, profile, &keygen_args, keybits, NULL); return r; } static int init_keyargs(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; return 0; } static void init_gost_params(struct sc_pkcs15init_keyarg_gost_params *params, EVP_PKEY *pkey) { #if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) EC_KEY *key; assert(pkey); if (EVP_PKEY_id(pkey) == NID_id_GostR3410_2001) { key = EVP_PKEY_get0(pkey); assert(key); assert(params); assert(EC_KEY_get0_group(key)); assert(EC_GROUP_get_curve_name(EC_KEY_get0_group(key)) > 0); switch (EC_GROUP_get_curve_name(EC_KEY_get0_group(key))) { 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(str[3])) goto parse_err; secret->reference = 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 = opt_pins[OPT_PIN1 & 3]; break; case SC_PKCS15INIT_USER_PUK: name = "User PIN unlock key"; secret = opt_pins[OPT_PUK1 & 3]; break; case SC_PKCS15INIT_SO_PIN: name = "Security officer PIN"; secret = opt_pins[OPT_PIN2 & 3]; break; case SC_PKCS15INIT_SO_PUK: name = "Security officer PIN unlock key"; secret = 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 = 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 = 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 = 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 = 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_no_prompt) 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 = card; hints.p15card = p15card; if ((r = get_pin(&hints, &secret)) < 0) { fprintf(stderr, "Failed to read PIN from user: %s\n", sc_strerror(r)); return r; } len = strlen(secret); allocated = 1; } if (len > *pinsize) 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_no_prompt) { printf("\n" "Refusing to prompt for transport key because --no-prompt\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, 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 = 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) { ossl_print_errors(); 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; } CRYPTO_add(&user_key->references, 1, CRYPTO_LOCK_EVP_PKEY); 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++) { CRYPTO_add(&certs[i]->references, 1, CRYPTO_LOCK_X509); } if (cacerts) sk_X509_free(cacerts); *key = user_key; return ncerts; error: ossl_print_errors(); 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 = 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 (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) ossl_print_errors(); 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) ossl_print_errors(); 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) ossl_print_errors(); 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) ossl_print_errors(); 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) util_fatal("Unable to read certificate from %s\n", name); return 0; } static size_t determine_filesize(const char *filename) { FILE *fp; long ll; if ((fp = fopen(filename,"rb")) == NULL) util_fatal("Unable to open %s: %m", filename); fseek(fp,0L,SEEK_END); 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) { FILE *inf; size_t filesize = determine_filesize(name); int c; *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 -1; } c = fread(*out, 1, filesize, inf); fclose(inf); if (c < 0) { perror("read"); return -1; } *outlen = filesize; return 0; } 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) return NULL; ne = X509_NAME_get_entry(X509_get_subject_name(x509), idx); if (!ne) return NULL; a_str = X509_NAME_ENTRY_get_data(ne); if (!a_str) return NULL; out_len = ASN1_STRING_to_UTF8(&tmp, a_str); if (!tmp) 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}, {NULL, 0} }; while (1) { int len, n; 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,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, untill there's a CA cert that certifies\n"); printf(" another cert on the card\n"); } else { printf("\nChange attribute argument: either privkey, pubkey, 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", 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 len, n, match = 0; 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", len, list); exit(1); } if (match > 1) { fprintf(stderr, "Ambiguous X.509 key usage %.*s\n", 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); 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_OPTIONS: read_options_file(optarg); break; case OPT_PIN1: case OPT_PUK1: case OPT_PIN2: case OPT_PUK2: opt_pins[opt->val & 3] = optarg; break; case OPT_SERIAL: opt_serial = optarg; break; case OPT_PASSPHRASE: opt_passphrase = optarg; break; case OPT_PUBKEY: this_action = ACTION_STORE_PUBKEY; 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_NO_PROMPT: opt_no_prompt = 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_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; 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++ = ':'; 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: ; } } /* * Read a file containing more command line options. * This allows you to specify PINs to pkcs15-init without * exposing them through ps. */ static void read_options_file(const char *filename) { const struct option *o; char buffer[1024], *name; FILE *fp; if ((fp = fopen(filename, "r")) == NULL) util_fatal("Unable to open %s: %m", filename); while (fgets(buffer, sizeof(buffer), fp) != NULL) { buffer[strcspn(buffer, "\n")] = '\0'; name = strtok(buffer, " \t"); while (name) { if (*name == '#') break; for (o = options; o->name; o++) if (!strcmp(o->name, name)) break; if (!o->name) { util_error("Unknown option \"%s\"\n", name); util_print_usage_and_die(app_name, options, option_help, NULL); } if (o->has_arg != no_argument) { optarg = strtok(NULL, ""); if (optarg) { while (isspace((int) *optarg)) optarg++; optarg = strdup(optarg); } } if (o->has_arg == required_argument && (!optarg || !*optarg)) { util_error("Option %s: missing argument\n", name); util_print_usage_and_die(app_name, options, option_help, NULL); } handle_option(o); name = strtok(NULL, " \t"); } } fclose(fp); } /* * OpenSSL helpers */ static void ossl_print_errors(void) { static int loaded = 0; long err; if (!loaded) { ERR_load_crypto_strings(); loaded = 1; } while ((err = ERR_get_error()) != 0) fprintf(stderr, "%s\n", ERR_error_string(err, NULL)); } /* * Retrieve a PIN from the user. * * @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"); 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[64]; char *pin; 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 = opt_pins[0]; } else { sc_ui_hints_t hints; if (opt_no_prompt) return SC_ERROR_OBJECT_NOT_FOUND; if (pin_obj->label) snprintf(pin_label, sizeof(pin_label), "User PIN [%s]", pin_obj->label); else snprintf(pin_label, sizeof(pin_label), "User PIN"); memset(&hints, 0, sizeof(hints)); hints.dialog_name = "pkcs15init.get_pin"; hints.prompt = "User PIN required"; hints.obj_label = pin_label; hints.usage = SC_UI_USAGE_OTHER; hints.card = card; hints.p15card = p15card; get_pin(&hints, &pin); } r = sc_pkcs15_verify_pin(p15card, pin_obj, (unsigned char *)pin, pin ? strlen((char *) pin) : 0); if (r < 0) fprintf(stderr, "Operation failed: %s\n", sc_strerror(r)); return r; } opensc-0.13.0/src/tools/westcos-tool.c0000644000015201777760000004267612057406034014601 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #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. Use several times to enable debug output." }; 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 *pin = NULL; static char *puk = NULL; 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, BIGNUM *src) { if (src == 0) return 0; dst->len = BN_num_bytes(src); dst->data = malloc(dst->len); BN_bn2bin(src, dst->data); return 1; } static int charge = 0; static void print_openssl_erreur(void) { long r; if (!charge) { ERR_load_crypto_strings(); charge = 1; } while ((r = ERR_get_error()) != 0) printf("%s\n", ERR_error_string(r, NULL)); } static int verify_pin(sc_card_t *card, int pin_reference, 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, char *pin_value1, 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, char *puk_value, 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 == NULL || puk == 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 *cert, u8 **value) { int len; u8 *p; len = i2d_X509(cert, NULL); p = *value = malloc(len); i2d_X509(cert, &p); return len; } static int creation_fichier_cert(sc_card_t *card) { int r; int size; 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; size = (file->size) - 32; if(file) { sc_file_free(file); file = NULL; } 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; RSA *rsa = NULL; BIGNUM *bn = NULL; BIO *mem = 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 = 1536; break; case 'o': overwrite = 1; break; case 'l': keylen = atoi(optarg); break; case 'i': install_pin = 1; break; case 'x': pin = optarg; break; case 'y': puk = optarg; 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]; r = sc_context_create(&ctx, &ctx_param); if (r) { printf("Failed to establish context: %s\n", sc_strerror(r)); return 1; } if (verbose > 1) { ctx->debug = verbose; sc_ctx_log_to_file(ctx, "stderr"); } if (opt_driver != NULL) { r = sc_set_card_driver(ctx, opt_driver); if (r) { printf("Driver '%s' not found!\n", opt_driver); goto out; } } r = util_connect_card(ctx, &card, opt_reader, opt_wait, 0); 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 enougth 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; memset(&key, 0, sizeof(key)); key.algorithm = SC_ALGORITHM_RSA; printf("Generate key of length %d.\n", keylen); #if OPENSSL_VERSION_NUMBER>=0x00908000L rsa = RSA_new(); bn = BN_new(); mem = BIO_new(BIO_s_mem()); if(rsa == NULL || bn == NULL || mem == NULL) { printf("Not enougth memory.\n"); goto out; } if(!BN_set_word(bn, RSA_F4) || !RSA_generate_key_ex(rsa, keylen, bn, NULL)) #else rsa = RSA_generate_key(keylen, RSA_F4, NULL, NULL); mem = BIO_new(BIO_s_mem()); if(mem == NULL) { printf("Not enougth memory.\n"); goto out; } if (!rsa) #endif { printf("RSA_generate_key_ex return %ld\n", ERR_get_error()); goto out; } rsa->meth = RSA_PKCS1_SSLeay(); if(!i2d_RSAPrivateKey_bio(mem, rsa)) { 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 enougth 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 %zd.\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 %zd\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 = creation_fichier_cert(card); if(r) goto out; if (!do_convert_bignum(&dst->modulus, rsa->n) || !do_convert_bignum(&dst->exponent, rsa->e)) goto out; r = sc_pkcs15_encode_pubkey(ctx, &key, &pdata, &lg); if(r) goto out; printf("Public key length %zd\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_erreur(); 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: verifier taille fichier compatible... */ 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 enougth 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 enougth memory.\n"); goto out; } memset(b, 0, file->size); fp = fopen(put_filename, "rb"); fread(b, 1, file->size, fp); 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(rsa) RSA_free(rsa); if(file) sc_file_free(file); if (card) { sc_unlock(card); sc_disconnect_card(card); } if (ctx) sc_release_context(ctx); return EXIT_SUCCESS; } opensc-0.13.0/src/tools/netkey-tool.c0000644000015201777760000004550512057406034014403 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #include #include "common/compat_getopt.h" #include "libopensc/opensc.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 Verschlsselungs Zertifikat"}, {"DF0143B1", 0, "User Verschlsselungs Zertifikat1"}, {"DF0143B2", 0, "User Verschlsselungs Zertifikat2"}, }; static struct { const char *path; const char *name; const char *label; int p1, p2; int tries; int 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("\nInvald 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(", Invald 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]; X509_NAME_get_text_by_NID(c->cert_info->subject, NID_commonName, buf2,sizeof(buf2)); printf(" Subject-CN: %s\n", buf2); X509_NAME_get_text_by_NID(c->cert_info->issuer, NID_commonName, buf2,sizeof(buf2)); printf(" Issuer-CN: %s\n", buf2); X509_free(c); } else 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 crypted Initial-PUK-file: "); sc_format_path("3F004350",&p); if((i=sc_select_file(card,&p,&f))<0){ printf("Cannot select crypted Initial-PUK-file, %s\n", sc_strerror(i)); return; } if((i=sc_read_binary(card,0,buf1,128,0))!=128){ printf("Cannot read crypted Initial-PUK-file, %s\n", sc_strerror(i)); return; } printf("OK\nDecrypting crypted 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("\nInvald 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, int pin1, int pin2, int do_change, u8 *newpin, int 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, int newlen) { sc_path_t p; sc_file_t *f; struct sc_apdu a; u8 ref, buf[40]; int i; printf("\nSetting initial PIN-value: "); sc_format_path(pinlist[0].path,&p); if((i=sc_select_file(card,&p,&f))<0){ printf("\nCannot select %s, %s\n", pinlist[0].label, sc_strerror(i)); return; } ref=f->prop_attr[2]; sc_format_apdu(card, &a, SC_APDU_CASE_1, 0x20, 0x00, f->prop_attr[2]); if((i=sc_transmit_apdu(card, &a))<0){ printf("sc_transmit_apdu() failed, %s\n", sc_strerror(i)); 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){ printf("cardfile contains %d bytes which are not a certificate\n", len); return; } printf("Writing Cert to %s: ", file); fflush(stdout); if((fp=fopen(file,"w"))==NULL) printf("Cannot open file, %s\n", strerror(errno)); else { fprintf(fp,"Certificate %d from Netkey E4 card\n\n", cert); PEM_write_X509(fp,c); printf("OK\n"); } X509_free(c); } static void handle_writecert(sc_card_t *card, int 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 conatin PEM-encoded certificate\n"); return; } printf("OK\nStoring Cert into Card-Certificate %d: ", 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 int pin_string2int(char *s) { size_t i; for(i=0;i=5 && len%3==2); if(hex){ len=(len+1)/3; hex=(len<=32); } for(i=0;hex && i0 && pin[3*i-1]!=':') hex=0; else j=strtol(pin+3*i,&p,16); if(hex && (j<0 || j>255 || (p-pin)!=3*i+2)) hex=0; } if(hex){ for(i=0;hex && i32) 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 carful - 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); } if (debug > 1) { ctx->debug = debug; sc_ctx_log_to_file(ctx, "stderr"); } 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"); 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); 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)); exit(1); } printf("\nCard detected (driver: %s)\nATR:", card->driver->name); for(i=0;iatr.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)); exit(1); } show_card(card); if(do_unblock || do_change){ int 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 %d cannot be changed\n", cert_nr); } else if(!pinlist[0].len && !pinlist[3].len){ fprintf(stderr, "\nNeed %s or %s to change Card-Certificate %d\n", 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.13.0/src/tools/opensc-explorer.c0000644000015201777760000012246512057406034015257 00000000000000/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #ifdef ENABLE_READLINE #include #include #endif #if !defined(_WIN32) #include /* for htons() */ #endif #ifdef HAVE_IO_H #include #endif #include "libopensc/opensc.h" #include "libopensc/asn1.h" #include "libopensc/cardctl.h" #include "libopensc/cards.h" #include "common/compat_strlcpy.h" #include "common/compat_getopt.h" #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 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]", "Selects path on start-up, or none if empty [3F00]", "Wait for card insertion", "Verbose operation. Use several times to enable debug output.", }; /* 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_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_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_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_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_cd, "cd", "{.. | | aid:}", "change to another DF" }, { do_cat, "cat", "[ | sfi:]" , "print the contents of 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_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_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" }, { 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) { static char buf[2*SC_MAX_PATH_STRING_SIZE]; size_t i, j; 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]); } 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) { if (current_file != NULL) sc_file_free(current_file); if (card) { sc_unlock(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_select_file(card, ¤t_path, NULL); if (r) { printf("unable to select parent DF: %s\n", sc_strerror(r)); die(1); } } } static struct command * ambiguous_match(struct command *table, const char *cmd) { struct command *last_match = NULL; int matches = 0; for (; table->name; table++) { if (strncasecmp(cmd, table->name, strlen(cmd)) == 0) { last_match = table; matches++; } } if (matches > 1) { printf("Ambiguous command: %s\n", cmd); return NULL; } 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) { if (strlen(arg) != 4) { printf("Wrong ID length.\n"); return -1; } if (sscanf(arg, "%02X%02X", &fid[0], &fid[1]) != 2) { printf("Invalid ID.\n"); return -1; } 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) { printf("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)) { printf("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 = "???"; 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); 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 multple 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(*pattern) == tolower(*string)) match++; } if (!match) return 0; pattern++; string++; } /* single character comparison / wildcard matching a single character */ else if (tolower(*pattern) == tolower(*string) || *pattern == '?') { pattern++; string++; } else return 0; if (*string == '\0' || *pattern == '\0') break; } return (*pattern != '\0' || *string != '\0' || tolower(*pattern) != tolower(*string)) ? 0 : 1; } static int do_ls(int argc, char **argv) { u8 buf[256], *cur = buf; int r, count; r = sc_list_files(card, buf, sizeof(buf)); 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) { sc_path_t path; sc_file_t *file = NULL; char filename[10]; 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) { if (current_path.type != SC_PATH_TYPE_DF_NAME) { path = current_path; sc_append_path_id(&path, cur, 2); } else { if (sc_path_set(&path, SC_PATH_TYPE_FILE_ID, cur, 2, 0, 0) != SC_SUCCESS) { printf("unable to set path.\n"); die(1); } } r = sc_select_file(card, &path, &file); if (r) { printf(" %02X%02X unable to select file, %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], end[2]; sc_path_t path; int r; fid[0] = 0; fid[1] = 0; end[0] = 0xFF; end[1] = 0xFF; switch (argc) { case 2: if (arg_to_fid(argv[1], end) != 0) 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, 2, 0, 0) != SC_SUCCESS) { printf("unable to set path.\n"); die(1); } } r = sc_select_file(card, &path, &file); 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_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) { printf("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_select_file(card, &path, &file); if (r) { printf("unable to go up: %s\n", sc_strerror(r)); return -1; } if (current_file) 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_select_file(card, &path, &file); if (r) { check_ret(r, SC_AC_OP_SELECT, "unable to select DF", current_file); return -1; } if ((file->type != SC_FILE_TYPE_DF) && (card->type != SC_CARD_TYPE_BELPIC_EID)) { printf("Error: file is not a DF.\n"); sc_file_free(file); select_current_path_or_die(); return -1; } current_path = path; if (current_file) sc_file_free(current_file); current_file = file; return 0; } static int read_and_util_print_binary_file(sc_file_t *file) { unsigned char *buf = NULL; int r; buf = malloc(file->size); if (!buf) return -1; r = sc_read_binary(card, 0, buf, file->size, 0); if (r < 0) { check_ret(r, SC_AC_OP_READ, "read failed", file); return -1; } if ((r != file->size) && (card->type != SC_CARD_TYPE_BELPIC_EID)) { printf("expecting %d, got only %d bytes.\n", file->size, r); return -1; } if ((r == 0) && (card->type == SC_CARD_TYPE_BELPIC_EID)) return -1; util_hex_dump_asc(stdout, buf, r, 0); free(buf); return 0; } static int read_and_print_record_file(sc_file_t *file, unsigned char sfi) { u8 buf[256]; int rec, r; for (rec = 1; ; rec++) { r = sc_read_record(card, rec, buf, sizeof(buf), SC_RECORD_BY_REC_NR | sfi); if (r == SC_ERROR_RECORD_NOT_FOUND) 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); } } static int do_cat(int argc, char **argv) { int r, err = 1; sc_path_t path; sc_file_t *file = NULL; int not_current = 1; int sfi = 0; if (argc > 1) return usage(do_cat); 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) { printf("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)) { printf("Invalid SFI: %s\n", sfi_n); return usage(do_cat); } } else { if (arg_to_path(argv[0], &path, 0) != 0) return usage(do_cat); r = sc_select_file(card, &path, &file); 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)) { printf("only working EFs may be read\n"); goto err; } if (file->ef_structure == SC_FILE_EF_TRANSPARENT && !sfi) read_and_util_print_binary_file(file); else read_and_print_record_file(file, sfi); err = 0; err: if (not_current) { if (file != NULL) 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; int r, not_current = 1; const id2str_t *ac_ops = 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_select_file(card, &path, &file); if (r) { printf("unable to select file: %s\n", sc_strerror(r)); return -1; } } else return usage(do_info); switch (file->type) { case SC_FILE_TYPE_WORKING_EF: case SC_FILE_TYPE_INTERNAL_EF: st = "Elementary File"; break; case SC_FILE_TYPE_DF: st = "Dedicated File"; break; default: st = "Unknown File"; break; } printf("\n%s ID %04X\n\n", st, file->id); printf("%-15s%s\n", "File path:", path_to_filename(&path, '/')); printf("%-15s%lu bytes\n", "File size:", (unsigned long) 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("%-15s", "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("%-15s%s\n", "EF structure:", ef_type); ac_ops = ac_ops_ef; } for (i = 0; ac_ops != NULL && ac_ops[i].str != NULL; i++) { int len = strlen(ac_ops[i].str); printf("ACL for %s:%*s %s\n", ac_ops[i].str, (12 > len) ? (12 - len) : 0, "", util_acl_to_str(sc_file_get_acl_entry(file, ac_ops[i].id))); } if (file->prop_attr_len) { printf("%-25s", "Proprietary attributes:"); util_hex_dump(stdout, file->prop_attr, file->prop_attr_len, " "); printf("\n"); } if (file->sec_attr_len) { printf("%-25s", "Security attributes:"); util_hex_dump(stdout, file->sec_attr, file->sec_attr_len, " "); printf("\n"); } 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_create_file(card, file); 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); 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_delete_file(card, &path); if (r) { check_ret(r, SC_AC_OP_DELETE, "DELETE FILE failed", current_file); return -1; } 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[64]; size_t buflen = sizeof(buf), i; struct sc_pin_cmd_data data; int 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) { printf("Invalid type.\n"); return usage(do_verify); } if (sscanf(argv[0] + prefix_len, "%d", &data.pin_reference) != 1) { printf("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) { printf("No PIN entered - aborting VERIFY.\n"); return -1; } if (strlcpy(buf, pin, sizeof(buf)) >= sizeof(buf)) { free(pin); printf("PIN too long - aborting VERIFY.\n"); return -1; } free(pin); data.pin1.data = buf; data.pin1.len = strlen(buf); } } else { r = parse_string_or_hexdata(argv[1], buf, &buflen); if (0 != r) { printf("Invalid key value.\n"); return usage(do_verify); } data.pin1.data = buf; data.pin1.len = buflen; } r = sc_pin_cmd(card, &data, &tries_left); 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 printf("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[64]; u8 newpin[64]; 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)) { printf("Invalid type.\n"); return usage(do_change); } if (sscanf(argv[0] + 3, "%d", &ref) != 1) { printf("Invalid key reference.\n"); return usage(do_change); } if (argc == 3) { oldpinlen = sizeof(oldpin); if (parse_string_or_hexdata(argv[1], oldpin, &oldpinlen) != 0) { printf("Invalid key value.\n"); return usage(do_change); } } if (argc >= 2) { newpinlen = sizeof(newpin); if (parse_string_or_hexdata(argv[argc-1], newpin, &newpinlen) != 0) { printf("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_pin_cmd(card, &data, &tries_left); 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"); } printf("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[64]; u8 newpin[64]; 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)) { printf("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) { printf("Invalid key value.\n"); return usage(do_unblock); } } if (argc > 2) { newpinlen = sizeof(newpin); if (parse_string_or_hexdata(argv[2], newpin, &newpinlen) != 0) { printf("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_pin_cmd(card, &data, NULL); if (r) { if (r == SC_ERROR_PIN_CODE_INCORRECT) printf("Incorrect code.\n"); printf("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[256]; 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, '_'); outf = (strcmp(filename, "-") == 0) ? stdout : fopen(filename, "wb"); if (outf == NULL) { perror(filename); goto err; } r = sc_select_file(card, &path, &file); if (r) { check_ret(r, SC_AC_OP_SELECT, "unable to select file", current_file); goto err; } if (file->type != SC_FILE_TYPE_WORKING_EF) { printf("only working EFs may be read\n"); goto err; } count = file->size; while (count) { int 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 ((r != c) && (card->type != SC_CARD_TYPE_BELPIC_EID)) { printf("expecting %d, 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: if (file) sc_file_free(file); 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[240]; size_t buflen = sizeof(buf); int r, err = 1; int 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); printf("in: %i; %s\n", offs, argv[2]); r = parse_string_or_hexdata(argv[2], buf, &buflen); if (r < 0) { printf("unable to parse data\n"); return -1; } r = sc_select_file(card, &path, &file); 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) { printf("EF structure should be SC_FILE_EF_TRANSPARENT\n"); goto err; } r = sc_update_binary(card, offs, buf, buflen, 0); if (r < 0) { printf("Cannot update %04X; return %i\n", file->id, r); goto err; } printf("Total of %d bytes written to %04X at %i offset.\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[240]; size_t buflen; int r, i, err = 1; int 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); printf("in: %i; %i; %s\n", rec, offs, argv[3]); r = sc_select_file(card, &path, &file); 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) { printf("EF structure should be SC_FILE_EF_LINEAR_VARIABLE\n"); goto err; } else if (rec < 1 || rec > file->record_count) { printf("Invalid record number %i\n", rec); goto err; } r = sc_read_record(card, rec, buf, sizeof(buf), SC_RECORD_BY_REC_NR); if (r<0) { printf("Cannot read record %i; return %i\n", rec, r); goto err;; } buflen = sizeof(buf) - offs; i = parse_string_or_hexdata(argv[3], buf + offs, &buflen); if (!i) { printf("unable to parse data\n"); goto err; } r = sc_update_record(card, rec, buf, r, SC_RECORD_BY_REC_NR); if (r<0) { printf("Cannot update record %i; return %i\n", rec, r); goto err; } printf("Total of %d bytes written to record %i at %i offset.\n", i, 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[256]; int r, err = 1; size_t count = 0; unsigned int idx = 0; sc_path_t path; sc_file_t *file = NULL; const char *filename; FILE *outf = NULL; if (argc < 1 || argc > 2) return usage(do_put); if (arg_to_path(argv[0], &path, 0) != 0) return usage(do_put); filename = (argc == 2) ? argv[1] : path_to_filename(&path, '_'); outf = fopen(filename, "rb"); if (outf == NULL) { perror(filename); goto err; } r = sc_select_file(card, &path, &file); if (r) { check_ret(r, SC_AC_OP_SELECT, "unable to select file", current_file); goto err; } count = file->size; while (count) { int c = count > sizeof(buf) ? sizeof(buf) : count; r = fread(buf, 1, c, outf); if (r < 0) { perror("fread"); goto err; } if (r != c) count = c = r; r = sc_update_binary(card, idx, buf, c, 0); if (r < 0) { check_ret(r, SC_AC_OP_READ, "update failed", file); goto err; } if (r != c) { printf("expecting %d, wrote only %d bytes.\n", c, r); goto err; } idx += c; count -= c; } printf("Total of %d bytes written.\n", idx); err = 0; err: if (file) sc_file_free(file); if (outf) fclose(outf); 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_card_ctl(card, SC_CARDCTL_ERASE_CARD, NULL); if (r) { printf("Failed to erase card: %s\n", sc_strerror (r)); return -1; } return 0; } static int do_random(int argc, char **argv) { unsigned char buffer[256]; int r, count; if (argc != 1) return usage(do_random); count = atoi(argv[0]); if (count < 0 || count > 256) { printf("Number must be in range 0..256\n"); return -1; } r = sc_get_challenge(card, buffer, count); if (r < 0) { printf("Failed to get random bytes: %s\n", sc_strerror(r)); return -1; } util_hex_dump_asc(stdout, buffer, count, 0); return 0; } static int do_get_data(int argc, char **argv) { unsigned char buffer[256]; unsigned int tag; FILE *fp; int r; if (argc != 1 && argc != 2) return usage(do_get_data); tag = strtoul(argv[0], NULL, 16); r = sc_get_data(card, tag, buffer, sizeof(buffer)); if (r < 0) { printf("Failed to get data object: %s\n", sc_strerror(r)); return -1; } if (argc == 2) { const char *filename = argv[1]; if (!(fp = fopen(filename, "w"))) { perror(filename); return -1; } fwrite(buffer, r, 1, fp); fclose(fp); } else { printf("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) { unsigned int tag; u8 buf[8192]; size_t buflen = sizeof(buf); int r; if (argc != 2) return usage(do_put_data); /* Extract DO's tag */ tag = strtoul(argv[0], NULL, 16); /* Extract the new content */ /* buflen is the max length of reception buffer */ r = parse_string_or_hexdata(argv[1], buf, &buflen); if (r < 0) { printf("unable to parse data\n"); return -1; } /* Call OpenSC to do put data */ r = sc_put_data(card, tag, buf, buflen); if (r < 0) { printf("Cannot put data to %04X; return %i\n", tag, 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_APDU_BUFFER_SIZE * 2]; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE * 2]; size_t len, i; int r; if (argc < 1) return usage(do_apdu); for (i = 0, len = 0; i < (unsigned) argc; i++) { size_t len0 = strlen(argv[i]); 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_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); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) printf("Failure: %s\n", sc_strerror(r)); else printf("Success!\n"); return 0; } static int do_asn1(int argc, char **argv) { int r, err = 1; sc_path_t path; sc_file_t *file = NULL; int not_current = 1; size_t len; unsigned char *buf = NULL; if (argc > 1) return usage(do_asn1); /* select file */ if (argc) { if (arg_to_path(argv[0], &path, 0) != 0) { puts("Invalid file path"); return -1; } r = sc_select_file(card, &path, &file); 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) { printf("only working EFs may be read\n"); goto err; } /* read */ if (file->ef_structure != SC_FILE_EF_TRANSPARENT) { printf("only transparent file type is supported at the moment\n"); goto err; } len = file->size; buf = calloc(1, len); if (!buf) { goto err; } r = sc_read_binary(card, 0, buf, len, 0); if (r < 0) { check_ret(r, SC_AC_OP_READ, "read failed", file); goto err; } if ((size_t)r != len) { printf("expecting %lu, got only %d bytes.\n", (unsigned long) len, r); goto err; } /* asn1 dump */ sc_asn1_print_tags(buf, len); err = 0; err: if (buf) free(buf); if (not_current) { if (file) sc_file_free(file); select_current_path_or_die(); } return -err; } static int do_help(int argc, char **argv) { struct command *cmd; if (argc) return usage(do_help); printf("Supported commands:\n"); for (cmd = cmds; cmd->name; cmd++) { int len = strlen(cmd->name) + strlen(cmd->args); printf(" %s %s%*s %s\n", cmd->name, cmd->args, (len > 40) ? 0 : (40 - len), "", cmd->help); } return 0; } static int do_quit(int argc, char **argv) { die(0); return 0; } static int parse_cmdline(char *in, char **argv, int maxargc) { int argc; for (argc = 0; argc < maxargc; argc++) { in += strspn(in, " \t\n"); if (*in == '\0') return argc; if (*in == '"') { /* Parse quoted string */ argv[argc] = in++; in += strcspn(in, "\""); if (*in++ != '"') return 0; } else { /* White space delimited word */ argv[argc] = in; in += strcspn(in, " \t\n"); } if (*in != '\0') *in++ = '\0'; } return argc; } static char *read_cmdline(FILE *script, char *prompt) { static char buf[256]; static int initialized; static int interactive; if (!initialized) { initialized = 1; interactive = isatty(fileno(script)); #ifdef ENABLE_READLINE if (interactive) using_history(); #endif } #ifdef ENABLE_READLINE if (interactive) { char *line = readline(prompt); if (line && strlen(line) > 2 ) add_history(line); return line; } #endif /* Either we don't have readline or we are not running interactively */ #ifndef ENABLE_READLINE if (interactive) printf("%s", prompt); #endif fflush(stdout); if (fgets(buf, sizeof(buf), script) == NULL) 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 * const argv[]) { int r, c, long_optind = 0, err = 0; char *line; int cargc; char *cargv[260]; sc_context_param_t ctx_param; int lcycle = SC_CARDCTRL_LIFECYCLE_ADMIN; FILE *script; 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; r = sc_context_create(&ctx, &ctx_param); if (r) { fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); return 1; } if (verbose > 1) { ctx->debug = verbose; ctx->debug_file = stderr; } if (opt_driver != NULL) { err = sc_set_card_driver(ctx, opt_driver); if (err) { fprintf(stderr, "Driver '%s' not found!\n", opt_driver); err = 1; goto end; } } err = util_connect_card(ctx, &card, opt_reader, opt_wait, 0); if (err) goto end; if (opt_startfile) { if(*opt_startfile) { char startpath[1024]; char *args[] = { startpath }; strncpy(startpath, opt_startfile, sizeof(startpath)-1); r = do_cd(1, args); if (r) { printf("unable to select file %s: %s\n", opt_startfile, sc_strerror(r)); return -1; } } } else { sc_format_path("3F00", ¤t_path); r = sc_select_file(card, ¤t_path, ¤t_file); if (r) { printf("unable to select MF: %s\n", sc_strerror(r)); return 1; } } r = sc_card_ctl(card, SC_CARDCTL_LIFECYCLE_SET, &lcycle); 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: script = stdin; break; case 1: 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)) { struct command *cmd; char prompt[3*SC_MAX_PATH_STRING_SIZE]; sprintf(prompt, "OpenSC [%s]> ", path_to_filename(¤t_path, '/')); line = read_cmdline(script, prompt); if (line == NULL) break; cargc = parse_cmdline(line, cargv, DIM(cargv)); if ((cargc < 1) || (*cargv[0] == '#')) continue; for (r=cargc; r < (int)DIM(cargv); r++) cargv[r] = ""; cmd = ambiguous_match(cmds, cargv[0]); if (cmd == NULL) { do_help(0, NULL); } else { cmd->func(cargc-1, cargv+1); } } end: die(err); return 0; /* not reached */ } opensc-0.13.0/src/tools/util.h0000644000015201777760000000216212057406034013103 00000000000000#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 "common/compat_getopt.h" #include "libopensc/opensc.h" #ifdef __cplusplus extern "C" { #endif void util_print_binary(FILE *f, const u8 *buf, int count); void util_hex_dump(FILE *f, const u8 *in, int len, const char *sep); void util_hex_dump_asc(FILE *f, const u8 *in, size_t count, int addr); void util_print_usage_and_die(const char *app_name, const struct option options[], const char *option_help[], const char *args); const char * util_acl_to_str(const struct sc_acl_entry *e); void util_warn(const char *fmt, ...); void util_error(const char *fmt, ...); void util_fatal(const char *fmt, ...); /* All singing all dancing card connect routine */ int util_connect_card(struct sc_context *, struct sc_card **, const char *reader_id, int wait, int verbose); int util_getpass (char **lineptr, size_t *n, FILE *stream); #ifdef __cplusplus } #endif #endif opensc-0.13.0/win32/0000755000015201777760000000000012057406120011043 500000000000000opensc-0.13.0/win32/OpenSC.iss.in0000644000015201777760000000472012057406034013246 00000000000000[Setup] AppName=OpenSC AppVerName=OpenSC @PACKAGE_VERSION@ AppPublisher=OpenSC Project AppPublisherURL=http://www.opensc-project.org/ AppSupportURL=http://www.opensc-project.org/opensc/ AppUpdatesURL=http://www.opensc-project.org/opensc/ 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=v@PACKAGE_VERSION@ DisableReadyPage=true InternalCompressLevel=max VersionInfoCopyright=2010 OpenSC Project DisableStartupPrompt=true AlwaysShowComponentsList=false ShowComponentSizes=false FlatComponentsList=false WizardImageBackColor=clWhite DisableFinishedPage=false InfoBeforeFile=README.rtf VersionInfoProductName=OpenSC VersionInfoProductVersion=@OPENSC_VERSION_MAJOR@.@OPENSC_VERSION_MINOR@.@OPENSC_VERSION_FIX@ AllowRootDirectory=true UninstallDisplayName=OpenSC DefaultGroupName=OpenSC [Tasks] [Files] Source: opensc\*.profile; DestDir: {app}\profiles Source: opensc\*.dll; DestDir: {sys}; Flags: overwritereadonly replacesameversion ignoreversion uninsnosharedfileprompt restartreplace Source: opensc\*.exe; DestDir: {app}; Flags: overwritereadonly replacesameversion ignoreversion Source: engine_pkcs11\*.dll; DestDir: {sys}; Components: OpenSSL_engine; Flags: overwritereadonly replacesameversion ignoreversion Source: 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.13.0/win32/OpenSC.wxs0000644000015201777760000003460112057406062012666 00000000000000 opensc-0.13.0/win32/ltrc.inc0000644000015201777760000000042112057406034012423 00000000000000# Required to build Windows resource file RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE) .rc.lo: $(LTRCCOMPILE) -i "$<" -o "$@" .rc.o: $(RCCOMPILE) -i "$<" -o "$@" opensc-0.13.0/win32/Makefile.mak0000644000015201777760000000062612057406034013202 00000000000000TOPDIR = .. !INCLUDE $(TOPDIR)\win32\Make.rules.mak all: config.h config.h: winconfig.h copy /y winconfig.h config.h OpenSC.msi: OpenSC.wixobj $(WIX_PATH)\bin\light.exe -sh -ext WixUIExtension -ext WiXUtilExtension $? OpenSC.wixobj: OpenSC.wxs $(WIX_PATH)\bin\candle.exe -ext WiXUtilExtension -dSOURCE_DIR=$(TOPDIR) $(CANDLEFLAGS) $? clean:: del /Q config.h *.msi *.wixobj *.wixpdb opensc-0.13.0/win32/license.rtf0000644000015201777760000017111612057406034013135 00000000000000{\rtf1\ansi\deff0\adeflang1025 {\fonttbl{\f0\froman\fprq2\fcharset0 Times New Roman;}{\f1\froman\fprq2\fcharset0 Times New Roman;}{\f2\fswiss\fprq2\fcharset0 Arial;}{\f3\froman\fprq2\fcharset0 Times New Roman;}{\f4\fmodern\fprq1\fcharset0 DejaVu Sans Mono;}{\f5\fnil\fprq0\fcharset128 StarSymbol{\*\falt Arial Unicode MS};}{\f6\fnil\fprq2\fcharset0 DejaVu Sans;}} {\colortbl;\red0\green0\blue0;\red128\green128\blue128;} {\stylesheet{\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061\snext1 Normal;} {\s2\sb240\sa120\keepn\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\afs28\lang255\ltrch\dbch\langfe255\hich\f2\fs28\lang1061\loch\f2\fs28\lang1061\sbasedon1\snext3 Heading;} {\s3\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061\sbasedon1\snext3 Body Text;} {\s4\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061\sbasedon3\snext4 List;} {\s5\sb120\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af6\afs24\lang255\ai\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\i\loch\f0\fs24\lang1061\i\sbasedon1\snext5 caption;} {\s6\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061\sbasedon1\snext6 Index;} {\s7\sb240\sa120\keepn\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\afs28\lang255\ab\ltrch\dbch\langfe255\hich\f0\fs28\lang1061\b\loch\f0\fs28\lang1061\b\sbasedon2\snext3{\*\soutlvl2} heading 3;} {\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061\sbasedon1\snext8 Preformatted Text;} {\*\cs10\cf0\rtlch\af6\afs24\lang255\ab\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b Strong Emphasis;} {\*\cs11\cf0\rtlch\af5\afs18\lang255\ltrch\dbch\af5\langfe255\hich\f5\fs18\lang1061\loch\f5\fs18\lang1061 Bullet Symbols;} {\*\cs12\cf0\rtlch\af6\afs24\lang255\ai\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\i\loch\f0\fs24\lang1061\i Variable;} }{\*\listtable{\list\listtemplateid1 {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li707} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li1414} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li2121} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li2828} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li3535} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li4242} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li4949} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li5656} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li6363} {\*\soutlvl{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li7070}}\listid1} {\list\listtemplateid2 {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li707} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li1414} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li2121} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li2828} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li3535} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li4242} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li4949} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li5656} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li6363} {\*\soutlvl{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li7070}}\listid2} {\list\listtemplateid3 {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li707} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li1414} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li2121} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li2828} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li3535} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li4242} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li4949} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li5656} {\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li6363} {\*\soutlvl{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\fs18\f5\fs18\f5\fs18\f5\fi-283\li7070}}\listid3} }{\listoverridetable{\listoverride\listid1\listoverridecount0\ls0}{\listoverride\listid2\listoverridecount0\ls1}{\listoverride\listid3\listoverridecount0\ls2}} {\info{\author Alvar Kusma}{\creatim\yr2011\mo4\dy3\hr16\min20}{\revtim\yr0\mo0\dy0\hr0\min0}{\printim\yr0\mo0\dy0\hr0\min0}{\comment StarWriter}{\vern6800}}\deftab709 {\*\pgdsctbl {\pgdsc0\pgdscuse195\pgwsxn11905\pghsxn16837\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\pgdscnxt0 Standard;}} \paperh16837\paperw11905\margl1134\margr1134\margt1134\margb1134\sectd\sbknone\pgwsxn11905\pghsxn16837\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\ftnbj\ftnstart1\ftnrstcont\ftnnar\aenddoc\aftnrstcont\aftnstart1\aftnnrlc \pard\plain \ltrpar\s7\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sb240\sa120\keepn\rtlch\afs28\lang255\ab\ltrch\dbch\langfe255\hich\f0\fs28\lang1061\b\loch\f0\fs28\lang1061\b {\rtlch \ltrch\loch\f0\fs28\lang1061\i0\b {\*\bkmkstart SEC1}{\*\bkmkend SEC1}GNU LESSER GENERAL PUBLIC LICENSE} \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 Version 2.1, February 1999 } \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 Copyright (C) 1991, 1999 Free Software Foundation, Inc.} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 Everyone is permitted to copy and distribute verbatim copies} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 of this license document, but changing it is not allowed.} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 [This is the first released version of the Lesser GPL. It also counts} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 as the successor of the GNU Library Public License, version 2, hence} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa283\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 the version number 2.1.]} \par \pard\plain \ltrpar\s7\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sb240\sa120\keepn\rtlch\afs28\lang255\ab\ltrch\dbch\langfe255\hich\f0\fs28\lang1061\b\loch\f0\fs28\lang1061\b {\rtlch \ltrch\loch\f0\fs28\lang1061\i0\b {\*\bkmkstart SEC2}{\*\bkmkend SEC2}Preamble} \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 i ts users. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 careful ly 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 y ou modify it. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 t he original author's reputation will not be affected by problems that might be introduced by others. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 Licens e. We use this license for certain libraries in order to permit linking those libraries into non-free programs. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. The se disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 th at 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 w hole GNU operating system, as well as its variant, the GNU/Linux operating system. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 t he latter must be combined with the library in order to run. } \par \pard\plain \ltrpar\s7\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sb240\sa120\keepn\rtlch\afs28\lang255\ab\ltrch\dbch\langfe255\hich\f0\fs28\lang1061\b\loch\f0\fs28\lang1061\b {\rtlch \ltrch\loch\f0\fs28\lang1061\i0\b {\*\bkmkstart SEC3}{\*\bkmkend SEC3}TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION} \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b 0.}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 "thi s License"). Each licensee is addressed as "you". } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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".) } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 "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 t o control compilation and installation of the library. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 const itute 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b 1.}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 i ntact 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b 2.}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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: } \par \pard\plain {\listtext\pard\plain \li707\ri0\lin707\rin0\fi-283\f5\fs18\f5\fs18\f5\fs18 \'95\tab}\ilvl0 \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\ls2\li707\ri0\lin707\rin0\fi-283\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b a)}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 The modified work must itself be a software library. } \par \pard\plain {\listtext\pard\plain \li707\ri0\lin707\rin0\fi-283\f5\fs18\f5\fs18\f5\fs18 \'95\tab}\ilvl0 \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\ls2\li707\ri0\lin707\rin0\fi-283\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b b)}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. } \par \pard\plain {\listtext\pard\plain \li707\ri0\lin707\rin0\fi-283\f5\fs18\f5\fs18\f5\fs18 \'95\tab}\ilvl0 \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\ls2\li707\ri0\lin707\rin0\fi-283\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b c)}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. } \par \pard\plain {\listtext\pard\plain \li707\ri0\lin707\rin0\fi-283\sa120\f5\fs18\f5\fs18\f5\fs18 \'95\tab}\ilvl0 \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\ls2\li707\ri0\lin707\rin0\fi-283\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b d)}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 ensur e 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. } \par \pard\plain {\listtext\pard\plain \li707\ri0\lin707\rin0\fi-283\sa120\f5\fs18\f5\fs18\f5\fs18 \'95\tab}\ilvl0 \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 (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 option al: if the application does not supply it, the square root function must still compute square roots.)} \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b 3.}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 Publi c 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 This option is useful when you wish to copy part of the code of the Library into a program that is not a library. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b 4.}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 sourc e code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 no t compelled to copy the source along with the object code. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b 5.}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 Libra ry, and therefore falls outside the scope of this License. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 thi s License. Section 6 states terms for distribution of such executables. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 d erivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 it self. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b 6.}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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: } \par \pard\plain {\listtext\pard\plain \li707\ri0\lin707\rin0\fi-283\f5\fs18\f5\fs18\f5\fs18 \'95\tab}\ilvl0 \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\ls1\li707\ri0\lin707\rin0\fi-283\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b a)}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 th e 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.) } \par \pard\plain {\listtext\pard\plain \li707\ri0\lin707\rin0\fi-283\f5\fs18\f5\fs18\f5\fs18 \'95\tab}\ilvl0 \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\ls1\li707\ri0\lin707\rin0\fi-283\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b b)}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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, a nd (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. } \par \pard\plain {\listtext\pard\plain \li707\ri0\lin707\rin0\fi-283\f5\fs18\f5\fs18\f5\fs18 \'95\tab}\ilvl0 \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\ls1\li707\ri0\lin707\rin0\fi-283\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b c)}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain {\listtext\pard\plain \li707\ri0\lin707\rin0\fi-283\f5\fs18\f5\fs18\f5\fs18 \'95\tab}\ilvl0 \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\ls1\li707\ri0\lin707\rin0\fi-283\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b d)}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain {\listtext\pard\plain \li707\ri0\lin707\rin0\fi-283\sa120\f5\fs18\f5\fs18\f5\fs18 \'95\tab}\ilvl0 \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\ls1\li707\ri0\lin707\rin0\fi-283\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b e)}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 Verify that the user has already received a copy of these materials or that you have already sent this user a copy. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 th at 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b 7.}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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: } \par \pard\plain {\listtext\pard\plain \li707\ri0\lin707\rin0\fi-283\f5\fs18\f5\fs18\f5\fs18 \'95\tab}\ilvl0 \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\ls0\li707\ri0\lin707\rin0\fi-283\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b a)}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain {\listtext\pard\plain \li707\ri0\lin707\rin0\fi-283\sa120\f5\fs18\f5\fs18\f5\fs18 \'95\tab}\ilvl0 \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\ls0\li707\ri0\lin707\rin0\fi-283\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b b)}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b 8.}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b 9.}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. Th erefore, 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b 10.}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b 11.}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 entire ly from distribution of the Library. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 whi ch 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b 12.}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 limit ation 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b 13.}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 concern s. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 v ersion 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b 14.}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 t he 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. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b NO WARRANTY}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b 15.}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 WARR ANTY 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 P ROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b{\*\cs10\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\b\loch\f0\fs24\lang1061\b 16.}}{\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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, INCIDEN TAL 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 OTH ER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. } \par \pard\plain \ltrpar\s7\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sb240\sa120\keepn\rtlch\afs28\lang255\ab\ltrch\dbch\langfe255\hich\f0\fs28\lang1061\b\loch\f0\fs28\lang1061\b {\rtlch \ltrch\loch\f0\fs28\lang1061\i0\b END OF TERMS AND CONDITIONS} \par \pard\plain \ltrpar\s7\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sb240\sa120\keepn\rtlch\afs28\lang255\ab\ltrch\dbch\langfe255\hich\f0\fs28\lang1061\b\loch\f0\fs28\lang1061\b {\rtlch \ltrch\loch\f0\fs28\lang1061\i0\b {\*\bkmkstart SEC4}{\*\bkmkend SEC4}How to Apply These Terms to Your New Libraries} \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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). } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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 w here the full notice is found. } \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061{\rtlch \ltrch\loch\f4\fs20\lang1061\i\b0\*\cs12\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\i\loch\f0\fs24\lang1061\i one line to give the library's name and an idea of what it does.} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 Copyright (C) {\*\cs12\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\i\loch\f0\fs24\lang1061\i year} {\*\cs12\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\i\loch\f0\fs24\lang1061\i name of author}} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 This library is free software; you can redistribute it and/or} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 modify it under the terms of the GNU Lesser General Public} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 License as published by the Free Software Foundation; either} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 version 2.1 of the License, or (at your option) any later version.} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 This library is distributed in the hope that it will be useful,} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 but WITHOUT ANY WARRANTY; without even the implied warranty of} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 Lesser General Public License for more details.} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 You should have received a copy of the GNU Lesser General Public} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 License along with this library; if not, write to the Free Software} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa283\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA} \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 Also add information on how to contact you by electronic and paper mail. } \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 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: } \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 Yoyodyne, Inc., hereby disclaims all copyright interest in} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 the library `Frob' (a library for tweaking knobs) written} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 by James Random Hacker.} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061{\rtlch \ltrch\loch\f4\fs20\lang1061\i\b0{\*\cs12\cf0\rtlch\ltrch\dbch\hich\f0\fs24\lang1061\i\loch\f0\fs24\lang1061\i signature of Ty Coon}}{\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 , 1 April 1990} \par \pard\plain \ltrpar\s8\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa283\rtlch\af4\afs20\lang255\ltrch\dbch\af4\langfe255\hich\f4\fs20\lang1061\loch\f4\fs20\lang1061 {\rtlch \ltrch\loch\f4\fs20\lang1061\i0\b0 Ty Coon, President of Vice} \par \pard\plain \ltrpar\s3\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\sa120\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 {\rtlch \ltrch\loch\f0\fs24\lang1061\i0\b0 That's all there is to it!} \par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af6\afs24\lang255\ltrch\dbch\af6\langfe255\hich\f0\fs24\lang1061\loch\f0\fs24\lang1061 \par }opensc-0.13.0/win32/Make.rules.mak0000644000015201777760000000734012057406034013473 00000000000000OPENSC_FEATURES = pcsc #Include support for minidriver MINIDRIVER_DEF = /DENABLE_MINIDRIVER #Build MSI with the Windows Installer XML (WIX) toolkit, requires WIX >= 3.6 !IF "$(BUILD_ON)" == "WIN64" WIX_PATH = "C:\Program Files (x86)\Windows Installer XML v3.6" !ELSE WIX_PATH = "C:\Program Files\Windows Installer XML v3.6" !ENDIF #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 "$(BUILD_FOR)" == "WIN64" OPENSSL_DIR = C:\OpenSSL-Win64 !ELSE OPENSSL_DIR = C:\OpenSSL-Win32 !ENDIF OPENSSL_INCL_DIR = /I$(OPENSSL_DIR)\include !IF "$(DEBUG_DEF)" == "/DDEBUG" OPENSSL_LIB = $(OPENSSL_DIR)\lib\VC\static\libeay32MTd.lib $(OPENSSL_DIR)\lib\VC\static\ssleay32MTd.lib user32.lib advapi32.lib crypt32.lib !ELSE OPENSSL_LIB = $(OPENSSL_DIR)\lib\VC\static\libeay32MT.lib $(OPENSSL_DIR)\lib\VC\static\ssleay32MT.lib user32.lib advapi32.lib crypt32.lib !ENDIF PROGRAMS_OPENSSL = pkcs15-init.exe cryptoflex-tool.exe netkey-tool.exe piv-tool.exe westcos-tool.exe OPENSC_FEATURES = $(OPENSC_FEATURES) openssl !ENDIF # If you want support for zlib (Used for PIV, infocamere and actalis): # - Download zlib and build with "nmake /f win32\Makefile.msc zlib.lib" # - 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 "$(ZLIB_DEF)" == "/DENABLE_ZLIB" ZLIB_INCL_DIR = /IC:\zlib-1.2.5 ZLIB_LIB = C:\zlib-1.2.5\zlib.lib OPENSC_FEATURES = $(OPENSC_FEATURES) zlib !ENDIF # Used for MiniDriver !IF "$(BUILD_ON)" == "WIN64" CNGSDK_INCL_DIR = "/IC:\Program Files (x86)\Microsoft CNG Development Kit\Include" !ELSE CNGSDK_INCL_DIR = "/IC:\Program Files\Microsoft CNG 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 $(OPENSSL_INCL_DIR) $(ZLIB_INCL_DIR) $(LIBLTDL_INCL) $(INTTYPES_INCL_DIR) $(CNGSDK_INCL_DIR) !IF "$(DEBUG_DEF)" == "/DDEBUG" LINKDEBUGFLAGS = /NODEFAULTLIB:LIBCMT /DEBUG CODE_OPTIMIZATION = COPTS = /W3 /D_CRT_SECURE_NO_DEPRECATE /MTd /nologo /DHAVE_CONFIG_H $(ALL_INCLUDES) /D_WIN32_WINNT=0x0502 /DWIN32_LEAN_AND_MEAN $(OPENSSL_DEF) $(ZLIB_DEF) $(MINIDRIVER_DEF) $(SM_DEF) /DOPENSC_FEATURES="\"$(OPENSC_FEATURES)\"" /DDEBUG /Zi /Od !ELSE LINKDEBUGFLAGS = /NODEFAULTLIB:LIBCMTD COPTS = /W3 /D_CRT_SECURE_NO_DEPRECATE /MT /nologo /DHAVE_CONFIG_H $(ALL_INCLUDES) /D_WIN32_WINNT=0x0502 /DWIN32_LEAN_AND_MEAN $(OPENSSL_DEF) $(ZLIB_DEF) $(MINIDRIVER_DEF) $(SM_DEF) /DOPENSC_FEATURES="\"$(OPENSC_FEATURES)\"" !ENDIF !IF "$(BUILD_FOR)" == "WIN64" LINKFLAGS = /NOLOGO /INCREMENTAL:NO /MACHINE:X64 /MANIFEST:NO /NODEFAULTLIB:MSVCRTD /NODEFAULTLIB:MSVCRT $(LINKDEBUGFLAGS) LIBFLAGS = /nologo /machine:x64 CANDLEFLAGS = -dPlatform=x64 !ELSE LINKFLAGS = /NOLOGO /INCREMENTAL:NO /MACHINE:X86 /MANIFEST:NO /NODEFAULTLIB:MSVCRTD /NODEFAULTLIB:MSVCRT $(LINKDEBUGFLAGS) LIBFLAGS = /nologo /machine:x86 CANDLEFLAGS = -dPlatform=x86 !ENDIF .c.obj:: cl $(CODE_OPTIMIZATION) $(COPTS) /c $< .rc.res:: rc /l 0x0409 $< clean:: del /Q *.obj *.dll *.exe *.pdb *.lib *.def *.manifest opensc-0.13.0/win32/OpenSC.ico0000755000015201777760000002156612057406034012627 00000000000000=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.13.0/win32/OpenSC.iss0000644000015201777760000000445412057406062012646 00000000000000[Setup] AppName=OpenSC AppVerName=OpenSC 0.13.0 AppPublisher=OpenSC Project AppPublisherURL=http://www.opensc-project.org/ AppSupportURL=http://www.opensc-project.org/opensc/ AppUpdatesURL=http://www.opensc-project.org/opensc/ DefaultDirName={pf}\OpenSC Project\OpenSC OutputBaseFilename=OpenSC-0.13.0 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=0.13.0 VersionInfoVersion=0.13.0 VersionInfoDescription=OpenSC tools and libraries VersionInfoTextVersion=v0.13.0 DisableReadyPage=true InternalCompressLevel=max VersionInfoCopyright=2010 OpenSC Project DisableStartupPrompt=true AlwaysShowComponentsList=false ShowComponentSizes=false FlatComponentsList=false WizardImageBackColor=clWhite DisableFinishedPage=false InfoBeforeFile=README.rtf VersionInfoProductName=OpenSC VersionInfoProductVersion=0.13.0 AllowRootDirectory=true UninstallDisplayName=OpenSC DefaultGroupName=OpenSC [Tasks] [Files] Source: opensc\*.profile; DestDir: {app}\profiles Source: opensc\*.dll; DestDir: {sys}; Flags: overwritereadonly replacesameversion ignoreversion uninsnosharedfileprompt restartreplace Source: opensc\*.exe; DestDir: {app}; Flags: overwritereadonly replacesameversion ignoreversion Source: engine_pkcs11\*.dll; DestDir: {sys}; Components: OpenSSL_engine; Flags: overwritereadonly replacesameversion ignoreversion Source: 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.13.0/win32/OpenSC.wxs.in0000755000015201777760000003467512057406034013310 00000000000000 opensc-0.13.0/win32/Makefile.am0000644000015201777760000000071312057406034013024 00000000000000MAINTAINERCLEANFILES = $(srcdir)/Makefile.in $(srcdir)/versioninfo.rc $(srcdir)/winconfig.h \ $(srcdir)/OpenSC.iss $(srcdir)/OpenSC.wxs $(srcdir)/OpenSC.ico $(srcdir)/license.rtf EXTRA_DIST = ltrc.inc Makefile.mak Make.rules.mak opensc-install.bat \ versioninfo.rc.in winconfig.h.in OpenSC.iss.in OpenSC.wxs.in dist_noinst_HEADERS = versioninfo.rc winconfig.h OpenSC.iss OpenSC.wxs license.rtf OpenSC.ico if WIN32 sbin_SCRIPTS = opensc-install.bat endif opensc-0.13.0/win32/winconfig.h.in0000644000015201777760000000301412057406034013526 00000000000000#ifndef _OPENSC_WINCONFIG_H #define _OPENSC_WINCONFIG_H #include #include #include #include #include #ifndef strcasecmp #define strcasecmp stricmp #endif #ifndef strncasecmp #define strncasecmp strnicmp #endif #ifndef snprintf #define snprintf _snprintf #endif #ifndef vsnprintf #define vsnprintf _vsnprintf #endif #ifndef isatty #define isatty _isatty #endif #ifndef strnicmp #define strnicmp _strnicmp #endif #ifndef stricmp #define stricmp _stricmp #endif #ifndef strdup #define strdup _strdup #endif #ifndef fileno #define fileno _fileno #endif #ifndef mkdir #define mkdir _mkdir #endif #ifndef access #define access _access #endif #ifndef unlink #define unlink _unlink #endif #ifndef putenv #define putenv _putenv #endif #ifndef R_OK #define R_OK 4 /* test whether readable. */ #define W_OK 2 /* test whether writable. */ #define X_OK 1 /* test whether execubale. */ #define F_OK 0 /* test whether exist. */ #endif #ifndef S_IRUSR #define S_IRUSR S_IREAD #endif #ifndef S_IWUSR #define S_IWUSR S_IWRITE #endif #define HAVE_IO_H #define ENABLE_PCSC #define HAVE_WINSCARD_H #define DEFAULT_PCSC_PROVIDER "winscard.dll" #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 PACKAGE_NAME #define PACKAGE_NAME "@PACKAGE_NAME@" #endif #ifndef OPENSC_FEATURES #define OPENSC_FEATURES "N/A" #endif #endif opensc-0.13.0/win32/versioninfo.rc0000644000015201777760000000135012057406062013656 00000000000000#include VS_VERSION_INFO VERSIONINFO FILEVERSION 0,13,0,0 PRODUCTVERSION 0,13,0,0 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", "Provided under the terms of the GNU Lesser General Public License (LGPLv2.1+).\0" VALUE "CompanyName", "OpenSC Project\0" VALUE "FileVersion", "0.13.0.0\0" VALUE "InternalName", "opensc\0" VALUE "LegalCopyright", "OpenSC Project\0" VALUE "LegalTrademarks", "\0" VALUE "PrivateBuild", "\0" VALUE "ProductName", "OpenSC\0" VALUE "ProductVersion", "0,13,0,0\0" VALUE "SpecialBuild", "\0" END END END opensc-0.13.0/win32/Makefile.in0000644000015201777760000004114112057406056013041 00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program 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. @SET_MAKE@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = win32 DIST_COMMON = $(dist_noinst_HEADERS) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in $(srcdir)/OpenSC.iss.in \ $(srcdir)/OpenSC.wxs.in $(srcdir)/versioninfo.rc.in \ $(srcdir)/winconfig.h.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = versioninfo.rc winconfig.h OpenSC.iss OpenSC.wxs CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__installdirs = "$(DESTDIR)$(sbindir)" SCRIPTS = $(sbin_SCRIPTS) AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ SOURCES = DIST_SOURCES = HEADERS = $(dist_noinst_HEADERS) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUG_FILE = @DEBUG_FILE@ DEFAULT_PCSC_PROVIDER = @DEFAULT_PCSC_PROVIDER@ DEFAULT_SM_MODULE = @DEFAULT_SM_MODULE@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GIT = @GIT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBRARY_BITNESS = @LIBRARY_BITNESS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCT_CFLAGS = @OPENCT_CFLAGS@ OPENCT_LIBS = @OPENCT_LIBS@ OPENSC_LT_AGE = @OPENSC_LT_AGE@ OPENSC_LT_CURRENT = @OPENSC_LT_CURRENT@ OPENSC_LT_OLDEST = @OPENSC_LT_OLDEST@ OPENSC_LT_REVISION = @OPENSC_LT_REVISION@ OPENSC_VERSION_FIX = @OPENSC_VERSION_FIX@ OPENSC_VERSION_MAJOR = @OPENSC_VERSION_MAJOR@ OPENSC_VERSION_MINOR = @OPENSC_VERSION_MINOR@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OPTIONAL_OPENCT_CFLAGS = @OPTIONAL_OPENCT_CFLAGS@ OPTIONAL_OPENCT_LIBS = @OPTIONAL_OPENCT_LIBS@ OPTIONAL_OPENSSL_CFLAGS = @OPTIONAL_OPENSSL_CFLAGS@ OPTIONAL_OPENSSL_LIBS = @OPTIONAL_OPENSSL_LIBS@ OPTIONAL_PCSC_CFLAGS = @OPTIONAL_PCSC_CFLAGS@ OPTIONAL_READLINE_CFLAGS = @OPTIONAL_READLINE_CFLAGS@ OPTIONAL_READLINE_LIBS = @OPTIONAL_READLINE_LIBS@ OPTIONAL_ZLIB_CFLAGS = @OPTIONAL_ZLIB_CFLAGS@ OPTIONAL_ZLIB_LIBS = @OPTIONAL_ZLIB_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCSC_CFLAGS = @PCSC_CFLAGS@ PCSC_LIBS = @PCSC_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RC = @RC@ READLINE_CFLAGS = @READLINE_CFLAGS@ READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ WIN_LIBPREFIX = @WIN_LIBPREFIX@ XSLTPROC = @XSLTPROC@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ git = @git@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkcs11dir = @pkcs11dir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ xslstylesheetsdir = @xslstylesheetsdir@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in $(srcdir)/versioninfo.rc $(srcdir)/winconfig.h \ $(srcdir)/OpenSC.iss $(srcdir)/OpenSC.wxs $(srcdir)/OpenSC.ico $(srcdir)/license.rtf EXTRA_DIST = ltrc.inc Makefile.mak Make.rules.mak opensc-install.bat \ versioninfo.rc.in winconfig.h.in OpenSC.iss.in OpenSC.wxs.in dist_noinst_HEADERS = versioninfo.rc winconfig.h OpenSC.iss OpenSC.wxs license.rtf OpenSC.ico @WIN32_TRUE@sbin_SCRIPTS = opensc-install.bat all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign win32/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign win32/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): versioninfo.rc: $(top_builddir)/config.status $(srcdir)/versioninfo.rc.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ winconfig.h: $(top_builddir)/config.status $(srcdir)/winconfig.h.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ OpenSC.iss: $(top_builddir)/config.status $(srcdir)/OpenSC.iss.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ OpenSC.wxs: $(top_builddir)/config.status $(srcdir)/OpenSC.wxs.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ install-sbinSCRIPTS: $(sbin_SCRIPTS) @$(NORMAL_INSTALL) test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)" @list='$(sbin_SCRIPTS)'; test -n "$(sbindir)" || list=; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ } \ ; done uninstall-sbinSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(sbin_SCRIPTS)'; test -n "$(sbindir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(sbindir)" && rm -f $$files mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: CTAGS CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(SCRIPTS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(sbindir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-sbinSCRIPTS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-sbinSCRIPTS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ clean-libtool ctags distclean distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-sbinSCRIPTS install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \ uninstall-am uninstall-sbinSCRIPTS # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: opensc-0.13.0/win32/versioninfo.rc.in0000644000015201777760000000174012057406034014265 00000000000000#include VS_VERSION_INFO VERSIONINFO FILEVERSION @OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,0 PRODUCTVERSION @OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,0 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", "Provided under the terms of the GNU Lesser General Public License (LGPLv2.1+).\0" VALUE "CompanyName", "OpenSC Project\0" VALUE "FileVersion", "@OPENSC_VERSION_MAJOR@.@OPENSC_VERSION_MINOR@.@OPENSC_VERSION_FIX@.0\0" VALUE "InternalName", "@PACKAGE_NAME@\0" VALUE "LegalCopyright", "OpenSC Project\0" VALUE "LegalTrademarks", "\0" VALUE "PrivateBuild", "\0" VALUE "ProductName", "OpenSC\0" VALUE "ProductVersion", "@OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,0\0" VALUE "SpecialBuild", "\0" END END END opensc-0.13.0/win32/opensc-install.bat0000755000015201777760000000201612057406034014414 00000000000000@echo off rem This script installs OpenSC rem Parameters: rem user - Install for this user only. setlocal set MODE=%1 set KEY=HKEY_LOCAL_MACHINE if "%MODE%" == "user" set KEY=HKEY_CURRENT_USER cd %0\..\.. if not exist bin\opensc-tool.exe goto error for /f %%f in (".") do set OPENSC_HOME=%%~ff set OPENSC_HOME_ESCAPED=%OPENSC_HOME:\=\\% set REG_FILE=%TEMP%\opensc-install.reg echo Windows Registry Editor Version 5.00 > %REG_FILE% echo [%KEY%\SOFTWARE\OpenSC Project\OpenSC] >> %REG_FILE% echo "ConfigFile"="%OPENSC_HOME_ESCAPED%\\etc\\opensc.conf" >> %REG_FILE% echo [%KEY%\SOFTWARE\PKCS11-Spy] >> %REG_FILE% echo "Module"="%OPENSC_HOME_ESCAPED%\\bin\\opensc-pkcs11.dll" >> %REG_FILE% regedit /s %REG_FILE% del /q %REG_FILE% "%OPENSC_HOME%\bin\opensc-tool" -S "app:default:profile_dir:%OPENSC_HOME%\share\opensc" echo You may also want to add "%OPENSC_HOME%\bin" to your PATH, for use by other applications. goto end :error echo Invalid installation goto end :end endlocal opensc-0.13.0/win32/winconfig.h0000644000015201777760000000277112057406062013133 00000000000000#ifndef _OPENSC_WINCONFIG_H #define _OPENSC_WINCONFIG_H #include #include #include #include #include #ifndef strcasecmp #define strcasecmp stricmp #endif #ifndef strncasecmp #define strncasecmp strnicmp #endif #ifndef snprintf #define snprintf _snprintf #endif #ifndef vsnprintf #define vsnprintf _vsnprintf #endif #ifndef isatty #define isatty _isatty #endif #ifndef strnicmp #define strnicmp _strnicmp #endif #ifndef stricmp #define stricmp _stricmp #endif #ifndef strdup #define strdup _strdup #endif #ifndef fileno #define fileno _fileno #endif #ifndef mkdir #define mkdir _mkdir #endif #ifndef access #define access _access #endif #ifndef unlink #define unlink _unlink #endif #ifndef putenv #define putenv _putenv #endif #ifndef R_OK #define R_OK 4 /* test whether readable. */ #define W_OK 2 /* test whether writable. */ #define X_OK 1 /* test whether execubale. */ #define F_OK 0 /* test whether exist. */ #endif #ifndef S_IRUSR #define S_IRUSR S_IREAD #endif #ifndef S_IWUSR #define S_IWUSR S_IWRITE #endif #define HAVE_IO_H #define ENABLE_PCSC #define HAVE_WINSCARD_H #define DEFAULT_PCSC_PROVIDER "winscard.dll" #define SC_PKCS15_PROFILE_DIRECTORY "C:\\Program Files\\OpenSC Project\\OpenSC\\profiles" #define PATH_MAX FILENAME_MAX #ifndef PACKAGE_VERSION #define PACKAGE_VERSION "0.13.0" #endif #ifndef PACKAGE_NAME #define PACKAGE_NAME "opensc" #endif #ifndef OPENSC_FEATURES #define OPENSC_FEATURES "N/A" #endif #endif opensc-0.13.0/m4/0000755000015201777760000000000012057406120010421 500000000000000opensc-0.13.0/m4/ltversion.m40000644000015201777760000000127712057406053012644 00000000000000# ltversion.m4 -- version numbers -*- Autoconf -*- # # Copyright (C) 2004 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # Generated from ltversion.in. # serial 3017 ltversion.m4 # This file is part of GNU Libtool m4_define([LT_PACKAGE_VERSION], [2.2.6b]) m4_define([LT_PACKAGE_REVISION], [1.3017]) AC_DEFUN([LTVERSION_VERSION], [macro_version='2.2.6b' macro_revision='1.3017' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) opensc-0.13.0/m4/ltsugar.m40000644000015201777760000001042412057406053012272 00000000000000# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- # # Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 6 ltsugar.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) # lt_join(SEP, ARG1, [ARG2...]) # ----------------------------- # Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their # associated separator. # Needed until we can rely on m4_join from Autoconf 2.62, since all earlier # versions in m4sugar had bugs. m4_define([lt_join], [m4_if([$#], [1], [], [$#], [2], [[$2]], [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) m4_define([_lt_join], [m4_if([$#$2], [2], [], [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) # lt_car(LIST) # lt_cdr(LIST) # ------------ # Manipulate m4 lists. # These macros are necessary as long as will still need to support # Autoconf-2.59 which quotes differently. m4_define([lt_car], [[$1]]) m4_define([lt_cdr], [m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], [$#], 1, [], [m4_dquote(m4_shift($@))])]) m4_define([lt_unquote], $1) # lt_append(MACRO-NAME, STRING, [SEPARATOR]) # ------------------------------------------ # Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'. # Note that neither SEPARATOR nor STRING are expanded; they are appended # to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). # No SEPARATOR is output if MACRO-NAME was previously undefined (different # than defined and empty). # # This macro is needed until we can rely on Autoconf 2.62, since earlier # versions of m4sugar mistakenly expanded SEPARATOR but not STRING. m4_define([lt_append], [m4_define([$1], m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) # lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) # ---------------------------------------------------------- # Produce a SEP delimited list of all paired combinations of elements of # PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list # has the form PREFIXmINFIXSUFFIXn. # Needed until we can rely on m4_combine added in Autoconf 2.62. m4_define([lt_combine], [m4_if(m4_eval([$# > 3]), [1], [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl [[m4_foreach([_Lt_prefix], [$2], [m4_foreach([_Lt_suffix], ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) # lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) # ----------------------------------------------------------------------- # Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited # by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. m4_define([lt_if_append_uniq], [m4_ifdef([$1], [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], [lt_append([$1], [$2], [$3])$4], [$5])], [lt_append([$1], [$2], [$3])$4])]) # lt_dict_add(DICT, KEY, VALUE) # ----------------------------- m4_define([lt_dict_add], [m4_define([$1($2)], [$3])]) # lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) # -------------------------------------------- m4_define([lt_dict_add_subkey], [m4_define([$1($2:$3)], [$4])]) # lt_dict_fetch(DICT, KEY, [SUBKEY]) # ---------------------------------- m4_define([lt_dict_fetch], [m4_ifval([$3], m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) # lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) # ----------------------------------------------------------------- m4_define([lt_if_dict_fetch], [m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], [$5], [$6])]) # lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) # -------------------------------------------------------------- m4_define([lt_dict_filter], [m4_if([$5], [], [], [lt_join(m4_quote(m4_default([$4], [[, ]])), lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl ]) opensc-0.13.0/m4/libtool.m40000644000015201777760000077464712057406052012304 00000000000000# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- # # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, # 2006, 2007, 2008 Free Software Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. m4_define([_LT_COPYING], [dnl # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, # 2006, 2007, 2008 Free Software Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is part of GNU Libtool. # # GNU Libtool 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. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that 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 GNU Libtool; see the file COPYING. If not, a copy # can be downloaded from http://www.gnu.org/licenses/gpl.html, or # obtained by writing to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ]) # serial 56 LT_INIT # LT_PREREQ(VERSION) # ------------------ # Complain and exit if this libtool version is less that VERSION. m4_defun([LT_PREREQ], [m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, [m4_default([$3], [m4_fatal([Libtool version $1 or higher is required], 63)])], [$2])]) # _LT_CHECK_BUILDDIR # ------------------ # Complain if the absolute build directory name contains unusual characters m4_defun([_LT_CHECK_BUILDDIR], [case `pwd` in *\ * | *\ *) AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; esac ]) # LT_INIT([OPTIONS]) # ------------------ AC_DEFUN([LT_INIT], [AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT AC_BEFORE([$0], [LT_LANG])dnl AC_BEFORE([$0], [LT_OUTPUT])dnl AC_BEFORE([$0], [LTDL_INIT])dnl m4_require([_LT_CHECK_BUILDDIR])dnl dnl Autoconf doesn't catch unexpanded LT_ macros by default: m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 dnl unless we require an AC_DEFUNed macro: AC_REQUIRE([LTOPTIONS_VERSION])dnl AC_REQUIRE([LTSUGAR_VERSION])dnl AC_REQUIRE([LTVERSION_VERSION])dnl AC_REQUIRE([LTOBSOLETE_VERSION])dnl m4_require([_LT_PROG_LTMAIN])dnl dnl Parse OPTIONS _LT_SET_OPTIONS([$0], [$1]) # This can be used to rebuild libtool when needed LIBTOOL_DEPS="$ltmain" # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' AC_SUBST(LIBTOOL)dnl _LT_SETUP # Only expand once: m4_define([LT_INIT]) ])# LT_INIT # Old names: AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PROG_LIBTOOL], []) dnl AC_DEFUN([AM_PROG_LIBTOOL], []) # _LT_CC_BASENAME(CC) # ------------------- # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. m4_defun([_LT_CC_BASENAME], [for cc_temp in $1""; do case $cc_temp in compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` ]) # _LT_FILEUTILS_DEFAULTS # ---------------------- # It is okay to use these file commands and assume they have been set # sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'. m4_defun([_LT_FILEUTILS_DEFAULTS], [: ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} ])# _LT_FILEUTILS_DEFAULTS # _LT_SETUP # --------- m4_defun([_LT_SETUP], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl _LT_DECL([], [host_alias], [0], [The host system])dnl _LT_DECL([], [host], [0])dnl _LT_DECL([], [host_os], [0])dnl dnl _LT_DECL([], [build_alias], [0], [The build system])dnl _LT_DECL([], [build], [0])dnl _LT_DECL([], [build_os], [0])dnl dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl dnl AC_REQUIRE([AC_PROG_LN_S])dnl test -z "$LN_S" && LN_S="ln -s" _LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl dnl AC_REQUIRE([LT_CMD_MAX_LEN])dnl _LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl _LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_CMD_RELOAD])dnl m4_require([_LT_CHECK_MAGIC_METHOD])dnl m4_require([_LT_CMD_OLD_ARCHIVE])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl _LT_CONFIG_LIBTOOL_INIT([ # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi ]) if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi _LT_CHECK_OBJDIR m4_require([_LT_TAG_COMPILER])dnl _LT_PROG_ECHO_BACKSLASH case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\([["`\\]]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a `.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld="$lt_cv_prog_gnu_ld" old_CC="$CC" old_CFLAGS="$CFLAGS" # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o _LT_CC_BASENAME([$compiler]) # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then _LT_PATH_MAGIC fi ;; esac # Use C for the default configuration in the libtool script LT_SUPPORTED_TAG([CC]) _LT_LANG_C_CONFIG _LT_LANG_DEFAULT_CONFIG _LT_CONFIG_COMMANDS ])# _LT_SETUP # _LT_PROG_LTMAIN # --------------- # Note that this code is called both from `configure', and `config.status' # now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, # `config.status' has no value for ac_aux_dir unless we are using Automake, # so we pass a copy along to make sure it has a sensible value anyway. m4_defun([_LT_PROG_LTMAIN], [m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl _LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) ltmain="$ac_aux_dir/ltmain.sh" ])# _LT_PROG_LTMAIN ## ------------------------------------- ## ## Accumulate code for creating libtool. ## ## ------------------------------------- ## # So that we can recreate a full libtool script including additional # tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS # in macros and then make a single call at the end using the `libtool' # label. # _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) # ---------------------------------------- # Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL_INIT], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_INIT], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_INIT]) # _LT_CONFIG_LIBTOOL([COMMANDS]) # ------------------------------ # Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) # _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) # ----------------------------------------------------- m4_defun([_LT_CONFIG_SAVE_COMMANDS], [_LT_CONFIG_LIBTOOL([$1]) _LT_CONFIG_LIBTOOL_INIT([$2]) ]) # _LT_FORMAT_COMMENT([COMMENT]) # ----------------------------- # Add leading comment marks to the start of each line, and a trailing # full-stop to the whole comment if one is not present already. m4_define([_LT_FORMAT_COMMENT], [m4_ifval([$1], [ m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) )]) ## ------------------------ ## ## FIXME: Eliminate VARNAME ## ## ------------------------ ## # _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) # ------------------------------------------------------------------- # CONFIGNAME is the name given to the value in the libtool script. # VARNAME is the (base) name used in the configure script. # VALUE may be 0, 1 or 2 for a computed quote escaped value based on # VARNAME. Any other value will be used directly. m4_define([_LT_DECL], [lt_if_append_uniq([lt_decl_varnames], [$2], [, ], [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], [m4_ifval([$1], [$1], [$2])]) lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) m4_ifval([$4], [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) lt_dict_add_subkey([lt_decl_dict], [$2], [tagged?], [m4_ifval([$5], [yes], [no])])]) ]) # _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) # -------------------------------------------------------- m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) # lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_tag_varnames], [_lt_decl_filter([tagged?], [yes], $@)]) # _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) # --------------------------------------------------------- m4_define([_lt_decl_filter], [m4_case([$#], [0], [m4_fatal([$0: too few arguments: $#])], [1], [m4_fatal([$0: too few arguments: $#: $1])], [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], [lt_dict_filter([lt_decl_dict], $@)])[]dnl ]) # lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) # -------------------------------------------------- m4_define([lt_decl_quote_varnames], [_lt_decl_filter([value], [1], $@)]) # lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_dquote_varnames], [_lt_decl_filter([value], [2], $@)]) # lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_varnames_tagged], [m4_assert([$# <= 2])dnl _$0(m4_quote(m4_default([$1], [[, ]])), m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) m4_define([_lt_decl_varnames_tagged], [m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) # lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_all_varnames], [_$0(m4_quote(m4_default([$1], [[, ]])), m4_if([$2], [], m4_quote(lt_decl_varnames), m4_quote(m4_shift($@))))[]dnl ]) m4_define([_lt_decl_all_varnames], [lt_join($@, lt_decl_varnames_tagged([$1], lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl ]) # _LT_CONFIG_STATUS_DECLARE([VARNAME]) # ------------------------------------ # Quote a variable value, and forward it to `config.status' so that its # declaration there will have the same value as in `configure'. VARNAME # must have a single quote delimited value for this to work. m4_define([_LT_CONFIG_STATUS_DECLARE], [$1='`$ECHO "X$][$1" | $Xsed -e "$delay_single_quote_subst"`']) # _LT_CONFIG_STATUS_DECLARATIONS # ------------------------------ # We delimit libtool config variables with single quotes, so when # we write them to config.status, we have to be sure to quote all # embedded single quotes properly. In configure, this macro expands # each variable declared with _LT_DECL (and _LT_TAGDECL) into: # # ='`$ECHO "X$" | $Xsed -e "$delay_single_quote_subst"`' m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], [m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAGS # ---------------- # Output comment and list of tags supported by the script m4_defun([_LT_LIBTOOL_TAGS], [_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl available_tags="_LT_TAGS"dnl ]) # _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) # ----------------------------------- # Extract the dictionary values for VARNAME (optionally with TAG) and # expand to a commented shell variable setting: # # # Some comment about what VAR is for. # visible_name=$lt_internal_name m4_define([_LT_LIBTOOL_DECLARE], [_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [description])))[]dnl m4_pushdef([_libtool_name], m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), [0], [_libtool_name=[$]$1], [1], [_libtool_name=$lt_[]$1], [2], [_libtool_name=$lt_[]$1], [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl ]) # _LT_LIBTOOL_CONFIG_VARS # ----------------------- # Produce commented declarations of non-tagged libtool config variables # suitable for insertion in the LIBTOOL CONFIG section of the `libtool' # script. Tagged libtool config variables (even for the LIBTOOL CONFIG # section) are produced by _LT_LIBTOOL_TAG_VARS. m4_defun([_LT_LIBTOOL_CONFIG_VARS], [m4_foreach([_lt_var], m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAG_VARS(TAG) # ------------------------- m4_define([_LT_LIBTOOL_TAG_VARS], [m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) # _LT_TAGVAR(VARNAME, [TAGNAME]) # ------------------------------ m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) # _LT_CONFIG_COMMANDS # ------------------- # Send accumulated output to $CONFIG_STATUS. Thanks to the lists of # variables for single and double quote escaping we saved from calls # to _LT_DECL, we can put quote escaped variables declarations # into `config.status', and then the shell code to quote escape them in # for loops in `config.status'. Finally, any additional code accumulated # from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. m4_defun([_LT_CONFIG_COMMANDS], [AC_PROVIDE_IFELSE([LT_OUTPUT], dnl If the libtool generation code has been placed in $CONFIG_LT, dnl instead of duplicating it all over again into config.status, dnl then we will have config.status run $CONFIG_LT later, so it dnl needs to know what name is stored there: [AC_CONFIG_COMMANDS([libtool], [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], dnl If the libtool generation code is destined for config.status, dnl expand the accumulated commands and init code now: [AC_CONFIG_COMMANDS([libtool], [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) ])#_LT_CONFIG_COMMANDS # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], [ # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' _LT_CONFIG_STATUS_DECLARATIONS LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # Quote evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_quote_varnames); do case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_dquote_varnames); do case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Fix-up fallback echo if it was mangled by the above quoting rules. case \$lt_ECHO in *'\\\[$]0 --fallback-echo"')dnl " lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\[$]0 --fallback-echo"\[$]/\[$]0 --fallback-echo"/'\` ;; esac _LT_OUTPUT_LIBTOOL_INIT ]) # LT_OUTPUT # --------- # This macro allows early generation of the libtool script (before # AC_OUTPUT is called), incase it is used in configure for compilation # tests. AC_DEFUN([LT_OUTPUT], [: ${CONFIG_LT=./config.lt} AC_MSG_NOTICE([creating $CONFIG_LT]) cat >"$CONFIG_LT" <<_LTEOF #! $SHELL # Generated by $as_me. # Run this file to recreate a libtool stub with the current configuration. lt_cl_silent=false SHELL=\${CONFIG_SHELL-$SHELL} _LTEOF cat >>"$CONFIG_LT" <<\_LTEOF AS_SHELL_SANITIZE _AS_PREPARE exec AS_MESSAGE_FD>&1 exec AS_MESSAGE_LOG_FD>>config.log { echo AS_BOX([Running $as_me.]) } >&AS_MESSAGE_LOG_FD lt_cl_help="\ \`$as_me' creates a local libtool stub from the current configuration, for use in further configure time tests before the real libtool is generated. Usage: $[0] [[OPTIONS]] -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files Report bugs to ." lt_cl_version="\ m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) configured by $[0], generated by m4_PACKAGE_STRING. Copyright (C) 2008 Free Software Foundation, Inc. This config.lt script is free software; the Free Software Foundation gives unlimited permision to copy, distribute and modify it." while test $[#] != 0 do case $[1] in --version | --v* | -V ) echo "$lt_cl_version"; exit 0 ;; --help | --h* | -h ) echo "$lt_cl_help"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --quiet | --q* | --silent | --s* | -q ) lt_cl_silent=: ;; -*) AC_MSG_ERROR([unrecognized option: $[1] Try \`$[0] --help' for more information.]) ;; *) AC_MSG_ERROR([unrecognized argument: $[1] Try \`$[0] --help' for more information.]) ;; esac shift done if $lt_cl_silent; then exec AS_MESSAGE_FD>/dev/null fi _LTEOF cat >>"$CONFIG_LT" <<_LTEOF _LT_OUTPUT_LIBTOOL_COMMANDS_INIT _LTEOF cat >>"$CONFIG_LT" <<\_LTEOF AC_MSG_NOTICE([creating $ofile]) _LT_OUTPUT_LIBTOOL_COMMANDS AS_EXIT(0) _LTEOF chmod +x "$CONFIG_LT" # configure is writing to config.log, but config.lt does its own redirection, # appending to config.log, which fails on DOS, as config.log is still kept # open by configure. Here we exec the FD to /dev/null, effectively closing # config.log, so it can be properly (re)opened and appended to by config.lt. if test "$no_create" != yes; then lt_cl_success=: test "$silent" = yes && lt_config_lt_args="$lt_config_lt_args --quiet" exec AS_MESSAGE_LOG_FD>/dev/null $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false exec AS_MESSAGE_LOG_FD>>config.log $lt_cl_success || AS_EXIT(1) fi ])# LT_OUTPUT # _LT_CONFIG(TAG) # --------------- # If TAG is the built-in tag, create an initial libtool script with a # default configuration from the untagged config vars. Otherwise add code # to config.status for appending the configuration named by TAG from the # matching tagged config vars. m4_defun([_LT_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_CONFIG_SAVE_COMMANDS([ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl m4_if(_LT_TAG, [C], [ # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi cfgfile="${ofile}T" trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. # Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION # Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # _LT_COPYING _LT_LIBTOOL_TAGS # ### BEGIN LIBTOOL CONFIG _LT_LIBTOOL_CONFIG_VARS _LT_LIBTOOL_TAG_VARS # ### END LIBTOOL CONFIG _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac _LT_PROG_LTMAIN # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) _LT_PROG_XSI_SHELLFNS sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" ], [cat <<_LT_EOF >> "$ofile" dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded dnl in a comment (ie after a #). # ### BEGIN LIBTOOL TAG CONFIG: $1 _LT_LIBTOOL_TAG_VARS(_LT_TAG) # ### END LIBTOOL TAG CONFIG: $1 _LT_EOF ])dnl /m4_if ], [m4_if([$1], [], [ PACKAGE='$PACKAGE' VERSION='$VERSION' TIMESTAMP='$TIMESTAMP' RM='$RM' ofile='$ofile'], []) ])dnl /_LT_CONFIG_SAVE_COMMANDS ])# _LT_CONFIG # LT_SUPPORTED_TAG(TAG) # --------------------- # Trace this macro to discover what tags are supported by the libtool # --tag option, using: # autoconf --trace 'LT_SUPPORTED_TAG:$1' AC_DEFUN([LT_SUPPORTED_TAG], []) # C support is built-in for now m4_define([_LT_LANG_C_enabled], []) m4_define([_LT_TAGS], []) # LT_LANG(LANG) # ------------- # Enable libtool support for the given language if not already enabled. AC_DEFUN([LT_LANG], [AC_BEFORE([$0], [LT_OUTPUT])dnl m4_case([$1], [C], [_LT_LANG(C)], [C++], [_LT_LANG(CXX)], [Java], [_LT_LANG(GCJ)], [Fortran 77], [_LT_LANG(F77)], [Fortran], [_LT_LANG(FC)], [Windows Resource], [_LT_LANG(RC)], [m4_ifdef([_LT_LANG_]$1[_CONFIG], [_LT_LANG($1)], [m4_fatal([$0: unsupported language: "$1"])])])dnl ])# LT_LANG # _LT_LANG(LANGNAME) # ------------------ m4_defun([_LT_LANG], [m4_ifdef([_LT_LANG_]$1[_enabled], [], [LT_SUPPORTED_TAG([$1])dnl m4_append([_LT_TAGS], [$1 ])dnl m4_define([_LT_LANG_]$1[_enabled], [])dnl _LT_LANG_$1_CONFIG($1)])dnl ])# _LT_LANG # _LT_LANG_DEFAULT_CONFIG # ----------------------- m4_defun([_LT_LANG_DEFAULT_CONFIG], [AC_PROVIDE_IFELSE([AC_PROG_CXX], [LT_LANG(CXX)], [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) AC_PROVIDE_IFELSE([AC_PROG_F77], [LT_LANG(F77)], [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) AC_PROVIDE_IFELSE([AC_PROG_FC], [LT_LANG(FC)], [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal dnl pulling things in needlessly. AC_PROVIDE_IFELSE([AC_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([LT_PROG_GCJ], [LT_LANG(GCJ)], [m4_ifdef([AC_PROG_GCJ], [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([A][M_PROG_GCJ], [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([LT_PROG_GCJ], [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) AC_PROVIDE_IFELSE([LT_PROG_RC], [LT_LANG(RC)], [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) ])# _LT_LANG_DEFAULT_CONFIG # Obsolete macros: AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_CXX], []) dnl AC_DEFUN([AC_LIBTOOL_F77], []) dnl AC_DEFUN([AC_LIBTOOL_FC], []) dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) # _LT_TAG_COMPILER # ---------------- m4_defun([_LT_TAG_COMPILER], [AC_REQUIRE([AC_PROG_CC])dnl _LT_DECL([LTCC], [CC], [1], [A C compiler])dnl _LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl _LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl _LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC ])# _LT_TAG_COMPILER # _LT_COMPILER_BOILERPLATE # ------------------------ # Check for compiler boilerplate output or warnings with # the simple compiler test code. m4_defun([_LT_COMPILER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ])# _LT_COMPILER_BOILERPLATE # _LT_LINKER_BOILERPLATE # ---------------------- # Check for linker boilerplate output or warnings with # the simple link test code. m4_defun([_LT_LINKER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ])# _LT_LINKER_BOILERPLATE # _LT_REQUIRED_DARWIN_CHECKS # ------------------------- m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ case $host_os in rhapsody* | darwin*) AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) AC_CHECK_TOOL([LIPO], [lipo], [:]) AC_CHECK_TOOL([OTOOL], [otool], [:]) AC_CHECK_TOOL([OTOOL64], [otool64], [:]) _LT_DECL([], [DSYMUTIL], [1], [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) _LT_DECL([], [NMEDIT], [1], [Tool to change global to local symbols on Mac OS X]) _LT_DECL([], [LIPO], [1], [Tool to manipulate fat objects and archives on Mac OS X]) _LT_DECL([], [OTOOL], [1], [ldd/readelf like tool for Mach-O binaries on Mac OS X]) _LT_DECL([], [OTOOL64], [1], [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], [lt_cv_apple_cc_single_mod=no if test -z "${LT_MULTI_MODULE}"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -rf libconftest.dylib* rm -f conftest.* fi]) AC_CACHE_CHECK([for -exported_symbols_list linker flag], [lt_cv_ld_exported_symbols_list], [lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [lt_cv_ld_exported_symbols_list=yes], [lt_cv_ld_exported_symbols_list=no]) LDFLAGS="$save_LDFLAGS" ]) case $host_os in rhapsody* | darwin1.[[012]]) _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; 10.[[012]]*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test "$lt_cv_apple_cc_single_mod" = "yes"; then _lt_dar_single_mod='$single_module' fi if test "$lt_cv_ld_exported_symbols_list" = "yes"; then _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' fi if test "$DSYMUTIL" != ":"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac ]) # _LT_DARWIN_LINKER_FEATURES # -------------------------- # Checks for linker and compiler features on darwin m4_defun([_LT_DARWIN_LINKER_FEATURES], [ m4_require([_LT_REQUIRED_DARWIN_CHECKS]) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(whole_archive_flag_spec, $1)='' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined" case $cc_basename in ifort*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test "$_lt_dar_can_shared" = "yes"; then output_verbose_link_cmd=echo _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" m4_if([$1], [CXX], [ if test "$lt_cv_apple_cc_single_mod" != "yes"; then _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" fi ],[]) else _LT_TAGVAR(ld_shlibs, $1)=no fi ]) # _LT_SYS_MODULE_PATH_AIX # ----------------------- # Links a minimal program and checks the executable # for the system default hardcoded library path. In most cases, # this is /usr/lib:/lib, but when the MPI compilers are used # the location of the communication and MPI libs are included too. # If we don't find anything, use the default library path according # to the aix ld manual. m4_defun([_LT_SYS_MODULE_PATH_AIX], [m4_require([_LT_DECL_SED])dnl AC_LINK_IFELSE(AC_LANG_PROGRAM,[ lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/ p } }' aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi],[]) if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi ])# _LT_SYS_MODULE_PATH_AIX # _LT_SHELL_INIT(ARG) # ------------------- m4_define([_LT_SHELL_INIT], [ifdef([AC_DIVERSION_NOTICE], [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)], [AC_DIVERT_PUSH(NOTICE)]) $1 AC_DIVERT_POP ])# _LT_SHELL_INIT # _LT_PROG_ECHO_BACKSLASH # ----------------------- # Add some code to the start of the generated configure script which # will find an echo command which doesn't interpret backslashes. m4_defun([_LT_PROG_ECHO_BACKSLASH], [_LT_SHELL_INIT([ # Check that we are running under the correct shell. SHELL=${CONFIG_SHELL-/bin/sh} case X$lt_ECHO in X*--fallback-echo) # Remove one level of quotation (which was required for Make). ECHO=`echo "$lt_ECHO" | sed 's,\\\\\[$]\\[$]0,'[$]0','` ;; esac ECHO=${lt_ECHO-echo} if test "X[$]1" = X--no-reexec; then # Discard the --no-reexec flag, and continue. shift elif test "X[$]1" = X--fallback-echo; then # Avoid inline document here, it may be left over : elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then # Yippee, $ECHO works! : else # Restart under the correct shell. exec $SHELL "[$]0" --no-reexec ${1+"[$]@"} fi if test "X[$]1" = X--fallback-echo; then # used as fallback echo shift cat <<_LT_EOF [$]* _LT_EOF exit 0 fi # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH if test -z "$lt_ECHO"; then if test "X${echo_test_string+set}" != Xset; then # find a string as large as possible, as long as the shell can cope with it for cmd in 'sed 50q "[$]0"' 'sed 20q "[$]0"' 'sed 10q "[$]0"' 'sed 2q "[$]0"' 'echo test'; do # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ... if { echo_test_string=`eval $cmd`; } 2>/dev/null && { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null then break fi done fi if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' && echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then : else # The Solaris, AIX, and Digital Unix default echo programs unquote # backslashes. This makes it impossible to quote backslashes using # echo "$something" | sed 's/\\/\\\\/g' # # So, first we look for a working echo in the user's PATH. lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for dir in $PATH /usr/ucb; do IFS="$lt_save_ifs" if (test -f $dir/echo || test -f $dir/echo$ac_exeext) && test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' && echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then ECHO="$dir/echo" break fi done IFS="$lt_save_ifs" if test "X$ECHO" = Xecho; then # We didn't find a better echo, so look for alternatives. if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' && echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then # This shell has a builtin print -r that does the trick. ECHO='print -r' elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } && test "X$CONFIG_SHELL" != X/bin/ksh; then # If we have ksh, try running configure again with it. ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} export ORIGINAL_CONFIG_SHELL CONFIG_SHELL=/bin/ksh export CONFIG_SHELL exec $CONFIG_SHELL "[$]0" --no-reexec ${1+"[$]@"} else # Try using printf. ECHO='printf %s\n' if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' && echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then # Cool, printf works : elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && test "X$echo_testing_string" = 'X\t' && echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL export CONFIG_SHELL SHELL="$CONFIG_SHELL" export SHELL ECHO="$CONFIG_SHELL [$]0 --fallback-echo" elif echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && test "X$echo_testing_string" = 'X\t' && echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then ECHO="$CONFIG_SHELL [$]0 --fallback-echo" else # maybe with a smaller string... prev=: for cmd in 'echo test' 'sed 2q "[$]0"' 'sed 10q "[$]0"' 'sed 20q "[$]0"' 'sed 50q "[$]0"'; do if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null then break fi prev="$cmd" done if test "$prev" != 'sed 50q "[$]0"'; then echo_test_string=`eval $prev` export echo_test_string exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "[$]0" ${1+"[$]@"} else # Oops. We lost completely, so just stick with echo. ECHO=echo fi fi fi fi fi fi # Copy echo and quote the copy suitably for passing to libtool from # the Makefile, instead of quoting the original, which is used later. lt_ECHO=$ECHO if test "X$lt_ECHO" = "X$CONFIG_SHELL [$]0 --fallback-echo"; then lt_ECHO="$CONFIG_SHELL \\\$\[$]0 --fallback-echo" fi AC_SUBST(lt_ECHO) ]) _LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) _LT_DECL([], [ECHO], [1], [An echo program that does not interpret backslashes]) ])# _LT_PROG_ECHO_BACKSLASH # _LT_ENABLE_LOCK # --------------- m4_defun([_LT_ENABLE_LOCK], [AC_ARG_ENABLE([libtool-lock], [AS_HELP_STRING([--disable-libtool-lock], [avoid locking (might break parallel builds)])]) test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE="32" ;; *ELF-64*) HPUX_IA64_MODE="64" ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out which ABI we are using. echo '[#]line __oline__ "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then if test "$lt_cv_prog_gnu_ld" = yes; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_i386" ;; ppc64-*linux*|powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; ppc*-*linux*|powerpc*-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -belf" AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, [AC_LANG_PUSH(C) AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) AC_LANG_POP]) if test x"$lt_cv_cc_needs_belf" != x"yes"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS="$SAVE_CFLAGS" fi ;; sparc*-*solaris*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) LD="${LD-ld} -m elf64_sparc" ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks="$enable_libtool_lock" ])# _LT_ENABLE_LOCK # _LT_CMD_OLD_ARCHIVE # ------------------- m4_defun([_LT_CMD_OLD_ARCHIVE], [AC_CHECK_TOOL(AR, ar, false) test -z "$AR" && AR=ar test -z "$AR_FLAGS" && AR_FLAGS=cru _LT_DECL([], [AR], [1], [The archiver]) _LT_DECL([], [AR_FLAGS], [1]) AC_CHECK_TOOL(STRIP, strip, :) test -z "$STRIP" && STRIP=: _LT_DECL([], [STRIP], [1], [A symbol stripping program]) AC_CHECK_TOOL(RANLIB, ranlib, :) test -z "$RANLIB" && RANLIB=: _LT_DECL([], [RANLIB], [1], [Commands used to install an old-style archive]) # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" fi _LT_DECL([], [old_postinstall_cmds], [2]) _LT_DECL([], [old_postuninstall_cmds], [2]) _LT_TAGDECL([], [old_archive_cmds], [2], [Commands used to build an old-style archive]) ])# _LT_CMD_OLD_ARCHIVE # _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------------------- # Check whether the given compiler option works AC_DEFUN([_LT_COMPILER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$3" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi fi $RM conftest* ]) if test x"[$]$2" = xyes; then m4_if([$5], , :, [$5]) else m4_if([$6], , :, [$6]) fi ])# _LT_COMPILER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) # _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------- # Check whether the given linker option works AC_DEFUN([_LT_LINKER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $3" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&AS_MESSAGE_LOG_FD $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi else $2=yes fi fi $RM -r conftest* LDFLAGS="$save_LDFLAGS" ]) if test x"[$]$2" = xyes; then m4_if([$4], , :, [$4]) else m4_if([$5], , :, [$5]) fi ])# _LT_LINKER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) # LT_CMD_MAX_LEN #--------------- AC_DEFUN([LT_CMD_MAX_LEN], [AC_REQUIRE([AC_CANONICAL_HOST])dnl # find the maximum length of command line arguments AC_MSG_CHECKING([the maximum length of command line arguments]) AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl i=0 teststring="ABCD" case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8 ; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test "X"`$SHELL [$]0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \ = "XX$teststring$teststring"; } >/dev/null 2>&1 && test $i != 17 # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac ]) if test -n $lt_cv_sys_max_cmd_len ; then AC_MSG_RESULT($lt_cv_sys_max_cmd_len) else AC_MSG_RESULT(none) fi max_cmd_len=$lt_cv_sys_max_cmd_len _LT_DECL([], [max_cmd_len], [0], [What is the maximum length of a command?]) ])# LT_CMD_MAX_LEN # Old name: AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) # _LT_HEADER_DLFCN # ---------------- m4_defun([_LT_HEADER_DLFCN], [AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl ])# _LT_HEADER_DLFCN # _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, # ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) # ---------------------------------------------------------------- m4_defun([_LT_TRY_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test "$cross_compiling" = yes; then : [$4] else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF [#line __oline__ "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif void fnord() { int i=42;} int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; /* dlclose (self); */ } else puts (dlerror ()); return status; }] _LT_EOF if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) $1 ;; x$lt_dlneed_uscore) $2 ;; x$lt_dlunknown|x*) $3 ;; esac else : # compilation failed $3 fi fi rm -fr conftest* ])# _LT_TRY_DLOPEN_SELF # LT_SYS_DLOPEN_SELF # ------------------ AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test "x$enable_dlopen" != xyes; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen="load_add_on" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen="LoadLibrary" lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen="dlopen" lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ lt_cv_dlopen="dyld" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ]) ;; *) AC_CHECK_FUNC([shl_load], [lt_cv_dlopen="shl_load"], [AC_CHECK_LIB([dld], [shl_load], [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"], [AC_CHECK_FUNC([dlopen], [lt_cv_dlopen="dlopen"], [AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], [AC_CHECK_LIB([svld], [dlopen], [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], [AC_CHECK_LIB([dld], [dld_link], [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"]) ]) ]) ]) ]) ]) ;; esac if test "x$lt_cv_dlopen" != xno; then enable_dlopen=yes else enable_dlopen=no fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS="$CPPFLAGS" test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS="$LDFLAGS" wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS="$LIBS" LIBS="$lt_cv_dlopen_libs $LIBS" AC_CACHE_CHECK([whether a program can dlopen itself], lt_cv_dlopen_self, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) ]) if test "x$lt_cv_dlopen_self" = xyes; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" AC_CACHE_CHECK([whether a statically linked program can dlopen itself], lt_cv_dlopen_self_static, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) ]) fi CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi _LT_DECL([dlopen_support], [enable_dlopen], [0], [Whether dlopen is supported]) _LT_DECL([dlopen_self], [enable_dlopen_self], [0], [Whether dlopen of programs is supported]) _LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], [Whether dlopen of statically linked programs is supported]) ])# LT_SYS_DLOPEN_SELF # Old name: AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) # _LT_COMPILER_C_O([TAGNAME]) # --------------------------- # Check to see if options -c and -o are simultaneously supported by compiler. # This macro does not hard code the compiler like AC_PROG_CC_C_O. m4_defun([_LT_COMPILER_C_O], [m4_require([_LT_DECL_SED])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes fi fi chmod u+w . 2>&AS_MESSAGE_LOG_FD $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* ]) _LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], [Does compiler simultaneously support -c and -o options?]) ])# _LT_COMPILER_C_O # _LT_COMPILER_FILE_LOCKS([TAGNAME]) # ---------------------------------- # Check to see if we can do hard links to lock some files if needed m4_defun([_LT_COMPILER_FILE_LOCKS], [m4_require([_LT_ENABLE_LOCK])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_COMPILER_C_O([$1]) hard_links="nottested" if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then # do not overwrite the value of need_locks provided by the user AC_MSG_CHECKING([if we can lock with hard links]) hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no AC_MSG_RESULT([$hard_links]) if test "$hard_links" = no; then AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) need_locks=warn fi else need_locks=no fi _LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) ])# _LT_COMPILER_FILE_LOCKS # _LT_CHECK_OBJDIR # ---------------- m4_defun([_LT_CHECK_OBJDIR], [AC_CACHE_CHECK([for objdir], [lt_cv_objdir], [rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null]) objdir=$lt_cv_objdir _LT_DECL([], [objdir], [0], [The name of the directory that contains temporary libtool files])dnl m4_pattern_allow([LT_OBJDIR])dnl AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/", [Define to the sub-directory in which libtool stores uninstalled libraries.]) ])# _LT_CHECK_OBJDIR # _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) # -------------------------------------- # Check hardcoding attributes. m4_defun([_LT_LINKER_HARDCODE_LIBPATH], [AC_MSG_CHECKING([how to hardcode library paths into programs]) _LT_TAGVAR(hardcode_action, $1)= if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || test -n "$_LT_TAGVAR(runpath_var, $1)" || test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then # We can hardcode non-existent directories. if test "$_LT_TAGVAR(hardcode_direct, $1)" != no && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no && test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then # Linking always hardcodes the temporary library directory. _LT_TAGVAR(hardcode_action, $1)=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. _LT_TAGVAR(hardcode_action, $1)=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. _LT_TAGVAR(hardcode_action, $1)=unsupported fi AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) if test "$_LT_TAGVAR(hardcode_action, $1)" = relink || test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then # Fast installation is not supported enable_fast_install=no elif test "$shlibpath_overrides_runpath" = yes || test "$enable_shared" = no; then # Fast installation is not necessary enable_fast_install=needless fi _LT_TAGDECL([], [hardcode_action], [0], [How to hardcode a shared library path into an executable]) ])# _LT_LINKER_HARDCODE_LIBPATH # _LT_CMD_STRIPLIB # ---------------- m4_defun([_LT_CMD_STRIPLIB], [m4_require([_LT_DECL_EGREP]) striplib= old_striplib= AC_MSG_CHECKING([whether stripping libraries is possible]) if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" AC_MSG_RESULT([yes]) else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP" ; then striplib="$STRIP -x" old_striplib="$STRIP -S" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ;; *) AC_MSG_RESULT([no]) ;; esac fi _LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) _LT_DECL([], [striplib], [1]) ])# _LT_CMD_STRIPLIB # _LT_SYS_DYNAMIC_LINKER([TAG]) # ----------------------------- # PORTME Fill in your ld.so characteristics m4_defun([_LT_SYS_DYNAMIC_LINKER], [AC_REQUIRE([AC_CANONICAL_HOST])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_OBJDUMP])dnl m4_require([_LT_DECL_SED])dnl AC_MSG_CHECKING([dynamic linker characteristics]) m4_if([$1], [], [ if test "$GCC" = yes; then case $host_os in darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; *) lt_awk_arg="/^libraries:/" ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"` if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'` else lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary. lt_tmp_lt_search_path_spec= lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path/$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" else test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk ' BEGIN {RS=" "; FS="/|\n";} { lt_foo=""; lt_count=0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo="/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[[lt_foo]]++; } if (lt_freq[[lt_foo]] == 1) { print lt_foo; } }'` sys_lib_search_path_spec=`$ECHO $lt_search_path_spec` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi]) library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=".so" postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='${libname}${release}${shared_ext}$major' ;; aix[[4-9]]*) version_type=linux need_lib_prefix=no need_version=no hardcode_into_libs=yes if test "$host_cpu" = ia64; then # AIX 5 supports IA64 library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line `#! .'. This would cause the generated library to # depend on `.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[[01]] | aix4.[[01]].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # AIX (on Power*) has no versioning support, so currently we can not hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. if test "$aix_use_runtimelinking" = yes; then # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' else # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='${libname}${release}.a $libname.a' soname_spec='${libname}${release}${shared_ext}$major' fi shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='${libname}${shared_ext}' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[[45]]*) version_type=linux need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=".dll" need_version=no need_lib_prefix=no case $GCC,$host_os in yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*) library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then # It is most probably a Windows format PATH printed by # mingw gcc, but we are running on Cygwin. Gcc prints its search # path with ; separators, and with drive letters. We can handle the # drive letters (cygwin fileutils understands them), so leave them, # especially as we might pass files found there to a mingw objdump, # which wouldn't understand a cygwinified path. Ahh. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' ;; esac ;; *) library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' ;; esac dynamic_linker='Win32 ld.exe' # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' soname_spec='${libname}${release}${major}$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd1*) dynamic_linker=no ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[[123]]*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2*) shlibpath_overrides_runpath=yes ;; freebsd3.[[01]]* | freebsdelf3.[[01]]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; gnu*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' if test "X$HPUX_IA64_MODE" = X32; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" fi sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555. postinstall_cmds='chmod 555 $lib' ;; interix[[3-9]]*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test "$lt_cv_prog_gnu_ld" = yes; then version_type=linux else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; # This must be Linux ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], [shlibpath_overrides_runpath=yes])]) LDFLAGS=$save_LDFLAGS libdir=$save_libdir # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Append ld.so.conf contents to the search path if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd*) version_type=sunos sys_lib_dlsearch_path_spec="/usr/lib" need_lib_prefix=no # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. case $host_os in openbsd3.3 | openbsd3.3.*) need_version=yes ;; *) need_version=no ;; esac library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then case $host_os in openbsd2.[[89]] | openbsd2.[[89]].*) shlibpath_overrides_runpath=no ;; *) shlibpath_overrides_runpath=yes ;; esac else shlibpath_overrides_runpath=yes fi ;; os2*) libname_spec='$name' shrext_cmds=".dll" need_lib_prefix=no library_names_spec='$libname${shared_ext} $libname.a' dynamic_linker='OS/2 ld.exe' shlibpath_var=LIBPATH ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test "$with_gnu_ld" = yes; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec ;then version_type=linux library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' soname_spec='$libname${shared_ext}.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=freebsd-elf need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test "$with_gnu_ld" = yes; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac AC_MSG_RESULT([$dynamic_linker]) test "$dynamic_linker" = no && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test "$GCC" = yes; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" fi if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" fi _LT_DECL([], [variables_saved_for_relink], [1], [Variables whose values should be saved in libtool wrapper scripts and restored at link time]) _LT_DECL([], [need_lib_prefix], [0], [Do we need the "lib" prefix for modules?]) _LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) _LT_DECL([], [version_type], [0], [Library versioning type]) _LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) _LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) _LT_DECL([], [shlibpath_overrides_runpath], [0], [Is shlibpath searched before the hard-coded library search path?]) _LT_DECL([], [libname_spec], [1], [Format of library name prefix]) _LT_DECL([], [library_names_spec], [1], [[List of archive names. First name is the real one, the rest are links. The last name is the one that the linker finds with -lNAME]]) _LT_DECL([], [soname_spec], [1], [[The coded name of the library, if different from the real name]]) _LT_DECL([], [postinstall_cmds], [2], [Command to use after installation of a shared archive]) _LT_DECL([], [postuninstall_cmds], [2], [Command to use after uninstallation of a shared archive]) _LT_DECL([], [finish_cmds], [2], [Commands used to finish a libtool library installation in a directory]) _LT_DECL([], [finish_eval], [1], [[As "finish_cmds", except a single script fragment to be evaled but not shown]]) _LT_DECL([], [hardcode_into_libs], [0], [Whether we should hardcode library paths into libraries]) _LT_DECL([], [sys_lib_search_path_spec], [2], [Compile-time system search path for libraries]) _LT_DECL([], [sys_lib_dlsearch_path_spec], [2], [Run-time system search path for libraries]) ])# _LT_SYS_DYNAMIC_LINKER # _LT_PATH_TOOL_PREFIX(TOOL) # -------------------------- # find a file program which can recognize shared library AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl AC_MSG_CHECKING([for $1]) AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, [case $MAGIC_CMD in [[\\/*] | ?:[\\/]*]) lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD="$MAGIC_CMD" lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR dnl $ac_dummy forces splitting on constant user-supplied paths. dnl POSIX.2 word splitting is done only on the output of word expansions, dnl not every word. This closes a longstanding sh security hole. ac_dummy="m4_if([$2], , $PATH, [$2])" for ac_dir in $ac_dummy; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$1; then lt_cv_path_MAGIC_CMD="$ac_dir/$1" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS="$lt_save_ifs" MAGIC_CMD="$lt_save_MAGIC_CMD" ;; esac]) MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if test -n "$MAGIC_CMD"; then AC_MSG_RESULT($MAGIC_CMD) else AC_MSG_RESULT(no) fi _LT_DECL([], [MAGIC_CMD], [0], [Used to examine libraries when file_magic_cmd begins with "file"])dnl ])# _LT_PATH_TOOL_PREFIX # Old name: AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) # _LT_PATH_MAGIC # -------------- # find a file program which can recognize a shared library m4_defun([_LT_PATH_MAGIC], [_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) else MAGIC_CMD=: fi fi ])# _LT_PATH_MAGIC # LT_PATH_LD # ---------- # find the pathname to the GNU or non-GNU linker AC_DEFUN([LT_PATH_LD], [AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl AC_ARG_WITH([gnu-ld], [AS_HELP_STRING([--with-gnu-ld], [assume the C compiler uses GNU ld @<:@default=no@:>@])], [test "$withval" = no || with_gnu_ld=yes], [with_gnu_ld=no])dnl ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by $CC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]]* | ?:[[\\/]]*) re_direlt='/[[^/]][[^/]]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL(lt_cv_path_LD, [if test -z "$LD"; then lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; gnu*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]'] lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]].[[0-9]]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[[3-9]]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be Linux ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; esac ]) file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown _LT_DECL([], [deplibs_check_method], [1], [Method to check whether dependent libraries are shared objects]) _LT_DECL([], [file_magic_cmd], [1], [Command to use when deplibs_check_method == "file_magic"]) ])# _LT_CHECK_MAGIC_METHOD # LT_PATH_NM # ---------- # find the pathname to a BSD- or MS-compatible name lister AC_DEFUN([LT_PATH_NM], [AC_REQUIRE([AC_PROG_CC])dnl AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, [if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM="$NM" else lt_nm_to_check="${ac_tool_prefix}nm" if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. tmp_nm="$ac_dir/$lt_tmp_nm" if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then # Check to see if the nm accepts a BSD-compat flag. # Adding the `sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in */dev/null* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS="$lt_save_ifs" done : ${lt_cv_path_NM=no} fi]) if test "$lt_cv_path_NM" != "no"; then NM="$lt_cv_path_NM" else # Didn't find any BSD compatible name lister, look for dumpbin. AC_CHECK_TOOLS(DUMPBIN, ["dumpbin -symbols" "link -dump -symbols"], :) AC_SUBST([DUMPBIN]) if test "$DUMPBIN" != ":"; then NM="$DUMPBIN" fi fi test -z "$NM" && NM=nm AC_SUBST([NM]) _LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], [lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:__oline__: $ac_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:__oline__: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:__oline__: output\"" >&AS_MESSAGE_LOG_FD) cat conftest.out >&AS_MESSAGE_LOG_FD if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest*]) ])# LT_PATH_NM # Old names: AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_PROG_NM], []) dnl AC_DEFUN([AC_PROG_NM], []) # LT_LIB_M # -------- # check for math library AC_DEFUN([LT_LIB_M], [AC_REQUIRE([AC_CANONICAL_HOST])dnl LIBM= case $host in *-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*) # These system don't have libm, or don't need it ;; *-ncr-sysv4.3*) AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") ;; *) AC_CHECK_LIB(m, cos, LIBM="-lm") ;; esac AC_SUBST([LIBM]) ])# LT_LIB_M # Old name: AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_CHECK_LIBM], []) # _LT_COMPILER_NO_RTTI([TAGNAME]) # ------------------------------- m4_defun([_LT_COMPILER_NO_RTTI], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= if test "$GCC" = yes; then _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], lt_cv_prog_compiler_rtti_exceptions, [-fno-rtti -fno-exceptions], [], [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) fi _LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], [Compiler flag to turn off builtin functions]) ])# _LT_COMPILER_NO_RTTI # _LT_CMD_GLOBAL_SYMBOLS # ---------------------- m4_defun([_LT_CMD_GLOBAL_SYMBOLS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([LT_PATH_NM])dnl AC_REQUIRE([LT_PATH_LD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_TAG_COMPILER])dnl # Check for command to grab the raw symbol name followed by C symbol from nm. AC_MSG_CHECKING([command to parse $NM output from $compiler object]) AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], [ # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[[BCDEGRST]]' # Regexp to match symbols that can be accessed directly from C. sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[[BCDT]]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[[ABCDGISTW]]' ;; hpux*) if test "$host_cpu" = ia64; then symcode='[[ABCDEGRST]]' fi ;; irix* | nonstopux*) symcode='[[BCDEGRST]]' ;; osf*) symcode='[[BCDEGQRST]]' ;; solaris*) symcode='[[BDRT]]' ;; sco3.2v5*) symcode='[[DT]]' ;; sysv4.2uw2*) symcode='[[DT]]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[[ABDT]]' ;; sysv4) symcode='[[DFNSTU]]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[[ABCDGIRSTW]]' ;; esac # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'" lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function # and D for any global variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK ['"\ " {last_section=section; section=\$ 3};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ " {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ " s[1]~/^[@?]/{print s[1], s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx]" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if AC_TRY_EVAL(ac_compile); then # Now try to grab the symbols. nlist=conftest.nm if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ const struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[[]] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_save_LIBS="$LIBS" lt_save_CFLAGS="$CFLAGS" LIBS="conftstm.$ac_objext" CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then pipe_works=yes fi LIBS="$lt_save_LIBS" CFLAGS="$lt_save_CFLAGS" else echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD fi else echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test "$pipe_works" = yes; then break else lt_cv_sys_global_symbol_pipe= fi done ]) if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then AC_MSG_RESULT(failed) else AC_MSG_RESULT(ok) fi _LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], [Take the output of nm and produce a listing of raw symbols and C names]) _LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], [Transform the output of nm in a proper C declaration]) _LT_DECL([global_symbol_to_c_name_address], [lt_cv_sys_global_symbol_to_c_name_address], [1], [Transform the output of nm in a C name address pair]) _LT_DECL([global_symbol_to_c_name_address_lib_prefix], [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], [Transform the output of nm in a C name address pair when lib prefix is needed]) ]) # _LT_CMD_GLOBAL_SYMBOLS # _LT_COMPILER_PIC([TAGNAME]) # --------------------------- m4_defun([_LT_COMPILER_PIC], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_wl, $1)= _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)= AC_MSG_CHECKING([for $compiler option to produce PIC]) m4_if([$1], [CXX], [ # C++ specific cases for pic, static, wl, etc. if test "$GXX" = yes; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac else case $host_os in aix[[4-9]]*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; dgux*) case $cc_basename in ec++*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; ghcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' if test "$host_cpu" != ia64; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' fi ;; aCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) case $cc_basename in KCC*) # KAI C++ Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64 which still supported -KPIC. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xlc* | xlC*) # IBM XL 8.0 on PPC _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' ;; *) ;; esac ;; netbsd* | netbsdelf*-gnu) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; cxx*) # Digital/Compaq C++ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; lcc*) # Lucid _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ], [ if test "$GCC" = yes; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; hpux9* | hpux10* | hpux11*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC (with -KPIC) is the default. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) case $cc_basename in # old Intel for x86_64 which still supported -KPIC. ecc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # Lahey Fortran 8.1. lf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' ;; pgcc* | pgf77* | pgf90* | pgf95*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; ccc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All Alpha code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xl*) # IBM XL C 8.0/Fortran 10.1 on PPC _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' ;; *Sun\ F*) # Sun Fortran 8.3 passes all unrecognized flags to the linker _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='' ;; esac ;; esac ;; newsos6) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All OSF/1 code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; rdos*) _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; solaris*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' case $cc_basename in f77* | f90* | f95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; *) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; esac ;; sunos4*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec ;then _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; unicos*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; uts4*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ]) case $host_os in # For platforms which do not support PIC, -DPIC is meaningless: *djgpp*) _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" ;; esac AC_MSG_RESULT([$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) _LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], [How to pass a linker flag through the compiler]) # # Check to make sure the PIC flag actually works. # if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in "" | " "*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; esac], [_LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) fi _LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], [Additional compiler flags for building library objects]) # # Check to make sure the static flag actually works. # wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" _LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), $lt_tmp_static_flag, [], [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) _LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], [Compiler flag to prevent dynamic linking]) ])# _LT_COMPILER_PIC # _LT_LINKER_SHLIBS([TAGNAME]) # ---------------------------- # See if the linker supports building shared libraries. m4_defun([_LT_LINKER_SHLIBS], [AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) m4_if([$1], [CXX], [ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' case $host_os in aix[[4-9]]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to AIX nm, but means don't demangle with GNU nm if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' fi ;; pw32*) _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" ;; cygwin* | mingw* | cegcc*) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;/^.*[[ ]]__nm__/s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' ;; linux* | k*bsd*-gnu) _LT_TAGVAR(link_all_deplibs, $1)=no ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] ], [ runpath_var= _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_cmds, $1)= _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(old_archive_from_new_cmds, $1)= _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= _LT_TAGVAR(thread_safe_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list _LT_TAGVAR(include_expsyms, $1)= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ` (' and `)$', so one must not match beginning or # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', # as well as any symbol that contains `d'. _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. dnl Note also adjust exclude_expsyms for C++ above. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test "$GCC" != yes; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd*) with_gnu_ld=no ;; linux* | k*bsd*-gnu) _LT_TAGVAR(link_all_deplibs, $1)=no ;; esac _LT_TAGVAR(ld_shlibs, $1)=yes if test "$with_gnu_ld" = yes; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='${wl}' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi supports_anon_versioning=no case `$LD -v 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[[3-9]]*) # On AIX/PPC, the GNU linker is very broken if test "$host_cpu" != ia64; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.9.1, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to modify your PATH *** so that a non-GNU linker is found, and then restart. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file (1st line # is EXPORTS), use it as is; otherwise, prepend... _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test "$host_os" = linux-dietlibc; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test "$tmp_diet" = no then tmp_addflag= tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 _LT_TAGVAR(whole_archive_flag_spec, $1)= tmp_sharedflag='--shared' ;; xl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' if test "x$supports_anon_versioning" = xyes; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' fi case $cc_basename in xlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir' _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib' if test "x$supports_anon_versioning" = xyes; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; sunos4*) _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then runpath_var= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. _LT_TAGVAR(hardcode_minus_L, $1)=yes if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. _LT_TAGVAR(hardcode_direct, $1)=unsupported fi ;; aix[[4-9]]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag="" else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to AIX nm, but means don't demangle with GNU nm if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then aix_use_runtimelinking=yes break fi done ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' if test "$GCC" = yes; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi ;; esac shared_flag='-shared' if test "$aix_use_runtimelinking" = yes; then shared_flag="$shared_flag "'${wl}-G' fi _LT_TAGVAR(link_all_deplibs, $1)=no else # not using gcc if test "$host_cpu" = ia64; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test "$aix_use_runtimelinking" = yes; then shared_flag='${wl}-G' else shared_flag='${wl}-bM:SRE' fi fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. _LT_TAGVAR(always_export_symbols, $1)=yes if test "$aix_use_runtimelinking" = yes; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" else if test "$host_cpu" = ia64; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' _LT_TAGVAR(archive_cmds_need_lc, $1)=yes # This is similar to how AIX traditionally builds its shared libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; bsdi[[45]]*) _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' # FIXME: Should let the user specify the lib program. _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' _LT_TAGVAR(fix_srcfile_path, $1)='`cygpath -w "$srcfile"`' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; freebsd1*) _LT_TAGVAR(ld_shlibs, $1)=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; hpux9*) if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' else _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' ;; hpux10*) if test "$GCC" = yes -a "$with_gnu_ld" = no; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test "$with_gnu_ld" = no; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes fi ;; hpux11*) if test "$GCC" = yes -a "$with_gnu_ld" = no; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac fi if test "$with_gnu_ld" = no; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" AC_LINK_IFELSE(int foo(void) {}, _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' ) LDFLAGS="$save_LDFLAGS" else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes _LT_TAGVAR(link_all_deplibs, $1)=yes ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; newsos6) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *nto* | *qnx*) ;; openbsd*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' else case $host_os in openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' ;; esac fi else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' ;; osf3*) if test "$GCC" = yes; then _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test "$GCC" = yes; then _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; solaris*) _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' if test "$GCC" = yes; then wlarc='${wl}' _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='${wl}' _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands `-z linker_flag'. GCC discards it without `$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test "$GCC" = yes; then _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' else _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' fi ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes ;; sunos4*) if test "x$host_vendor" = xsequent; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4) case $host_vendor in sni) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' _LT_TAGVAR(hardcode_direct, $1)=no ;; motorola) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4.3*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes _LT_TAGVAR(ld_shlibs, $1)=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We can NOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(ld_shlibs, $1)=no ;; esac if test x$host_vendor = xsni; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym' ;; esac fi fi ]) AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no _LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld _LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl _LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl _LT_DECL([], [extract_expsyms_cmds], [2], [The commands to extract the exported symbol list from a shared archive]) # # Do we need to explicitly link libc? # case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in x|xyes) # Assume -lc should be added _LT_TAGVAR(archive_cmds_need_lc, $1)=yes if test "$enable_shared" = yes && test "$GCC" = yes; then case $_LT_TAGVAR(archive_cmds, $1) in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. AC_MSG_CHECKING([whether -lc should be explicitly linked in]) $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if AC_TRY_EVAL(ac_compile) 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) _LT_TAGVAR(allow_undefined_flag, $1)= if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) then _LT_TAGVAR(archive_cmds_need_lc, $1)=no else _LT_TAGVAR(archive_cmds_need_lc, $1)=yes fi _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* AC_MSG_RESULT([$_LT_TAGVAR(archive_cmds_need_lc, $1)]) ;; esac fi ;; esac _LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], [Whether or not to add -lc for building shared libraries]) _LT_TAGDECL([allow_libtool_libs_with_static_runtimes], [enable_shared_with_static_runtimes], [0], [Whether or not to disallow shared libs when runtime libs are static]) _LT_TAGDECL([], [export_dynamic_flag_spec], [1], [Compiler flag to allow reflexive dlopens]) _LT_TAGDECL([], [whole_archive_flag_spec], [1], [Compiler flag to generate shared objects directly from archives]) _LT_TAGDECL([], [compiler_needs_object], [1], [Whether the compiler copes with passing no objects directly]) _LT_TAGDECL([], [old_archive_from_new_cmds], [2], [Create an old-style archive from a shared archive]) _LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], [Create a temporary old-style archive to link instead of a shared archive]) _LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) _LT_TAGDECL([], [archive_expsym_cmds], [2]) _LT_TAGDECL([], [module_cmds], [2], [Commands used to build a loadable module if different from building a shared archive.]) _LT_TAGDECL([], [module_expsym_cmds], [2]) _LT_TAGDECL([], [with_gnu_ld], [1], [Whether we are building with GNU ld or not]) _LT_TAGDECL([], [allow_undefined_flag], [1], [Flag that allows shared libraries with undefined symbols to be built]) _LT_TAGDECL([], [no_undefined_flag], [1], [Flag that enforces no undefined symbols]) _LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], [Flag to hardcode $libdir into a binary during linking. This must work even if $libdir does not exist]) _LT_TAGDECL([], [hardcode_libdir_flag_spec_ld], [1], [[If ld is used when linking, flag to hardcode $libdir into a binary during linking. This must work even if $libdir does not exist]]) _LT_TAGDECL([], [hardcode_libdir_separator], [1], [Whether we need a single "-rpath" flag with a separated argument]) _LT_TAGDECL([], [hardcode_direct], [0], [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_direct_absolute], [0], [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the resulting binary and the resulting library dependency is "absolute", i.e impossible to change by setting ${shlibpath_var} if the library is relocated]) _LT_TAGDECL([], [hardcode_minus_L], [0], [Set to "yes" if using the -LDIR flag during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_shlibpath_var], [0], [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_automatic], [0], [Set to "yes" if building a shared library automatically hardcodes DIR into the library and all subsequent libraries and executables linked against it]) _LT_TAGDECL([], [inherit_rpath], [0], [Set to yes if linker adds runtime paths of dependent libraries to runtime path list]) _LT_TAGDECL([], [link_all_deplibs], [0], [Whether libtool must link a program against all its dependency libraries]) _LT_TAGDECL([], [fix_srcfile_path], [1], [Fix the shell variable $srcfile for the compiler]) _LT_TAGDECL([], [always_export_symbols], [0], [Set to "yes" if exported symbols are required]) _LT_TAGDECL([], [export_symbols_cmds], [2], [The commands to list exported symbols]) _LT_TAGDECL([], [exclude_expsyms], [1], [Symbols that should not be listed in the preloaded symbols]) _LT_TAGDECL([], [include_expsyms], [1], [Symbols that must always be exported]) _LT_TAGDECL([], [prelink_cmds], [2], [Commands necessary for linking programs (against libraries) with templates]) _LT_TAGDECL([], [file_list_spec], [1], [Specify filename containing input files]) dnl FIXME: Not yet implemented dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], dnl [Compiler flag to generate thread safe objects]) ])# _LT_LINKER_SHLIBS # _LT_LANG_C_CONFIG([TAG]) # ------------------------ # Ensure that the configuration variables for a C compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to `libtool'. m4_defun([_LT_LANG_C_CONFIG], [m4_require([_LT_DECL_EGREP])dnl lt_save_CC="$CC" AC_LANG_PUSH(C) # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' _LT_TAG_COMPILER # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) LT_SYS_DLOPEN_SELF _LT_CMD_STRIPLIB # Report which library types will actually be built AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_CONFIG($1) fi AC_LANG_POP CC="$lt_save_CC" ])# _LT_LANG_C_CONFIG # _LT_PROG_CXX # ------------ # Since AC_PROG_CXX is broken, in that it returns g++ if there is no c++ # compiler, we have our own version here. m4_defun([_LT_PROG_CXX], [ pushdef([AC_MSG_ERROR], [_lt_caught_CXX_error=yes]) AC_PROG_CXX if test -n "$CXX" && ( test "X$CXX" != "Xno" && ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || (test "X$CXX" != "Xg++"))) ; then AC_PROG_CXXCPP else _lt_caught_CXX_error=yes fi popdef([AC_MSG_ERROR]) ])# _LT_PROG_CXX dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([_LT_PROG_CXX], []) # _LT_LANG_CXX_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a C++ compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to `libtool'. m4_defun([_LT_LANG_CXX_CONFIG], [AC_REQUIRE([_LT_PROG_CXX])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl AC_LANG_PUSH(C++) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_caught_CXX_error" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test "$GXX" = yes; then _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' else _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= fi if test "$GXX" = yes; then # Set up default GNU C++ configuration LT_PATH_LD # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test "$with_gnu_ld" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='${wl}' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) _LT_TAGVAR(ld_shlibs, $1)=yes case $host_os in aix3*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aix[[4-9]]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag="" else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' if test "$GXX" = yes; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi esac shared_flag='-shared' if test "$aix_use_runtimelinking" = yes; then shared_flag="$shared_flag "'${wl}-G' fi else # not using gcc if test "$host_cpu" = ia64; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test "$aix_use_runtimelinking" = yes; then shared_flag='${wl}-G' else shared_flag='${wl}-bM:SRE' fi fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. _LT_TAGVAR(always_export_symbols, $1)=yes if test "$aix_use_runtimelinking" = yes; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an empty # executable. _LT_SYS_MODULE_PATH_AIX _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" else if test "$host_cpu" = ia64; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' _LT_TAGVAR(archive_cmds_need_lc, $1)=yes # This is similar to how AIX traditionally builds its shared # libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file (1st line # is EXPORTS), use it as is; otherwise, prepend... _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; freebsd[[12]]*) # C++ shared libraries reported to be fairly broken before # switch to ELF _LT_TAGVAR(ld_shlibs, $1)=no ;; freebsd-elf*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions _LT_TAGVAR(ld_shlibs, $1)=yes ;; gnu*) ;; hpux9*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' ;; *) if test "$GXX" = yes; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; hpux10*|hpux11*) if test $with_gnu_ld = no; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) ;; *) _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' ;; *) if test "$GXX" = yes; then if test $with_gnu_ld = no; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test "$GXX" = yes; then if test "$with_gnu_ld" = no; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` -o $lib' fi fi _LT_TAGVAR(link_all_deplibs, $1)=yes ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [[1-5]]* | *pgcpp\ [[1-5]]*) _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"' _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~ $RANLIB $oldlib' _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' ;; *) # Version 6 will use weak symbols _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' ;; cxx*) # Compaq C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' ;; xl*) # IBM XL 8.0 on PPC, with GNU ld _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' if test "x$supports_anon_versioning" = xyes; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='echo' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; m88k*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) _LT_TAGVAR(ld_shlibs, $1)=yes ;; openbsd2*) # C++ shared libraries are fairly broken _LT_TAGVAR(ld_shlibs, $1)=no ;; openbsd*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' fi output_verbose_link_cmd=echo else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; cxx*) case $host in osf3*) _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && $ECHO "X${wl}-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' ;; *) _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~ $RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' ;; esac _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' ;; *) if test "$GXX" = yes && test "$with_gnu_ld" = no; then _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' case $host in osf3*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; solaris*) case $cc_basename in CC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands `-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes output_verbose_link_cmd='echo' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test "$GXX" = yes && test "$with_gnu_ld" = no; then _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' else # g++ 2.7 appears to require `-G' NOT `-shared' on this # platform. _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We can NOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no _LT_TAGVAR(GCC, $1)="$GXX" _LT_TAGVAR(LD, $1)="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" CC=$lt_save_CC LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test "$_lt_caught_CXX_error" != yes AC_LANG_POP ])# _LT_LANG_CXX_CONFIG # _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) # --------------------------------- # Figure out "hidden" library dependencies from verbose # compiler output when linking a shared library. # Parse the compiler output and extract the necessary # objects, libraries and library flags. m4_defun([_LT_SYS_HIDDEN_LIBDEPS], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl # Dependencies to place before and after the object being linked: _LT_TAGVAR(predep_objects, $1)= _LT_TAGVAR(postdep_objects, $1)= _LT_TAGVAR(predeps, $1)= _LT_TAGVAR(postdeps, $1)= _LT_TAGVAR(compiler_lib_search_path, $1)= dnl we can't use the lt_simple_compile_test_code here, dnl because it contains code intended for an executable, dnl not a library. It's possible we should let each dnl tag define a new lt_????_link_test_code variable, dnl but it's only used here... m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF int a; void foo (void) { a = 0; } _LT_EOF ], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF ], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer*4 a a=0 return end _LT_EOF ], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer a a=0 return end _LT_EOF ], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF public class foo { private int a; public void bar (void) { a = 0; } }; _LT_EOF ]) dnl Parse the compiler output and extract the necessary dnl objects, libraries and library flags. if AC_TRY_EVAL(ac_compile); then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case $p in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test $p = "-L" || test $p = "-R"; then prev=$p continue else prev= fi if test "$pre_test_object_deps_done" = no; then case $p in -L* | -R*) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}" else _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$_LT_TAGVAR(postdeps, $1)"; then _LT_TAGVAR(postdeps, $1)="${prev}${p}" else _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}" fi fi ;; *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test "$pre_test_object_deps_done" = no; then if test -z "$_LT_TAGVAR(predep_objects, $1)"; then _LT_TAGVAR(predep_objects, $1)="$p" else _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" fi else if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then _LT_TAGVAR(postdep_objects, $1)="$p" else _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling $1 test program" fi $RM -f confest.$objext # PORTME: override above test on systems where it is broken m4_if([$1], [CXX], [case $host_os in interix[[3-9]]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. _LT_TAGVAR(predep_objects,$1)= _LT_TAGVAR(postdep_objects,$1)= _LT_TAGVAR(postdeps,$1)= ;; linux*) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 # The more standards-conforming stlport4 library is # incompatible with the Cstd library. Avoid specifying # it if it's in CXXFLAGS. Ignore libCrun as # -library=stlport4 depends on it. case " $CXX $CXXFLAGS " in *" -library=stlport4 "*) solaris_use_stlport4=yes ;; esac if test "$solaris_use_stlport4" != yes; then _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' fi ;; esac ;; solaris*) case $cc_basename in CC*) # The more standards-conforming stlport4 library is # incompatible with the Cstd library. Avoid specifying # it if it's in CXXFLAGS. Ignore libCrun as # -library=stlport4 depends on it. case " $CXX $CXXFLAGS " in *" -library=stlport4 "*) solaris_use_stlport4=yes ;; esac # Adding this requires a known-good setup of shared libraries for # Sun compiler versions before 5.6, else PIC objects from an old # archive will be linked into the output, leading to subtle bugs. if test "$solaris_use_stlport4" != yes; then _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' fi ;; esac ;; esac ]) case " $_LT_TAGVAR(postdeps, $1) " in *" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; esac _LT_TAGVAR(compiler_lib_search_dirs, $1)= if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` fi _LT_TAGDECL([], [compiler_lib_search_dirs], [1], [The directories searched by this compiler when creating a shared library]) _LT_TAGDECL([], [predep_objects], [1], [Dependencies to place before and after the objects being linked to create a shared library]) _LT_TAGDECL([], [postdep_objects], [1]) _LT_TAGDECL([], [predeps], [1]) _LT_TAGDECL([], [postdeps], [1]) _LT_TAGDECL([], [compiler_lib_search_path], [1], [The library search path used internally by the compiler when linking a shared library]) ])# _LT_SYS_HIDDEN_LIBDEPS # _LT_PROG_F77 # ------------ # Since AC_PROG_F77 is broken, in that it returns the empty string # if there is no fortran compiler, we have our own version here. m4_defun([_LT_PROG_F77], [ pushdef([AC_MSG_ERROR], [_lt_disable_F77=yes]) AC_PROG_F77 if test -z "$F77" || test "X$F77" = "Xno"; then _lt_disable_F77=yes fi popdef([AC_MSG_ERROR]) ])# _LT_PROG_F77 dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([_LT_PROG_F77], []) # _LT_LANG_F77_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a Fortran 77 compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_F77_CONFIG], [AC_REQUIRE([_LT_PROG_F77])dnl AC_LANG_PUSH(Fortran 77) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for f77 test sources. ac_ext=f # Object file extension for compiled f77 test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the F77 compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_disable_F77" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_GCC=$GCC CC=${F77-"f77"} compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) GCC=$G77 if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)="$G77" _LT_TAGVAR(LD, $1)="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC="$lt_save_CC" fi # test "$_lt_disable_F77" != yes AC_LANG_POP ])# _LT_LANG_F77_CONFIG # _LT_PROG_FC # ----------- # Since AC_PROG_FC is broken, in that it returns the empty string # if there is no fortran compiler, we have our own version here. m4_defun([_LT_PROG_FC], [ pushdef([AC_MSG_ERROR], [_lt_disable_FC=yes]) AC_PROG_FC if test -z "$FC" || test "X$FC" = "Xno"; then _lt_disable_FC=yes fi popdef([AC_MSG_ERROR]) ])# _LT_PROG_FC dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([_LT_PROG_FC], []) # _LT_LANG_FC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for a Fortran compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_FC_CONFIG], [AC_REQUIRE([_LT_PROG_FC])dnl AC_LANG_PUSH(Fortran) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for fc test sources. ac_ext=${ac_fc_srcext-f} # Object file extension for compiled fc test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the FC compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_disable_FC" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_GCC=$GCC CC=${FC-"f95"} compiler=$CC GCC=$ac_cv_fc_compiler_gnu _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu" _LT_TAGVAR(LD, $1)="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC="$lt_save_CC" fi # test "$_lt_disable_FC" != yes AC_LANG_POP ])# _LT_LANG_FC_CONFIG # _LT_LANG_GCJ_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Java Compiler compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_GCJ_CONFIG], [AC_REQUIRE([LT_PROG_GCJ])dnl AC_LANG_SAVE # Source file extension for Java test sources. ac_ext=java # Object file extension for compiled Java test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="class foo {}" # Code to be used in simple link tests lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_GCC=$GCC GCC=yes CC=${GCJ-"gcj"} compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)="$LD" _LT_CC_BASENAME([$compiler]) # GCJ did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC="$lt_save_CC" ])# _LT_LANG_GCJ_CONFIG # _LT_LANG_RC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for the Windows resource compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_RC_CONFIG], [AC_REQUIRE([LT_PROG_RC])dnl AC_LANG_SAVE # Source file extension for RC test sources. ac_ext=rc # Object file extension for compiled RC test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' # Code to be used in simple link tests lt_simple_link_test_code="$lt_simple_compile_test_code" # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_GCC=$GCC GCC= CC=${RC-"windres"} compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes if test -n "$compiler"; then : _LT_CONFIG($1) fi GCC=$lt_save_GCC AC_LANG_RESTORE CC="$lt_save_CC" ])# _LT_LANG_RC_CONFIG # LT_PROG_GCJ # ----------- AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], [AC_CHECK_TOOL(GCJ, gcj,) test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" AC_SUBST(GCJFLAGS)])])[]dnl ]) # Old name: AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_GCJ], []) # LT_PROG_RC # ---------- AC_DEFUN([LT_PROG_RC], [AC_CHECK_TOOL(RC, windres,) ]) # Old name: AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_RC], []) # _LT_DECL_EGREP # -------------- # If we don't have a new enough Autoconf to choose the best grep # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_EGREP], [AC_REQUIRE([AC_PROG_EGREP])dnl AC_REQUIRE([AC_PROG_FGREP])dnl test -z "$GREP" && GREP=grep _LT_DECL([], [GREP], [1], [A grep program that handles long lines]) _LT_DECL([], [EGREP], [1], [An ERE matcher]) _LT_DECL([], [FGREP], [1], [A literal string matcher]) dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too AC_SUBST([GREP]) ]) # _LT_DECL_OBJDUMP # -------------- # If we don't have a new enough Autoconf to choose the best objdump # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_OBJDUMP], [AC_CHECK_TOOL(OBJDUMP, objdump, false) test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) AC_SUBST([OBJDUMP]) ]) # _LT_DECL_SED # ------------ # Check for a fully-functional sed program, that truncates # as few characters as possible. Prefer GNU sed if found. m4_defun([_LT_DECL_SED], [AC_PROG_SED test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" _LT_DECL([], [SED], [1], [A sed program that does not truncate output]) _LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], [Sed that helps us avoid accidentally triggering echo(1) options like -n]) ])# _LT_DECL_SED m4_ifndef([AC_PROG_SED], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_SED. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_SED], [AC_MSG_CHECKING([for a sed that does not truncate output]) AC_CACHE_VAL(lt_cv_path_SED, [# Loop through the user's path and test for sed and gsed. # Then use that list of sed's as ones to test for truncation. as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for lt_ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" fi done done done IFS=$as_save_IFS lt_ac_max=0 lt_ac_count=0 # Add /usr/xpg4/bin/sed as it is typically found on Solaris # along with /bin/sed that truncates output. for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do test ! -f $lt_ac_sed && continue cat /dev/null > conftest.in lt_ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >conftest.in # Check for GNU sed and select it if it is found. if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then lt_cv_path_SED=$lt_ac_sed break fi while true; do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo >>conftest.nl $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break cmp -s conftest.out conftest.nl || break # 10000 chars as input seems more than enough test $lt_ac_count -gt 10 && break lt_ac_count=`expr $lt_ac_count + 1` if test $lt_ac_count -gt $lt_ac_max; then lt_ac_max=$lt_ac_count lt_cv_path_SED=$lt_ac_sed fi done done ]) SED=$lt_cv_path_SED AC_SUBST([SED]) AC_MSG_RESULT([$SED]) ])#AC_PROG_SED ])#m4_ifndef # Old name: AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_SED], []) # _LT_CHECK_SHELL_FEATURES # ------------------------ # Find out whether the shell is Bourne or XSI compatible, # or has some other useful features. m4_defun([_LT_CHECK_SHELL_FEATURES], [AC_MSG_CHECKING([whether the shell understands some XSI constructs]) # Try some XSI features xsi_shell=no ( _lt_dummy="a/b/c" test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \ = c,a/b,, \ && eval 'test $(( 1 + 1 )) -eq 2 \ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ && xsi_shell=yes AC_MSG_RESULT([$xsi_shell]) _LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell']) AC_MSG_CHECKING([whether the shell understands "+="]) lt_shell_append=no ( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \ >/dev/null 2>&1 \ && lt_shell_append=yes AC_MSG_RESULT([$lt_shell_append]) _LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append']) if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi _LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac _LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl _LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl ])# _LT_CHECK_SHELL_FEATURES # _LT_PROG_XSI_SHELLFNS # --------------------- # Bourne and XSI compatible variants of some useful shell functions. m4_defun([_LT_PROG_XSI_SHELLFNS], [case $xsi_shell in yes) cat << \_LT_EOF >> "$cfgfile" # func_dirname file append nondir_replacement # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. func_dirname () { case ${1} in */*) func_dirname_result="${1%/*}${2}" ;; * ) func_dirname_result="${3}" ;; esac } # func_basename file func_basename () { func_basename_result="${1##*/}" } # func_dirname_and_basename file append nondir_replacement # perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # Implementation must be kept synchronized with func_dirname # and func_basename. For efficiency, we do not delegate to # those functions but instead duplicate the functionality here. func_dirname_and_basename () { case ${1} in */*) func_dirname_result="${1%/*}${2}" ;; * ) func_dirname_result="${3}" ;; esac func_basename_result="${1##*/}" } # func_stripname prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). func_stripname () { # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary parameter first. func_stripname_result=${3} func_stripname_result=${func_stripname_result#"${1}"} func_stripname_result=${func_stripname_result%"${2}"} } # func_opt_split func_opt_split () { func_opt_split_opt=${1%%=*} func_opt_split_arg=${1#*=} } # func_lo2o object func_lo2o () { case ${1} in *.lo) func_lo2o_result=${1%.lo}.${objext} ;; *) func_lo2o_result=${1} ;; esac } # func_xform libobj-or-source func_xform () { func_xform_result=${1%.*}.lo } # func_arith arithmetic-term... func_arith () { func_arith_result=$(( $[*] )) } # func_len string # STRING may not start with a hyphen. func_len () { func_len_result=${#1} } _LT_EOF ;; *) # Bourne compatible functions. cat << \_LT_EOF >> "$cfgfile" # func_dirname file append nondir_replacement # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. func_dirname () { # Extract subdirectory from the argument. func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"` if test "X$func_dirname_result" = "X${1}"; then func_dirname_result="${3}" else func_dirname_result="$func_dirname_result${2}" fi } # func_basename file func_basename () { func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"` } dnl func_dirname_and_basename dnl A portable version of this function is already defined in general.m4sh dnl so there is no need for it here. # func_stripname prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # func_strip_suffix prefix name func_stripname () { case ${2} in .*) func_stripname_result=`$ECHO "X${3}" \ | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;; *) func_stripname_result=`$ECHO "X${3}" \ | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;; esac } # sed scripts: my_sed_long_opt='1s/^\(-[[^=]]*\)=.*/\1/;q' my_sed_long_arg='1s/^-[[^=]]*=//' # func_opt_split func_opt_split () { func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"` func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"` } # func_lo2o object func_lo2o () { func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"` } # func_xform libobj-or-source func_xform () { func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[[^.]]*$/.lo/'` } # func_arith arithmetic-term... func_arith () { func_arith_result=`expr "$[@]"` } # func_len string # STRING may not start with a hyphen. func_len () { func_len_result=`expr "$[1]" : ".*" 2>/dev/null || echo $max_cmd_len` } _LT_EOF esac case $lt_shell_append in yes) cat << \_LT_EOF >> "$cfgfile" # func_append var value # Append VALUE to the end of shell variable VAR. func_append () { eval "$[1]+=\$[2]" } _LT_EOF ;; *) cat << \_LT_EOF >> "$cfgfile" # func_append var value # Append VALUE to the end of shell variable VAR. func_append () { eval "$[1]=\$$[1]\$[2]" } _LT_EOF ;; esac ]) opensc-0.13.0/m4/ax_pthread.m40000644000015201777760000003036612057406034012736 00000000000000# =========================================================================== # http://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 to any special C compiler that is needed for # multi-threaded programs (defaults to the value of CC otherwise). (This # is necessary on AIX to use the special cc_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also link it with them as well. e.g. you should link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threads programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, 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. # # 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 18 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) 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 True64 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_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC(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 LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" 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 starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -kthread lthread -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) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case ${host_os} in 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. (We need to link with -pthreads/-mt/ # -lpthread.) (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 just look for -pthreads and -lpthread first: ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" ;; darwin*) ax_pthread_flags="-pthread $ax_pthread_flags" ;; esac if test x"$ax_pthread_ok" = xno; then for flag in $ax_pthread_flags; do case $flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $flag]) PTHREAD_CFLAGS="$flag" ;; pthread-config) AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) if test x"$ax_pthread_config" = xno; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$flag]) PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # 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 static void routine(void *a) { a = 0; } static void *start_routine(void *a) { return a; }], [pthread_t th; pthread_attr_t attr; pthread_create(&th, 0, start_routine, 0); pthread_join(th, 0); pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_cleanup_pop(0) /* ; */])], [ax_pthread_ok=yes], []) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" AC_MSG_RESULT($ax_pthread_ok) if test "x$ax_pthread_ok" = xyes; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$ax_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_MSG_CHECKING([for joinable pthread attribute]) attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], [int attr = $attr; return attr /* ; */])], [attr_name=$attr; break], []) done AC_MSG_RESULT($attr_name) if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, [Define to necessary symbol if this constant uses a non-standard name on your system.]) fi AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no case ${host_os} in aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; osf* | hpux*) flag="-D_REENTRANT";; solaris*) if test "$GCC" = "yes"; then flag="-D_REENTRANT" else flag="-mt -D_REENTRANT" fi ;; esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], ax_cv_PTHREAD_PRIO_INHERIT, [ AC_LINK_IFELSE([ AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT;]])], [ax_cv_PTHREAD_PRIO_INHERIT=yes], [ax_cv_PTHREAD_PRIO_INHERIT=no]) ]) AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.])) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with xlc_r or cc_r if test x"$GCC" != xyes; then AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) else PTHREAD_CC=$CC fi else PTHREAD_CC="$CC" fi AC_SUBST(PTHREAD_LIBS) AC_SUBST(PTHREAD_CFLAGS) AC_SUBST(PTHREAD_CC) # 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.13.0/m4/lt~obsolete.m40000644000015201777760000001311312057406053013161 00000000000000# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- # # Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004. # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 4 lt~obsolete.m4 # These exist entirely to fool aclocal when bootstrapping libtool. # # In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN) # which have later been changed to m4_define as they aren't part of the # exported API, or moved to Autoconf or Automake where they belong. # # The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN # in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us # using a macro with the same name in our local m4/libtool.m4 it'll # pull the old libtool.m4 in (it doesn't see our shiny new m4_define # and doesn't know about Autoconf macros at all.) # # So we provide this file, which has a silly filename so it's always # included after everything else. This provides aclocal with the # AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything # because those macros already exist, or will be overwritten later. # We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. # # Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. # Yes, that means every name once taken will need to remain here until # we give up compatibility with versions before 1.7, at which point # we need to keep only those names which we still refer to. # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) m4_ifndef([AC_LIBTOOL_RC], [AC_DEFUN([AC_LIBTOOL_RC])]) m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) opensc-0.13.0/m4/ltoptions.m40000644000015201777760000002724212057406053012652 00000000000000# Helper functions for option handling. -*- Autoconf -*- # # Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 6 ltoptions.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) # _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) # ------------------------------------------ m4_define([_LT_MANGLE_OPTION], [[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) # _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) # --------------------------------------- # Set option OPTION-NAME for macro MACRO-NAME, and if there is a # matching handler defined, dispatch to it. Other OPTION-NAMEs are # saved as a flag. m4_define([_LT_SET_OPTION], [m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), _LT_MANGLE_DEFUN([$1], [$2]), [m4_warning([Unknown $1 option `$2'])])[]dnl ]) # _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) # ------------------------------------------------------------ # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. m4_define([_LT_IF_OPTION], [m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) # _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) # ------------------------------------------------------- # Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME # are set. m4_define([_LT_UNLESS_OPTIONS], [m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), [m4_define([$0_found])])])[]dnl m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 ])[]dnl ]) # _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) # ---------------------------------------- # OPTION-LIST is a space-separated list of Libtool options associated # with MACRO-NAME. If any OPTION has a matching handler declared with # LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about # the unknown option and exit. m4_defun([_LT_SET_OPTIONS], [# Set options m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [_LT_SET_OPTION([$1], _LT_Option)]) m4_if([$1],[LT_INIT],[ dnl dnl Simply set some default values (i.e off) if boolean options were not dnl specified: _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no ]) _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no ]) dnl dnl If no reference was made to various pairs of opposing options, then dnl we run the default mode handler for the pair. For example, if neither dnl `shared' nor `disable-shared' was passed, we enable building of shared dnl archives by default: _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], [_LT_ENABLE_FAST_INSTALL]) ]) ])# _LT_SET_OPTIONS ## --------------------------------- ## ## Macros to handle LT_INIT options. ## ## --------------------------------- ## # _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) # ----------------------------------------- m4_define([_LT_MANGLE_DEFUN], [[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) # LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) # ----------------------------------------------- m4_define([LT_OPTION_DEFINE], [m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl ])# LT_OPTION_DEFINE # dlopen # ------ LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes ]) AU_DEFUN([AC_LIBTOOL_DLOPEN], [_LT_SET_OPTION([LT_INIT], [dlopen]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `dlopen' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) # win32-dll # --------- # Declare package support for building win32 dll's. LT_OPTION_DEFINE([LT_INIT], [win32-dll], [enable_win32_dll=yes case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-cegcc*) AC_CHECK_TOOL(AS, as, false) AC_CHECK_TOOL(DLLTOOL, dlltool, false) AC_CHECK_TOOL(OBJDUMP, objdump, false) ;; esac test -z "$AS" && AS=as _LT_DECL([], [AS], [0], [Assembler program])dnl test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [0], [DLL creation program])dnl test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [0], [Object dumper program])dnl ])# win32-dll AU_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_REQUIRE([AC_CANONICAL_HOST])dnl _LT_SET_OPTION([LT_INIT], [win32-dll]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `win32-dll' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) # _LT_ENABLE_SHARED([DEFAULT]) # ---------------------------- # implement the --enable-shared flag, and supports the `shared' and # `disable-shared' LT_INIT options. # DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. m4_define([_LT_ENABLE_SHARED], [m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([shared], [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS="$lt_save_ifs" ;; esac], [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) _LT_DECL([build_libtool_libs], [enable_shared], [0], [Whether or not to build shared libraries]) ])# _LT_ENABLE_SHARED LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) # Old names: AC_DEFUN([AC_ENABLE_SHARED], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) ]) AC_DEFUN([AC_DISABLE_SHARED], [_LT_SET_OPTION([LT_INIT], [disable-shared]) ]) AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_SHARED], []) dnl AC_DEFUN([AM_DISABLE_SHARED], []) # _LT_ENABLE_STATIC([DEFAULT]) # ---------------------------- # implement the --enable-static flag, and support the `static' and # `disable-static' LT_INIT options. # DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. m4_define([_LT_ENABLE_STATIC], [m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([static], [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS="$lt_save_ifs" ;; esac], [enable_static=]_LT_ENABLE_STATIC_DEFAULT) _LT_DECL([build_old_libs], [enable_static], [0], [Whether or not to build static libraries]) ])# _LT_ENABLE_STATIC LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) # Old names: AC_DEFUN([AC_ENABLE_STATIC], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) ]) AC_DEFUN([AC_DISABLE_STATIC], [_LT_SET_OPTION([LT_INIT], [disable-static]) ]) AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_STATIC], []) dnl AC_DEFUN([AM_DISABLE_STATIC], []) # _LT_ENABLE_FAST_INSTALL([DEFAULT]) # ---------------------------------- # implement the --enable-fast-install flag, and support the `fast-install' # and `disable-fast-install' LT_INIT options. # DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. m4_define([_LT_ENABLE_FAST_INSTALL], [m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([fast-install], [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS="$lt_save_ifs" ;; esac], [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) _LT_DECL([fast_install], [enable_fast_install], [0], [Whether or not to optimize for fast installation])dnl ])# _LT_ENABLE_FAST_INSTALL LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) # Old names: AU_DEFUN([AC_ENABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `fast-install' option into LT_INIT's first parameter.]) ]) AU_DEFUN([AC_DISABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], [disable-fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `disable-fast-install' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) # _LT_WITH_PIC([MODE]) # -------------------- # implement the --with-pic flag, and support the `pic-only' and `no-pic' # LT_INIT options. # MODE is either `yes' or `no'. If omitted, it defaults to `both'. m4_define([_LT_WITH_PIC], [AC_ARG_WITH([pic], [AS_HELP_STRING([--with-pic], [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], [pic_mode="$withval"], [pic_mode=default]) test -z "$pic_mode" && pic_mode=m4_default([$1], [default]) _LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl ])# _LT_WITH_PIC LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) # Old name: AU_DEFUN([AC_LIBTOOL_PICMODE], [_LT_SET_OPTION([LT_INIT], [pic-only]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `pic-only' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) ## ----------------- ## ## LTDL_INIT Options ## ## ----------------- ## m4_define([_LTDL_MODE], []) LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], [m4_define([_LTDL_MODE], [nonrecursive])]) LT_OPTION_DEFINE([LTDL_INIT], [recursive], [m4_define([_LTDL_MODE], [recursive])]) LT_OPTION_DEFINE([LTDL_INIT], [subproject], [m4_define([_LTDL_MODE], [subproject])]) m4_define([_LTDL_TYPE], []) LT_OPTION_DEFINE([LTDL_INIT], [installable], [m4_define([_LTDL_TYPE], [installable])]) LT_OPTION_DEFINE([LTDL_INIT], [convenience], [m4_define([_LTDL_TYPE], [convenience])]) opensc-0.13.0/configure0000755000015201777760000171667212057406054011762 00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.67 for opensc 0.13.0. # # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software # Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV export CONFIG_SHELL exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi if test -x / >/dev/null 2>&1; then as_test_x='test -x' else if ls -dL / >/dev/null 2>&1; then as_ls_L_option=L else as_ls_L_option= fi as_test_x=' eval sh -c '\'' if test -d "$1"; then test -d "$1/."; else case $1 in #( -*)set "./$1";; esac; case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( ???[sx]*):;;*)false;;esac;fi '\'' sh ' fi as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" # Check that we are running under the correct shell. SHELL=${CONFIG_SHELL-/bin/sh} case X$lt_ECHO in X*--fallback-echo) # Remove one level of quotation (which was required for Make). ECHO=`echo "$lt_ECHO" | sed 's,\\\\\$\\$0,'$0','` ;; esac ECHO=${lt_ECHO-echo} if test "X$1" = X--no-reexec; then # Discard the --no-reexec flag, and continue. shift elif test "X$1" = X--fallback-echo; then # Avoid inline document here, it may be left over : elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then # Yippee, $ECHO works! : else # Restart under the correct shell. exec $SHELL "$0" --no-reexec ${1+"$@"} fi if test "X$1" = X--fallback-echo; then # used as fallback echo shift cat <<_LT_EOF $* _LT_EOF exit 0 fi # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH if test -z "$lt_ECHO"; then if test "X${echo_test_string+set}" != Xset; then # find a string as large as possible, as long as the shell can cope with it for cmd in 'sed 50q "$0"' 'sed 20q "$0"' 'sed 10q "$0"' 'sed 2q "$0"' 'echo test'; do # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ... if { echo_test_string=`eval $cmd`; } 2>/dev/null && { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null then break fi done fi if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' && echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then : else # The Solaris, AIX, and Digital Unix default echo programs unquote # backslashes. This makes it impossible to quote backslashes using # echo "$something" | sed 's/\\/\\\\/g' # # So, first we look for a working echo in the user's PATH. lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for dir in $PATH /usr/ucb; do IFS="$lt_save_ifs" if (test -f $dir/echo || test -f $dir/echo$ac_exeext) && test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' && echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then ECHO="$dir/echo" break fi done IFS="$lt_save_ifs" if test "X$ECHO" = Xecho; then # We didn't find a better echo, so look for alternatives. if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' && echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then # This shell has a builtin print -r that does the trick. ECHO='print -r' elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } && test "X$CONFIG_SHELL" != X/bin/ksh; then # If we have ksh, try running configure again with it. ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} export ORIGINAL_CONFIG_SHELL CONFIG_SHELL=/bin/ksh export CONFIG_SHELL exec $CONFIG_SHELL "$0" --no-reexec ${1+"$@"} else # Try using printf. ECHO='printf %s\n' if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' && echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then # Cool, printf works : elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` && test "X$echo_testing_string" = 'X\t' && echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL export CONFIG_SHELL SHELL="$CONFIG_SHELL" export SHELL ECHO="$CONFIG_SHELL $0 --fallback-echo" elif echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` && test "X$echo_testing_string" = 'X\t' && echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then ECHO="$CONFIG_SHELL $0 --fallback-echo" else # maybe with a smaller string... prev=: for cmd in 'echo test' 'sed 2q "$0"' 'sed 10q "$0"' 'sed 20q "$0"' 'sed 50q "$0"'; do if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null then break fi prev="$cmd" done if test "$prev" != 'sed 50q "$0"'; then echo_test_string=`eval $prev` export echo_test_string exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "$0" ${1+"$@"} else # Oops. We lost completely, so just stick with echo. ECHO=echo fi fi fi fi fi fi # Copy echo and quote the copy suitably for passing to libtool from # the Makefile, instead of quoting the original, which is used later. lt_ECHO=$ECHO if test "X$lt_ECHO" = "X$CONFIG_SHELL $0 --fallback-echo"; then lt_ECHO="$CONFIG_SHELL \\\$\$0 --fallback-echo" fi test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='opensc' PACKAGE_TARNAME='opensc' PACKAGE_VERSION='0.13.0' PACKAGE_STRING='opensc 0.13.0' PACKAGE_BUGREPORT='' PACKAGE_URL='' ac_unique_file="src/libopensc/sc.c" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS ENABLE_SM_FALSE ENABLE_SM_TRUE ENABLE_MINIDRIVER_FALSE ENABLE_MINIDRIVER_TRUE CYGWIN_FALSE CYGWIN_TRUE WIN32_FALSE WIN32_TRUE ENABLE_DOC_FALSE ENABLE_DOC_TRUE ENABLE_OPENCT_FALSE ENABLE_OPENCT_TRUE ENABLE_OPENSSL_FALSE ENABLE_OPENSSL_TRUE ENABLE_READLINE_FALSE ENABLE_READLINE_TRUE ENABLE_ZLIB_FALSE ENABLE_ZLIB_TRUE ENABLE_MAN_FALSE ENABLE_MAN_TRUE DEBUG_FILE DEFAULT_SM_MODULE LIBRARY_BITNESS OPTIONAL_PCSC_CFLAGS OPTIONAL_OPENCT_LIBS OPTIONAL_OPENCT_CFLAGS OPTIONAL_OPENSSL_LIBS OPTIONAL_OPENSSL_CFLAGS OPTIONAL_READLINE_LIBS OPTIONAL_READLINE_CFLAGS OPTIONAL_ZLIB_LIBS OPTIONAL_ZLIB_CFLAGS DEFAULT_PCSC_PROVIDER WIN_LIBPREFIX OPENSC_LT_OLDEST OPENSC_LT_AGE OPENSC_LT_REVISION OPENSC_LT_CURRENT OPENSC_VERSION_FIX OPENSC_VERSION_MINOR OPENSC_VERSION_MAJOR xslstylesheetsdir pkcs11dir PCSC_LIBS PCSC_CFLAGS OPENCT_LIBS OPENCT_CFLAGS OPENSSL_LIBS OPENSSL_CFLAGS READLINE_LIBS READLINE_CFLAGS ZLIB_LIBS ZLIB_CFLAGS PTHREAD_CFLAGS PTHREAD_LIBS PTHREAD_CC ax_pthread_config LIBOBJS GIT git XSLTPROC RC OTOOL64 OTOOL LIPO NMEDIT DSYMUTIL lt_ECHO RANLIB AR NM ac_ct_DUMPBIN DUMPBIN LD FGREP LIBTOOL OBJDUMP DLLTOOL AS SED LN_S EGREP GREP CPP PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE am__quote am__include DEPDIR OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC host_os host_vendor host_cpu host build_os build_vendor build_cpu build AM_BACKSLASH AM_DEFAULT_VERBOSITY am__untar am__tar AMTAR am__leading_dot SET_MAKE AWK mkdir_p MKDIR_P INSTALL_STRIP_PROGRAM STRIP install_sh MAKEINFO AUTOHEADER AUTOMAKE AUTOCONF ACLOCAL VERSION PACKAGE CYGPATH_W am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules enable_dependency_tracking with_cygwin_native enable_strict enable_pedantic enable_zlib enable_readline enable_openssl enable_openct enable_pcsc enable_ctapi enable_minidriver enable_sm enable_man enable_doc with_xsl_stylesheetsdir with_pcsc_provider enable_shared enable_static with_pic enable_fast_install with_gnu_ld enable_libtool_lock enable_assert ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR CPP XSLTPROC git ZLIB_CFLAGS ZLIB_LIBS READLINE_CFLAGS READLINE_LIBS OPENSSL_CFLAGS OPENSSL_LIBS OPENCT_CFLAGS OPENCT_LIBS PCSC_CFLAGS PCSC_LIBS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. If a cross compiler is detected then cross compile mode will be used" >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures opensc 0.13.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/opensc] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of opensc 0.13.0:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-silent-rules less verbose build output (undo: `make V=1') --disable-silent-rules verbose build output (undo: `make V=0') --disable-dependency-tracking speeds up one-time build --enable-dependency-tracking do not reject slow dependency extractors --enable-strict enable strict compile mode [disabled] --enable-pedantic enable pedantic compile mode [disabled] --enable-zlib enable zlib linkage [detect] --enable-readline enable readline linkage [detect] --enable-openssl enable openssl linkage [detect] --enable-openct enable openct linkage [disabled] --disable-pcsc disable pcsc support [enabled] --enable-ctapi enable CT-API support [disabled] --enable-minidriver enable minidriver on Windows [disabled] --enable-sm enable secure messaging support and modules [disabled] --disable-man disable installation of manuals [enabled for none Windows] --enable-doc enable installation of documents [disabled] --enable-shared[=PKGS] build shared libraries [default=yes] --enable-static[=PKGS] build static libraries [default=yes] --enable-fast-install[=PKGS] optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) --disable-assert turn off assertions Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-cygwin-native compile native win32 --with-xsl-stylesheetsdir=PATH docbook xsl-stylesheets for svn build [detect] --with-pcsc-provider=PATH Path to system pcsc provider [system default] --with-pic try to use only PIC/non-PIC objects [default=use both] --with-gnu-ld assume the C compiler uses GNU ld [default=no] Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path CPP C preprocessor XSLTPROC xsltproc utility git git ZLIB_CFLAGS C compiler flags for zlib ZLIB_LIBS linker flags for zlib READLINE_CFLAGS C compiler flags for readline READLINE_LIBS linker flags for readline OPENSSL_CFLAGS C compiler flags for OPENSSL, overriding pkg-config OPENSSL_LIBS linker flags for OPENSSL, overriding pkg-config OPENCT_CFLAGS C compiler flags for OPENCT, overriding pkg-config OPENCT_LIBS linker flags for OPENCT, overriding pkg-config PCSC_CFLAGS C compiler flags for PCSC, overriding pkg-config PCSC_LIBS linker flags for PCSC, overriding pkg-config Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to the package provider. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF opensc configure 0.13.0 generated by GNU Autoconf 2.67 Copyright (C) 2010 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} } # ac_fn_c_check_header_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} } # ac_fn_c_check_func # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval "test \"\${$3+set}\"" = set; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} } # ac_fn_c_check_header_mongrel # ac_fn_c_check_type LINENO TYPE VAR INCLUDES # ------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache # variable VAR accordingly. ac_fn_c_check_type () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 else eval "$3=no" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { if (sizeof ($2)) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { if (sizeof (($2))) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else eval "$3=yes" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} } # ac_fn_c_check_type # ac_fn_c_compute_int LINENO EXPR VAR INCLUDES # -------------------------------------------- # Tries to find the compile-time value of EXPR in a program that includes # INCLUDES, setting VAR accordingly. Returns whether the value could be # computed ac_fn_c_compute_int () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if test "$cross_compiling" = yes; then # Depending upon the size, compute the lo and hi bounds. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) >= 0)]; test_array [0] = 0 ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_lo=0 ac_mid=0 while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_hi=$ac_mid; break else as_fn_arith $ac_mid + 1 && ac_lo=$as_val if test $ac_lo -le $ac_mid; then ac_lo= ac_hi= break fi as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) < 0)]; test_array [0] = 0 ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_hi=-1 ac_mid=-1 while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) >= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_lo=$ac_mid; break else as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val if test $ac_mid -le $ac_hi; then ac_lo= ac_hi= break fi as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done else ac_lo= ac_hi= fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext # Binary search between lo and hi bounds. while test "x$ac_lo" != "x$ac_hi"; do as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_hi=$ac_mid else as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done case $ac_lo in #(( ?*) eval "$3=\$ac_lo"; ac_retval=0 ;; '') ac_retval=1 ;; esac else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 static long int longval () { return $2; } static unsigned long int ulongval () { return $2; } #include #include int main () { FILE *f = fopen ("conftest.val", "w"); if (! f) return 1; if (($2) < 0) { long int i = longval (); if (i != ($2)) return 1; fprintf (f, "%ld", i); } else { unsigned long int i = ulongval (); if (i != ($2)) return 1; fprintf (f, "%lu", i); } /* Do not output a trailing newline, as this causes \r\n confusion on some platforms. */ return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : echo >>conftest.val; read $3 config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by opensc $as_me 0.13.0, which was generated by GNU Autoconf 2.67. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5 ; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_aux_dir= for ac_dir in . "$srcdir"/.; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in . \"$srcdir\"/." "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. ac_config_headers="$ac_config_headers config.h" am__api_version='1.11' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if test "${ac_cv_path_install+set}" = set; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 $as_echo_n "checking whether build environment is sane... " >&6; } # Just in case sleep 1 echo timestamp > conftest.file # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5 ;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) as_fn_error $? "unsafe srcdir value: \`$srcdir'" "$LINENO" 5 ;; esac # Do `set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$*" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi rm -f conftest.file if test "$*" != "X $srcdir/configure conftest.file" \ && test "$*" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". as_fn_error $? "ls -t appears to fail. Make sure there is not a broken alias in your environment" "$LINENO" 5 fi test "$2" = conftest.file ) then # Ok. : else as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s&\$&$program_suffix&;$program_transform_name" # Double any \ or $. # By default was `s,x,x', remove it if useless. ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` # expand $ac_aux_dir to an absolute path am_aux_dir=`cd $ac_aux_dir && pwd` if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --run true"; then am_missing_run="$MISSING --run " else am_missing_run= { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`missing' script is too old or missing" >&5 $as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} fi if test x"${install_sh}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi # Installed binaries are usually stripped using `strip' when the user # run `make install-strip'. However `strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the `STRIP' environment variable to overrule this program. if test "$cross_compiling" != no; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_STRIP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 $as_echo_n "checking for a thread-safe mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if test "${ac_cv_path_mkdir+set}" = set; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir (GNU coreutils) '* | \ 'mkdir (coreutils) '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test "${ac_cv_path_mkdir+set}" = set; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 $as_echo "$MKDIR_P" >&6; } mkdir_p="$MKDIR_P" case $mkdir_p in [\\/$]* | ?:[\\/]*) ;; */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; esac for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_AWK+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 $as_echo "$AWK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AWK" && break done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\"" = set; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi # Define the identity of the package. PACKAGE='opensc' VERSION='0.13.0' cat >>confdefs.h <<_ACEOF #define PACKAGE "$PACKAGE" _ACEOF cat >>confdefs.h <<_ACEOF #define VERSION "$VERSION" _ACEOF # Some tools Automake needs. ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # We need awk for the "check" target. The system "awk" is bad on # some platforms. # Always define AMTAR for backward compatibility. AMTAR=${AMTAR-"${am_missing_run}tar"} am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -' OPENSC_VERSION_MAJOR="0" OPENSC_VERSION_MINOR="13" OPENSC_VERSION_FIX="0" # LT Version numbers, remember to change them just *before* a release. # (Code changed: REVISION++) # (Oldest interface removed: OLDEST++) # (Interfaces added: CURRENT++, REVISION=0) OPENSC_LT_CURRENT="3" OPENSC_LT_OLDEST="3" OPENSC_LT_REVISION="0" OPENSC_LT_AGE="0" OPENSC_LT_AGE="$((${OPENSC_LT_CURRENT}-${OPENSC_LT_OLDEST}))" # silent build by default # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : enableval=$enable_silent_rules; fi case $enable_silent_rules in yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=0;; esac AM_BACKSLASH='\' # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } if test "${ac_cv_build+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5 ;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } if test "${ac_cv_host+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5 ;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5 ; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5 ; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5 ; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5 ; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if test "${ac_cv_objext+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5 ; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if test "${ac_cv_c_compiler_gnu+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if test "${ac_cv_prog_cc_g+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if test "${ac_cv_prog_cc_c89+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 $as_echo_n "checking for style of include used by $am_make... " >&6; } am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from `make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 $as_echo "$_am_result" >&6; } rm -f confinc confmf # Check whether --enable-dependency-tracking was given. if test "${enable_dependency_tracking+set}" = set; then : enableval=$enable_dependency_tracking; fi if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' fi if test "x$enable_dependency_tracking" != xno; then AMDEP_TRUE= AMDEP_FALSE='#' else AMDEP_TRUE='#' AMDEP_FALSE= fi depcc="$CC" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named `D' -- because `-MD' means `put the output # in D'. mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with # Solaris 8's {/usr,}/bin/sh. touch sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with `-c' and `-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle `-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # after this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvisualcpp | msvcmsys) # This compiler won't grok `-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 $as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_path_PKG_CONFIG+set}" = set; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_path_ac_pt_PKG_CONFIG+set}" = set; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 $as_echo "$ac_pt_PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.9.0 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 $as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } PKG_CONFIG="" fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if test "${ac_cv_prog_CPP+set}" = set; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5 ; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if test "${ac_cv_path_GREP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if test "${ac_cv_path_EGREP+set}" = set; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if test "${ac_cv_header_stdc+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 $as_echo_n "checking whether byte ordering is bigendian... " >&6; } if test "${ac_cv_c_bigendian+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # Check for potential -arch flags. It is not universal unless # there are at least two -arch flags with different values. ac_arch= ac_prev= for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do if test -n "$ac_prev"; then case $ac_word in i?86 | x86_64 | ppc | ppc64) if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then ac_arch=$ac_word else ac_cv_c_bigendian=universal break fi ;; esac ac_prev= elif test "x$ac_word" = "x-arch"; then ac_prev=arch fi done fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to _BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. if test "$cross_compiling" = yes; then : # Try to guess by grepping values from an object file. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int main () { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_c_bigendian=no else ac_cv_c_bigendian=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 $as_echo "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h ;; #( no) ;; #( universal) $as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h ;; #( *) as_fn_error $? "unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac # Check whether --with-cygwin-native was given. if test "${with_cygwin_native+set}" = set; then : withval=$with_cygwin_native; else with_cygwin_native="no" fi 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*) { $as_echo "$as_me:${as_lineno-$LINENO}: checking cygwin mode to use" >&5 $as_echo_n "checking cygwin mode to use... " >&6; } CYGWIN="yes" if test "${with_cygwin_native}" = "yes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using native win32" >&5 $as_echo "Using native win32" >&6; } CPPFLAGS="${CPPFLAGS} -DWIN32_LEAN_AND_MEAN" CFLAGS="${CFLAGS} -mno-cygwin" WIN32="yes" else { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using cygwin" >&5 $as_echo "Using cygwin" >&6; } CPPFLAGS="${CPPFLAGS} -DCRYPTOKI_FORCE_WIN32" WIN_LIBPREFIX="cyg" $as_echo "#define USE_CYGWIN 1" >>confdefs.h fi ;; esac case "${host}" in *-mingw*|*-winnt*|*-cygwin*) DEBUG_FILE="%TEMP%\opensc-debug.log" ;; *) DEBUG_FILE="/tmp/opensc-debug.log" ;; esac cat >>confdefs.h <<_ACEOF #define DEBUG_FILE "${DEBUG_FILE}" _ACEOF # Check whether --enable-strict was given. if test "${enable_strict+set}" = set; then : enableval=$enable_strict; else enable_strict="no" fi # Check whether --enable-pedantic was given. if test "${enable_pedantic+set}" = set; then : enableval=$enable_pedantic; else enable_pedantic="no" fi # Check whether --enable-zlib was given. if test "${enable_zlib+set}" = set; then : enableval=$enable_zlib; else enable_zlib="detect" fi # Check whether --enable-readline was given. if test "${enable_readline+set}" = set; then : enableval=$enable_readline; else enable_readline="detect" fi # Check whether --enable-openssl was given. if test "${enable_openssl+set}" = set; then : enableval=$enable_openssl; else enable_openssl="detect" fi # Check whether --enable-openct was given. if test "${enable_openct+set}" = set; then : enableval=$enable_openct; else enable_openct="no" fi # Check whether --enable-pcsc was given. if test "${enable_pcsc+set}" = set; then : enableval=$enable_pcsc; else enable_pcsc="yes" fi # Check whether --enable-ctapi was given. if test "${enable_ctapi+set}" = set; then : enableval=$enable_ctapi; else enable_ctapi="no" fi # Check whether --enable-minidriver was given. if test "${enable_minidriver+set}" = set; then : enableval=$enable_minidriver; else enable_minidriver="no" fi # Check whether --enable-sm was given. if test "${enable_sm+set}" = set; then : enableval=$enable_sm; else enable_sm="no" fi # Check whether --enable-man was given. if test "${enable_man+set}" = set; then : enableval=$enable_man; else enable_man="detect" fi # Check whether --enable-doc was given. if test "${enable_doc+set}" = set; then : enableval=$enable_doc; else enable_doc="no" fi # Check whether --with-xsl-stylesheetsdir was given. if test "${with_xsl_stylesheetsdir+set}" = set; then : withval=$with_xsl_stylesheetsdir; xslstylesheetsdir="${withval}" else xslstylesheetsdir="detect" fi # Check whether --with-pcsc-provider was given. if test "${with_pcsc_provider+set}" = set; then : withval=$with_pcsc_provider; else with_pcsc_provider="detect" fi reader_count="" for rdriver in "${enable_pcsc}" "${enable_openct}" "${enable_ctapi}"; do test "${rdriver}" = "yes" && reader_count="${reader_count}x" done if test "${reader_count}" != "x"; then as_fn_error $? "Only one of --enable-pcsc, --enable-openct, --enable-ctapi can be specified!" "$LINENO" 5 fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if test "${ac_cv_prog_CPP+set}" = set; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5 ; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 $as_echo_n "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 $as_echo "no, using $LN_S" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 $as_echo_n "checking for a sed that does not truncate output... " >&6; } if test "${ac_cv_path_SED+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed { ac_script=; unset ac_script;} if test -z "$SED"; then ac_path_SED_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo '' >> "conftest.nl" "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_SED_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_SED="$ac_path_SED" ac_path_SED_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_SED_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 $as_echo "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\"" = set; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi case `pwd` in *\ * | *\ *) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 $as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; esac macro_version='2.2.6b' macro_revision='1.3017' ltmain="$ac_aux_dir/ltmain.sh" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 $as_echo_n "checking for a sed that does not truncate output... " >&6; } if test "${ac_cv_path_SED+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed { ac_script=; unset ac_script;} if test -z "$SED"; then ac_path_SED_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo '' >> "conftest.nl" "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_SED_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_SED="$ac_path_SED" ac_path_SED_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_SED_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 $as_echo "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 $as_echo_n "checking for fgrep... " >&6; } if test "${ac_cv_path_FGREP+set}" = set; then : $as_echo_n "(cached) " >&6 else if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 then ac_cv_path_FGREP="$GREP -F" else if test -z "$FGREP"; then ac_path_FGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in fgrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_FGREP" && $as_test_x "$ac_path_FGREP"; } || continue # Check for GNU ac_path_FGREP and select it if it is found. # Check for GNU $ac_path_FGREP case `"$ac_path_FGREP" --version 2>&1` in *GNU*) ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'FGREP' >> "conftest.nl" "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_FGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_FGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_FGREP"; then as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_FGREP=$FGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 $as_echo "$ac_cv_path_FGREP" >&6; } FGREP="$ac_cv_path_FGREP" test -z "$GREP" && GREP=grep # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes else with_gnu_ld=no fi ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 $as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 $as_echo_n "checking for GNU ld... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi if test "${lt_cv_path_LD+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } if test "${lt_cv_prog_gnu_ld+set}" = set; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 $as_echo "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 $as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } if test "${lt_cv_path_NM+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM="$NM" else lt_nm_to_check="${ac_tool_prefix}nm" if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. tmp_nm="$ac_dir/$lt_tmp_nm" if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then # Check to see if the nm accepts a BSD-compat flag. # Adding the `sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in */dev/null* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS="$lt_save_ifs" done : ${lt_cv_path_NM=no} fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 $as_echo "$lt_cv_path_NM" >&6; } if test "$lt_cv_path_NM" != "no"; then NM="$lt_cv_path_NM" else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$ac_tool_prefix"; then for ac_prog in "dumpbin -symbols" "link -dump -symbols" do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_DUMPBIN+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$DUMPBIN"; then ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DUMPBIN=$ac_cv_prog_DUMPBIN if test -n "$DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 $as_echo "$DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$DUMPBIN" && break done fi if test -z "$DUMPBIN"; then ac_ct_DUMPBIN=$DUMPBIN for ac_prog in "dumpbin -symbols" "link -dump -symbols" do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_DUMPBIN+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DUMPBIN"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN if test -n "$ac_ct_DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 $as_echo "$ac_ct_DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_DUMPBIN" && break done if test "x$ac_ct_DUMPBIN" = x; then DUMPBIN=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DUMPBIN=$ac_ct_DUMPBIN fi fi if test "$DUMPBIN" != ":"; then NM="$DUMPBIN" fi fi test -z "$NM" && NM=nm { $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 $as_echo_n "checking the name lister ($NM) interface... " >&6; } if test "${lt_cv_nm_interface+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:5929: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 (eval echo "\"\$as_me:5932: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 (eval echo "\"\$as_me:5935: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 $as_echo "$lt_cv_nm_interface" >&6; } # find the maximum length of command line arguments { $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 $as_echo_n "checking the maximum length of command line arguments... " >&6; } if test "${lt_cv_sys_max_cmd_len+set}" = set; then : $as_echo_n "(cached) " >&6 else i=0 teststring="ABCD" case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8 ; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test "X"`$SHELL $0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \ = "XX$teststring$teststring"; } >/dev/null 2>&1 && test $i != 17 # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac fi if test -n $lt_cv_sys_max_cmd_len ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 $as_echo "$lt_cv_sys_max_cmd_len" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 $as_echo "none" >&6; } fi max_cmd_len=$lt_cv_sys_max_cmd_len : ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5 $as_echo_n "checking whether the shell understands some XSI constructs... " >&6; } # Try some XSI features xsi_shell=no ( _lt_dummy="a/b/c" test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \ = c,a/b,, \ && eval 'test $(( 1 + 1 )) -eq 2 \ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ && xsi_shell=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5 $as_echo "$xsi_shell" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5 $as_echo_n "checking whether the shell understands \"+=\"... " >&6; } lt_shell_append=no ( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \ >/dev/null 2>&1 \ && lt_shell_append=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5 $as_echo "$lt_shell_append" >&6; } if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 $as_echo_n "checking for $LD option to reload object files... " >&6; } if test "${lt_cv_ld_reload_flag+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_reload_flag='-r' fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 $as_echo "$lt_cv_ld_reload_flag" >&6; } reload_flag=$lt_cv_ld_reload_flag case $reload_flag in "" | " "*) ;; *) reload_flag=" $reload_flag" ;; esac reload_cmds='$LD$reload_flag -o $output$reload_objs' case $host_os in darwin*) if test "$GCC" = yes; then reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs' else reload_cmds='$LD$reload_flag -o $output$reload_objs' fi ;; esac if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. set dummy ${ac_tool_prefix}objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_OBJDUMP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$OBJDUMP"; then ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OBJDUMP=$ac_cv_prog_OBJDUMP if test -n "$OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 $as_echo "$OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OBJDUMP"; then ac_ct_OBJDUMP=$OBJDUMP # Extract the first word of "objdump", so it can be a program name with args. set dummy objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_OBJDUMP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OBJDUMP"; then ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_OBJDUMP="objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP if test -n "$ac_ct_OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 $as_echo "$ac_ct_OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OBJDUMP" = x; then OBJDUMP="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OBJDUMP=$ac_ct_OBJDUMP fi else OBJDUMP="$ac_cv_prog_OBJDUMP" fi test -z "$OBJDUMP" && OBJDUMP=objdump { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 $as_echo_n "checking how to recognize dependent libraries... " >&6; } if test "${lt_cv_deplibs_check_method+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # `unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # which responds to the $file_magic_cmd with a given extended regex. # If you have `file' or equivalent on your system and you're not sure # whether `pass_all' will *always* work, you probably want this one. case $host_os in aix[4-9]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[45]*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='/usr/bin/file -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; gnu*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]' lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9].[0-9]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[3-9]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be Linux ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 $as_echo "$lt_cv_deplibs_check_method" >&6; } file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. set dummy ${ac_tool_prefix}ar; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_AR+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_AR="${ac_tool_prefix}ar" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_AR"; then ac_ct_AR=$AR # Extract the first word of "ar", so it can be a program name with args. set dummy ar; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_AR+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_AR="ar" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 $as_echo "$ac_ct_AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_AR" = x; then AR="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi else AR="$ac_cv_prog_AR" fi test -z "$AR" && AR=ar test -z "$AR_FLAGS" && AR_FLAGS=cru if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_STRIP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi test -z "$STRIP" && STRIP=: if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_RANLIB+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi test -z "$RANLIB" && RANLIB=: # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" fi # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Check for command to grab the raw symbol name followed by C symbol from nm. { $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 $as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } if test "${lt_cv_sys_global_symbol_pipe+set}" = set; then : $as_echo_n "(cached) " >&6 else # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[BCDEGRST]' # Regexp to match symbols that can be accessed directly from C. sympat='\([_A-Za-z][_A-Za-z0-9]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[BCDT]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[ABCDGISTW]' ;; hpux*) if test "$host_cpu" = ia64; then symcode='[ABCDEGRST]' fi ;; irix* | nonstopux*) symcode='[BCDEGRST]' ;; osf*) symcode='[BCDEGQRST]' ;; solaris*) symcode='[BDRT]' ;; sco3.2v5*) symcode='[DT]' ;; sysv4.2uw2*) symcode='[DT]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[ABDT]' ;; sysv4) symcode='[DFNSTU]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[ABCDGIRSTW]' ;; esac # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'" lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function # and D for any global variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK '"\ " {last_section=section; section=\$ 3};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ " {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ " s[1]~/^[@?]/{print s[1], s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Now try to grab the symbols. nlist=conftest.nm if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist\""; } >&5 (eval $NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ const struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_save_LIBS="$LIBS" lt_save_CFLAGS="$CFLAGS" LIBS="conftstm.$ac_objext" CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest${ac_exeext}; then pipe_works=yes fi LIBS="$lt_save_LIBS" CFLAGS="$lt_save_CFLAGS" else echo "cannot find nm_test_func in $nlist" >&5 fi else echo "cannot find nm_test_var in $nlist" >&5 fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 fi else echo "$progname: failed program was:" >&5 cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test "$pipe_works" = yes; then break else lt_cv_sys_global_symbol_pipe= fi done fi if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 $as_echo "failed" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 $as_echo "ok" >&6; } fi # Check whether --enable-libtool-lock was given. if test "${enable_libtool_lock+set}" = set; then : enableval=$enable_libtool_lock; fi test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE="32" ;; *ELF-64*) HPUX_IA64_MODE="64" ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out which ABI we are using. echo '#line 7126 "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then if test "$lt_cv_prog_gnu_ld" = yes; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_i386" ;; ppc64-*linux*|powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; ppc*-*linux*|powerpc*-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -belf" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 $as_echo_n "checking whether the C compiler needs -belf... " >&6; } if test "${lt_cv_cc_needs_belf+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_cc_needs_belf=yes else lt_cv_cc_needs_belf=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 $as_echo "$lt_cv_cc_needs_belf" >&6; } if test x"$lt_cv_cc_needs_belf" != x"yes"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS="$SAVE_CFLAGS" fi ;; sparc*-*solaris*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) LD="${LD-ld} -m elf64_sparc" ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks="$enable_libtool_lock" case $host_os in rhapsody* | darwin*) if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_DSYMUTIL+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$DSYMUTIL"; then ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DSYMUTIL=$ac_cv_prog_DSYMUTIL if test -n "$DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 $as_echo "$DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DSYMUTIL"; then ac_ct_DSYMUTIL=$DSYMUTIL # Extract the first word of "dsymutil", so it can be a program name with args. set dummy dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_DSYMUTIL+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DSYMUTIL"; then ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL if test -n "$ac_ct_DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 $as_echo "$ac_ct_DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DSYMUTIL" = x; then DSYMUTIL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DSYMUTIL=$ac_ct_DSYMUTIL fi else DSYMUTIL="$ac_cv_prog_DSYMUTIL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. set dummy ${ac_tool_prefix}nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_NMEDIT+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$NMEDIT"; then ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi NMEDIT=$ac_cv_prog_NMEDIT if test -n "$NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 $as_echo "$NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_NMEDIT"; then ac_ct_NMEDIT=$NMEDIT # Extract the first word of "nmedit", so it can be a program name with args. set dummy nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_NMEDIT+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_NMEDIT"; then ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_NMEDIT="nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT if test -n "$ac_ct_NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 $as_echo "$ac_ct_NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_NMEDIT" = x; then NMEDIT=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac NMEDIT=$ac_ct_NMEDIT fi else NMEDIT="$ac_cv_prog_NMEDIT" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. set dummy ${ac_tool_prefix}lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_LIPO+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$LIPO"; then ac_cv_prog_LIPO="$LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_LIPO="${ac_tool_prefix}lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi LIPO=$ac_cv_prog_LIPO if test -n "$LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 $as_echo "$LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_LIPO"; then ac_ct_LIPO=$LIPO # Extract the first word of "lipo", so it can be a program name with args. set dummy lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_LIPO+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_LIPO"; then ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_LIPO="lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO if test -n "$ac_ct_LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 $as_echo "$ac_ct_LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_LIPO" = x; then LIPO=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac LIPO=$ac_ct_LIPO fi else LIPO="$ac_cv_prog_LIPO" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. set dummy ${ac_tool_prefix}otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_OTOOL+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL"; then ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_OTOOL="${ac_tool_prefix}otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL=$ac_cv_prog_OTOOL if test -n "$OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 $as_echo "$OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL"; then ac_ct_OTOOL=$OTOOL # Extract the first word of "otool", so it can be a program name with args. set dummy otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_OTOOL+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL"; then ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_OTOOL="otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL if test -n "$ac_ct_OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 $as_echo "$ac_ct_OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL" = x; then OTOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL=$ac_ct_OTOOL fi else OTOOL="$ac_cv_prog_OTOOL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. set dummy ${ac_tool_prefix}otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_OTOOL64+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL64"; then ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL64=$ac_cv_prog_OTOOL64 if test -n "$OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 $as_echo "$OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL64"; then ac_ct_OTOOL64=$OTOOL64 # Extract the first word of "otool64", so it can be a program name with args. set dummy otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_OTOOL64+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL64"; then ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_OTOOL64="otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 if test -n "$ac_ct_OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 $as_echo "$ac_ct_OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL64" = x; then OTOOL64=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL64=$ac_ct_OTOOL64 fi else OTOOL64="$ac_cv_prog_OTOOL64" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 $as_echo_n "checking for -single_module linker flag... " >&6; } if test "${lt_cv_apple_cc_single_mod+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_apple_cc_single_mod=no if test -z "${LT_MULTI_MODULE}"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&5 $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&5 fi rm -rf libconftest.dylib* rm -f conftest.* fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 $as_echo "$lt_cv_apple_cc_single_mod" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 $as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } if test "${lt_cv_ld_exported_symbols_list+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_ld_exported_symbols_list=yes else lt_cv_ld_exported_symbols_list=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS="$save_LDFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 $as_echo "$lt_cv_ld_exported_symbols_list" >&6; } case $host_os in rhapsody* | darwin1.[012]) _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[91]*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; 10.[012]*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test "$lt_cv_apple_cc_single_mod" = "yes"; then _lt_dar_single_mod='$single_module' fi if test "$lt_cv_ld_exported_symbols_list" = "yes"; then _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' fi if test "$DSYMUTIL" != ":"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac for ac_header in dlfcn.h do : ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default " if test "x$ac_cv_header_dlfcn_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_DLFCN_H 1 _ACEOF fi done # Set options enable_win32_dll=yes case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-cegcc*) if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}as", so it can be a program name with args. set dummy ${ac_tool_prefix}as; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_AS+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$AS"; then ac_cv_prog_AS="$AS" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_AS="${ac_tool_prefix}as" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AS=$ac_cv_prog_AS if test -n "$AS"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AS" >&5 $as_echo "$AS" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_AS"; then ac_ct_AS=$AS # Extract the first word of "as", so it can be a program name with args. set dummy as; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_AS+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AS"; then ac_cv_prog_ac_ct_AS="$ac_ct_AS" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_AS="as" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AS=$ac_cv_prog_ac_ct_AS if test -n "$ac_ct_AS"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AS" >&5 $as_echo "$ac_ct_AS" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_AS" = x; then AS="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AS=$ac_ct_AS fi else AS="$ac_cv_prog_AS" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. set dummy ${ac_tool_prefix}dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_DLLTOOL+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$DLLTOOL"; then ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DLLTOOL=$ac_cv_prog_DLLTOOL if test -n "$DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 $as_echo "$DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DLLTOOL"; then ac_ct_DLLTOOL=$DLLTOOL # Extract the first word of "dlltool", so it can be a program name with args. set dummy dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_DLLTOOL+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DLLTOOL"; then ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_DLLTOOL="dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL if test -n "$ac_ct_DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 $as_echo "$ac_ct_DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DLLTOOL" = x; then DLLTOOL="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DLLTOOL=$ac_ct_DLLTOOL fi else DLLTOOL="$ac_cv_prog_DLLTOOL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. set dummy ${ac_tool_prefix}objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_OBJDUMP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$OBJDUMP"; then ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OBJDUMP=$ac_cv_prog_OBJDUMP if test -n "$OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 $as_echo "$OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OBJDUMP"; then ac_ct_OBJDUMP=$OBJDUMP # Extract the first word of "objdump", so it can be a program name with args. set dummy objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_OBJDUMP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OBJDUMP"; then ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_OBJDUMP="objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP if test -n "$ac_ct_OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 $as_echo "$ac_ct_OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OBJDUMP" = x; then OBJDUMP="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OBJDUMP=$ac_ct_OBJDUMP fi else OBJDUMP="$ac_cv_prog_OBJDUMP" fi ;; esac test -z "$AS" && AS=as test -z "$DLLTOOL" && DLLTOOL=dlltool test -z "$OBJDUMP" && OBJDUMP=objdump enable_dlopen=no # Check whether --enable-shared was given. if test "${enable_shared+set}" = set; then : enableval=$enable_shared; p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS="$lt_save_ifs" ;; esac else enable_shared=yes fi # Check whether --enable-static was given. if test "${enable_static+set}" = set; then : enableval=$enable_static; p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS="$lt_save_ifs" ;; esac else enable_static=yes fi # Check whether --with-pic was given. if test "${with_pic+set}" = set; then : withval=$with_pic; pic_mode="$withval" else pic_mode=default fi test -z "$pic_mode" && pic_mode=default # Check whether --enable-fast-install was given. if test "${enable_fast_install+set}" = set; then : enableval=$enable_fast_install; p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS="$lt_save_ifs" ;; esac else enable_fast_install=yes fi # This can be used to rebuild libtool when needed LIBTOOL_DEPS="$ltmain" # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' test -z "$LN_S" && LN_S="ln -s" if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 $as_echo_n "checking for objdir... " >&6; } if test "${lt_cv_objdir+set}" = set; then : $as_echo_n "(cached) " >&6 else rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 $as_echo "$lt_cv_objdir" >&6; } objdir=$lt_cv_objdir cat >>confdefs.h <<_ACEOF #define LT_OBJDIR "$lt_cv_objdir/" _ACEOF case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='s/\(["`$\\]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a `.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld="$lt_cv_prog_gnu_ld" old_CC="$CC" old_CFLAGS="$CFLAGS" # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o for cc_temp in $compiler""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 $as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } if test "${lt_cv_path_MAGIC_CMD+set}" = set; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD="$MAGIC_CMD" lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/${ac_tool_prefix}file; then lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS="$lt_save_ifs" MAGIC_CMD="$lt_save_MAGIC_CMD" ;; esac fi MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 $as_echo_n "checking for file... " >&6; } if test "${lt_cv_path_MAGIC_CMD+set}" = set; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD="$MAGIC_CMD" lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/file; then lt_cv_path_MAGIC_CMD="$ac_dir/file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS="$lt_save_ifs" MAGIC_CMD="$lt_save_MAGIC_CMD" ;; esac fi MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi else MAGIC_CMD=: fi fi fi ;; esac # Use C for the default configuration in the libtool script lt_save_CC="$CC" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o objext=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then lt_prog_compiler_no_builtin_flag= if test "$GCC" = yes; then lt_prog_compiler_no_builtin_flag=' -fno-builtin' { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 $as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } if test "${lt_cv_prog_compiler_rtti_exceptions+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_rtti_exceptions=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-fno-rtti -fno-exceptions" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:8686: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:8690: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_rtti_exceptions=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 $as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" else : fi fi lt_prog_compiler_wl= lt_prog_compiler_pic= lt_prog_compiler_static= { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 $as_echo_n "checking for $compiler option to produce PIC... " >&6; } if test "$GCC" = yes; then lt_prog_compiler_wl='-Wl,' lt_prog_compiler_static='-static' case $host_os in aix*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic='-DDLL_EXPORT' ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) lt_prog_compiler_pic='-fPIC' ;; esac ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. lt_prog_compiler_can_build_shared=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic=-Kconform_pic fi ;; *) lt_prog_compiler_pic='-fPIC' ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) lt_prog_compiler_wl='-Wl,' if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' else lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' fi ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). lt_prog_compiler_pic='-DDLL_EXPORT' ;; hpux9* | hpux10* | hpux11*) lt_prog_compiler_wl='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? lt_prog_compiler_static='${wl}-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) lt_prog_compiler_wl='-Wl,' # PIC (with -KPIC) is the default. lt_prog_compiler_static='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) case $cc_basename in # old Intel for x86_64 which still supported -KPIC. ecc*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; # Lahey Fortran 8.1. lf95*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='--shared' lt_prog_compiler_static='--static' ;; pgcc* | pgf77* | pgf90* | pgf95*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; ccc*) lt_prog_compiler_wl='-Wl,' # All Alpha code is PIC. lt_prog_compiler_static='-non_shared' ;; xl*) # IBM XL C 8.0/Fortran 10.1 on PPC lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-qpic' lt_prog_compiler_static='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Wl,' ;; *Sun\ F*) # Sun Fortran 8.3 passes all unrecognized flags to the linker lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='' ;; esac ;; esac ;; newsos6) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; osf3* | osf4* | osf5*) lt_prog_compiler_wl='-Wl,' # All OSF/1 code is PIC. lt_prog_compiler_static='-non_shared' ;; rdos*) lt_prog_compiler_static='-non_shared' ;; solaris*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' case $cc_basename in f77* | f90* | f95*) lt_prog_compiler_wl='-Qoption ld ';; *) lt_prog_compiler_wl='-Wl,';; esac ;; sunos4*) lt_prog_compiler_wl='-Qoption ld ' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec ;then lt_prog_compiler_pic='-Kconform_pic' lt_prog_compiler_static='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; unicos*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_can_build_shared=no ;; uts4*) lt_prog_compiler_pic='-pic' lt_prog_compiler_static='-Bstatic' ;; *) lt_prog_compiler_can_build_shared=no ;; esac fi case $host_os in # For platforms which do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic= ;; *) lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_prog_compiler_pic" >&5 $as_echo "$lt_prog_compiler_pic" >&6; } # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 $as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } if test "${lt_cv_prog_compiler_pic_works+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_works=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic -DPIC" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:9025: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:9029: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 $as_echo "$lt_cv_prog_compiler_pic_works" >&6; } if test x"$lt_cv_prog_compiler_pic_works" = xyes; then case $lt_prog_compiler_pic in "" | " "*) ;; *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; esac else lt_prog_compiler_pic= lt_prog_compiler_can_build_shared=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 $as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if test "${lt_cv_prog_compiler_static_works+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_static_works=no save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works=yes fi else lt_cv_prog_compiler_static_works=yes fi fi $RM -r conftest* LDFLAGS="$save_LDFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 $as_echo "$lt_cv_prog_compiler_static_works" >&6; } if test x"$lt_cv_prog_compiler_static_works" = xyes; then : else lt_prog_compiler_static= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if test "${lt_cv_prog_compiler_c_o+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:9130: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:9134: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if test "${lt_cv_prog_compiler_c_o+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:9185: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:9189: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } hard_links="nottested" if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then # do not overwrite the value of need_locks provided by the user { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 $as_echo_n "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 $as_echo "$hard_links" >&6; } if test "$hard_links" = no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 $as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } runpath_var= allow_undefined_flag= always_export_symbols=no archive_cmds= archive_expsym_cmds= compiler_needs_object=no enable_shared_with_static_runtimes=no export_dynamic_flag_spec= export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' hardcode_automatic=no hardcode_direct=no hardcode_direct_absolute=no hardcode_libdir_flag_spec= hardcode_libdir_flag_spec_ld= hardcode_libdir_separator= hardcode_minus_L=no hardcode_shlibpath_var=unsupported inherit_rpath=no link_all_deplibs=unknown module_cmds= module_expsym_cmds= old_archive_from_new_cmds= old_archive_from_expsyms_cmds= thread_safe_flag_spec= whole_archive_flag_spec= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list include_expsyms= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ` (' and `)$', so one must not match beginning or # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', # as well as any symbol that contains `d'. exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test "$GCC" != yes; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd*) with_gnu_ld=no ;; linux* | k*bsd*-gnu) link_all_deplibs=no ;; esac ld_shlibs=yes if test "$with_gnu_ld" = yes; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='${wl}' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' export_dynamic_flag_spec='${wl}--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' else whole_archive_flag_spec= fi supports_anon_versioning=no case `$LD -v 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[3-9]*) # On AIX/PPC, the GNU linker is very broken if test "$host_cpu" != ia64; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.9.1, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to modify your PATH *** so that a non-GNU linker is found, and then restart. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' else ld_shlibs=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' allow_undefined_flag=unsupported always_export_symbols=no enable_shared_with_static_runtimes=yes export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file (1st line # is EXPORTS), use it as is; otherwise, prepend... archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs=no fi ;; interix[3-9]*) hardcode_direct=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='${wl}-rpath,$libdir' export_dynamic_flag_spec='${wl}-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test "$host_os" = linux-dietlibc; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test "$tmp_diet" = no then tmp_addflag= tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 whole_archive_flag_spec= tmp_sharedflag='--shared' ;; xl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' compiler_needs_object=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' if test "x$supports_anon_versioning" = xyes; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' fi case $cc_basename in xlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' hardcode_libdir_flag_spec= hardcode_libdir_flag_spec_ld='-rpath $libdir' archive_cmds='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib' if test "x$supports_anon_versioning" = xyes; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else ld_shlibs=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac ;; sunos4*) archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= hardcode_direct=yes hardcode_shlibpath_var=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac if test "$ld_shlibs" = no; then runpath_var= hardcode_libdir_flag_spec= export_dynamic_flag_spec= whole_archive_flag_spec= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) allow_undefined_flag=unsupported always_export_symbols=yes archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix[4-9]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag="" else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to AIX nm, but means don't demangle with GNU nm if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' else export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then aix_use_runtimelinking=yes break fi done ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds='' hardcode_direct=yes hardcode_direct_absolute=yes hardcode_libdir_separator=':' link_all_deplibs=yes file_list_spec='${wl}-f,' if test "$GCC" = yes; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi ;; esac shared_flag='-shared' if test "$aix_use_runtimelinking" = yes; then shared_flag="$shared_flag "'${wl}-G' fi link_all_deplibs=no else # not using gcc if test "$host_cpu" = ia64; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test "$aix_use_runtimelinking" = yes; then shared_flag='${wl}-G' else shared_flag='${wl}-bM:SRE' fi fi fi export_dynamic_flag_spec='${wl}-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. always_export_symbols=yes if test "$aix_use_runtimelinking" = yes; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. allow_undefined_flag='-berok' # Determine the default libpath from the value encoded in an # empty executable. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/ p } }' aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" else if test "$host_cpu" = ia64; then hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' allow_undefined_flag="-z nodefs" archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/ p } }' aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag=' ${wl}-bernotok' allow_undefined_flag=' ${wl}-berok' # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec='$convenience' archive_cmds_need_lc=yes # This is similar to how AIX traditionally builds its shared libraries. archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; bsdi[45]*) export_dynamic_flag_spec=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. old_archive_from_new_cmds='true' # FIXME: Should let the user specify the lib program. old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' fix_srcfile_path='`cygpath -w "$srcfile"`' enable_shared_with_static_runtimes=yes ;; darwin* | rhapsody*) archive_cmds_need_lc=no hardcode_direct=no hardcode_automatic=yes hardcode_shlibpath_var=unsupported whole_archive_flag_spec='' link_all_deplibs=yes allow_undefined_flag="$_lt_dar_allow_undefined" case $cc_basename in ifort*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test "$_lt_dar_can_shared" = "yes"; then output_verbose_link_cmd=echo archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" else ld_shlibs=no fi ;; dgux*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; freebsd1*) ld_shlibs=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; hpux9*) if test "$GCC" = yes; then archive_cmds='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' else archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' fi hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes export_dynamic_flag_spec='${wl}-E' ;; hpux10*) if test "$GCC" = yes -a "$with_gnu_ld" = no; then archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_flag_spec_ld='+b $libdir' hardcode_libdir_separator=: hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes fi ;; hpux11*) if test "$GCC" = yes -a "$with_gnu_ld" = no; then case $host_cpu in hppa*64*) archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac fi if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: case $host_cpu in hppa*64*|ia64*) hardcode_direct=no hardcode_shlibpath_var=no ;; *) hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test "$GCC" = yes; then archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int foo(void) {} _ACEOF if ac_fn_c_try_link "$LINENO"; then : archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS="$save_LDFLAGS" else archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: inherit_rpath=yes link_all_deplibs=yes ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; newsos6) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: hardcode_shlibpath_var=no ;; *nto* | *qnx*) ;; openbsd*) if test -f /usr/libexec/ld.so; then hardcode_direct=yes hardcode_shlibpath_var=no hardcode_direct_absolute=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' hardcode_libdir_flag_spec='${wl}-rpath,$libdir' export_dynamic_flag_spec='${wl}-E' else case $host_os in openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-R$libdir' ;; *) archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='${wl}-rpath,$libdir' ;; esac fi else ld_shlibs=no fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' ;; osf3*) if test "$GCC" = yes; then allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test "$GCC" = yes; then allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi archive_cmds_need_lc='no' hardcode_libdir_separator=: ;; solaris*) no_undefined_flag=' -z defs' if test "$GCC" = yes; then wlarc='${wl}' archive_cmds='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='${wl}' archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi hardcode_libdir_flag_spec='-R$libdir' hardcode_shlibpath_var=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands `-z linker_flag'. GCC discards it without `$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test "$GCC" = yes; then whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' else whole_archive_flag_spec='-z allextract$convenience -z defaultextract' fi ;; esac link_all_deplibs=yes ;; sunos4*) if test "x$host_vendor" = xsequent; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; sysv4) case $host_vendor in sni) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' reload_cmds='$CC -r -o $output$reload_objs' hardcode_direct=no ;; motorola) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' hardcode_shlibpath_var=no ;; sysv4.3*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no export_dynamic_flag_spec='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes ld_shlibs=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag='${wl}-z,text' archive_cmds_need_lc=no hardcode_shlibpath_var=no runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We can NOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag='${wl}-z,text' allow_undefined_flag='${wl}-z,nodefs' archive_cmds_need_lc=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='${wl}-R,$libdir' hardcode_libdir_separator=':' link_all_deplibs=yes export_dynamic_flag_spec='${wl}-Bexport' runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; *) ld_shlibs=no ;; esac if test x$host_vendor = xsni; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) export_dynamic_flag_spec='${wl}-Blargedynsym' ;; esac fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 $as_echo "$ld_shlibs" >&6; } test "$ld_shlibs" = no && can_build_shared=no with_gnu_ld=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc" in x|xyes) # Assume -lc should be added archive_cmds_need_lc=yes if test "$enable_shared" = yes && test "$GCC" = yes; then case $archive_cmds in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 $as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl pic_flag=$lt_prog_compiler_pic compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag allow_undefined_flag= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then archive_cmds_need_lc=no else archive_cmds_need_lc=yes fi allow_undefined_flag=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* { $as_echo "$as_me:${as_lineno-$LINENO}: result: $archive_cmds_need_lc" >&5 $as_echo "$archive_cmds_need_lc" >&6; } ;; esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 $as_echo_n "checking dynamic linker characteristics... " >&6; } if test "$GCC" = yes; then case $host_os in darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; *) lt_awk_arg="/^libraries:/" ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"` if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'` else lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary. lt_tmp_lt_search_path_spec= lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path/$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" else test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk ' BEGIN {RS=" "; FS="/|\n";} { lt_foo=""; lt_count=0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo="/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[lt_foo]++; } if (lt_freq[lt_foo] == 1) { print lt_foo; } }'` sys_lib_search_path_spec=`$ECHO $lt_search_path_spec` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=".so" postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='${libname}${release}${shared_ext}$major' ;; aix[4-9]*) version_type=linux need_lib_prefix=no need_version=no hardcode_into_libs=yes if test "$host_cpu" = ia64; then # AIX 5 supports IA64 library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line `#! .'. This would cause the generated library to # depend on `.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # AIX (on Power*) has no versioning support, so currently we can not hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. if test "$aix_use_runtimelinking" = yes; then # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' else # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='${libname}${release}.a $libname.a' soname_spec='${libname}${release}${shared_ext}$major' fi shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='${libname}${shared_ext}' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=".dll" need_version=no need_lib_prefix=no case $GCC,$host_os in yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*) library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH printed by # mingw gcc, but we are running on Cygwin. Gcc prints its search # path with ; separators, and with drive letters. We can handle the # drive letters (cygwin fileutils understands them), so leave them, # especially as we might pass files found there to a mingw objdump, # which wouldn't understand a cygwinified path. Ahh. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' ;; esac ;; *) library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' ;; esac dynamic_linker='Win32 ld.exe' # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' soname_spec='${libname}${release}${major}$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd1*) dynamic_linker=no ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[123]*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; gnu*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' if test "X$HPUX_IA64_MODE" = X32; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" fi sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555. postinstall_cmds='chmod 555 $lib' ;; interix[3-9]*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test "$lt_cv_prog_gnu_ld" = yes; then version_type=linux else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; # This must be Linux ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Append ld.so.conf contents to the search path if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd*) version_type=sunos sys_lib_dlsearch_path_spec="/usr/lib" need_lib_prefix=no # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. case $host_os in openbsd3.3 | openbsd3.3.*) need_version=yes ;; *) need_version=no ;; esac library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then case $host_os in openbsd2.[89] | openbsd2.[89].*) shlibpath_overrides_runpath=no ;; *) shlibpath_overrides_runpath=yes ;; esac else shlibpath_overrides_runpath=yes fi ;; os2*) libname_spec='$name' shrext_cmds=".dll" need_lib_prefix=no library_names_spec='$libname${shared_ext} $libname.a' dynamic_linker='OS/2 ld.exe' shlibpath_var=LIBPATH ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test "$with_gnu_ld" = yes; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec ;then version_type=linux library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' soname_spec='$libname${shared_ext}.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=freebsd-elf need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test "$with_gnu_ld" = yes; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 $as_echo "$dynamic_linker" >&6; } test "$dynamic_linker" = no && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test "$GCC" = yes; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" fi if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 $as_echo_n "checking how to hardcode library paths into programs... " >&6; } hardcode_action= if test -n "$hardcode_libdir_flag_spec" || test -n "$runpath_var" || test "X$hardcode_automatic" = "Xyes" ; then # We can hardcode non-existent directories. if test "$hardcode_direct" != no && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no && test "$hardcode_minus_L" != no; then # Linking always hardcodes the temporary library directory. hardcode_action=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action=unsupported fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 $as_echo "$hardcode_action" >&6; } if test "$hardcode_action" = relink || test "$inherit_rpath" = yes; then # Fast installation is not supported enable_fast_install=no elif test "$shlibpath_overrides_runpath" = yes || test "$enable_shared" = no; then # Fast installation is not necessary enable_fast_install=needless fi if test "x$enable_dlopen" != xyes; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen="load_add_on" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen="LoadLibrary" lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen="dlopen" lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if test "${ac_cv_lib_dl_dlopen+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = x""yes; then : lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" else lt_cv_dlopen="dyld" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes fi ;; *) ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" if test "x$ac_cv_func_shl_load" = x""yes; then : lt_cv_dlopen="shl_load" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 $as_echo_n "checking for shl_load in -ldld... " >&6; } if test "${ac_cv_lib_dld_shl_load+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char shl_load (); int main () { return shl_load (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_shl_load=yes else ac_cv_lib_dld_shl_load=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 $as_echo "$ac_cv_lib_dld_shl_load" >&6; } if test "x$ac_cv_lib_dld_shl_load" = x""yes; then : lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld" else ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" if test "x$ac_cv_func_dlopen" = x""yes; then : lt_cv_dlopen="dlopen" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if test "${ac_cv_lib_dl_dlopen+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = x""yes; then : lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 $as_echo_n "checking for dlopen in -lsvld... " >&6; } if test "${ac_cv_lib_svld_dlopen+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsvld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_svld_dlopen=yes else ac_cv_lib_svld_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 $as_echo "$ac_cv_lib_svld_dlopen" >&6; } if test "x$ac_cv_lib_svld_dlopen" = x""yes; then : lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 $as_echo_n "checking for dld_link in -ldld... " >&6; } if test "${ac_cv_lib_dld_dld_link+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dld_link (); int main () { return dld_link (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_dld_link=yes else ac_cv_lib_dld_dld_link=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 $as_echo "$ac_cv_lib_dld_dld_link" >&6; } if test "x$ac_cv_lib_dld_dld_link" = x""yes; then : lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld" fi fi fi fi fi fi ;; esac if test "x$lt_cv_dlopen" != xno; then enable_dlopen=yes else enable_dlopen=no fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS="$CPPFLAGS" test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS="$LDFLAGS" wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS="$LIBS" LIBS="$lt_cv_dlopen_libs $LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 $as_echo_n "checking whether a program can dlopen itself... " >&6; } if test "${lt_cv_dlopen_self+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : lt_cv_dlopen_self=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line 11569 "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif void fnord() { int i=42;} int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; esac else : # compilation failed lt_cv_dlopen_self=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 $as_echo "$lt_cv_dlopen_self" >&6; } if test "x$lt_cv_dlopen_self" = xyes; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 $as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } if test "${lt_cv_dlopen_self_static+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : lt_cv_dlopen_self_static=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line 11665 "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif void fnord() { int i=42;} int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; esac else : # compilation failed lt_cv_dlopen_self_static=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 $as_echo "$lt_cv_dlopen_self_static" >&6; } fi CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi striplib= old_striplib= { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 $as_echo_n "checking whether stripping libraries is possible... " >&6; } if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP" ; then striplib="$STRIP -x" old_striplib="$STRIP -S" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; esac fi # Report which library types will actually be built { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 $as_echo_n "checking if libtool supports shared libraries... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 $as_echo "$can_build_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 $as_echo_n "checking whether to build shared libraries... " >&6; } test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[4-9]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 $as_echo "$enable_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 $as_echo_n "checking whether to build static libraries... " >&6; } # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 $as_echo "$enable_static" >&6; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu CC="$lt_save_CC" ac_config_commands="$ac_config_commands libtool" # Only expand once: if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}windres", so it can be a program name with args. set dummy ${ac_tool_prefix}windres; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_RC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$RC"; then ac_cv_prog_RC="$RC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_RC="${ac_tool_prefix}windres" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RC=$ac_cv_prog_RC if test -n "$RC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RC" >&5 $as_echo "$RC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RC"; then ac_ct_RC=$RC # Extract the first word of "windres", so it can be a program name with args. set dummy windres; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_RC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RC"; then ac_cv_prog_ac_ct_RC="$ac_ct_RC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_RC="windres" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RC=$ac_cv_prog_ac_ct_RC if test -n "$ac_ct_RC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RC" >&5 $as_echo "$ac_ct_RC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RC" = x; then RC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RC=$ac_ct_RC fi else RC="$ac_cv_prog_RC" fi # Source file extension for RC test sources. ac_ext=rc # Object file extension for compiled RC test sources. objext=o objext_RC=$objext # Code to be used in simple compile tests lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' # Code to be used in simple link tests lt_simple_link_test_code="$lt_simple_compile_test_code" # ltmain only uses $CC for tagged configurations so make sure $CC is set. # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_GCC=$GCC GCC= CC=${RC-"windres"} compiler=$CC compiler_RC=$CC for cc_temp in $compiler""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` lt_cv_prog_compiler_c_o_RC=yes if test -n "$compiler"; then : fi GCC=$lt_save_GCC ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu CC="$lt_save_CC" for ac_prog in xsltproc do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_XSLTPROC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$XSLTPROC"; then ac_cv_prog_XSLTPROC="$XSLTPROC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_XSLTPROC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi XSLTPROC=$ac_cv_prog_XSLTPROC if test -n "$XSLTPROC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XSLTPROC" >&5 $as_echo "$XSLTPROC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$XSLTPROC" && break done for ac_prog in git do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_GIT+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$GIT"; then ac_cv_prog_GIT="$GIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_GIT="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi GIT=$ac_cv_prog_GIT if test -n "$GIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GIT" >&5 $as_echo "$GIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$GIT" && break done { $as_echo "$as_me:${as_lineno-$LINENO}: checking xsl-stylesheets" >&5 $as_echo_n "checking xsl-stylesheets... " >&6; } 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" || as_fn_error $? "invalid" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${xslstylesheetsdir}" >&5 $as_echo "${xslstylesheetsdir}" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 $as_echo_n "checking for inline... " >&6; } if test "${ac_cv_c_inline+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __cplusplus typedef int foo_t; static $ac_kw foo_t static_foo () {return 0; } $ac_kw foo_t foo () {return 0; } #endif _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_inline=$ac_kw fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext test "$ac_cv_c_inline" != no && break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 $as_echo "$ac_cv_c_inline" >&6; } case $ac_cv_c_inline in inline | yes) ;; *) case $ac_cv_c_inline in no) ac_val=;; *) ac_val=$ac_cv_c_inline;; esac cat >>confdefs.h <<_ACEOF #ifndef __cplusplus #define inline $ac_val #endif _ACEOF ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if test "${ac_cv_header_stdc+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5 $as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; } if test "${ac_cv_header_sys_wait_h+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifndef WEXITSTATUS # define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8) #endif #ifndef WIFEXITED # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif int main () { int s; wait (&s); s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_sys_wait_h=yes else ac_cv_header_sys_wait_h=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5 $as_echo "$ac_cv_header_sys_wait_h" >&6; } if test $ac_cv_header_sys_wait_h = yes; then $as_echo "#define HAVE_SYS_WAIT_H 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable assertions" >&5 $as_echo_n "checking whether to enable assertions... " >&6; } # Check whether --enable-assert was given. if test "${enable_assert+set}" = set; then : enableval=$enable_assert; ac_enable_assert=$enableval if test "x$enableval" = xno; then : $as_echo "#define NDEBUG 1" >>confdefs.h elif test "x$enableval" != xyes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: invalid argument supplied to --enable-assert" >&5 $as_echo "$as_me: WARNING: invalid argument supplied to --enable-assert" >&2;} ac_enable_assert=yes fi else ac_enable_assert=yes fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_enable_assert" >&5 $as_echo "$ac_enable_assert" >&6; } for ac_header in \ errno.h fcntl.h malloc.h stdlib.h \ inttypes.h string.h strings.h \ sys/time.h unistd.h getopt.h sys/mman.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 $as_echo_n "checking for an ANSI C-conforming const... " >&6; } if test "${ac_cv_c_const+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { /* FIXME: Include the comments suggested by Paul. */ #ifndef __cplusplus /* Ultrix mips cc rejects this. */ typedef int charset[2]; const charset cs; /* SunOS 4.1.1 cc rejects this. */ char const *const *pcpcc; char **ppc; /* NEC SVR4.0.2 mips cc rejects this. */ struct point {int x, y;}; static struct point const zero = {0,0}; /* AIX XL C 1.02.0.0 rejects this. It does not let you subtract one const X* pointer from another in an arm of an if-expression whose if-part is not a constant expression */ const char *g = "string"; pcpcc = &g + (g ? g-g : 0); /* HPUX 7.0 cc rejects these. */ ++pcpcc; ppc = (char**) pcpcc; pcpcc = (char const *const *) ppc; { /* SCO 3.2v4 cc rejects this. */ char *t; char const *s = 0 ? (char *) 0 : (char const *) 0; *t++ = 0; if (s) return 0; } { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ int x[] = {25, 17}; const int *foo = &x[0]; ++foo; } { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ typedef const int *iptr; iptr p = 0; ++p; } { /* AIX XL C 1.02.0.0 rejects this saying "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ struct s { int j; const int *ap[3]; }; struct s *b; b->j = 5; } { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ const int foo = 10; if (!foo) return 0; } return !cs[0] && !zero.x; #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_const=yes else ac_cv_c_const=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 $as_echo "$ac_cv_c_const" >&6; } if test $ac_cv_c_const = no; then $as_echo "#define const /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5 $as_echo_n "checking for uid_t in sys/types.h... " >&6; } if test "${ac_cv_type_uid_t+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "uid_t" >/dev/null 2>&1; then : ac_cv_type_uid_t=yes else ac_cv_type_uid_t=no fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5 $as_echo "$ac_cv_type_uid_t" >&6; } if test $ac_cv_type_uid_t = no; then $as_echo "#define uid_t int" >>confdefs.h $as_echo "#define gid_t int" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" if test "x$ac_cv_type_size_t" = x""yes; then : else cat >>confdefs.h <<_ACEOF #define size_t unsigned int _ACEOF fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5 $as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; } if test "${ac_cv_header_time+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { if ((struct tm *) 0) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_time=yes else ac_cv_header_time=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5 $as_echo "$ac_cv_header_time" >&6; } if test $ac_cv_header_time = yes; then $as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for error_at_line" >&5 $as_echo_n "checking for error_at_line... " >&6; } if test "${ac_cv_lib_error_at_line+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { error_at_line (0, 0, "", 0, "an error occurred"); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_error_at_line=yes else ac_cv_lib_error_at_line=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_error_at_line" >&5 $as_echo "$ac_cv_lib_error_at_line" >&6; } if test $ac_cv_lib_error_at_line = no; then case " $LIBOBJS " in *" error.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS error.$ac_objext" ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether lstat correctly handles trailing slash" >&5 $as_echo_n "checking whether lstat correctly handles trailing slash... " >&6; } if test "${ac_cv_func_lstat_dereferences_slashed_symlink+set}" = set; then : $as_echo_n "(cached) " >&6 else rm -f conftest.sym conftest.file echo >conftest.file if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then if test "$cross_compiling" = yes; then : ac_cv_func_lstat_dereferences_slashed_symlink=no else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { struct stat sbuf; /* Linux will dereference the symlink and fail, as required by POSIX. That is better in the sense that it means we will not have to compile and use the lstat wrapper. */ return lstat ("conftest.sym/", &sbuf) == 0; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_func_lstat_dereferences_slashed_symlink=yes else ac_cv_func_lstat_dereferences_slashed_symlink=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi else # If the `ln -s' command failed, then we probably don't even # have an lstat function. ac_cv_func_lstat_dereferences_slashed_symlink=no fi rm -f conftest.sym conftest.file fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5 $as_echo "$ac_cv_func_lstat_dereferences_slashed_symlink" >&6; } test $ac_cv_func_lstat_dereferences_slashed_symlink = yes && cat >>confdefs.h <<_ACEOF #define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 _ACEOF if test "x$ac_cv_func_lstat_dereferences_slashed_symlink" = xno; then case " $LIBOBJS " in *" lstat.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS lstat.$ac_objext" ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stat accepts an empty string" >&5 $as_echo_n "checking whether stat accepts an empty string... " >&6; } if test "${ac_cv_func_stat_empty_string_bug+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : ac_cv_func_stat_empty_string_bug=yes else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { struct stat sbuf; return stat ("", &sbuf) == 0; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_func_stat_empty_string_bug=no else ac_cv_func_stat_empty_string_bug=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_stat_empty_string_bug" >&5 $as_echo "$ac_cv_func_stat_empty_string_bug" >&6; } if test $ac_cv_func_stat_empty_string_bug = yes; then case " $LIBOBJS " in *" stat.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS stat.$ac_objext" ;; esac cat >>confdefs.h <<_ACEOF #define HAVE_STAT_EMPTY_STRING_BUG 1 _ACEOF fi for ac_func in vprintf do : ac_fn_c_check_func "$LINENO" "vprintf" "ac_cv_func_vprintf" if test "x$ac_cv_func_vprintf" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_VPRINTF 1 _ACEOF ac_fn_c_check_func "$LINENO" "_doprnt" "ac_cv_func__doprnt" if test "x$ac_cv_func__doprnt" = x""yes; then : $as_echo "#define HAVE_DOPRNT 1" >>confdefs.h fi fi done for ac_func in \ getpass gettimeofday memset mkdir \ strdup strerror getopt_long getopt_long_only \ strlcpy strlcat do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of void *" >&5 $as_echo_n "checking size of void *... " >&6; } if test "${ac_cv_sizeof_void_p+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (void *))" "ac_cv_sizeof_void_p" "$ac_includes_default"; then : else if test "$ac_cv_type_void_p" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (void *) See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_void_p=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p" >&5 $as_echo "$ac_cv_sizeof_void_p" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_VOID_P $ac_cv_sizeof_void_p _ACEOF if test "${ac_cv_sizeof_void_p}" = 8; then LIBRARY_BITNESS="64" else LIBRARY_BITNESS="32" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for socket in -lsocket" >&5 $as_echo_n "checking for socket in -lsocket... " >&6; } if test "${ac_cv_lib_socket_socket+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char socket (); int main () { return socket (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_socket_socket=yes else ac_cv_lib_socket_socket=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_socket" >&5 $as_echo "$ac_cv_lib_socket_socket" >&6; } if test "x$ac_cv_lib_socket_socket" = x""yes; then : LIBS="${LIBS} -lsocket" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for res_query in -lresolv" >&5 $as_echo_n "checking for res_query in -lresolv... " >&6; } if test "${ac_cv_lib_resolv_res_query+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lresolv $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char res_query (); int main () { return res_query (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_resolv_res_query=yes else ac_cv_lib_resolv_res_query=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_resolv_res_query" >&5 $as_echo "$ac_cv_lib_resolv_res_query" >&6; } if test "x$ac_cv_lib_resolv_res_query" = x""yes; then : LIBS="${LIBS} -lresolv" fi fi if test "${WIN32}" = "no"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if test "${ac_cv_lib_dl_dlopen+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBDL 1 _ACEOF LIBS="-ldl $LIBS" else as_fn_error $? "libdl required" "$LINENO" 5 fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu 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 True64 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_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS" >&5 $as_echo_n "checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pthread_join (); int main () { return pthread_join (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_pthread_ok=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 $as_echo "$ax_pthread_ok" >&6; } if test x"$ax_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" 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 starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -kthread lthread -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) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case ${host_os} in 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. (We need to link with -pthreads/-mt/ # -lpthread.) (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 just look for -pthreads and -lpthread first: ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" ;; darwin*) ax_pthread_flags="-pthread $ax_pthread_flags" ;; esac if test x"$ax_pthread_ok" = xno; then for flag in $ax_pthread_flags; do case $flag in none) { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5 $as_echo_n "checking whether pthreads work without any flags... " >&6; } ;; -*) { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $flag" >&5 $as_echo_n "checking whether pthreads work with $flag... " >&6; } PTHREAD_CFLAGS="$flag" ;; pthread-config) # Extract the first word of "pthread-config", so it can be a program name with args. set dummy pthread-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ax_pthread_config+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ax_pthread_config"; then ac_cv_prog_ax_pthread_config="$ax_pthread_config" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ax_pthread_config="yes" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_prog_ax_pthread_config" && ac_cv_prog_ax_pthread_config="no" fi fi ax_pthread_config=$ac_cv_prog_ax_pthread_config if test -n "$ax_pthread_config"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_config" >&5 $as_echo "$ax_pthread_config" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test x"$ax_pthread_config" = xno; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$flag" >&5 $as_echo_n "checking for the pthreads library -l$flag... " >&6; } PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # 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. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include static void routine(void *a) { a = 0; } static void *start_routine(void *a) { return a; } int main () { 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) /* ; */ ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_pthread_ok=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 $as_echo "$ax_pthread_ok" >&6; } if test "x$ax_pthread_ok" = xyes; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$ax_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5 $as_echo_n "checking for joinable pthread attribute... " >&6; } attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { int attr = $attr; return attr /* ; */ ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : attr_name=$attr; break fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext done { $as_echo "$as_me:${as_lineno-$LINENO}: result: $attr_name" >&5 $as_echo "$attr_name" >&6; } if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then cat >>confdefs.h <<_ACEOF #define PTHREAD_CREATE_JOINABLE $attr_name _ACEOF fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if more special flags are required for pthreads" >&5 $as_echo_n "checking if more special flags are required for pthreads... " >&6; } flag=no case ${host_os} in aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; osf* | hpux*) flag="-D_REENTRANT";; solaris*) if test "$GCC" = "yes"; then flag="-D_REENTRANT" else flag="-mt -D_REENTRANT" fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${flag}" >&5 $as_echo "${flag}" >&6; } if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PTHREAD_PRIO_INHERIT" >&5 $as_echo_n "checking for PTHREAD_PRIO_INHERIT... " >&6; } if test "${ax_cv_PTHREAD_PRIO_INHERIT+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { int i = PTHREAD_PRIO_INHERIT; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_cv_PTHREAD_PRIO_INHERIT=yes else ax_cv_PTHREAD_PRIO_INHERIT=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_PRIO_INHERIT" >&5 $as_echo "$ax_cv_PTHREAD_PRIO_INHERIT" >&6; } if test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"; then : $as_echo "#define HAVE_PTHREAD_PRIO_INHERIT 1" >>confdefs.h fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with xlc_r or cc_r if test x"$GCC" != xyes; then for ac_prog in xlc_r cc_r do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_PTHREAD_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$PTHREAD_CC"; then ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_PTHREAD_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi PTHREAD_CC=$ac_cv_prog_PTHREAD_CC if test -n "$PTHREAD_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5 $as_echo "$PTHREAD_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$PTHREAD_CC" && break done test -n "$PTHREAD_CC" || PTHREAD_CC="${CC}" else PTHREAD_CC=$CC fi else PTHREAD_CC="$CC" fi # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$ax_pthread_ok" = xyes; then $as_echo "#define HAVE_PTHREAD 1" >>confdefs.h : else ax_pthread_ok=no as_fn_error $? "POSIX thread support required" "$LINENO" 5 fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu CC="${PTHREAD_CC}" fi if test "${enable_minidriver}" = "yes"; then ac_fn_c_check_header_mongrel "$LINENO" "cardmod.h" "ac_cv_header_cardmod_h" "$ac_includes_default" if test "x$ac_cv_header_cardmod_h" = x""yes; then : else as_fn_error $? "cardmod.h is not found and required for minidriver" "$LINENO" 5 fi $as_echo "#define ENABLE_MINIDRIVER 1" >>confdefs.h fi if test "${enable_sm}" = "yes"; then $as_echo "#define ENABLE_SM 1" >>confdefs.h case "${host}" in *-mingw*|*-winnt*|*-cygwin*) DEFAULT_SM_MODULE="smm-local.dll" ;; *) DEFAULT_SM_MODULE="libsmm-local.so.3" ;; esac cat >>confdefs.h <<_ACEOF #define DEFAULT_SM_MODULE "${DEFAULT_SM_MODULE}" _ACEOF fi if test -z "${ZLIB_LIBS}"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inflate in -lz" >&5 $as_echo_n "checking for inflate in -lz... " >&6; } if test "${ac_cv_lib_z_inflate+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lz $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char inflate (); int main () { return inflate (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_z_inflate=yes else ac_cv_lib_z_inflate=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_inflate" >&5 $as_echo "$ac_cv_lib_z_inflate" >&6; } if test "x$ac_cv_lib_z_inflate" = x""yes; then : ZLIB_LIBS="-lz" fi fi saved_CFLAGS="${CFLAGS}" CFLAGS="${CFLAGS} ${ZLIB_CFLAGS}" for ac_header in zlib.h do : ac_fn_c_check_header_mongrel "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default" if test "x$ac_cv_header_zlib_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_ZLIB_H 1 _ACEOF fi done 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 $as_echo "#define ENABLE_ZLIB 1" >>confdefs.h else as_fn_error $? "zlib linkage required, but no zlib was found" "$LINENO" 5 fi fi if test -z "${READLINE_LIBS}"; then for l in "" -lncurses -ltermcap; do unset ac_cv_lib_readline_readline { $as_echo "$as_me:${as_lineno-$LINENO}: checking for readline in -lreadline" >&5 $as_echo_n "checking for readline in -lreadline... " >&6; } if test "${ac_cv_lib_readline_readline+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lreadline "${l}" $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char readline (); int main () { return readline (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_readline_readline=yes else ac_cv_lib_readline_readline=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_readline_readline" >&5 $as_echo "$ac_cv_lib_readline_readline" >&6; } if test "x$ac_cv_lib_readline_readline" = x""yes; then : READLINE_LIBS="-lreadline ${l}" fi test -n "${READLINE_LIBS}" && break; done fi saved_CFLAGS="${CFLAGS}" CFLAGS="${CFLAGS} ${READLINE_CFLAGS}" for ac_header in readline/readline.h do : ac_fn_c_check_header_mongrel "$LINENO" "readline/readline.h" "ac_cv_header_readline_readline_h" "$ac_includes_default" if test "x$ac_cv_header_readline_readline_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_READLINE_READLINE_H 1 _ACEOF fi done 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 $as_echo "#define ENABLE_READLINE 1" >>confdefs.h else as_fn_error $? "readline linkage required, but no readline was found" "$LINENO" 5 fi fi pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OPENSSL" >&5 $as_echo_n "checking for OPENSSL... " >&6; } if test -n "$OPENSSL_CFLAGS"; then pkg_cv_OPENSSL_CFLAGS="$OPENSSL_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcrypto >= 0.9.7\""; } >&5 ($PKG_CONFIG --exists --print-errors "libcrypto >= 0.9.7") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_OPENSSL_CFLAGS=`$PKG_CONFIG --cflags "libcrypto >= 0.9.7" 2>/dev/null` else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$OPENSSL_LIBS"; then pkg_cv_OPENSSL_LIBS="$OPENSSL_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcrypto >= 0.9.7\""; } >&5 ($PKG_CONFIG --exists --print-errors "libcrypto >= 0.9.7") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_OPENSSL_LIBS=`$PKG_CONFIG --libs "libcrypto >= 0.9.7" 2>/dev/null` else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then OPENSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libcrypto >= 0.9.7" 2>&1` else OPENSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors "libcrypto >= 0.9.7" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$OPENSSL_PKG_ERRORS" >&5 pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OPENSSL" >&5 $as_echo_n "checking for OPENSSL... " >&6; } if test -n "$OPENSSL_CFLAGS"; then pkg_cv_OPENSSL_CFLAGS="$OPENSSL_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl >= 0.9.7\""; } >&5 ($PKG_CONFIG --exists --print-errors "openssl >= 0.9.7") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_OPENSSL_CFLAGS=`$PKG_CONFIG --cflags "openssl >= 0.9.7" 2>/dev/null` else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$OPENSSL_LIBS"; then pkg_cv_OPENSSL_LIBS="$OPENSSL_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl >= 0.9.7\""; } >&5 ($PKG_CONFIG --exists --print-errors "openssl >= 0.9.7") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_OPENSSL_LIBS=`$PKG_CONFIG --libs "openssl >= 0.9.7" 2>/dev/null` else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then OPENSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "openssl >= 0.9.7" 2>&1` else OPENSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors "openssl >= 0.9.7" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$OPENSSL_PKG_ERRORS" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for RSA_version in -lcrypto" >&5 $as_echo_n "checking for RSA_version in -lcrypto... " >&6; } if test "${ac_cv_lib_crypto_RSA_version+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcrypto $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char RSA_version (); int main () { return RSA_version (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_crypto_RSA_version=yes else ac_cv_lib_crypto_RSA_version=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypto_RSA_version" >&5 $as_echo "$ac_cv_lib_crypto_RSA_version" >&6; } if test "x$ac_cv_lib_crypto_RSA_version" = x""yes; then : have_openssl="yes" OPENSSL_LIBS="-lcrypto" else have_openssl="no" fi elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for RSA_version in -lcrypto" >&5 $as_echo_n "checking for RSA_version in -lcrypto... " >&6; } if test "${ac_cv_lib_crypto_RSA_version+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcrypto $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char RSA_version (); int main () { return RSA_version (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_crypto_RSA_version=yes else ac_cv_lib_crypto_RSA_version=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypto_RSA_version" >&5 $as_echo "$ac_cv_lib_crypto_RSA_version" >&6; } if test "x$ac_cv_lib_crypto_RSA_version" = x""yes; then : have_openssl="yes" OPENSSL_LIBS="-lcrypto" else have_openssl="no" fi else OPENSSL_CFLAGS=$pkg_cv_OPENSSL_CFLAGS OPENSSL_LIBS=$pkg_cv_OPENSSL_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } have_openssl="yes" fi elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OPENSSL" >&5 $as_echo_n "checking for OPENSSL... " >&6; } if test -n "$OPENSSL_CFLAGS"; then pkg_cv_OPENSSL_CFLAGS="$OPENSSL_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl >= 0.9.7\""; } >&5 ($PKG_CONFIG --exists --print-errors "openssl >= 0.9.7") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_OPENSSL_CFLAGS=`$PKG_CONFIG --cflags "openssl >= 0.9.7" 2>/dev/null` else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$OPENSSL_LIBS"; then pkg_cv_OPENSSL_LIBS="$OPENSSL_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl >= 0.9.7\""; } >&5 ($PKG_CONFIG --exists --print-errors "openssl >= 0.9.7") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_OPENSSL_LIBS=`$PKG_CONFIG --libs "openssl >= 0.9.7" 2>/dev/null` else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then OPENSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "openssl >= 0.9.7" 2>&1` else OPENSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors "openssl >= 0.9.7" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$OPENSSL_PKG_ERRORS" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for RSA_version in -lcrypto" >&5 $as_echo_n "checking for RSA_version in -lcrypto... " >&6; } if test "${ac_cv_lib_crypto_RSA_version+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcrypto $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char RSA_version (); int main () { return RSA_version (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_crypto_RSA_version=yes else ac_cv_lib_crypto_RSA_version=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypto_RSA_version" >&5 $as_echo "$ac_cv_lib_crypto_RSA_version" >&6; } if test "x$ac_cv_lib_crypto_RSA_version" = x""yes; then : have_openssl="yes" OPENSSL_LIBS="-lcrypto" else have_openssl="no" fi elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for RSA_version in -lcrypto" >&5 $as_echo_n "checking for RSA_version in -lcrypto... " >&6; } if test "${ac_cv_lib_crypto_RSA_version+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcrypto $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char RSA_version (); int main () { return RSA_version (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_crypto_RSA_version=yes else ac_cv_lib_crypto_RSA_version=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypto_RSA_version" >&5 $as_echo "$ac_cv_lib_crypto_RSA_version" >&6; } if test "x$ac_cv_lib_crypto_RSA_version" = x""yes; then : have_openssl="yes" OPENSSL_LIBS="-lcrypto" else have_openssl="no" fi else OPENSSL_CFLAGS=$pkg_cv_OPENSSL_CFLAGS OPENSSL_LIBS=$pkg_cv_OPENSSL_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } have_openssl="yes" fi else OPENSSL_CFLAGS=$pkg_cv_OPENSSL_CFLAGS OPENSSL_LIBS=$pkg_cv_OPENSSL_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } have_openssl="yes" fi case "${enable_openssl}" in no) have_openssl="no" ;; detect) 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 $as_echo "#define ENABLE_OPENSSL 1" >>confdefs.h else as_fn_error $? "OpenSSL linkage required, but no OpenSSL was found" "$LINENO" 5 fi fi if test "${enable_openct}" = "yes"; then pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OPENCT" >&5 $as_echo_n "checking for OPENCT... " >&6; } if test -n "$OPENCT_CFLAGS"; then pkg_cv_OPENCT_CFLAGS="$OPENCT_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libopenct\""; } >&5 ($PKG_CONFIG --exists --print-errors "libopenct") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_OPENCT_CFLAGS=`$PKG_CONFIG --cflags "libopenct" 2>/dev/null` else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$OPENCT_LIBS"; then pkg_cv_OPENCT_LIBS="$OPENCT_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libopenct\""; } >&5 ($PKG_CONFIG --exists --print-errors "libopenct") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_OPENCT_LIBS=`$PKG_CONFIG --libs "libopenct" 2>/dev/null` else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then OPENCT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libopenct" 2>&1` else OPENCT_PKG_ERRORS=`$PKG_CONFIG --print-errors "libopenct" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$OPENCT_PKG_ERRORS" >&5 as_fn_error $? "openct requested but not available" "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "openct requested but not available" "$LINENO" 5 else OPENCT_CFLAGS=$pkg_cv_OPENCT_CFLAGS OPENCT_LIBS=$pkg_cv_OPENCT_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } $as_echo "#define ENABLE_OPENCT 1" >>confdefs.h fi fi if test "${enable_ctapi}" = "yes"; then $as_echo "#define ENABLE_CTAPI 1" >>confdefs.h fi if test "${enable_pcsc}" = "yes"; then if test "${WIN32}" != "yes"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libpcsclite\""; } >&5 ($PKG_CONFIG --exists --print-errors "libpcsclite") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PCSC" >&5 $as_echo_n "checking for PCSC... " >&6; } if test -n "$PCSC_CFLAGS"; then pkg_cv_PCSC_CFLAGS="$PCSC_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libpcsclite\""; } >&5 ($PKG_CONFIG --exists --print-errors "libpcsclite") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_PCSC_CFLAGS=`$PKG_CONFIG --cflags "libpcsclite" 2>/dev/null` else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$PCSC_LIBS"; then pkg_cv_PCSC_LIBS="$PCSC_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libpcsclite\""; } >&5 ($PKG_CONFIG --exists --print-errors "libpcsclite") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_PCSC_LIBS=`$PKG_CONFIG --libs "libpcsclite" 2>/dev/null` else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then PCSC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libpcsclite" 2>&1` else PCSC_PKG_ERRORS=`$PKG_CONFIG --print-errors "libpcsclite" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$PCSC_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (libpcsclite) were not met: $PCSC_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables PCSC_CFLAGS and PCSC_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables PCSC_CFLAGS and PCSC_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5 ; } else PCSC_CFLAGS=$pkg_cv_PCSC_CFLAGS PCSC_LIBS=$pkg_cv_PCSC_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi fi if test -z "${PCSC_CFLAGS}"; then case "${host}" in *-*-darwin*) PCSC_CFLAGS="-I/System/Library/Frameworks/PCSC.framework/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. for ac_header in winscard.h do : ac_fn_c_check_header_mongrel "$LINENO" "winscard.h" "ac_cv_header_winscard_h" "$ac_includes_default" if test "x$ac_cv_header_winscard_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_WINSCARD_H 1 _ACEOF else test "${WIN32}" != "yes" && as_fn_error $? "winscard.h is required for pcsc" "$LINENO" 5 fi done 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 cat >>confdefs.h <<_ACEOF #define DEFAULT_PCSC_PROVIDER "${DEFAULT_PCSC_PROVIDER}" _ACEOF $as_echo "#define ENABLE_PCSC 1" >>confdefs.h 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 { $as_echo "$as_me:${as_lineno-$LINENO}: checking XSLTPROC requirement" >&5 $as_echo_n "checking XSLTPROC requirement... " >&6; } test -n "${XSLTPROC}" || as_fn_error $? "Missing XSLTPROC" "$LINENO" 5 test "${xslstylesheetsdir}" != "no" || as_fn_error $? "Missing xslstylesheetsdir" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 $as_echo "ok" >&6; } fi OPENSC_FEATURES="" 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_ctapi}" = "yes"; then OPENSC_FEATURES="${OPENSC_FEATURES} ctapi" fi cat >>confdefs.h <<_ACEOF #define OPENSC_VERSION_MAJOR ${OPENSC_VERSION_MAJOR} _ACEOF cat >>confdefs.h <<_ACEOF #define OPENSC_VERSION_MINOR ${OPENSC_VERSION_MINOR} _ACEOF cat >>confdefs.h <<_ACEOF #define OPENSC_VERSION_FIX ${OPENSC_VERSION_FIX} _ACEOF cat >>confdefs.h <<_ACEOF #define OPENSC_FEATURES "${OPENSC_FEATURES}" _ACEOF pkcs11dir="\$(libdir)/pkcs11" if test "${enable_man}" = "yes"; then ENABLE_MAN_TRUE= ENABLE_MAN_FALSE='#' else ENABLE_MAN_TRUE='#' ENABLE_MAN_FALSE= fi if test "${enable_zlib}" = "yes"; then ENABLE_ZLIB_TRUE= ENABLE_ZLIB_FALSE='#' else ENABLE_ZLIB_TRUE='#' ENABLE_ZLIB_FALSE= fi if test "${enable_readline}" = "yes"; then ENABLE_READLINE_TRUE= ENABLE_READLINE_FALSE='#' else ENABLE_READLINE_TRUE='#' ENABLE_READLINE_FALSE= fi if test "${enable_openssl}" = "yes"; then ENABLE_OPENSSL_TRUE= ENABLE_OPENSSL_FALSE='#' else ENABLE_OPENSSL_TRUE='#' ENABLE_OPENSSL_FALSE= fi if test "${enable_openct}" = "yes"; then ENABLE_OPENCT_TRUE= ENABLE_OPENCT_FALSE='#' else ENABLE_OPENCT_TRUE='#' ENABLE_OPENCT_FALSE= fi if test "${enable_doc}" = "yes"; then ENABLE_DOC_TRUE= ENABLE_DOC_FALSE='#' else ENABLE_DOC_TRUE='#' ENABLE_DOC_FALSE= fi if test "${WIN32}" = "yes"; then WIN32_TRUE= WIN32_FALSE='#' else WIN32_TRUE='#' WIN32_FALSE= fi if test "${CYGWIN}" = "yes"; then CYGWIN_TRUE= CYGWIN_FALSE='#' else CYGWIN_TRUE='#' CYGWIN_FALSE= fi if test "${enable_minidriver}" = "yes"; then ENABLE_MINIDRIVER_TRUE= ENABLE_MINIDRIVER_FALSE='#' else ENABLE_MINIDRIVER_TRUE='#' ENABLE_MINIDRIVER_FALSE= fi if test "${enable_sm}" = "yes"; then ENABLE_SM_TRUE= ENABLE_SM_FALSE='#' else ENABLE_SM_TRUE='#' ENABLE_SM_FALSE= fi if test "${enable_pedantic}" = "yes"; then enable_strict="yes"; CFLAGS="${CFLAGS} -pedantic" fi if test "${enable_strict}" = "yes"; then CFLAGS="${CFLAGS} -Wall -Wextra" fi if test "$GCC" = "yes"; then # This should be resolved not ignored. CFLAGS="-fno-strict-aliasing ${CFLAGS}" fi ac_config_files="$ac_config_files Makefile doc/Makefile doc/tools/Makefile etc/Makefile src/Makefile src/common/Makefile src/libopensc/Makefile src/libopensc/libopensc.pc src/pkcs11/Makefile src/pkcs15init/Makefile src/scconf/Makefile src/tests/Makefile src/tests/regression/Makefile src/tools/Makefile src/sm/Makefile src/minidriver/Makefile src/minidriver/opensc-minidriver.inf win32/Makefile win32/versioninfo.rc win32/winconfig.h win32/OpenSC.iss win32/OpenSC.wxs MacOSX/Makefile MacOSX/build-package MacOSX/10.5/resources/ReadMe.html MacOSX/10.6/resources/ReadMe.html" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then test "x$cache_file" != "x/dev/null" && { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} cat confcache >$cache_file else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' else am__EXEEXT_TRUE='#' am__EXEEXT_FALSE= fi if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_MAN_TRUE}" && test -z "${ENABLE_MAN_FALSE}"; then as_fn_error $? "conditional \"ENABLE_MAN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_ZLIB_TRUE}" && test -z "${ENABLE_ZLIB_FALSE}"; then as_fn_error $? "conditional \"ENABLE_ZLIB\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_READLINE_TRUE}" && test -z "${ENABLE_READLINE_FALSE}"; then as_fn_error $? "conditional \"ENABLE_READLINE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_OPENSSL_TRUE}" && test -z "${ENABLE_OPENSSL_FALSE}"; then as_fn_error $? "conditional \"ENABLE_OPENSSL\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_OPENCT_TRUE}" && test -z "${ENABLE_OPENCT_FALSE}"; then as_fn_error $? "conditional \"ENABLE_OPENCT\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_DOC_TRUE}" && test -z "${ENABLE_DOC_FALSE}"; then as_fn_error $? "conditional \"ENABLE_DOC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WIN32_TRUE}" && test -z "${WIN32_FALSE}"; then as_fn_error $? "conditional \"WIN32\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${CYGWIN_TRUE}" && test -z "${CYGWIN_FALSE}"; then as_fn_error $? "conditional \"CYGWIN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_MINIDRIVER_TRUE}" && test -z "${ENABLE_MINIDRIVER_FALSE}"; then as_fn_error $? "conditional \"ENABLE_MINIDRIVER\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_SM_TRUE}" && test -z "${ENABLE_SM_FALSE}"; then as_fn_error $? "conditional \"ENABLE_SM\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi : ${CONFIG_STATUS=./config.status} ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi if test -x / >/dev/null 2>&1; then as_test_x='test -x' else if ls -dL / >/dev/null 2>&1; then as_ls_L_option=L else as_ls_L_option= fi as_test_x=' eval sh -c '\'' if test -d "$1"; then test -d "$1/."; else case $1 in #( -*)set "./$1";; esac; case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( ???[sx]*):;;*)false;;esac;fi '\'' sh ' fi as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by opensc $as_me 0.13.0, which was generated by GNU Autoconf 2.67. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" config_commands="$ac_config_commands" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Configuration commands: $config_commands Report bugs to the package provider." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ opensc config.status 0.13.0 configured by $0, generated by GNU Autoconf 2.67, with options \\"\$ac_cs_config\\" Copyright (C) 2010 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' macro_version='`$ECHO "X$macro_version" | $Xsed -e "$delay_single_quote_subst"`' macro_revision='`$ECHO "X$macro_revision" | $Xsed -e "$delay_single_quote_subst"`' AS='`$ECHO "X$AS" | $Xsed -e "$delay_single_quote_subst"`' DLLTOOL='`$ECHO "X$DLLTOOL" | $Xsed -e "$delay_single_quote_subst"`' OBJDUMP='`$ECHO "X$OBJDUMP" | $Xsed -e "$delay_single_quote_subst"`' enable_shared='`$ECHO "X$enable_shared" | $Xsed -e "$delay_single_quote_subst"`' enable_static='`$ECHO "X$enable_static" | $Xsed -e "$delay_single_quote_subst"`' pic_mode='`$ECHO "X$pic_mode" | $Xsed -e "$delay_single_quote_subst"`' enable_fast_install='`$ECHO "X$enable_fast_install" | $Xsed -e "$delay_single_quote_subst"`' host_alias='`$ECHO "X$host_alias" | $Xsed -e "$delay_single_quote_subst"`' host='`$ECHO "X$host" | $Xsed -e "$delay_single_quote_subst"`' host_os='`$ECHO "X$host_os" | $Xsed -e "$delay_single_quote_subst"`' build_alias='`$ECHO "X$build_alias" | $Xsed -e "$delay_single_quote_subst"`' build='`$ECHO "X$build" | $Xsed -e "$delay_single_quote_subst"`' build_os='`$ECHO "X$build_os" | $Xsed -e "$delay_single_quote_subst"`' SED='`$ECHO "X$SED" | $Xsed -e "$delay_single_quote_subst"`' Xsed='`$ECHO "X$Xsed" | $Xsed -e "$delay_single_quote_subst"`' GREP='`$ECHO "X$GREP" | $Xsed -e "$delay_single_quote_subst"`' EGREP='`$ECHO "X$EGREP" | $Xsed -e "$delay_single_quote_subst"`' FGREP='`$ECHO "X$FGREP" | $Xsed -e "$delay_single_quote_subst"`' LD='`$ECHO "X$LD" | $Xsed -e "$delay_single_quote_subst"`' NM='`$ECHO "X$NM" | $Xsed -e "$delay_single_quote_subst"`' LN_S='`$ECHO "X$LN_S" | $Xsed -e "$delay_single_quote_subst"`' max_cmd_len='`$ECHO "X$max_cmd_len" | $Xsed -e "$delay_single_quote_subst"`' ac_objext='`$ECHO "X$ac_objext" | $Xsed -e "$delay_single_quote_subst"`' exeext='`$ECHO "X$exeext" | $Xsed -e "$delay_single_quote_subst"`' lt_unset='`$ECHO "X$lt_unset" | $Xsed -e "$delay_single_quote_subst"`' lt_SP2NL='`$ECHO "X$lt_SP2NL" | $Xsed -e "$delay_single_quote_subst"`' lt_NL2SP='`$ECHO "X$lt_NL2SP" | $Xsed -e "$delay_single_quote_subst"`' reload_flag='`$ECHO "X$reload_flag" | $Xsed -e "$delay_single_quote_subst"`' reload_cmds='`$ECHO "X$reload_cmds" | $Xsed -e "$delay_single_quote_subst"`' deplibs_check_method='`$ECHO "X$deplibs_check_method" | $Xsed -e "$delay_single_quote_subst"`' file_magic_cmd='`$ECHO "X$file_magic_cmd" | $Xsed -e "$delay_single_quote_subst"`' AR='`$ECHO "X$AR" | $Xsed -e "$delay_single_quote_subst"`' AR_FLAGS='`$ECHO "X$AR_FLAGS" | $Xsed -e "$delay_single_quote_subst"`' STRIP='`$ECHO "X$STRIP" | $Xsed -e "$delay_single_quote_subst"`' RANLIB='`$ECHO "X$RANLIB" | $Xsed -e "$delay_single_quote_subst"`' old_postinstall_cmds='`$ECHO "X$old_postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`' old_postuninstall_cmds='`$ECHO "X$old_postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`' old_archive_cmds='`$ECHO "X$old_archive_cmds" | $Xsed -e "$delay_single_quote_subst"`' CC='`$ECHO "X$CC" | $Xsed -e "$delay_single_quote_subst"`' CFLAGS='`$ECHO "X$CFLAGS" | $Xsed -e "$delay_single_quote_subst"`' compiler='`$ECHO "X$compiler" | $Xsed -e "$delay_single_quote_subst"`' GCC='`$ECHO "X$GCC" | $Xsed -e "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_pipe='`$ECHO "X$lt_cv_sys_global_symbol_pipe" | $Xsed -e "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_cdecl='`$ECHO "X$lt_cv_sys_global_symbol_to_cdecl" | $Xsed -e "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address" | $Xsed -e "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`' objdir='`$ECHO "X$objdir" | $Xsed -e "$delay_single_quote_subst"`' SHELL='`$ECHO "X$SHELL" | $Xsed -e "$delay_single_quote_subst"`' ECHO='`$ECHO "X$ECHO" | $Xsed -e "$delay_single_quote_subst"`' MAGIC_CMD='`$ECHO "X$MAGIC_CMD" | $Xsed -e "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag='`$ECHO "X$lt_prog_compiler_no_builtin_flag" | $Xsed -e "$delay_single_quote_subst"`' lt_prog_compiler_wl='`$ECHO "X$lt_prog_compiler_wl" | $Xsed -e "$delay_single_quote_subst"`' lt_prog_compiler_pic='`$ECHO "X$lt_prog_compiler_pic" | $Xsed -e "$delay_single_quote_subst"`' lt_prog_compiler_static='`$ECHO "X$lt_prog_compiler_static" | $Xsed -e "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o='`$ECHO "X$lt_cv_prog_compiler_c_o" | $Xsed -e "$delay_single_quote_subst"`' need_locks='`$ECHO "X$need_locks" | $Xsed -e "$delay_single_quote_subst"`' DSYMUTIL='`$ECHO "X$DSYMUTIL" | $Xsed -e "$delay_single_quote_subst"`' NMEDIT='`$ECHO "X$NMEDIT" | $Xsed -e "$delay_single_quote_subst"`' LIPO='`$ECHO "X$LIPO" | $Xsed -e "$delay_single_quote_subst"`' OTOOL='`$ECHO "X$OTOOL" | $Xsed -e "$delay_single_quote_subst"`' OTOOL64='`$ECHO "X$OTOOL64" | $Xsed -e "$delay_single_quote_subst"`' libext='`$ECHO "X$libext" | $Xsed -e "$delay_single_quote_subst"`' shrext_cmds='`$ECHO "X$shrext_cmds" | $Xsed -e "$delay_single_quote_subst"`' extract_expsyms_cmds='`$ECHO "X$extract_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`' archive_cmds_need_lc='`$ECHO "X$archive_cmds_need_lc" | $Xsed -e "$delay_single_quote_subst"`' enable_shared_with_static_runtimes='`$ECHO "X$enable_shared_with_static_runtimes" | $Xsed -e "$delay_single_quote_subst"`' export_dynamic_flag_spec='`$ECHO "X$export_dynamic_flag_spec" | $Xsed -e "$delay_single_quote_subst"`' whole_archive_flag_spec='`$ECHO "X$whole_archive_flag_spec" | $Xsed -e "$delay_single_quote_subst"`' compiler_needs_object='`$ECHO "X$compiler_needs_object" | $Xsed -e "$delay_single_quote_subst"`' old_archive_from_new_cmds='`$ECHO "X$old_archive_from_new_cmds" | $Xsed -e "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds='`$ECHO "X$old_archive_from_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`' archive_cmds='`$ECHO "X$archive_cmds" | $Xsed -e "$delay_single_quote_subst"`' archive_expsym_cmds='`$ECHO "X$archive_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`' module_cmds='`$ECHO "X$module_cmds" | $Xsed -e "$delay_single_quote_subst"`' module_expsym_cmds='`$ECHO "X$module_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`' with_gnu_ld='`$ECHO "X$with_gnu_ld" | $Xsed -e "$delay_single_quote_subst"`' allow_undefined_flag='`$ECHO "X$allow_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`' no_undefined_flag='`$ECHO "X$no_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`' hardcode_libdir_flag_spec='`$ECHO "X$hardcode_libdir_flag_spec" | $Xsed -e "$delay_single_quote_subst"`' hardcode_libdir_flag_spec_ld='`$ECHO "X$hardcode_libdir_flag_spec_ld" | $Xsed -e "$delay_single_quote_subst"`' hardcode_libdir_separator='`$ECHO "X$hardcode_libdir_separator" | $Xsed -e "$delay_single_quote_subst"`' hardcode_direct='`$ECHO "X$hardcode_direct" | $Xsed -e "$delay_single_quote_subst"`' hardcode_direct_absolute='`$ECHO "X$hardcode_direct_absolute" | $Xsed -e "$delay_single_quote_subst"`' hardcode_minus_L='`$ECHO "X$hardcode_minus_L" | $Xsed -e "$delay_single_quote_subst"`' hardcode_shlibpath_var='`$ECHO "X$hardcode_shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`' hardcode_automatic='`$ECHO "X$hardcode_automatic" | $Xsed -e "$delay_single_quote_subst"`' inherit_rpath='`$ECHO "X$inherit_rpath" | $Xsed -e "$delay_single_quote_subst"`' link_all_deplibs='`$ECHO "X$link_all_deplibs" | $Xsed -e "$delay_single_quote_subst"`' fix_srcfile_path='`$ECHO "X$fix_srcfile_path" | $Xsed -e "$delay_single_quote_subst"`' always_export_symbols='`$ECHO "X$always_export_symbols" | $Xsed -e "$delay_single_quote_subst"`' export_symbols_cmds='`$ECHO "X$export_symbols_cmds" | $Xsed -e "$delay_single_quote_subst"`' exclude_expsyms='`$ECHO "X$exclude_expsyms" | $Xsed -e "$delay_single_quote_subst"`' include_expsyms='`$ECHO "X$include_expsyms" | $Xsed -e "$delay_single_quote_subst"`' prelink_cmds='`$ECHO "X$prelink_cmds" | $Xsed -e "$delay_single_quote_subst"`' file_list_spec='`$ECHO "X$file_list_spec" | $Xsed -e "$delay_single_quote_subst"`' variables_saved_for_relink='`$ECHO "X$variables_saved_for_relink" | $Xsed -e "$delay_single_quote_subst"`' need_lib_prefix='`$ECHO "X$need_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`' need_version='`$ECHO "X$need_version" | $Xsed -e "$delay_single_quote_subst"`' version_type='`$ECHO "X$version_type" | $Xsed -e "$delay_single_quote_subst"`' runpath_var='`$ECHO "X$runpath_var" | $Xsed -e "$delay_single_quote_subst"`' shlibpath_var='`$ECHO "X$shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`' shlibpath_overrides_runpath='`$ECHO "X$shlibpath_overrides_runpath" | $Xsed -e "$delay_single_quote_subst"`' libname_spec='`$ECHO "X$libname_spec" | $Xsed -e "$delay_single_quote_subst"`' library_names_spec='`$ECHO "X$library_names_spec" | $Xsed -e "$delay_single_quote_subst"`' soname_spec='`$ECHO "X$soname_spec" | $Xsed -e "$delay_single_quote_subst"`' postinstall_cmds='`$ECHO "X$postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`' postuninstall_cmds='`$ECHO "X$postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`' finish_cmds='`$ECHO "X$finish_cmds" | $Xsed -e "$delay_single_quote_subst"`' finish_eval='`$ECHO "X$finish_eval" | $Xsed -e "$delay_single_quote_subst"`' hardcode_into_libs='`$ECHO "X$hardcode_into_libs" | $Xsed -e "$delay_single_quote_subst"`' sys_lib_search_path_spec='`$ECHO "X$sys_lib_search_path_spec" | $Xsed -e "$delay_single_quote_subst"`' sys_lib_dlsearch_path_spec='`$ECHO "X$sys_lib_dlsearch_path_spec" | $Xsed -e "$delay_single_quote_subst"`' hardcode_action='`$ECHO "X$hardcode_action" | $Xsed -e "$delay_single_quote_subst"`' enable_dlopen='`$ECHO "X$enable_dlopen" | $Xsed -e "$delay_single_quote_subst"`' enable_dlopen_self='`$ECHO "X$enable_dlopen_self" | $Xsed -e "$delay_single_quote_subst"`' enable_dlopen_self_static='`$ECHO "X$enable_dlopen_self_static" | $Xsed -e "$delay_single_quote_subst"`' old_striplib='`$ECHO "X$old_striplib" | $Xsed -e "$delay_single_quote_subst"`' striplib='`$ECHO "X$striplib" | $Xsed -e "$delay_single_quote_subst"`' LD_RC='`$ECHO "X$LD_RC" | $Xsed -e "$delay_single_quote_subst"`' old_archive_cmds_RC='`$ECHO "X$old_archive_cmds_RC" | $Xsed -e "$delay_single_quote_subst"`' compiler_RC='`$ECHO "X$compiler_RC" | $Xsed -e "$delay_single_quote_subst"`' GCC_RC='`$ECHO "X$GCC_RC" | $Xsed -e "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag_RC='`$ECHO "X$lt_prog_compiler_no_builtin_flag_RC" | $Xsed -e "$delay_single_quote_subst"`' lt_prog_compiler_wl_RC='`$ECHO "X$lt_prog_compiler_wl_RC" | $Xsed -e "$delay_single_quote_subst"`' lt_prog_compiler_pic_RC='`$ECHO "X$lt_prog_compiler_pic_RC" | $Xsed -e "$delay_single_quote_subst"`' lt_prog_compiler_static_RC='`$ECHO "X$lt_prog_compiler_static_RC" | $Xsed -e "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o_RC='`$ECHO "X$lt_cv_prog_compiler_c_o_RC" | $Xsed -e "$delay_single_quote_subst"`' archive_cmds_need_lc_RC='`$ECHO "X$archive_cmds_need_lc_RC" | $Xsed -e "$delay_single_quote_subst"`' enable_shared_with_static_runtimes_RC='`$ECHO "X$enable_shared_with_static_runtimes_RC" | $Xsed -e "$delay_single_quote_subst"`' export_dynamic_flag_spec_RC='`$ECHO "X$export_dynamic_flag_spec_RC" | $Xsed -e "$delay_single_quote_subst"`' whole_archive_flag_spec_RC='`$ECHO "X$whole_archive_flag_spec_RC" | $Xsed -e "$delay_single_quote_subst"`' compiler_needs_object_RC='`$ECHO "X$compiler_needs_object_RC" | $Xsed -e "$delay_single_quote_subst"`' old_archive_from_new_cmds_RC='`$ECHO "X$old_archive_from_new_cmds_RC" | $Xsed -e "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds_RC='`$ECHO "X$old_archive_from_expsyms_cmds_RC" | $Xsed -e "$delay_single_quote_subst"`' archive_cmds_RC='`$ECHO "X$archive_cmds_RC" | $Xsed -e "$delay_single_quote_subst"`' archive_expsym_cmds_RC='`$ECHO "X$archive_expsym_cmds_RC" | $Xsed -e "$delay_single_quote_subst"`' module_cmds_RC='`$ECHO "X$module_cmds_RC" | $Xsed -e "$delay_single_quote_subst"`' module_expsym_cmds_RC='`$ECHO "X$module_expsym_cmds_RC" | $Xsed -e "$delay_single_quote_subst"`' with_gnu_ld_RC='`$ECHO "X$with_gnu_ld_RC" | $Xsed -e "$delay_single_quote_subst"`' allow_undefined_flag_RC='`$ECHO "X$allow_undefined_flag_RC" | $Xsed -e "$delay_single_quote_subst"`' no_undefined_flag_RC='`$ECHO "X$no_undefined_flag_RC" | $Xsed -e "$delay_single_quote_subst"`' hardcode_libdir_flag_spec_RC='`$ECHO "X$hardcode_libdir_flag_spec_RC" | $Xsed -e "$delay_single_quote_subst"`' hardcode_libdir_flag_spec_ld_RC='`$ECHO "X$hardcode_libdir_flag_spec_ld_RC" | $Xsed -e "$delay_single_quote_subst"`' hardcode_libdir_separator_RC='`$ECHO "X$hardcode_libdir_separator_RC" | $Xsed -e "$delay_single_quote_subst"`' hardcode_direct_RC='`$ECHO "X$hardcode_direct_RC" | $Xsed -e "$delay_single_quote_subst"`' hardcode_direct_absolute_RC='`$ECHO "X$hardcode_direct_absolute_RC" | $Xsed -e "$delay_single_quote_subst"`' hardcode_minus_L_RC='`$ECHO "X$hardcode_minus_L_RC" | $Xsed -e "$delay_single_quote_subst"`' hardcode_shlibpath_var_RC='`$ECHO "X$hardcode_shlibpath_var_RC" | $Xsed -e "$delay_single_quote_subst"`' hardcode_automatic_RC='`$ECHO "X$hardcode_automatic_RC" | $Xsed -e "$delay_single_quote_subst"`' inherit_rpath_RC='`$ECHO "X$inherit_rpath_RC" | $Xsed -e "$delay_single_quote_subst"`' link_all_deplibs_RC='`$ECHO "X$link_all_deplibs_RC" | $Xsed -e "$delay_single_quote_subst"`' fix_srcfile_path_RC='`$ECHO "X$fix_srcfile_path_RC" | $Xsed -e "$delay_single_quote_subst"`' always_export_symbols_RC='`$ECHO "X$always_export_symbols_RC" | $Xsed -e "$delay_single_quote_subst"`' export_symbols_cmds_RC='`$ECHO "X$export_symbols_cmds_RC" | $Xsed -e "$delay_single_quote_subst"`' exclude_expsyms_RC='`$ECHO "X$exclude_expsyms_RC" | $Xsed -e "$delay_single_quote_subst"`' include_expsyms_RC='`$ECHO "X$include_expsyms_RC" | $Xsed -e "$delay_single_quote_subst"`' prelink_cmds_RC='`$ECHO "X$prelink_cmds_RC" | $Xsed -e "$delay_single_quote_subst"`' file_list_spec_RC='`$ECHO "X$file_list_spec_RC" | $Xsed -e "$delay_single_quote_subst"`' hardcode_action_RC='`$ECHO "X$hardcode_action_RC" | $Xsed -e "$delay_single_quote_subst"`' LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # Quote evaled strings. for var in SED \ GREP \ EGREP \ FGREP \ LD \ NM \ LN_S \ lt_SP2NL \ lt_NL2SP \ reload_flag \ deplibs_check_method \ file_magic_cmd \ AR \ AR_FLAGS \ STRIP \ RANLIB \ CC \ CFLAGS \ compiler \ lt_cv_sys_global_symbol_pipe \ lt_cv_sys_global_symbol_to_cdecl \ lt_cv_sys_global_symbol_to_c_name_address \ lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ SHELL \ ECHO \ lt_prog_compiler_no_builtin_flag \ lt_prog_compiler_wl \ lt_prog_compiler_pic \ lt_prog_compiler_static \ lt_cv_prog_compiler_c_o \ need_locks \ DSYMUTIL \ NMEDIT \ LIPO \ OTOOL \ OTOOL64 \ shrext_cmds \ export_dynamic_flag_spec \ whole_archive_flag_spec \ compiler_needs_object \ with_gnu_ld \ allow_undefined_flag \ no_undefined_flag \ hardcode_libdir_flag_spec \ hardcode_libdir_flag_spec_ld \ hardcode_libdir_separator \ fix_srcfile_path \ exclude_expsyms \ include_expsyms \ file_list_spec \ variables_saved_for_relink \ libname_spec \ library_names_spec \ soname_spec \ finish_eval \ old_striplib \ striplib \ LD_RC \ compiler_RC \ lt_prog_compiler_no_builtin_flag_RC \ lt_prog_compiler_wl_RC \ lt_prog_compiler_pic_RC \ lt_prog_compiler_static_RC \ lt_cv_prog_compiler_c_o_RC \ export_dynamic_flag_spec_RC \ whole_archive_flag_spec_RC \ compiler_needs_object_RC \ with_gnu_ld_RC \ allow_undefined_flag_RC \ no_undefined_flag_RC \ hardcode_libdir_flag_spec_RC \ hardcode_libdir_flag_spec_ld_RC \ hardcode_libdir_separator_RC \ fix_srcfile_path_RC \ exclude_expsyms_RC \ include_expsyms_RC \ file_list_spec_RC; do case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in reload_cmds \ old_postinstall_cmds \ old_postuninstall_cmds \ old_archive_cmds \ extract_expsyms_cmds \ old_archive_from_new_cmds \ old_archive_from_expsyms_cmds \ archive_cmds \ archive_expsym_cmds \ module_cmds \ module_expsym_cmds \ export_symbols_cmds \ prelink_cmds \ postinstall_cmds \ postuninstall_cmds \ finish_cmds \ sys_lib_search_path_spec \ sys_lib_dlsearch_path_spec \ old_archive_cmds_RC \ old_archive_from_new_cmds_RC \ old_archive_from_expsyms_cmds_RC \ archive_cmds_RC \ archive_expsym_cmds_RC \ module_cmds_RC \ module_expsym_cmds_RC \ export_symbols_cmds_RC \ prelink_cmds_RC; do case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Fix-up fallback echo if it was mangled by the above quoting rules. case \$lt_ECHO in *'\\\$0 --fallback-echo"') lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\$0 --fallback-echo"\$/\$0 --fallback-echo"/'\` ;; esac ac_aux_dir='$ac_aux_dir' xsi_shell='$xsi_shell' lt_shell_append='$lt_shell_append' # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi PACKAGE='$PACKAGE' VERSION='$VERSION' TIMESTAMP='$TIMESTAMP' RM='$RM' ofile='$ofile' _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "doc/tools/Makefile") CONFIG_FILES="$CONFIG_FILES doc/tools/Makefile" ;; "etc/Makefile") CONFIG_FILES="$CONFIG_FILES etc/Makefile" ;; "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; "src/common/Makefile") CONFIG_FILES="$CONFIG_FILES src/common/Makefile" ;; "src/libopensc/Makefile") CONFIG_FILES="$CONFIG_FILES src/libopensc/Makefile" ;; "src/libopensc/libopensc.pc") CONFIG_FILES="$CONFIG_FILES src/libopensc/libopensc.pc" ;; "src/pkcs11/Makefile") CONFIG_FILES="$CONFIG_FILES src/pkcs11/Makefile" ;; "src/pkcs15init/Makefile") CONFIG_FILES="$CONFIG_FILES src/pkcs15init/Makefile" ;; "src/scconf/Makefile") CONFIG_FILES="$CONFIG_FILES src/scconf/Makefile" ;; "src/tests/Makefile") CONFIG_FILES="$CONFIG_FILES src/tests/Makefile" ;; "src/tests/regression/Makefile") CONFIG_FILES="$CONFIG_FILES src/tests/regression/Makefile" ;; "src/tools/Makefile") CONFIG_FILES="$CONFIG_FILES src/tools/Makefile" ;; "src/sm/Makefile") CONFIG_FILES="$CONFIG_FILES src/sm/Makefile" ;; "src/minidriver/Makefile") CONFIG_FILES="$CONFIG_FILES src/minidriver/Makefile" ;; "src/minidriver/opensc-minidriver.inf") CONFIG_FILES="$CONFIG_FILES src/minidriver/opensc-minidriver.inf" ;; "win32/Makefile") CONFIG_FILES="$CONFIG_FILES win32/Makefile" ;; "win32/versioninfo.rc") CONFIG_FILES="$CONFIG_FILES win32/versioninfo.rc" ;; "win32/winconfig.h") CONFIG_FILES="$CONFIG_FILES win32/winconfig.h" ;; "win32/OpenSC.iss") CONFIG_FILES="$CONFIG_FILES win32/OpenSC.iss" ;; "win32/OpenSC.wxs") CONFIG_FILES="$CONFIG_FILES win32/OpenSC.wxs" ;; "MacOSX/Makefile") CONFIG_FILES="$CONFIG_FILES MacOSX/Makefile" ;; "MacOSX/build-package") CONFIG_FILES="$CONFIG_FILES MacOSX/build-package" ;; "MacOSX/10.5/resources/ReadMe.html") CONFIG_FILES="$CONFIG_FILES MacOSX/10.5/resources/ReadMe.html" ;; "MacOSX/10.6/resources/ReadMe.html") CONFIG_FILES="$CONFIG_FILES MacOSX/10.6/resources/ReadMe.html" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5 ;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= trap 'exit_status=$? { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_t=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_t"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5 ;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5 ;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$tmp/stdin" case $ac_file in -) cat "$tmp/out" && rm -f "$tmp/out";; *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" } >"$tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi # Compute "$ac_file"'s index in $config_headers. _am_arg="$ac_file" _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || $as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$_am_arg" : 'X\(//\)[^/]' \| \ X"$_am_arg" : 'X\(//\)$' \| \ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$_am_arg" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'`/stamp-h$_am_stamp_count ;; :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 $as_echo "$as_me: executing $ac_file commands" >&6;} ;; esac case $ac_file$ac_mode in "depfiles":C) test x"$AMDEP_TRUE" != x"" || { # Autoconf 2.62 quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf do # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. # We used to match only the files named `Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`$as_dirname -- "$mf" || $as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$mf" : 'X\(//\)[^/]' \| \ X"$mf" : 'X\(//\)$' \| \ X"$mf" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running `make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # When using ansi2knr, U may be empty or an underscore; expand it U=`sed -n 's/^U = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`$as_dirname -- "$file" || $as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$file" : 'X\(//\)[^/]' \| \ X"$file" : 'X\(//\)$' \| \ X"$file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir=$dirpart/$fdir; as_fn_mkdir_p # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ;; "libtool":C) # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi cfgfile="${ofile}T" trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. # Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION # Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, # 2006, 2007, 2008 Free Software Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is part of GNU Libtool. # # GNU Libtool 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. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that 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 GNU Libtool; see the file COPYING. If not, a copy # can be downloaded from http://www.gnu.org/licenses/gpl.html, or # obtained by writing to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # The names of the tagged configurations supported by this script. available_tags="RC " # ### BEGIN LIBTOOL CONFIG # Which release of libtool.m4 was used? macro_version=$macro_version macro_revision=$macro_revision # Assembler program. AS=$AS # DLL creation program. DLLTOOL=$DLLTOOL # Object dumper program. OBJDUMP=$OBJDUMP # Whether or not to build shared libraries. build_libtool_libs=$enable_shared # Whether or not to build static libraries. build_old_libs=$enable_static # What type of objects to build. pic_mode=$pic_mode # Whether or not to optimize for fast installation. fast_install=$enable_fast_install # The host system. host_alias=$host_alias host=$host host_os=$host_os # The build system. build_alias=$build_alias build=$build build_os=$build_os # A sed program that does not truncate output. SED=$lt_SED # Sed that helps us avoid accidentally triggering echo(1) options like -n. Xsed="\$SED -e 1s/^X//" # A grep program that handles long lines. GREP=$lt_GREP # An ERE matcher. EGREP=$lt_EGREP # A literal string matcher. FGREP=$lt_FGREP # A BSD- or MS-compatible name lister. NM=$lt_NM # Whether we need soft or hard links. LN_S=$lt_LN_S # What is the maximum length of a command? max_cmd_len=$max_cmd_len # Object file suffix (normally "o"). objext=$ac_objext # Executable file suffix (normally ""). exeext=$exeext # whether the shell understands "unset". lt_unset=$lt_unset # turn spaces into newlines. SP2NL=$lt_lt_SP2NL # turn newlines into spaces. NL2SP=$lt_lt_NL2SP # How to create reloadable object files. reload_flag=$lt_reload_flag reload_cmds=$lt_reload_cmds # Method to check whether dependent libraries are shared objects. deplibs_check_method=$lt_deplibs_check_method # Command to use when deplibs_check_method == "file_magic". file_magic_cmd=$lt_file_magic_cmd # The archiver. AR=$lt_AR AR_FLAGS=$lt_AR_FLAGS # A symbol stripping program. STRIP=$lt_STRIP # Commands used to install an old-style archive. RANLIB=$lt_RANLIB old_postinstall_cmds=$lt_old_postinstall_cmds old_postuninstall_cmds=$lt_old_postuninstall_cmds # A C compiler. LTCC=$lt_CC # LTCC compiler flags. LTCFLAGS=$lt_CFLAGS # Take the output of nm and produce a listing of raw symbols and C names. global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe # Transform the output of nm in a proper C declaration. global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl # Transform the output of nm in a C name address pair. global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address # Transform the output of nm in a C name address pair when lib prefix is needed. global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix # The name of the directory that contains temporary libtool files. objdir=$objdir # Shell to use when invoking shell scripts. SHELL=$lt_SHELL # An echo program that does not interpret backslashes. ECHO=$lt_ECHO # Used to examine libraries when file_magic_cmd begins with "file". MAGIC_CMD=$MAGIC_CMD # Must we lock files when doing compilation? need_locks=$lt_need_locks # Tool to manipulate archived DWARF debug symbol files on Mac OS X. DSYMUTIL=$lt_DSYMUTIL # Tool to change global to local symbols on Mac OS X. NMEDIT=$lt_NMEDIT # Tool to manipulate fat objects and archives on Mac OS X. LIPO=$lt_LIPO # ldd/readelf like tool for Mach-O binaries on Mac OS X. OTOOL=$lt_OTOOL # ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. OTOOL64=$lt_OTOOL64 # Old archive suffix (normally "a"). libext=$libext # Shared library suffix (normally ".so"). shrext_cmds=$lt_shrext_cmds # The commands to extract the exported symbol list from a shared archive. extract_expsyms_cmds=$lt_extract_expsyms_cmds # Variables whose values should be saved in libtool wrapper scripts and # restored at link time. variables_saved_for_relink=$lt_variables_saved_for_relink # Do we need the "lib" prefix for modules? need_lib_prefix=$need_lib_prefix # Do we need a version for libraries? need_version=$need_version # Library versioning type. version_type=$version_type # Shared library runtime path variable. runpath_var=$runpath_var # Shared library path variable. shlibpath_var=$shlibpath_var # Is shlibpath searched before the hard-coded library search path? shlibpath_overrides_runpath=$shlibpath_overrides_runpath # Format of library name prefix. libname_spec=$lt_libname_spec # List of archive names. First name is the real one, the rest are links. # The last name is the one that the linker finds with -lNAME library_names_spec=$lt_library_names_spec # The coded name of the library, if different from the real name. soname_spec=$lt_soname_spec # Command to use after installation of a shared archive. postinstall_cmds=$lt_postinstall_cmds # Command to use after uninstallation of a shared archive. postuninstall_cmds=$lt_postuninstall_cmds # Commands used to finish a libtool library installation in a directory. finish_cmds=$lt_finish_cmds # As "finish_cmds", except a single script fragment to be evaled but # not shown. finish_eval=$lt_finish_eval # Whether we should hardcode library paths into libraries. hardcode_into_libs=$hardcode_into_libs # Compile-time system search path for libraries. sys_lib_search_path_spec=$lt_sys_lib_search_path_spec # Run-time system search path for libraries. sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec # Whether dlopen is supported. dlopen_support=$enable_dlopen # Whether dlopen of programs is supported. dlopen_self=$enable_dlopen_self # Whether dlopen of statically linked programs is supported. dlopen_self_static=$enable_dlopen_self_static # Commands to strip libraries. old_striplib=$lt_old_striplib striplib=$lt_striplib # The linker used to build libraries. LD=$lt_LD # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds # A language specific compiler. CC=$lt_compiler # Is the compiler the GNU compiler? with_gcc=$GCC # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds archive_expsym_cmds=$lt_archive_expsym_cmds # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds module_expsym_cmds=$lt_module_expsym_cmds # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec # If ld is used when linking, flag to hardcode \$libdir into a binary # during linking. This must work even if \$libdir does not exist. hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator # Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct # Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \${shlibpath_var} if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs # Fix the shell variable \$srcfile for the compiler. fix_srcfile_path=$lt_fix_srcfile_path # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms # Symbols that must always be exported. include_expsyms=$lt_include_expsyms # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds # Specify filename containing input files. file_list_spec=$lt_file_list_spec # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action # ### END LIBTOOL CONFIG _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac ltmain="$ac_aux_dir/ltmain.sh" # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) case $xsi_shell in yes) cat << \_LT_EOF >> "$cfgfile" # func_dirname file append nondir_replacement # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. func_dirname () { case ${1} in */*) func_dirname_result="${1%/*}${2}" ;; * ) func_dirname_result="${3}" ;; esac } # func_basename file func_basename () { func_basename_result="${1##*/}" } # func_dirname_and_basename file append nondir_replacement # perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # Implementation must be kept synchronized with func_dirname # and func_basename. For efficiency, we do not delegate to # those functions but instead duplicate the functionality here. func_dirname_and_basename () { case ${1} in */*) func_dirname_result="${1%/*}${2}" ;; * ) func_dirname_result="${3}" ;; esac func_basename_result="${1##*/}" } # func_stripname prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). func_stripname () { # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary parameter first. func_stripname_result=${3} func_stripname_result=${func_stripname_result#"${1}"} func_stripname_result=${func_stripname_result%"${2}"} } # func_opt_split func_opt_split () { func_opt_split_opt=${1%%=*} func_opt_split_arg=${1#*=} } # func_lo2o object func_lo2o () { case ${1} in *.lo) func_lo2o_result=${1%.lo}.${objext} ;; *) func_lo2o_result=${1} ;; esac } # func_xform libobj-or-source func_xform () { func_xform_result=${1%.*}.lo } # func_arith arithmetic-term... func_arith () { func_arith_result=$(( $* )) } # func_len string # STRING may not start with a hyphen. func_len () { func_len_result=${#1} } _LT_EOF ;; *) # Bourne compatible functions. cat << \_LT_EOF >> "$cfgfile" # func_dirname file append nondir_replacement # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. func_dirname () { # Extract subdirectory from the argument. func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"` if test "X$func_dirname_result" = "X${1}"; then func_dirname_result="${3}" else func_dirname_result="$func_dirname_result${2}" fi } # func_basename file func_basename () { func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"` } # func_stripname prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # func_strip_suffix prefix name func_stripname () { case ${2} in .*) func_stripname_result=`$ECHO "X${3}" \ | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;; *) func_stripname_result=`$ECHO "X${3}" \ | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;; esac } # sed scripts: my_sed_long_opt='1s/^\(-[^=]*\)=.*/\1/;q' my_sed_long_arg='1s/^-[^=]*=//' # func_opt_split func_opt_split () { func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"` func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"` } # func_lo2o object func_lo2o () { func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"` } # func_xform libobj-or-source func_xform () { func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[^.]*$/.lo/'` } # func_arith arithmetic-term... func_arith () { func_arith_result=`expr "$@"` } # func_len string # STRING may not start with a hyphen. func_len () { func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` } _LT_EOF esac case $lt_shell_append in yes) cat << \_LT_EOF >> "$cfgfile" # func_append var value # Append VALUE to the end of shell variable VAR. func_append () { eval "$1+=\$2" } _LT_EOF ;; *) cat << \_LT_EOF >> "$cfgfile" # func_append var value # Append VALUE to the end of shell variable VAR. func_append () { eval "$1=\$$1\$2" } _LT_EOF ;; esac sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" cat <<_LT_EOF >> "$ofile" # ### BEGIN LIBTOOL TAG CONFIG: RC # The linker used to build libraries. LD=$lt_LD_RC # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds_RC # A language specific compiler. CC=$lt_compiler_RC # Is the compiler the GNU compiler? with_gcc=$GCC_RC # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_RC # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl_RC # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic_RC # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static_RC # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o_RC # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc_RC # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_RC # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_RC # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec_RC # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object_RC # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_RC # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_RC # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds_RC archive_expsym_cmds=$lt_archive_expsym_cmds_RC # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds_RC module_expsym_cmds=$lt_module_expsym_cmds_RC # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld_RC # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag_RC # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag_RC # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_RC # If ld is used when linking, flag to hardcode \$libdir into a binary # during linking. This must work even if \$libdir does not exist. hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_RC # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator_RC # Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct_RC # Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \${shlibpath_var} if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute_RC # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L_RC # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var_RC # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic_RC # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath_RC # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs_RC # Fix the shell variable \$srcfile for the compiler. fix_srcfile_path=$lt_fix_srcfile_path_RC # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols_RC # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds_RC # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms_RC # Symbols that must always be exported. include_expsyms=$lt_include_expsyms_RC # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds_RC # Specify filename containing input files. file_list_spec=$lt_file_list_spec_RC # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action_RC # ### END LIBTOOL TAG CONFIG: RC _LT_EOF ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi cat <. # # 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, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # PKG_PROG_PKG_CONFIG([MIN-VERSION]) # ---------------------------------- AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])# PKG_PROG_PKG_CONFIG # PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # # Check to see whether a particular set of modules exists. Similar # to PKG_CHECK_MODULES(), but does not set variables or print errors. # # Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) # only at the first occurence in configure.ac, so if the first place # it's called might be skipped (such as if it is within an "if", you # have to call PKG_CHECK_EXISTS manually # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) # --------------------------------------------- m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])# _PKG_CONFIG # _PKG_SHORT_ERRORS_SUPPORTED # ----------------------------- AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])# _PKG_SHORT_ERRORS_SUPPORTED # PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], # [ACTION-IF-NOT-FOUND]) # # # Note that if there is a possibility the first call to # PKG_CHECK_MODULES might not happen, you should be sure to include an # explicit call to PKG_PROG_PKG_CONFIG in your configure.ac # # # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])# PKG_CHECK_MODULES # Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.11' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.11.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.11.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to # `$srcdir', `$srcdir/..', or `$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is `.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [dnl Rely on autoconf to set up CDPATH properly. AC_PREREQ([2.50])dnl # expand $ac_aux_dir to an absolute path am_aux_dir=`cd $ac_aux_dir && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 9 # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ(2.52)dnl ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 10 # There are a few dirty hacks below to avoid letting `AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "GCJ", or "OBJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl ifelse([$1], CC, [depcc="$CC" am_compiler_list=], [$1], CXX, [depcc="$CXX" am_compiler_list=], [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], UPC, [depcc="$UPC" am_compiler_list=], [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named `D' -- because `-MD' means `put the output # in D'. mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with # Solaris 8's {/usr,}/bin/sh. touch sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with `-c' and `-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle `-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # after this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvisualcpp | msvcmsys) # This compiler won't grok `-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE(dependency-tracking, [ --disable-dependency-tracking speeds up one-time build --enable-dependency-tracking do not reject slow dependency extractors]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. #serial 5 # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Autoconf 2.62 quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf do # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. # We used to match only the files named `Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`AS_DIRNAME("$mf")` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running `make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # When using ansi2knr, U may be empty or an underscore; expand it U=`sed -n 's/^U = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`AS_DIRNAME(["$file"])` AS_MKDIR_P([$dirpart/$fdir]) # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking # is enabled. FIXME. This creates each `.P' file that we will # need in order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) ]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, # 2005, 2006, 2008, 2009 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 16 # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.62])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) AM_MISSING_PROG(AUTOCONF, autoconf) AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) AM_MISSING_PROG(AUTOHEADER, autoheader) AM_MISSING_PROG(MAKEINFO, makeinfo) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AM_PROG_MKDIR_P])dnl # We need awk for the "check" target. The system "awk" is bad on # some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES(CC)], [define([AC_PROG_CC], defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES(CXX)], [define([AC_PROG_CXX], defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES(OBJC)], [define([AC_PROG_OBJC], defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl ]) _AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl dnl The `parallel-tests' driver may need to know about EXEEXT, so add the dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro dnl is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl ]) dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001, 2003, 2005, 2008 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST(install_sh)]) # Copyright (C) 2003, 2005 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 2 # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001, 2002, 2003, 2005, 2009 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 4 # AM_MAKE_INCLUDE() # ----------------- # Check to see how make treats includes. AC_DEFUN([AM_MAKE_INCLUDE], [am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. AC_MSG_CHECKING([for style of include used by $am_make]) am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from `make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi AC_SUBST([am__include]) AC_SUBST([am__quote]) AC_MSG_RESULT([$_am_result]) rm -f confinc confmf ]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 6 # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it supports --run. # If it does, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --run true"; then am_missing_run="$MISSING --run " else am_missing_run= AC_MSG_WARN([`missing' script is too old or missing]) fi ]) # Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_MKDIR_P # --------------- # Check for `mkdir -p'. AC_DEFUN([AM_PROG_MKDIR_P], [AC_PREREQ([2.60])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P, dnl while keeping a definition of mkdir_p for backward compatibility. dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile. dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of dnl Makefile.ins that do not define MKDIR_P, so we do our own dnl adjustment using top_builddir (which is defined more often than dnl MKDIR_P). AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl case $mkdir_p in [[\\/$]]* | ?:[[\\/]]*) ;; */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; esac ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001, 2002, 2003, 2005, 2008 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 4 # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # ------------------------------ # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), 1)]) # _AM_SET_OPTIONS(OPTIONS) # ---------------------------------- # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 5 # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Just in case sleep 1 echo timestamp > conftest.file # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);; esac # Do `set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi rm -f conftest.file if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT(yes)]) # Copyright (C) 2009 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 1 # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # (`yes' being less verbose, `no' or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [ --enable-silent-rules less verbose build output (undo: `make V=1') --disable-silent-rules verbose build output (undo: `make V=0')]) case $enable_silent_rules in yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor `install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in `make install-strip', and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using `strip' when the user # run `make install-strip'. However `strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the `STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be `maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006, 2008 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 2 # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004, 2005 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 2 # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of `v7', `ustar', or `pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. AM_MISSING_PROG([AMTAR], [tar]) m4_if([$1], [v7], [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'], [m4_case([$1], [ustar],, [pax],, [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' _am_tools=${am_cv_prog_tar_$1-$_am_tools} # Do not fold the above two line into one, because Tru64 sh and # Solaris sh will not grok spaces in the rhs of `-'. for _am_tool in $_am_tools do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR m4_include([m4/ax_pthread.m4]) m4_include([m4/libtool.m4]) m4_include([m4/ltoptions.m4]) m4_include([m4/ltsugar.m4]) m4_include([m4/ltversion.m4]) m4_include([m4/lt~obsolete.m4]) opensc-0.13.0/Makefile.am0000644000015201777760000000222012057406034012055 00000000000000ACLOCAL_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 svnignore SUBDIRS = etc src win32 doc MacOSX dist_noinst_SCRIPTS = bootstrap dist_noinst_DATA = README \ solaris/Makefile solaris/README solaris/checkinstall.in \ solaris/opensc.conf-dist solaris/pkginfo.in solaris/proto dist_doc_DATA = NEWS 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.13.0/config.guess0000755000015201777760000012763712057406055012370 00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 # Free Software Foundation, Inc. timestamp='2009-12-30' # This file 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, write to the Free Software # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA # 02110-1301, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Per Bothner. Please send patches (context # diff format) to and include a ChangeLog # entry. # # This script attempts to guess a canonical system name similar to # config.sub. If it succeeds, it prints the system name on stdout, and # exits with 0. Otherwise, it exits with 1. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` exit ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm:riscos:*:*|arm:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH="x86_64" fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[456]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) case ${UNAME_MACHINE} in pc98) echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-gnu else echo ${UNAME_MACHINE}-unknown-linux-gnueabi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; cris:Linux:*:*) echo cris-axis-linux-gnu exit ;; crisv32:Linux:*:*) echo crisv32-axis-linux-gnu exit ;; frv:Linux:*:*) echo frv-unknown-linux-gnu exit ;; i*86:Linux:*:*) LIBC=gnu eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __dietlibc__ LIBC=dietlibc #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` echo "${UNAME_MACHINE}-pc-linux-${LIBC}" exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } ;; or32:Linux:*:*) echo or32-unknown-linux-gnu exit ;; padre:Linux:*:*) echo sparc-unknown-linux-gnu exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-gnu exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-gnu ;; PA8*) echo hppa2.0-unknown-linux-gnu ;; *) echo hppa-unknown-linux-gnu ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-gnu exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-gnu exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-gnu exit ;; x86_64:Linux:*:*) echo x86_64-unknown-linux-gnu exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown case $UNAME_PROCESSOR in i386) eval $set_cc_for_build if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then UNAME_PROCESSOR="x86_64" fi fi ;; unknown) UNAME_PROCESSOR=powerpc ;; esac echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NSE-?:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; esac #echo '(No uname command or uname output not recognized.)' 1>&2 #echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 eval $set_cc_for_build cat >$dummy.c < # include #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (__arm) && defined (__acorn) && defined (__unix) printf ("arm-acorn-riscix\n"); exit (0); #endif #if defined (hp300) && !defined (hpux) printf ("m68k-hp-bsd\n"); exit (0); #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) # if !defined (ultrix) # include # if defined (BSD) # if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); # else # if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); # else printf ("vax-dec-bsd\n"); exit (0); # endif # endif # else printf ("vax-dec-bsd\n"); exit (0); # endif # else printf ("vax-dec-ultrix\n"); exit (0); # endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } # Convex versions that predate uname can use getsysinfo(1) if [ -x /usr/convex/getsysinfo ] then case `getsysinfo -f cpu_type` in c1*) echo c1-convex-bsd exit ;; c2*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; c34*) echo c34-convex-bsd exit ;; c38*) echo c38-convex-bsd exit ;; c4*) echo c4-convex-bsd exit ;; esac fi cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: opensc-0.13.0/install-sh0000755000015201777760000003253712057406055012046 00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2009-04-28.21; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # 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 # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. nl=' ' IFS=" "" $nl" # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} if test -z "$doit"; then doit_exec=exec else doit_exec=$doit fi # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_glob='?' initialize_posix_glob=' test "$posix_glob" != "?" || { if (set -f) 2>/dev/null; then posix_glob= else posix_glob=: fi } ' posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false no_target_directory= usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *' '* | *' '* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) dst_arg=$2 shift;; -T) no_target_directory=true;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call `install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then trap '(exit $?); exit' 1 2 13 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names starting with `-'. case $src in -*) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # Protect names starting with `-'. case $dst in -*) dst=./$dst;; esac # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test -n "$no_target_directory"; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else # Prefer dirname, but fall back on a substitute if dirname fails. dstdir=` (dirname "$dst") 2>/dev/null || expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$dst" : 'X\(//\)[^/]' \| \ X"$dst" : 'X\(//\)$' \| \ X"$dst" : 'X\(/\)' \| . 2>/dev/null || echo X"$dst" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q' ` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writeable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; -*) prefix='./';; *) prefix='';; esac eval "$initialize_posix_glob" oIFS=$IFS IFS=/ $posix_glob set -f set fnord $dstdir shift $posix_glob set +f IFS=$oIFS prefixes= for d do test -z "$d" && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && eval "$initialize_posix_glob" && $posix_glob set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && $posix_glob set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: opensc-0.13.0/NEWS0000644000015201777760000004231612057406034010532 00000000000000NEWS for OpenSC -- History of user visible changes Complete change history is available online: http://www.opensc-project.org/opensc/timeline 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 (usualy 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.13.0/Makefile.in0000644000015201777760000006664312057406056012115 00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program 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. @SET_MAKE@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = . DIST_COMMON = README $(am__configure_deps) $(dist_doc_DATA) \ $(dist_noinst_DATA) $(dist_noinst_SCRIPTS) \ $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ $(srcdir)/config.h.in \ $(top_srcdir)/MacOSX/10.5/resources/ReadMe.html.in \ $(top_srcdir)/MacOSX/10.6/resources/ReadMe.html.in \ $(top_srcdir)/configure COPYING ChangeLog NEWS config.guess \ config.sub depcomp install-sh ltmain.sh missing ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = MacOSX/10.5/resources/ReadMe.html \ MacOSX/10.6/resources/ReadMe.html CONFIG_CLEAN_VPATH_FILES = SCRIPTS = $(dist_noinst_SCRIPTS) AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ html-recursive info-recursive install-data-recursive \ install-dvi-recursive install-exec-recursive \ install-html-recursive install-info-recursive \ install-pdf-recursive install-ps-recursive install-recursive \ installcheck-recursive installdirs-recursive pdf-recursive \ ps-recursive uninstall-recursive am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__installdirs = "$(DESTDIR)$(docdir)" DATA = $(dist_doc_DATA) $(dist_noinst_DATA) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ distdir dist dist-all distcheck ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ { test ! -d "$(distdir)" \ || { find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -fr "$(distdir)"; }; } am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best distuninstallcheck_listfiles = find . -type f -print distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEBUG_FILE = @DEBUG_FILE@ DEFAULT_PCSC_PROVIDER = @DEFAULT_PCSC_PROVIDER@ DEFAULT_SM_MODULE = @DEFAULT_SM_MODULE@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GIT = @GIT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBRARY_BITNESS = @LIBRARY_BITNESS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENCT_CFLAGS = @OPENCT_CFLAGS@ OPENCT_LIBS = @OPENCT_LIBS@ OPENSC_LT_AGE = @OPENSC_LT_AGE@ OPENSC_LT_CURRENT = @OPENSC_LT_CURRENT@ OPENSC_LT_OLDEST = @OPENSC_LT_OLDEST@ OPENSC_LT_REVISION = @OPENSC_LT_REVISION@ OPENSC_VERSION_FIX = @OPENSC_VERSION_FIX@ OPENSC_VERSION_MAJOR = @OPENSC_VERSION_MAJOR@ OPENSC_VERSION_MINOR = @OPENSC_VERSION_MINOR@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OPTIONAL_OPENCT_CFLAGS = @OPTIONAL_OPENCT_CFLAGS@ OPTIONAL_OPENCT_LIBS = @OPTIONAL_OPENCT_LIBS@ OPTIONAL_OPENSSL_CFLAGS = @OPTIONAL_OPENSSL_CFLAGS@ OPTIONAL_OPENSSL_LIBS = @OPTIONAL_OPENSSL_LIBS@ OPTIONAL_PCSC_CFLAGS = @OPTIONAL_PCSC_CFLAGS@ OPTIONAL_READLINE_CFLAGS = @OPTIONAL_READLINE_CFLAGS@ OPTIONAL_READLINE_LIBS = @OPTIONAL_READLINE_LIBS@ OPTIONAL_ZLIB_CFLAGS = @OPTIONAL_ZLIB_CFLAGS@ OPTIONAL_ZLIB_LIBS = @OPTIONAL_ZLIB_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCSC_CFLAGS = @PCSC_CFLAGS@ PCSC_LIBS = @PCSC_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RC = @RC@ READLINE_CFLAGS = @READLINE_CFLAGS@ READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ WIN_LIBPREFIX = @WIN_LIBPREFIX@ XSLTPROC = @XSLTPROC@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ git = @git@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkcs11dir = @pkcs11dir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ xslstylesheetsdir = @xslstylesheetsdir@ ACLOCAL_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 svnignore SUBDIRS = etc src win32 doc MacOSX dist_noinst_SCRIPTS = bootstrap dist_noinst_DATA = README \ solaris/Makefile solaris/README solaris/checkinstall.in \ solaris/opensc.conf-dist solaris/pkginfo.in solaris/proto dist_doc_DATA = NEWS all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: am--refresh: @: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): config.h: stamp-h1 @if test ! -f $@; then \ rm -f stamp-h1; \ $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \ else :; fi stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status @rm -f stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status config.h $(srcdir)/config.h.in: $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f stamp-h1 touch $@ distclean-hdr: -rm -f config.h stamp-h1 MacOSX/10.5/resources/ReadMe.html: $(top_builddir)/config.status $(top_srcdir)/MacOSX/10.5/resources/ReadMe.html.in cd $(top_builddir) && $(SHELL) ./config.status $@ MacOSX/10.6/resources/ReadMe.html: $(top_builddir)/config.status $(top_srcdir)/MacOSX/10.6/resources/ReadMe.html.in cd $(top_builddir) && $(SHELL) ./config.status $@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs distclean-libtool: -rm -f libtool config.lt install-dist_docDATA: $(dist_doc_DATA) @$(NORMAL_INSTALL) test -z "$(docdir)" || $(MKDIR_P) "$(DESTDIR)$(docdir)" @list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(docdir)" || exit $$?; \ done uninstall-dist_docDATA: @$(NORMAL_UNINSTALL) @list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ test -n "$$files" || exit 0; \ echo " ( cd '$(DESTDIR)$(docdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(docdir)" && rm -f $$files # This directory's subdirectories are mostly independent; you can cd # into them and run `make' without going through this Makefile. # To change the values of `make' variables: instead of editing Makefiles, # (1) if the variable is set in `config.status', edit `config.status' # (which will cause the Makefiles to be regenerated when you run `make'); # (2) otherwise, pass the desired values on the `make' command line. $(RECURSIVE_TARGETS): @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ *k*) failcom='fail=yes';; \ esac; \ done; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ list='$(SUBDIRS)'; for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" $(RECURSIVE_CLEAN_TARGETS): @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ *k*) failcom='fail=yes';; \ esac; \ done; \ dot_seen=no; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ rev=''; for subdir in $$list; do \ if test "$$subdir" = "."; then :; else \ rev="$$subdir $$rev"; \ fi; \ done; \ rev="$$rev ."; \ target=`echo $@ | sed s/-recursive//`; \ for subdir in $$rev; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done && test -z "$$fail" tags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ done ctags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ done ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: CTAGS CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz $(am__remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2 $(am__remove_distdir) dist-lzma: distdir tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma $(am__remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | xz -c >$(distdir).tar.xz $(am__remove_distdir) dist-tarZ: distdir tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__remove_distdir) dist-shar: distdir shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz $(am__remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__remove_distdir) dist dist-all: distdir tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz $(am__remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lzma*) \ lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac chmod -R a-w $(distdir); chmod u+w $(distdir) mkdir $(distdir)/_build mkdir $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build \ && ../configure --srcdir=.. --prefix="$$dc_install_base" \ $(DISTCHECK_CONFIGURE_FLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @$(am__cd) '$(distuninstallcheck_dir)' \ && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am check: check-recursive all-am: Makefile $(SCRIPTS) $(DATA) config.h installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(docdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -f Makefile distclean-am: clean-am distclean-generic distclean-hdr \ distclean-libtool distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dist_docDATA install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-dist_docDATA .MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) all \ ctags-recursive install-am install-strip tags-recursive .PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ all all-am am--refresh check check-am clean clean-generic \ clean-libtool ctags ctags-recursive dist dist-all dist-bzip2 \ dist-gzip dist-lzma dist-shar dist-tarZ dist-xz dist-zip \ distcheck distclean distclean-generic distclean-hdr \ distclean-libtool distclean-tags distcleancheck distdir \ distuninstallcheck dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am \ install-dist_docDATA install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \ uninstall uninstall-am uninstall-dist_docDATA 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 ) # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: opensc-0.13.0/ChangeLog0000644000015201777760000713654012057406050011615 00000000000000# Generated by Makefile. Do not edit! commit 98ca66b1a048c0ffa7b1ff13539c004c7c3017c1 Author: Viktor Tarasov Date: Tue Dec 4 11:50:49 2012 +0100 release 0.13.0 commit 8b07b9c5a77fa9868f0151a7e09c07207b7964cf Author: Viktor Tarasov Date: Mon Dec 3 14:58:52 2012 +0100 compile on Windows, minor codding style issues commit 457426543dfa02597895d57013dde94cc9e7d038 Author: sjoblomt Date: Wed Nov 28 12:52:43 2012 +0200 MyEID ECDSA support commit 9e9b3d0bd86e072dff4ab36ff0ac773a75b735fb Author: Viktor Tarasov Date: Wed Nov 28 11:09:36 2012 +0100 tool: in 'do_apdu' increase size of send/receive buffers commit 60b7e52e066e4a5849d363951635aefa07bc2325 Author: Viktor Tarasov Date: Tue Nov 20 22:58:37 2012 +0100 pkcs15: mandatory 'publicKeyCoefficients' in encode/decode public key procedures http://www.opensc-project.org/pipermail/opensc-devel/2012-November/018586.html commit 81bbddfc24dec139a26a17c5b05f169b00c2edb6 Author: Ludovic Rousseau Date: Tue Nov 20 18:03:45 2012 +0100 card-gemsafeV1: Add a GemSafe V1 ATR Thanks to Lukas Wunner for the patch commit 27c677188e278d66afea3969f51c59cd72d473ae Author: Anthony Foiani Date: Sun Nov 11 22:46:37 2012 -0700 pcks11: trivial: fix debug output for CKA_PRIME_1 and CKA_PRIME_2 Without this patch, debugging output issues these as unknown attributes: ... C_CreateObject(): CKA_PRIVATE_EXPONENT = 97F798... ... C_CreateObject(): Attribute 0x124 = EFE5AD... ... C_CreateObject(): Attribute 0x125 = D4D3F6... ... C_CreateObject(): CKA_EXPONENT_1 = 5815FD... With this patch, we see: ... C_CreateObject(): CKA_PRIVATE_EXPONENT = 97F798... ... C_CreateObject(): CKA_PRIME_1 = EFE5AD... ... C_CreateObject(): CKA_PRIME_2 = D4D3F6... ... C_CreateObject(): CKA_EXPONENT_1 = 5815FD... Signed-Off-By: Anthony Foiani commit f63135afabff7f93fff51d90e88a25314718092e Author: Anthony Foiani Date: Sun Nov 11 22:39:01 2012 -0700 tools: check return value after each call. It seems that this suffered some copy and paste damage at some point. Change so that we check each return value immediately after the API call. Signed-Off-By: Anthony Foiani commit fb8e0cc3b285b1d911d0ac3ef6d5cad86f467a3c Author: Andreas Schwier Date: Fri Nov 16 15:48:37 2012 +0100 sc-hsm: Improved checking in sc-hsm-tool commit 0adec1bdddea1db8bbd5f7aa2cf3f4198a2e361b Author: Andreas Schwier Date: Fri Nov 16 14:38:29 2012 +0100 sc-hsm: Fixed bug with memory released to early commit bbbfae4bf02809eb0e836d3706a0cf28f798417b Author: Andreas Schwier Date: Mon Nov 12 16:52:48 2012 +0100 sc-hsm: Now saving the internal CSR in place of the certificate and decoding the public key at initialization (RSA only) commit 6c051f8490412fb22b6fb7a3c095ac8265b18c68 Author: Viktor Tarasov Date: Mon Nov 12 16:46:25 2012 +0100 pkcs11: check arguments in get_bignum_bits() procedure t451: segmentation fault when getting public key bits number commit bd8606383599cf693928c0467dc0a3ca7e4362fd Author: Viktor Tarasov Date: Mon Nov 12 10:13:57 2012 +0100 t455: check validity of RSA/DSA public key components Segmentation fault happened when reading SSH key with the non-initilized public key components. commit 68ee0e76e0708b544d19100e5430d6a994c25049 Author: Viktor Tarasov Date: Sun Nov 11 22:17:17 2012 +0100 t447: return value from init() in reader driver not checked commit f7771c9b084417c6891d5a9d200b7adbd554e67c Author: Viktor Tarasov Date: Sun Nov 11 21:47:10 2012 +0100 libopensc: check data returned by 'read-public-key' fix error message commit da5934a6ff4dbc82a27f97b4bcc905642fa79541 Author: Viktor Tarasov Date: Sun Nov 11 20:38:30 2012 +0100 libopensc iso7816: retry SELECT with FCI if SELECT without FCI fails t457 (https://www.opensc-project.org/opensc/ticket/457) For some cards that currently use the common iso-7816 operations only SELECT with return of FCI/FCP can be applied. In iso-7816 'select-file' handle, if 'SELECT without FCI' fails with error code 6A86, then retry 'SELECT with FCI'. Other error code can be added. Sorry for the 'coding style' noise. commit a4ac33f32a09a634eef01dd22136c40d8cce092c Author: Viktor Tarasov Date: Sun Nov 11 20:36:57 2012 +0100 build sc-hsm-tool: link with OpenSSL libs commit 7c714860a22beb42e2e4d30f406f140e2e69d518 Author: Andreas Schwier Date: Fri Nov 9 18:06:14 2012 +0100 sc-hsm: Fixed bug decoding CVCs without domain parameter commit 33da14c45938d47601c5b4325bba8cac45c2d993 Author: Andreas Schwier Date: Thu Nov 8 21:38:47 2012 +0100 sc-hsm: Added code to prevent CV certificates being listed as X.509 certificates commit ffb20e5916550f0dd00592efa113ea23a364ebef Author: Andreas Schwier Date: Sun Oct 14 14:35:46 2012 +0200 sc-hsm: Added sc-hsm-tool with DKEK support and key wrap / unwrap commit 8d35b2cf58d45fa19a5d20b6450b32bfb36366c1 Author: Viktor Tarasov Date: Sun Nov 11 00:57:09 2012 +0100 build MSI: add openpgp.profile commit 1d1abe4c2122c441582c65e59f2fcb8d4a34833f Author: Viktor Tarasov Date: Sun Nov 11 00:42:52 2012 +0100 pkcs15-crypt tool: set HASH_NONE crypto flags when the hash do not asked without this for the cards that have only RAW mechanism it's not possible to compute signature with PKCS1 padding and without hash. commit 7fbca94698516b7785b86e901443e7204dfc5c58 Author: Viktor Tarasov Date: Fri Nov 9 14:36:14 2012 +0100 opensc: new card operation 'read-public-key' In PukDF of PKCS#15 the public key value can be presented by 'direct value', by path or by path and reference. For the different cards the public key can be stored in EF, internal EF or in card specific SDO (security data objects). A new card handle allows to read out the public key from the card specific SDOs. commit 6819b32e185f72c15a3028d06740dcd48424f9e5 Author: Viktor Tarasov Date: Fri Nov 9 14:34:46 2012 +0100 pkcs15-tool: for public key show the presence of 'direct' value commit 62fd67f6a8baf2e17dcbb645dc5273383d21653b Author: Viktor Tarasov Date: Fri Nov 9 14:33:23 2012 +0100 gemsafeV1: set 'auth-method' for the emulated PIN PKCS#15 object commit 01663218353dd2c1a23fdbcaeab93cd77ca24dd3 Author: Viktor Tarasov Date: Sun Nov 4 16:44:34 2012 +0100 libopensc: increase maximum number of card drivers http://www.opensc-project.org/pipermail/opensc-devel/2012-October/018552.html Default driver is disabled on 0.13 because there are more drivers listed in ctx.c. (leonardo.schenkel@gmail.com) SC_MAX_CARD_DRIVERS is increases from 32 to 48. It's not the best solution, but the most rapid. Will be waiting for the better proposals. commit e35febed5b1f95453bbbe04eb2d801b8fcea36c3 Author: Viktor Tarasov Date: Sun Oct 21 16:30:06 2012 +0200 pkcs15: use whe available the pkcs15 object content when reading certificate, try to get the pkcs15 object's content before reading the certificate file. commit 4cf3a3b2042456ab0f3247683e5ab5aa1a7174f8 Author: Viktor Tarasov Date: Sun Oct 21 16:08:04 2012 +0200 move CK_VERSION data from 'pkcs15' to 'sc-card' CK_VERSION is included into PKCS#11 data but is not specified by PKCS#15. CK_VERSION can be provided by card's pkcs15 emulator or by the card's driver, including the cards with the native support of pkcs#15 (and thus without pkcs15 emulator). That's why the more general solution is to have these data included into 'sc-card' data type. commit aaedef70b503b49c5b731e8c4e000751c3b510bb Author: Andreas Schwier Date: Sun Oct 14 21:01:28 2012 +0200 sc-hsm: Added ability to initialize SmartCard-HSM using C_Initialize and C_InitPIN on PKCS#11 interface commit fba298c6f44da9cdb1a9445ec6a561ae472b8195 Author: Andreas Schwier Date: Tue Oct 9 22:11:52 2012 +0200 pksc11: Added ability to indicate hardware and firmware version information at PKCS#11 interface commit 6b7d8af08ef98357ecdea13b444892b3b5ccfebd Author: Viktor Tarasov Date: Wed Oct 3 16:24:47 2012 +0200 build: release candidate 0.13.0 RC1 commit 40ff0e4ede64b28a060b97fe79c3aad06fb0296b Author: Viktor Tarasov Date: Tue Oct 2 09:26:35 2012 +0200 pkcs11: Fixed SIGV when deleting public key objects via PKCS#11 Thanks to Andreas Schwier. commit c91f0e84cb6888134fd9e77c6ade1a59d2b6a767 Author: riham Date: Thu Sep 13 15:38:47 2012 +0800 entersafe: Disable RSA:512bits that modified in entersafe_generate_key and entersafe_store_key function 1.Added a prompt while initializing ePass2003 \n 2.Modify code to disable 512bit key commit 4b9e82d3927afb867569e9da60c002af5ad53b94 Author: Viktor Tarasov Date: Mon Oct 1 13:30:35 2012 +0200 MSI: illegal caracters in component ID OpenSC.wxs(168) : error CNDL0014 : The Component/@Id attribute's value, 'sc-hsm.profile', is not a legal identifier. Identifiers may contain ASCII characters A-Z, a-z, digits, underscores (_), or periods (.). Every identifier must begin with either a letter or an underscore. commit 72786abe1f95763532a4c2462e332caaa7557908 Author: Andreas Schwier Date: Thu Sep 27 15:50:15 2012 +0200 sc-hsm: Added write support for RSA and ECC keys, certificates and data objects commit a9393aa983827f667881f247f18c1d38b7111221 Author: Andreas Schwier Date: Tue Sep 25 15:34:07 2012 +0200 framework-pkcs15: Fixed a SIGV when key generation returned ERROR_NOT_SUPPORTED commit 1619a423756f350fec381f83428adef0c03b526c Author: Andreas Schwier Date: Mon Sep 24 22:12:01 2012 +0200 ecc: Adding more curves commit db3f5f5f17ce34bc246d7cd98cad847da1a27485 Author: Andreas Schwier Date: Mon Sep 24 15:27:18 2012 +0200 framework-pkcs15: Fixed issued with uninitialized variable keysize commit f508b21253ce1fa8bd034a1e871cbfa6dedda642 Author: Andreas Schwier Date: Thu Sep 20 09:15:53 2012 +0200 pkcs15: Add support to encode EC private key description commit 7b943b934b82841178c045a6996d5202b9f808c0 Author: Andreas Schwier Date: Thu Sep 20 09:14:52 2012 +0200 pkcs15: Fixed typo commit 02fe6d474b94c2e4652fd0825dd846f583d31eef Author: Andreas Schwier Date: Fri Aug 31 11:34:21 2012 +0200 pkcs11-tool: Fixed issue with ID increment failing on constant data commit 249b769a4b4362e48190232c5a64d896c20f1b4e Author: Viktor Tarasov Date: Mon Oct 1 10:47:52 2012 +0200 pkcs11: unlink 'pubkey' FW object when deleting related certificate Thanks to Andreas Schwier. http://www.opensc-project.org/pipermail/opensc-devel/2012-September/018455.html In PKCS#11 FW, the 'certificate' FW object is used to create corresponding 'public'key' FW object or to get some of its attributes. Seg.fault occured when, in the same session, the related certificate was destroyed and after that there was the attempt to get such public key attributes. commit df9a4d0b2ce50dcdae4214c09e86a2acb03f5826 Author: Viktor Tarasov Date: Sun Sep 30 22:38:27 2012 +0200 pkcs15: for 'sc_pkcs15_cer's data use the 'der' object type To hold the raw certificate blob in 'sc_pkcs15_cert' data use the 'sc_pkcs15_der' data type. also: ; in 'pkcs15-cert.c' use short call of the debug messages; ; in 'destroy-object' pkcs15 framework handler take into account the multi-application cards: -- when binding card use the application info; -- when finalizing profile use the application ID. commit ea40e7fe24a3b410e38bbb18a0ca995ee19dae0e Author: Ludovic Rousseau Date: Tue Sep 25 22:57:52 2012 +0200 Use AM_CPPFLAGS instead of INCLUDES Fix autoreconf warnings: $ autoreconf -vis -Wall [...] src/common/Makefile.am:12: warning: 'INCLUDES' is the old name for 'AM_CPPFLAGS' (or '*_CPPFLAGS') src/libopensc/Makefile.am:19: warning: 'INCLUDES' is the old name for 'AM_CPPFLAGS' (or '*_CPPFLAGS') src/minidriver/Makefile.am:15: warning: 'INCLUDES' is the old name for 'AM_CPPFLAGS' (or '*_CPPFLAGS') src/pkcs11/Makefile.am:10: warning: 'INCLUDES' is the old name for 'AM_CPPFLAGS' (or '*_CPPFLAGS') src/pkcs15init/Makefile.am:36: warning: 'INCLUDES' is the old name for 'AM_CPPFLAGS' (or '*_CPPFLAGS') src/scconf/Makefile.am:12: warning: 'INCLUDES' is the old name for 'AM_CPPFLAGS' (or '*_CPPFLAGS') src/sm/Makefile.am:8: warning: 'INCLUDES' is the old name for 'AM_CPPFLAGS' (or '*_CPPFLAGS') src/tests/Makefile.am:9: warning: 'INCLUDES' is the old name for 'AM_CPPFLAGS' (or '*_CPPFLAGS') src/tools/Makefile.am:15: warning: 'INCLUDES' is the old name for 'AM_CPPFLAGS' (or '*_CPPFLAGS') commit 3656b478f08c6ea08a7fc200f9121b549754bccc Author: Ludovic Rousseau Date: Tue Sep 25 17:56:55 2012 +0200 Use AX_PTHREAD instead of ACX_PTHREAD configure.ac:304: warning: The macro `ACX_PTHREAD' is obsolete. configure.ac:304: You should run autoupdate. m4/ax_pthread.m4:87: ACX_PTHREAD is expanded from... commit 628ead7e410c136e4e269abf61f3a74ddaa111f6 Author: Viktor Tarasov Date: Mon Sep 24 11:48:33 2012 +0200 pkcs11-tool: suppress warnings about the missing attributes when showing an object do not print warnings about missing 'uncommon' attributes commit ceef4c0be399339ef8c5d6f522ebe79f3a62c801 Author: Viktor Tarasov Date: Mon Sep 24 11:48:02 2012 +0200 iasecc: remove ostentatious TODO messages commit d525ca97e39cf1ca7a89f03a3a8566d44a6dd27d Author: Viktor Tarasov Date: Sun Sep 23 21:02:31 2012 +0200 libopensc: OID with only zeros in array do not valid commit 760cd1cfbd6149d9115d9d55e6a98efe66df891e Author: Viktor Tarasov Date: Sun Sep 23 16:51:25 2012 +0200 pkcs15-tool: non-initialised OID commit 2a88453aaa4e8292f3f15f2bf8fe86ac999437f3 Author: Viktor Tarasov Date: Sun Sep 16 21:54:39 2012 +0200 build: Illegal char '-' in: 'Version: 0.13.0-pre1' commit 961059a05285921c5431a8a2d8b86a164c12da0e Author: Viktor Tarasov Date: Tue Sep 11 17:13:39 2012 +0200 build: include to 'dist' the files used by Windows build commit 58b43049576088dff48b01b20fbfedde20249054 Author: Viktor Tarasov Date: Sun Sep 2 00:12:57 2012 +0200 libopensc: 'init', 'format', 'compare', 'is-valid' OID procedures In a reason of number of bugs(*) that concern the OID management, the general usage OID procedures 'init', 'format', 'compare', 'is-valid' are introduced. These procedures should be used by all actors: libopensc, pkcs15, pkcs11, tools, .... (*) This bug reported by Andreas Schwier : https://github.com/OpenSC/OpenSC/commit/8e75d971cb7eadfef9b5b50adb3cb6d18e641ed2#commitcomment-1792477 In pkcs15-algo sc_asn1_get_algorithm_info() can return the OID without ending '-1's: https://github.com/OpenSC/OpenSC/blob/staging/src/libopensc/pkcs15-algo.c#L452 https://github.com/OpenSC/OpenSC/blob/staging/src/libopensc/pkcs15-algo.c#L459 commit d5ee8a80b501e4bfec08b3d3e78bb4d46970cf21 Author: Andreas Schwier Date: Sun Aug 26 15:14:21 2012 +0200 sc-hsm: Added fallback for readers that do not support extended length. Without extended length, RSA 2048 bit operations will not work. commit ee94020919d41ee35c49bf290fe59a12ed453dfe Author: Andreas Schwier Date: Fri Aug 31 11:57:16 2012 +0200 pkcs15-init: Fixed bugs and improved isolation between framework and emulation layer select_object_path: Fixed misplaced return and wrong return code. This bug is the cause why a profile must include a template even for fully emulated cards. sc_pkcs15init_store_certificate: Added a call to the emulation layer when the private key description requires an update after storing a certificate. Should not break existing code. sc_pkcs15init_delete_object: Now calling the emulation layer before the frameworks tries to delete files itself. An emulation that deletes object explicitly and leaves the deletion of some objects to the framework will now need to completely handle deleting objects (by calling the methods of the framework). sc_pkcs15init_update_certificate: Missing call to the emulation layer added. commit ed18b789d7249cc6b2d79c57ed180470b2cb1c36 Author: Viktor Tarasov Date: Fri Aug 31 15:03:57 2012 +0200 win32: add windows version of opensc.conf.in it do not contains macros that have to be resolved by 'configure'. commit 8c342ec7725dad8e354fd36e782d0ebd7d5eca41 Author: Viktor Tarasov Date: Sat Aug 25 20:08:56 2012 +0200 tools: t404: redesign treatment of 'reader' option in previous version first of all the 'reader' option's value was converted to hexadecimal form, used as ATR value and all present readers where scanned to find the inserted card with such ATR. Only after this the 'reader' option was used as reader's number or reader's name. Currently in use the 'hex-to-bin' procedure accepts for conversion one digit, and so even if the 'reader' option value is one digit, the useless search over all present readers take place. In the current version the order of checks if kept (ATR, reader's number, reader's name), but enforced the validity check of ATR, presented by 'reader' option. Also the option is accepted as reader's number only if the 'entire' option's string can be converted to integer. Thanks to 'jbwisemo' for cooperation. https://www.opensc-project.org/opensc/ticket/404 commit 41861e42b01539856b31f758dfde9ba53749eaaa Author: Viktor Tarasov Date: Sun Aug 26 11:16:30 2012 +0200 no 'pace' in common part 'PACE' is extremely card specific protocol and has not to be ostensibly present in the common part of OpenSC: * currently in OpenSC there is no card driver that supports or uses this protocol; * amazing content of the common 'sc_perform_pace' -- beside the verbose logs the only substantial action is to call the card/reader specific handler. According to the current sources and the pull request 83 this 'common' procedure is called by the card driver or card specific tool/operation. * currently the 'PACE' can be thouroghly tested only by one person (Frank Morgner), and only using the OpenSSL patched with the PACE specific patch. So, at least a dedicated configuration option could be introduced when comiting PACE to the common part. * common 'sc_perfom_pace' has the same role as the 'initialize-SM' handler of the existing SM framework and can be implemented as card specific SM, as the others cards do. This confirmed by Frank Morgner, the author of PACE commits and nPA card driver, himself. (https://github.com/OpenSC/OpenSC/pull/83) commit 7c39aeefb940fdf783d1b9a1963bf6bf50aa627a Author: Ludovic Rousseau Date: Fri Aug 24 00:23:32 2012 +0200 Fix potential NULL pointer dereference Coverity: Dereference before null check (REVERSE_INULL) Directly dereferencing pointer "pin_obj". commit 9ca47cded668ba14ed2a5a13544798e12473cb5c Author: Ludovic Rousseau Date: Fri Aug 24 00:14:00 2012 +0200 Fix a memory leak Do not forget to also free data in case of error. Coverity: Resource leak (RESOURCE_LEAK) Calling allocation function "calloc". commit ed959004dd09fbe6afc93ceb810a4dc8626eec34 Author: Ludovic Rousseau Date: Fri Aug 24 00:10:01 2012 +0200 Fix a potential memory leak If tlvblock is not stored then the value is lost and the allocated mempry is leaked. Coverity: Resource leak (RESOURCE_LEAK) Calling allocation function "pgp_build_tlv" on "tlvblock". commit b90a5572370f686b7ac6f474a0f113d18f6da650 Author: Ludovic Rousseau Date: Thu Aug 23 23:45:23 2012 +0200 Fix SC_PKCS15_TYPE_PRKEY_EC case Coverity: Missing break in switch (MISSING_BREAK) "A break statement was missing. The case SC_PKCS15_TYPE_PRKEY_EC was then managed as a SC_PKCS15_TYPE_PRKEY_DSA" (Ludovic Rousseau) "the break here has no sense, because LOG_TEST_RET will always return SC_ERROR_NOT_SUPPORTED before" (Frank Morgner https://github.com/OpenSC/OpenSC/pull/85) 'break' is kept to satisfy coverity. commit 68c87fd88ec98d1bee63b8fd7f72e5425705ad41 Author: Andreas Schwier Date: Fri Aug 24 13:15:39 2012 +0200 pkcs15-tool: Fix display error for fieldLength of ECC private keys commit cdfc22be732c047b6775c59b67f64ae921c1c131 Author: Andreas Schwier Date: Fri Aug 24 09:39:45 2012 +0200 sc-hsm: Removed dead code and memory leak if serial number is set more than once commit deeb1cde96f34c006093aed0d0b80fbe025a87b1 Author: Andreas Schwier Date: Fri Aug 24 09:25:45 2012 +0200 sc-hsm: Added registry key file to makefile.am commit 30e65047201ead74567326059a9f17cf600f83fa Author: Andreas Schwier Date: Wed Aug 22 18:33:31 2012 +0200 sc-hsm: Add registry key to enable SmartCard-HSM for use with minidriver commit cb46192d89cd3b791e83854cbe6985f048a46633 Author: Andreas Schwier Date: Wed Aug 22 12:45:03 2012 +0200 minidriver: Added support for ASCII encoded serial numbers commit d180fc324c8da81c32e298a3403c8af08333c631 Author: Andreas Schwier Date: Mon Aug 20 20:36:55 2012 +0200 sc-hsm: Added basic cvc support to read device identity. Used for token serial number and GUID generation commit f8cdb0856f95179c9de0a624cdcd02c6fbe584ac Author: Viktor Tarasov Date: Thu Aug 23 09:23:23 2012 +0200 asn1: 'asn1_prkey' members order Unwary rebase of Frank Tater's proposal introduced the bug of invalid order of the sub-key types in the 'asn1_prkey' array in 1a7ca32865 . commit 2e0ef7a0ab3d92fa3d20f6a7c15e48291fbdaef0 Author: Andreas Schwier Date: Thu Aug 16 16:00:30 2012 +0200 sc-hsm: Improved usablity in opensc-explorer commit 46efb61a2b844990500f667bbbcc120882e7bf37 Author: Andreas Schwier Date: Mon Aug 13 15:37:08 2012 +0200 sc-hsm: Fixed field size constants for 320 bit curve commit 0fa6e7f97773aa3a3eb71f03b25a8fa53b7c6c91 Author: Andreas Schwier Date: Mon Aug 13 12:45:11 2012 +0200 pkcs11-tool: Added CKM_SHA256_RSA_PKCS to tests in test_signatures commit efb4673ec8e3c307062f89600b7c890b349d4336 Author: Andreas Schwier Date: Mon Aug 13 11:34:18 2012 +0200 Revert change to pkcs11-tool because of upstream changes in RSA signature tests Fixed a typo that went undetected commit 8e8acf1ff6dd4b9694f15a0a3baece068b9e4945 Author: Viktor Tarasov Date: Sun Aug 12 21:19:57 2012 +0200 pkcs11-tool: implement 'find mechanism from the given list' and use it when testing signature commit dc927fbe3cbbcff85a267e3ada509b7ee9857c91 Author: Andreas Schwier Date: Sat Aug 11 21:21:31 2012 +0200 sc-hsm: Undo the change to mechanism.c in commit f24bcd346340d80b552c0859942a49fd5e8feeff sc-hsm: Change ECDSA signature format from DER to r/s format commit 03632f336e5987d31fc187a5489140987c37089b Author: Andreas Schwier Date: Sat Aug 11 17:47:21 2012 +0200 sc-hsm: Added support for ECDH commit e0f9edb121cb8bfe40c0364584a9b3f5f3383d58 Author: Andreas Schwier Date: Fri Aug 10 12:20:48 2012 +0200 sc-hsm: fixed to be compiled with Microsoft VC commit f678b68650dd4a5c0588b5a5fa8837141a011d41 Author: Andreas Schwier Date: Tue Aug 7 16:00:42 2012 +0200 Activated ECDSA for SmartCard-HSM Fixed issues in pkcs11-tool/test_signature is card has RSA and ECDSA keys Fixed bug in sc_pkcs11_signature_size that returns the wrong ECDSA signature size commit 4ff917bd251752d9fc1859a2d36d131d3126a94f Author: Frank Thater Date: Mon Aug 6 11:28:35 2012 +0200 Added obj files to Makefile.mak for windows build Fixed WIN32 compiler errors Adjusted log message calls to short form modified: src/libopensc/Makefile.mak modified: src/libopensc/card-sc-hsm.c modified: src/libopensc/pkcs15-sc-hsm.c commit 1a7ca328659fdcd11a4755dcab5645f9a2b6dff8 Author: Frank Thater Date: Tue Jul 31 14:57:00 2012 +0200 Added support for SmartCard-HSM and ECC keys modified: src/libopensc/Makefile.am new file: src/libopensc/card-sc-hsm.c new file: src/libopensc/card-sc-hsm.h modified: src/libopensc/cards.h modified: src/libopensc/ctx.c modified: src/libopensc/pkcs15-prkey.c new file: src/libopensc/pkcs15-sc-hsm.c modified: src/libopensc/pkcs15-syn.c commit feb2b96127571900b8dfd00cf0c445b96eabbf1e Author: Peter Marschall Date: Mon May 28 18:49:05 2012 +0200 opensc-explorer: allow filename-pattern as argument to 'ls' Make ls more flexible and more similar to the UNIX ls. commit 2f3ace06eca769747b1fa09d510b21020fc224e9 Author: Viktor Tarasov Date: Sun Aug 19 20:07:17 2012 +0200 doc: #329: explicit usage of --id for a new keys is deprecated 'Id' option in the pkcs15-init commands to import/generate a new key is deprecated. Better s to let the MW to derive an identifier from the key material. commit ca08e97ab751654a753c60986a8d6e491d6e289a Author: Viktor Tarasov Date: Sun Aug 19 18:02:16 2012 +0200 pkcs11: #162,#370,#115,#413: reselect application DF issue Limit the number of cases when applicated re-selection of application DF to strict minimum. I.e. only when pkcs11 login session is not locked and private key PKCS#15 object do not contain the 'path' attribute. commit cb54ebf747e0a8fd11718c51560efc35102996ad Author: Viktor Tarasov Date: Thu Aug 16 16:00:35 2012 +0200 pkcs11: #371: Public key create template should not include CKA_MODULUS_BITS Thanks to 'z22' and Martin. commit 080010d21139c41ab452021ea7330e1f205f660b Author: Viktor Tarasov Date: Thu Aug 16 15:04:14 2012 +0200 pkcs15: #268: Wrong ASN1 tagging of GOSTR3401Key "The encoding of {public,private}GOSTR3410Key uses tag [CONTEXT 3] which is reserved for KEAKey. Caused by the fact, that the specifications (pkcs15,iso) don't define a encoding for GOST, the genericKey encoding [CONTEXT 4] from iso-7816 should be used." (Andre) commit 5a0cd4139f6de866063571d4f3aaf547f329f60c Author: Viktor Tarasov Date: Thu Aug 16 14:18:08 2012 +0200 libopensc: #311: possible memory corruption Thanks to 'frankmpunkt'. commit c6065fcfe2fbd3cb2bebffe304015da1dea6ba06 Author: Viktor Tarasov Date: Thu Aug 16 10:59:03 2012 +0200 doc: #377: no optional parameters for sc_context_create() thanks to 'TimBarton'. commit 12ec13d43c74ffc5329d044b6c85de5f3584f09a Author: Stef Walter Date: Tue Oct 4 20:05:44 2011 +0200 libopensc: Make OPENSC_DEBUG environment variable without conf file * Make the OPENSC_DEBUG environment variable work even when no conf file is available. https://www.opensc-project.org/opensc/ticket/388 Signed-off-by: Viktor Tarasov commit 7e42c6d97d35d51b2ce7669082c2d8d693d406e4 Author: Viktor Tarasov Date: Wed Aug 15 23:06:58 2012 +0200 pkcs11-tool: #407: on Windows use O_BINARY open file mode https://www.opensc-project.org/opensc/ticket/407 http://msdn.microsoft.com/en-us/library/ee2849wt%28v=VS.71%29.aspx commit dfbc3996bf1d714ad45ac82d8294bcde73d56dd6 Author: Viktor Tarasov Date: Wed Aug 15 19:59:09 2012 +0200 pkcs11: #439: 'SEQUENCE' of 'SET' issue when comparing cert attributes Thanks to 'crank'. https://www.opensc-project.org/opensc/ticket/439 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. commit 16b4cb6a3fbfe55c3937affa03eee3a11fc9da4e Author: Viktor Tarasov Date: Tue Aug 14 15:37:24 2012 +0200 MyEID: always select in mode 'return FCI template' According to ch.4.2 of MyEID reference manual v1.7.6 the only possible value of P2 of 'SELECT' APDU is '00'. For this reason, when caller do not request to return 'sc_file' data, use the non-null dummy 'sc_file' pointer in the call of iso->select_file, and thus avoid the P2 different from '00'. Also log calls are replaced by its short forms, and resolved the 'trailing spaces' issues. commit 25f7bc5ac58758cda598e91f5082bdb0e740e161 Author: Viktor Tarasov Date: Sun Aug 12 21:19:57 2012 +0200 pkcs11-tool: implement 'find mechanism from the given list' and use it when testing signature commit 1adbb3fae7d7cef08816a30eef7d84cdb03dd373 Author: Nguyễn Hồng Quân Date: Wed Aug 8 10:12:11 2012 +0700 OpenPGP: Set write access more restrictive for pubkey blobs. marschap: "The WRITE_ALWAYS ACL tells anyone can write to this file at any time." commit a3b516a1e1b31d546d766e8c3aecde0c154e5aaf Author: Doug Engert Date: Tue Aug 7 13:53:44 2012 -0500 Add pin_cache_ignore_user_consent parameter to opensc.conf When OpenSC is used with a card that enforces user_consent and the calling PKCS#11 application does not understand how to handle the CKA_ALWAYS_AUTHENTICATE, signature operations will fail. OpenSC will not cache a PIN that protects a user_consent object as one would expect. This mods allows PINs to be cached even if protecting a user_consent object by adding pin_cache_ignore_user_consent = true; option in opensc.conf. Thunderbird is the prime example of this situation. Mozilla has accepted mods (357025 and 613507) to support CKA_ALWAYS_AUTHENTICATE that will appear in NSS-3.14 but this may be some time before this version is in vendor distribution. commit 4e44cabcf02dcdb5df33d4d8b7a3ad7dae9a5b28 Author: Doug Engert Date: Tue Aug 7 13:07:48 2012 -0500 Only 9C key needs "Pin Always" i.e. user_consent The 9D key does not nee the user_consent flag, as the PIV card only enforces the "PIN Always" for the 9C signing key. Various comments cleaned up. commit 4035245649632106616cb2f82312263574ed1282 Author: Peter Marschall Date: Sun Jun 17 18:55:24 2012 +0200 opensc-explorer: allow longer PINs in CHANGE & UNBLOCK Harmonize the allowed PIN length in CHANGE & UNBLOCK with the one in VERIFY, making sure they are large enough for OpenPGP, which allows up ro 32 characters, and giving additional security margin for other cards. commit 71ec763604aac8b3eb15d69ce2549531eb001526 Author: Viktor Tarasov Date: Sat Aug 11 20:27:49 2012 +0200 opensc-explorer: warnings when compiling with Windows VC opensc-explorer.c(447) : warning C4101: 'count' : unreferenced local variable opensc-explorer.c(940) : warning C4013: 'strlcpy' undefined; assuming extern returning int opensc-explorer.c(1683) : warning C4013: '_isatty' undefined; assuming extern returning int opensc-explorer.c(1726) : warning C4090: 'function' : different 'const' qualifiers commit 7f22ede919d17fa0b584d31a8bb578f0f2a46d85 Author: Peter Marschall Date: Sun Jun 17 21:17:09 2012 +0200 opensc-explorer: show text message for APDU result commit 95cbfbb7999743396f9ce26a745341caa45a82a2 Author: Peter Marschall Date: Sun Jun 17 21:16:17 2012 +0200 opensc-explorer: increase allowed bytes in RANDOM Allow to fetch up to 256 random bytes from the card - 236 being the maximal value for Le in short APDUs. commit ea8a64d95a4f6d303588d8d72dcc632e4cc646d5 Author: Peter Marschall Date: Sun Jun 17 13:05:52 2012 +0200 opensc-explorer: allow entering the PIN interactively In VERIFY, allow the user to enter the PIN unteractively if it was not given on the command line, and if the card reader does not support PIN input. If it was not given on the command line and the card reader supports PIN input, then the bahaviour is unchanged: enter PIN via card reader. commit 322e3cf40386d5efbaf9d4d0e1fb1d50dce24110 Author: Peter Marschall Date: Sun Jun 17 16:48:21 2012 +0200 opensc-explorer: convert CHANGE & UNBLOCK to sc_pin_cmd commit 81695df211e40ec3a652273e331926303d18c3a3 Author: Peter Marschall Date: Sun Jun 17 18:46:08 2012 +0200 opensc-explorer: use standard usage() everywhere With the improvements of the man page, there is no need any more to have local help texts for functions. commit 5f47903a73b06593dfe20e12ab0b5900bbe414d7 Merge: c5dd9de 72c4060 Author: Ludovic Rousseau Date: Wed Aug 8 00:13:49 2012 -0700 Merge pull request #80 from LudovicRousseau/staging Fix typos in NEWS file commit 72c4060f1d20656d4d8f4808400a33974f2e3635 Author: Ludovic Rousseau Date: Tue Aug 7 00:16:47 2012 +0200 Fix compilation error on Mac OS X In file included from pkcs15.c:30: cardctl.h:870: error: expected specifier-qualifier-list before 'time_t' Change-Id: I5faad5462ba6268fd7cf48a04f41e1755597ad0c commit 9bfe3fed831420d7628759415d50aeca6cd20563 Author: Ludovic Rousseau Date: Mon Aug 6 09:10:28 2012 +0200 More typo fixed commit 6849c909b38d31d7612cc5dfa445b4288c98d445 Author: Ludovic Rousseau Date: Mon Aug 6 09:02:42 2012 +0200 Fix typo commit 12b545f9a9edb6a1f72fff7a2440275415c26d1e Merge: a63c924 c5dd9de Author: Ludovic Rousseau Date: Mon Aug 6 09:01:52 2012 +0200 Merge remote-tracking branch 'upstream/staging' into staging commit c5dd9decaa1ee8f90c0f4eec1917db871b070d6a Author: Viktor Tarasov Date: Sun Aug 5 19:10:06 2012 +0200 release 0.13.0: update NEWS file commit c7827e5e4b309cf8ad3cd45aab9cf691d74c7e7d Author: Viktor Tarasov Date: Sun Jul 29 21:41:44 2012 +0200 sm: change SM data types, thanks to Frank Morgner for revision http://www.opensc-project.org/pipermail/opensc-devel/2012-July/018232.html commit 7301715624b472d62e19208d1ebf949675852328 Author: Viktor Tarasov Date: Sun Jul 29 21:40:47 2012 +0200 sm: link 'local' SM module with OpenSSL's crypto library commit 49e7f529b2a987706dc6684f250832b95e5ac4d9 Author: Viktor Tarasov Date: Sun Jul 29 21:38:39 2012 +0200 sm: misplaced check of the 'get-sm-apdus' return code commit e34866f18819e05f6c89a50c9c3b69929887e580 Author: Nguyễn Hồng Quân Date: Tue Jul 17 20:24:42 2012 +0700 OpenPGP: Accept authentication key for S/MIME decrypting. The card contains only 1 certificate, which can be used for encrypting. But this certificate is bound with authentication key, so when decrypting, the authentication key will be presented to check. This commit allows to bypass the check in driver. However, it is not enough. The users have to import the same key to "Encryption key" to help the card find right key to work. OpenPGP: Add log and comments. OpenPGP: Pretend to select dummy files. Some files are needed by pkcs15init, but not exist in OpenPGP card. We pretend to know these dummy files to make pkcs15init successful. Compilation error on windows: when declaring array use explicit size, add pkcs15-openpgp.obj in Makefile.mak commit 241bfded4d62c4576b58ada36644f55da4e30593 Author: Nguyễn Hồng Quân Date: Fri Jul 6 11:01:47 2012 +0700 OpenPGP-pkcs15init: Support generating key via pkcs15-init tool. OpenPGP-pkcs15int: Add more debug log. OpenPGP-pkcs15init: Add more checks in key generation. Check for key ID. Set default key. Check for result of key generation from driver. commit 30e61d9fbf4dd87b3076ee7f8a10575e311d45a8 Author: Nguyễn Hồng Quân Date: Thu Jul 5 16:49:01 2012 +0700 OpenPGP: Handle access on dummy files, which is assumed to exist by pkcs15init. OpenPGP: Update card algorithms after importing key. OpenPGP: Add SC_ALGORITHM_ONBOARD_KEY_GEN flag to card algorithms. commit b67063dcd836e417250886c55d8eb6c0fabfbd66 Author: Nguyễn Hồng Quân Date: Thu Jun 21 16:11:58 2012 +0700 pkcs15init-openpgp: Support for private key import in pkcs15init. Example command: pkcs15-init --delete-objects privkey,pubkey --id 3 --store-private-key quan-key.pem --auth-id 3 --verify-pin --extractable --id 3 pkcs15init-OpenPGP: Some parts in openpgp.profile are not used. commit 7123638454e2098dd766d6bac8667a69bbbf3602 Author: Nguyễn Hồng Quân Date: Thu Jun 21 15:22:44 2012 +0700 OpenPGP: Correct access condition for some pubkey blobs. OpenPGP: Don't really delete pubkeys blobs. commit affb38304e1241c6d68664b596898617d3189bc6 Author: Nguyễn Hồng Quân Date: Wed Jun 20 18:03:09 2012 +0700 pkcs15-init tool: Change the order of action. DELETE_OBJECT will be done before STORE_XXX. commit 70b7b4aa54f9748fdf2e4a3e4179fe5856e4bad6 Author: Nguyễn Hồng Quân Date: Wed Jun 20 17:56:54 2012 +0700 pkcs15init-OpenPGP: Add store_data functions. We need this function to use OpenPGP's specific action flow instead pkcs15init's default. This will help to avoid redundant steps which may make the overall process fail. commit 0518ced8e0e472a75603c9057038339a1d67ad15 Author: Viktor Tarasov Date: Sun Jul 29 12:56:03 2012 +0200 asn1: export 'asn1-write-element' function ... used by OpenPGP driver commit 42ad3c1dff1ec5324e780961fb20807e3f289996 Author: Nguyễn Hồng Quân Date: Mon Jun 18 09:43:25 2012 +0700 OpenPGP: Add support for key import at driver level. OpenPGP: Some indentations need to be tab-size-independent. OpenPGP: Check for null data when storing fingerprints. OpenPGP: Allow to provide creation time to store (when gen/import key). Old: Only store current time. New: Can provide time to store, not only calculate current time. OpenPGP: Correct setting content of pubkey blobs after key generation. cardctl: Add definitions to support key import in OpenPGP. OpenPGP: Add support for key import at driver level. commit 69e30ead8998abab129b9319a51f134fd324f84a Author: Nguyễn Hồng Quân Date: Wed Jun 6 17:04:33 2012 +0700 openpgp-tool: Key generation support. openpgp-tool: PIN verfication support. openpgp-tool: Add notification in case of error. openpgp-tool: Add manual for key generation and PIN verification. commit 50e37aa849e697c940276554212ad42c51a78787 Author: Nguyễn Hồng Quân Date: Thu Jun 14 09:05:16 2012 +0700 OpenPGP: Correct the way to set output after generating key. Correct the way to parse response data. Updated wrong blob for pubkey info <~~ Fix. OpenPGP: Store creation time after generating keys. OpenPGP: Put_data: Handle the case that DO exists but its blob does not. When checking DO before writing, relying on blobs only will miss the case that DO exists but its blob does not, when DO is non-readable. OpenPGP: Set algorithm attributes before generating key. OpenPGP: Add dependency of OpenSSL. OpenPGP: Calculate and store fingerprint. Calculate and store fingerprint after generating key. OpenPGP: Update blob of pubkey info. Update blob holding pubkey info after generating key. OpenPGP: Add step to update card algorithms. Update card algorithms after generating key. However, this step is not implemented yet, because of suspection about wrong data (see code comment). commit 8261490ec1abd90af83328c176143fe516fdc9ae Author: Nguyễn Hồng Quân Date: Mon Jun 4 09:54:40 2012 +0700 PKCS15-OpenPGP: Change the way emu_init return in error case. commit ed0e2be30573b6428f0f31e5ec92682c45b675c9 Author: Nguyễn Hồng Quân Date: Fri Jun 1 17:48:30 2012 +0200 Add pkcs15init-binding for OpenPGP commit 58a4821689b3587c443edeaf7706ae876112f445 Author: Doug Engert Date: Mon Jul 23 13:38:32 2012 -0500 piv-tool -s not sending APDU - missing code The code to send the APDU to the piv card when using piv-tool -s xx:xx:xx... was inadvertently removed on 2011-04-26 02:29:53 by: 1cdb3fa9716bf287387020f06892a5f45cde98fc APDU parsing: switch to Frank Morgner's implementation The missing code is replaced. The -s option is infrequently used, so the problem was not spotted earlier. commit 124d6e3a407415c051d862771ab813a73ec8a498 Author: Kalev Lember Date: Mon Feb 13 14:34:31 2012 +0200 esteid: Do not set CKA_ALWAYS_AUTHENTICATE for the "Allkirjastamine" key CKA_ALWAYS_AUTHENTICATE implies CKU_CONTEXT_SPECIFIC login, but all this key really should need is a C_Login with CKU_USER. The historical reason for having CKA_ALWAYS_AUTHENTICATE set was to keep Firefox/NSS from using that particular key for SSL connections. However, starting with Firefox 8, NSS ignores Non Repudiation certificates for SSL and that makes the CKA_ALWAYS_AUTHENTICATE workaround unnecessary. Now that Firefox is fixed, drop the workaround in OpenSC so that applications that follow the pkcs11 spec wouldn't have to login twice to access the key. commit 4e5ef8f1c93442b54cd0067db394746a603c8cb6 Author: Nguyễn Hồng Quân Date: Mon Jun 4 09:54:00 2012 +0700 PKCS15 - OpenPGP: Add more comments. commit 0b469ed31e7620e1bfb9e8b707bfe4cb9e94aee9 Author: Nguyễn Hồng Quân Date: Mon Jun 18 08:53:40 2012 +0700 OpenPGP: Add an explanation when building APDU. commit 4b9ca80434be3e3b0be64e7c10929ad304593fb4 Author: Peter Marschall Date: Fri Jun 15 20:00:20 2012 +0200 tools docu: correct typo, improve style commit 8583197b6b7907f7986b36bacae75ff6aca95892 Author: Peter Marschall Date: Sun Jun 17 18:45:33 2012 +0200 opensc-explorer docs: various improvements * document the UNBLOCK function * format the examples better, and explain what they do * extend the documentation for CD commit cf6b5e79038a1316a5a237d2a7b89fcf28c0388b Author: Nguyễn Hồng Quân Date: Tue May 29 17:27:24 2012 +0700 pkcs15init: Add more debug log. commit 31bdbd6b6a7678151288bc51c413ea42a342b00b Author: Frank Morgner Date: Tue Jul 3 22:27:43 2012 +0200 use error code if no SM was applied commit 1d6fae224176bff266b443ef09770d91b76908fe Author: Diego Elio Pettenò Date: Sun Jun 24 01:13:04 2012 -0700 build: use autoconf's MKDIR_P not automake's (deprecated) mkdir_p. commit e423bac47438cb6e9e0a9ae551afede03d8b994a Author: Mathias Brossard Date: Thu Jun 28 16:59:40 2012 +0200 Move case block so that fall-through works as intended commit bfbef6fe2bad58a767efa5677fd43d057717fa7e Author: Mathias Brossard Date: Thu Jun 28 16:56:59 2012 +0200 Support key type inference for CKM_GOSTR3410_KEY_PAIR_GEN commit 3fc4547e45710f8292e5c181212213e72c4c268d Author: Mathias Brossard Date: Mon Jun 25 17:45:19 2012 +0200 Add CKM_GOSTR3410_KEY_PAIR_GEN if card supports onboard generation commit 0914b1eca773769731b766d728a7419b6559ffd0 Author: Viktor Tarasov Date: Tue Jun 19 18:04:37 2012 +0200 libopensc: SM: not all the APDUs need to be wrapped For some cards some APDUs are always transmitted in a plain mode, even if SM session is opened. For these APDUs the 'get_sm_apdu' card's handler returns SUCCESS without wrapped APDU version. In such cases 'transmit' is called for the plain APDU. commit 68e217ceee253083f73c39f2359fcd0904964969 Author: Viktor Tarasov Date: Tue Jun 19 17:43:01 2012 +0200 pkcs15: free NULL key is not error when freeing key object, do not throw an error if supplied key pointer is NULL; sc_pkcs15_free_prkey() procedure should not free the supplied key pointer, the body of this procedure is replaced by body of sc_pkcs15_erase_prkey(). staitc sc_pkcs15_erase_prkey() is not more used. commit f9a13179d88bb1aa09b3d3ca74c93991ec92ecd4 Author: Viktor Tarasov Date: Tue Jun 19 17:37:51 2012 +0200 AuthentIC: when using SM decrease max-send-size value update list of APDUs that always sent in plain (non SM wrapped) mode commit 34541e95c48b25b6e2944699e3fe4738445f8b26 Author: Peter Marschall Date: Sun Jun 17 10:49:00 2012 +0200 OpenPGP: add LOG_FUNC_CALLED() for "symmetric" logging commit 8db8f76bad9bb123d07a1d6fc18dfb5b56d35ae4 Author: Nguyễn Hồng Quân Date: Thu Jun 14 09:17:28 2012 +0700 OpenPGP: Change whitespace. commit ccb1067d7582a758db8abc54b224153203a1427b Author: Nguyễn Hồng Quân Date: Wed Jun 13 18:31:33 2012 +0700 OpenPGP: Correct the APDU preparation step for key generation. commit 0b0aae8bc866c72cada45fb52332b1257e589b83 Author: Peter Marschall Date: Sat Jun 16 15:26:19 2012 +0200 openpgp-tool: remove unnecessary commas in man page commit db06b043ca237d49e1b34221ae3a137a17cff64a Author: Peter Marschall Date: Fri Jun 15 14:56:46 2012 +0200 PKCS15 - OpenPGP: re-factor certificate object init restrict scope of some variables. commit 735883d8f616acef84840d513a7e85596fe74eae Author: Nguyễn Hồng Quân Date: Thu Jun 14 08:37:22 2012 +0700 PKCS15 - OpenPGP: Change way to check certificate object. Old: If cert is missing, raise error. New: If cert have data, declare object, otherwise, just ignore it. commit 5e295ba08d4e44ebda8fab5b8d601fc1249b7884 Author: Nguyễn Hồng Quân Date: Thu Jun 14 09:10:17 2012 +0700 OpenPGP: Remove unused constants. commit c64a5f34d32926a0131a907514779c4a694823f9 Author: Viktor Tarasov Date: Wed Jun 13 00:22:50 2012 +0200 pkcs15init: application path selection in bind procedure Application path can contain non-zero length path value and AID. In this case select AID as DF_NAME only if length of path value is zero. Segfault: dereferencing NULL pointer, thanks to Magosányi Árpád commit d3dbe444e148258fbffdfc48668ba483784d4973 Author: Viktor Tarasov Date: Mon Jun 11 21:28:37 2012 +0200 pkcs15-init: coverity scan warning NEGATIVE-RETURNS CID 402540: Argument cannot be negative (NEGATIVE_RETURNS) commit 61254f8651b60d19e0ac1baae5427e7e8a620ede Author: Viktor Tarasov Date: Mon Jun 11 21:16:47 2012 +0200 muscle: coverity scan warning OVERRUN-STATIC CID 402559: Out-of-bounds access (OVERRUN_STATIC) CID 402558: Out-of-bounds access (OVERRUN_STATIC) commit ce23bba622c1b1c8ca4c8d1553dab98a370bdc3e Author: Viktor Tarasov Date: Mon Jun 11 21:08:21 2012 +0200 gemsafeV1: coverity scan warning OVERRUN-STATIC CID 402560: Out-of-bounds read (OVERRUN_STATIC) commit aa46a210fc0533629335e393a3a519240652865a Author: Viktor Tarasov Date: Mon Jun 11 20:59:45 2012 +0200 iasecc: coverity scan warning OVERRUN-STATIC CID 402562: Out-of-bounds read (OVERRUN_STATIC) commit 12dcf132369d14742ff2323bec29ec9edda8d0aa Author: Viktor Tarasov Date: Mon Jun 11 20:10:42 2012 +0200 pkcs15-init: coverity scan warning RESOURCE-LEAK CID 402619: Resource leak (RESOURCE_LEAK) commit c6f4e09477ee8c41843530c7432ff5d77a6d5c83 Author: Viktor Tarasov Date: Mon Jun 11 20:04:08 2012 +0200 pkcs15-tool: coverity scan warning RESOURCE-LEAK Coverity scan CID 402622: Resource leak (RESOURCE_LEAK) commit 62eb0d05d7fa5829c46f6b861bf299705e984cc4 Author: Viktor Tarasov Date: Mon Jun 11 19:59:36 2012 +0200 jcop: coverity scan warning RETURN_LOCAL CID 402623: Pointer to local outside scope (RETURN_LOCAL) commit 629c0e8df496ccff212b93cb83b3c2b12825be1b Author: Viktor Tarasov Date: Mon Jun 11 19:45:26 2012 +0200 libopensc: resource leak coverity scan warning Coverity warning CID: 203380: RESOURCE_LEAK; use short call form of the debug messages; code layout. commit 9466891752abb7fb6d7bd826f47a37e6976e56c7 Author: Viktor Tarasov Date: Mon Jun 11 18:48:37 2012 +0200 openpgp: coverity scan warning MISSING_RETURN Coverity Scan CID: 709045 commit a63c924c37d16702c25ad492be3bfd4d8390a070 Merge: d7e09b4 a305f4a Author: Ludovic Rousseau Date: Mon Jun 11 13:55:54 2012 +0200 Merge remote-tracking branch 'upstream/staging' into staging commit a305f4a99b330589a20ecdb28a70fe3df4b9230d Author: Viktor Tarasov Date: Sun Jun 10 14:32:46 2012 +0200 build: increase minor version number let's start to prepare next release commit a09f3048173dd61df38eaca2756200a5f4368b70 Author: Peter Marschall Date: Thu Jun 7 20:31:44 2012 +0200 OpenPGP: clean up & fix pgp_delete_file() * use LOG_FUNC_CALLED() .. LOG_FUNC_RETURN for "symmetric" logging * don't zero-fill the DO's contents but empty it * get rid of unnecessary variables * select parent DF after deletion (required by to ISO 7816-9) * don't try to delete MF commit 2aa4cf57eda8bd81b6d5558247cdf3d7fd6509c9 Author: Peter Marschall Date: Fri Jun 8 10:18:46 2012 +0200 OpenPGP: simplify pgp_update_binary() even more Fail on idx > 0 in order to avoid the requirement to read from the DO. The DO may be read-protected, and this might either fail or produce wrong results. commit d7f58f7ea7131b30cf99f65c77cf6bc531784077 Author: Peter Marschall Date: Thu Jun 7 19:55:21 2012 +0200 OpenPGP: cleanup & fix pgp_update_binary() * use LOG_FUNC_CALLED() .. LOG_FUNC_RETURN for "symmetric" logging * update comment * check that blob->data is defined * fix writing new data to the correct offset * use calloc() instead of malloc() & memset() * align pgp_ops function pointer list commit c8e802eab6ccfc6e2a0fa5265e82660e7bdbe258 Author: Peter Marschall Date: Thu Jun 7 19:31:02 2012 +0200 OpenPGP: simplify & clean up pgp_put_data() * make sure variables of type u8 do only get passed fitting data * use LOG_FUNC_CALLED() .. LOG_FUNC_RETURN for "symmetric" logging * leave most of the spcial casing in ADPU handling to sc_adpu_transmit() * use SC_ADPU_CASE_1 for empty buffer (avoids special casing Lc=0) * clean up log strings & comments commit 0010fcbe6b9c5356b8a626364d18edfee04c394a Author: Peter Marschall Date: Fri Jun 8 11:00:18 2012 +0200 OpenPGP: make pgp_set_blob() a bit more resilient * use calloc() instead of malloc() to have defined contents * only copy from passed data when it is not NULL commit 73387d4b847fb937b8f4982074cefd4577e06420 Author: Nguyễn Hồng Quân Date: Sun Jun 3 11:01:19 2012 +0700 OpenPGP: Correct ACL key_ref Correct ACL key_ref after redefining conventional OpenPGP key-ref (0x81-0x83 -> 1-3). commit 825c8578a9a84bc2963883eb2e45e10940173605 Author: Peter Marschall Date: Sat Jun 2 17:33:00 2012 +0200 tools: fix typo/thinko in util_print_usage_and_die() Fix a typo/thinko in util_print_usage_and_die() that led to the short option names not to be shown at all. commit a56bebd952591320ca2fc1e5a5721bd2df9afc0d Author: Peter Marschall Date: Sat Jun 2 17:27:56 2012 +0200 opensc-explorer: clean-up help text "asn1 file" -> "ASN.1 file" commit 56affa612f909f1a7710cb4cde247ab2e67cfc76 Author: Peter Marschall Date: Sat Jun 2 17:25:26 2012 +0200 pkcs15-tool: help text clean-up "to be binded to " -> "to bind to" commit 67136befe8da3842a23bc50308db44376842a2ef Author: Viktor Tarasov Date: Thu Jun 7 22:38:40 2012 +0200 pkcs15-tool: print content of DATA object ... list the 'Authentication' objects instead of 'PIN' objects. commit 73f324010e426c9ac66a8b0e4468ec98243e0baf Author: Viktor Tarasov Date: Thu Jun 7 22:35:30 2012 +0200 config: ignore non 'auto-configurated' values When getting string configuration parameter, ignore non 'auto-configurated' in configuration file value (ex. @SOME_VALUE_IN_OPENSC_CONF@) and return it's default value. commit 7c05e8ce21b7e709770a1a9721e8e62aae94b317 Author: Viktor Tarasov Date: Thu Jun 7 22:22:58 2012 +0200 win32: build debug version, updates to MSI Build with debug options, include profiles and SM module into MSI. commit da894500c9a014cc3f87de82c3a973e39fb4fadd Author: Viktor Tarasov Date: Thu Jun 7 22:13:33 2012 +0200 minidriver: build dependence on opensc_a.lib and pkcs15init.lib spelling of comments and debug messages commit 667949019abbf51875d3dfdcacac40f909116ad4 Author: Viktor Tarasov Date: Thu Jun 7 22:12:37 2012 +0200 coverity scan: warning use non-initialized data commit 0410a0c9e8a7ff7bf506ba10ba777d99205a3a57 Author: Viktor Tarasov Date: Thu Jun 7 19:16:07 2012 +0200 build: 'auto-config' parameters In configuration file replace the 'auto-config' parameters with the windows specific values. commit 78fe16654e38efbf01a4401729609ac3a5103043 Author: Viktor Tarasov Date: Thu Jun 7 18:52:03 2012 +0200 pkcs15init: iasecc: create objects for minidriver support - Create/delete the PKCS#15 'DATA' objects destinated to supply support of minidriver. For a while only 'Gemalto' style of such support is implemented. - Declare epass2003 pkcs15init operations. - include into OpenSC configuration the SM related sections commit 92e0c94c3b588d329f160d0d0f115056cd22e70f Author: Viktor Tarasov Date: Mon Jun 4 14:30:03 2012 +0200 bootstrap: script argument to define the PACKAGE_SUFFIX commit 2249cfde72a48145967efc34c8f624c7890e1c6d Author: Viktor Tarasov Date: Mon Jun 4 13:12:28 2012 +0200 sm: loadable 'local' secure-messaging module With loadable secure-messaging module the SM wrapping of APDUs is performed by entity external to the running application. So that the SM keysets TODO: amend detailed description commit 00b069a08e43397e02571674d105c38b5967f459 Author: Viktor Tarasov Date: Mon Jun 4 11:52:50 2012 +0200 epass2003: support for ePass2003 card in read/write modes commit ea5a19e27eaedb5314a71c416c5741aef85f967f Author: Viktor Tarasov Date: Mon Jun 4 11:18:23 2012 +0200 iasecc: SM add related procedures commit cfd5aaba7d4f87372f72d2b0849d4eb435671b9a Author: Viktor Tarasov Date: Mon Jun 4 09:26:56 2012 +0200 SM: initial implementation of secure messaging framework commit 2078654d2bd166b05b793aad03ac5cecd0265093 Author: Viktor Tarasov Date: Fri Jun 8 18:58:20 2012 +0200 OpenPGP: to be compiled on windows commit 9e04ae46bb42aeabd94cc49de2db68927cdb3bb2 Author: Peter Marschall Date: Sat Jun 2 08:51:09 2012 +0200 OpenPGP: implement pgp_find_blob() Replace the "one-trick-pony" pgp_do_iswritable() with a more generic function returning the blob matching the passed tag. This way we can get rid of the one-line function pgp_blob_iswritable() too. comparisons like these can be done in the caller. commit f5dc252aa96ca1f3116785f97bc05ab49f394e00 Author: Peter Marschall Date: Sat Jun 2 08:13:55 2012 +0200 OpenPGP: set pin references to 0x01 - 0x03 Set pin references to 0x01 - 0x03 instead of 0x81 - 0x83. The PINs are referenced as PIN1- PIN3 (resp. PW1 - PW3) in the OpenPGP card specification. Technically the APDUs to verify/change the PINs contain the values OR-ed with 0x80, but this is just a technical detail of the implementation which the emulated file system can hide in pgp_pin_cmd(). Pros & Cons: + consistent PIN naming + no trouble entering the correct PIN names in opensc-explorer et.al. ("verify CHV1" is way better than "verify CHV129") - manually entering the correct APDU for VERIFY is a bit more complex. (who does this anyway, when there are better functions) While at it, change if .. elsif ... cascade to switch statement. commit 89c1dd37e430bfe24b75aa2bd68a65d63a1d462c Author: hongquan Date: Sat Jun 2 12:14:38 2012 +0700 OpenPGP PKCS#15: Add declaration of certificate object. Allow to read certificate via PKCS#11 (not writing yet). commit f515ed5be36036e237bfa80a95d3dd7b17484aac Author: hongquan Date: Sat Jun 2 16:51:51 2012 +0700 OpenPGP: Change the criteria to use ext APDU for put data. Saner whitespace in logs Correct spelling commit df39590aa40254968307adcc59ba26fe01460669 Author: hongquan Date: Sat Jun 2 16:51:10 2012 +0700 OpenPGP: Correct adding read access to ACL. commit e269907e1db4e9e5a40a736c057e689aaeb780d1 Author: hongquan Date: Sat Jun 2 17:09:12 2012 +0700 OpenPGP: support key generation in driver. commit 9dd5a64d43549634e03d985fc3a8a67e067b791a Author: Nguyễn Hồng Quân Date: Wed May 30 17:31:39 2012 +0700 OpenPGP: Support another variant of PUT DATA to write to Extended Header list. commit fefe8bf2e6da3b6483fac80495680961c829527b Author: Nguyễn Hồng Quân Date: Wed May 30 17:13:36 2012 +0700 OpenPGP: Check data size in pgp_put_data. commit f188a5dda3b8447a413e677280e513f887d57a65 Author: Nguyễn Hồng Quân Date: Tue May 29 16:20:08 2012 +0700 OpenPGP: Implement pgp_update_binary() to update file content. commit 935a7505d7e498d9a5125660dd1495ee6e4a21dd Author: Nguyễn Hồng Quân Date: Fri Jun 1 20:00:13 2012 +0200 OpenPGP: Add Access Control List for emulated files. commit c8915449903801db84c5a559ce3c731e3590515a Author: Nguyễn Hồng Quân Date: Fri Jun 1 19:57:26 2012 +0200 OpenPGP: Use command chaining to send large data if extended APDU is not supported. commit 387da4c79314522405ce25ba29fb5ce0930a3a16 Author: Peter Marschall Date: Thu May 31 21:22:14 2012 +0200 opensc-explorer: document 'echo' command commit f3955e24752de2452ef1e2a9a4115d042a6d8ed3 Author: Nguyễn Hồng Quân Date: Fri Jun 1 17:41:49 2012 +0200 opensc-explorer: implement put_data opensc-explorer: use larger buffer for put_data commit 5673d13c15bb72455490fc556d6d52250c70dca9 Author: Nguyễn Hồng Quân Date: Fri Jun 1 17:59:20 2012 +0200 OpenPGP: implement pgp_delete_file() commit 72088b6eebb208439f41364d88de0494fcfa6531 Author: Nguyễn Hồng Quân Date: Fri Jun 1 17:57:36 2012 +0200 OpenPGP: implement pgp_strip_path() Strip away the non-existant DF 5015 from paths passed the driver. commit f7785ad1ec83730966877c8ec647c063fef847bd Author: Nguyễn Hồng Quân Date: Fri Jun 1 17:41:17 2012 +0200 OpenPGP: correct use of pin_reference commit 3ac834e27748dab81d2ab07a5f958f1f34f62b0b Author: Nguyễn Hồng Quân Date: Fri Jun 1 17:40:39 2012 +0200 OpenPGP: implement pgp_put_data() commit a1ed22b6fd573251e0a38f3696f494c59a8ae062 Author: Nguyễn Hồng Quân Date: Fri Jun 1 18:23:44 2012 +0200 OpenPGP: implement pgp_do_iswritable() commit d8d6902699ee3a35900ef8c39e23a2d4d628e29d Author: Nguyễn Hồng Quân Date: Fri Jun 1 17:45:12 2012 +0200 OpenPGP: implement pgp_seek_blob() commit 7e24102f2b7d1fcd4172fc9e71a35eb71158056d Author: Nguyễn Hồng Quân Date: Fri Jun 1 17:46:32 2012 +0200 OpenPGP: Make DO 7F21 treated as simple DO Make DO 7F21 treated as simple DO, to make certificate contained in it visible. commit d7e09b48cba385daa4e2b1e45224bee3f6586b85 Merge: 2cff47d 02a2b3d Author: Ludovic Rousseau Date: Mon Jun 4 06:52:56 2012 -0700 Merge pull request #54 from LudovicRousseau/spy Display the flags argument of C_Initialize() commit 02a2b3d887c656b66ecbb3f378acf3fd27791082 Author: Ludovic Rousseau Date: Mon Jun 4 15:42:57 2012 +0200 Display the flags argument of C_Initialize() The the pInitArgs argument of C_Initialize() is not NULL it is a pointer to a CK_C_INITIALIZE_ARGS structure. This structure contains a flags bitfield with possible values: - CKF_LIBRARY_CANT_CREATE_OS_THREADS - CKF_OS_LOCKING_OK This flags parameter is now parsed and displayed. commit 2cff47d9b335a1f3984af8ab804192d7ac296d93 Author: Viktor Tarasov Date: Fri Jun 1 16:44:28 2012 +0200 pkcs11-tool: support for secret-key objects support of 'secret-key' objects key conversion procedures are now in libopensc library change name for OPENSC NON-REPUDIATION CKA attribute commit 53330f9f89993a145e2f9f971d65f8f074a2bab9 Author: Viktor Tarasov Date: Fri Jun 1 16:04:17 2012 +0200 tools: avoid possible matching of zero length ATR commit c7258a6b523eeae585cafa474df9fb519f25d194 Author: Viktor Tarasov Date: Fri Jun 1 16:00:05 2012 +0200 pkcs15-tool: list secret keys objects ... and print its attributes. Print attributes of 'authentication-key' object. commit 525f61af73e7ffe7c573be914942cf3261013f46 Author: Viktor Tarasov Date: Fri Jun 1 15:54:17 2012 +0200 pkcs15-init: 'erase-application', 'update-lastUpdate', ... New operations: - 'erase-application' -- erase on-card application indicated by it's AID; - 'update-lastupdate' -- parse tokenInfo, set 'lastUpdate' value to the current date and write back tokenInfo content; - 'ignore-ca-certificates' -- when importing PKCS#12 ignore all CA certificates; commit fbc9bb35dc5e49bb84667c358d4c5b8f63205ab6 Author: Viktor Tarasov Date: Fri Jun 1 15:37:06 2012 +0200 opensc-explorer: when printing file content read entire file When reading and printing file content, do not read it by small chunks, but read an entire file. It allows to verify how card driver reads the data of maximal size that is allowed for one transaction ('max_recv_size'). commit d7022d33000ab0a26a841617041d086b230c7acd Author: Viktor Tarasov Date: Fri Jun 1 15:35:27 2012 +0200 ias-ecc: add tool with IAS/ECC specific query operations commit 635be92a0c3d6cd070a53d14eeb1ab7210d02727 Author: Viktor Tarasov Date: Thu May 31 18:13:20 2012 +0200 pkcs15init: call sc_profile_finish() with 'app-info' data ... call sc_profile_finish() with application info data as an argument; in delete-by-path procedure, when getting authorization to delete file, make distinction between 'DELETE' and 'DELETE-SELF'; call card specific 'store' handler updating PrKDF and PubKDF files; deduce the private key pkcs#15 attributes (like subject) from the friend certificate; ignore SM authentication type when getting authorisation for operation; copy GUID from the object create data to the pkcs#15 object attributes. commit 7819e5ab893a9ff4251da29c03528c6915a3122c Author: Viktor Tarasov Date: Thu May 31 16:40:36 2012 +0200 minidriver: 'write' mode Picked from SM branch of OpenSC, where it was tested mostrly with IAS/ECC card. Also was used with rutoken and other cards. commit 6c2fa7b462b216ebef11fce7c3671f69681ba5c4 Author: Viktor Tarasov Date: Thu May 31 11:34:32 2012 +0200 pkcs15-skey: support for 'SecretKey' object type For a while only implemented decoding of 'genericSecretKey', 'desKey', 'des2Key' and 'des3Key' keys. commit 1d2661c7ce900e439f3836c62aa046da71a8a6fe Author: Viktor Tarasov Date: Thu May 31 10:37:38 2012 +0200 pkcs15-pin: 'PIN' as particular choice of 'AuthenticationType' Encode/decode 'PIN' pkcs15 object as particular choice of 'AuthenticationType' data. Rudimental encoding/decoding of 'authKey' object. commit 666e08ffde3649d8eff7d2c33eedda7afa490c8d Author: Viktor Tarasov Date: Thu May 31 10:26:38 2012 +0200 pkcs15-pubkey: add 'subjectName' to 'CommonPubKeyAttributes' Decode/encode 'subjectName' attribute from the 'CommonPubkeyAttributes' Use macros for the size of the asn1_entry tables. Use short call form for debug messages. Procedure to convert the pkcs15 public key to EVP_PKEY type key. Coding style issues. commit 4be79f3d30975565143653809405e547e9bcc81e Author: Viktor Tarasov Date: Thu May 31 09:32:21 2012 +0200 pkcs15-prkey: 'algReference' from 'CommonKeyAttributes' Decoding/encoding of the 'algReference' attribute from the 'CommonKeyAttributes'. Use macros for the size of the asn1_entry tables. Use short call form for debug messages. Procedure to deduce from certificate the attributes of private key PKCS#15 object. Procedure to convert the pkcs15 private key to EVP_PKEY type key. Coding style issues. commit f7b5a6b6af4e14e386a514b85b4929f5866e0296 Author: Viktor Tarasov Date: Wed May 30 19:40:05 2012 +0200 PIV: pickup card driver from the SM branch ... where it was tested by Douglas. commit fe80ffdbd2dbb5444f2937f8df8811d98ec7e3e2 Author: Viktor Tarasov Date: Wed May 30 10:05:37 2012 +0200 pkcs11-spy: invalid ExpandEnvironmentStrings() call parameter pkcs11-spy.c(168) : warning C4047: 'function' : 'DWORD' differs in levels of indirection from 'DWORD *' pkcs11-spy.c(168) : warning C4024: 'ExpandEnvironmentStringsA' : different types for formal and actual parameter 3 pkcs11-spy.c(205) : warning C4047: 'function' : 'DWORD' differs in levels of indirection from 'DWORD *' pkcs11-spy.c(205) : warning C4024: 'ExpandEnvironmentStringsA' : different types for formal and actual parameter 3 commit aca314b1f85de7a43581462183b6109002b53e6c Author: Peter Marschall Date: Sun May 27 22:37:27 2012 +0200 opensc-explorer: remove outdated usage text for verify Wuth the key types explicitly listed in the usage line, it is not necessary to list them separately. commit f04a1c5fefdf56c7763aba434010d3b77c98fe2b Author: Peter Marschall Date: Sun May 27 11:29:24 2012 +0200 OpenPGP: stricter validity checks in PKCS15 init Only add private/public keys that habe been defined: check finger prints & algorithm identifier commit 742186f32f20b7448b97cec5e187692f1f731706 Author: Peter Marschall Date: Sun May 27 09:16:00 2012 +0200 OpenPGP: remove unnecessary call to sc_get_data commit d38d615025da329c477e406872ec729f5942828e Author: Peter Marschall Date: Sat May 26 22:27:58 2012 +0200 OpenPGP: parse more (extended) capabilities on init * parse more extened capabilities & features into a private enum * for v2.0 cards, always parse the "historical bytes" DO reason: ATR may be static and thus cannot reflect the state commit 67136b178148073fd367cbb4757761126f845f11 Author: Peter Marschall Date: Sat May 26 20:42:22 2012 +0200 OpenPGP: revert 7b4532736e0c7923b972aa22344d156760b40adf PKCS#15 spec v1.1 says: TokenInfo.version: This field contains the number of the particular version of this specification the application is based upon. For this version of this document, the value of version shall be 0 (v1). Thanks to Martin Paljak for the finding. commit 93cb77079b6eb3d548f3a892ac9c21775e3dad36 Author: Peter Marschall Date: Sat May 26 18:01:37 2012 +0200 tools: re-factor usage message function * change order of long & short option names: letters first, then the long names Effect: nicely aligned short and long option names in the help text * more space between option names and explanation Effect: better readability on long options * print "Options:" header only if there is at least one non-hidden options Effect: nicer output when all options are hidden * only show printable, non-space short options letters Effect: no control codes printed to terminal * get rid of a temporary variable commit 367ebd94d4864969001a2202e4fdf3ba7a0b70c9 Author: Peter Marschall Date: Sat May 26 18:53:09 2012 +0200 tools: allow arguments to be printed in usage message Extend util_print_usage_and_die() by a string argument that describes further arguments to the program. 1st user: opensc-explorer commit 61ca69c251708ff9aa85dfc89afd731e526c7895 Author: Peter Marschall Date: Sat May 26 17:11:40 2012 +0200 OpenPGP: documentation for openpgp-tool commit 82382398523abffce8f53c1d4422c0ddd8b520f7 Author: Peter Marschall Date: Sat May 26 18:52:05 2012 +0200 OpenPGP: extend openpgp-tool commit c58e94810b85d1677319d332a3ca29a91f6baccc Author: Peter Marschall Date: Sat May 26 09:59:04 2012 +0200 opensc-explorer: update documentation commit 965d44ec4052b8b63ea1b07c67d56d48acc152e3 Author: Peter Marschall Date: Sat May 26 09:50:13 2012 +0200 opensc-explorer: use case-insensitive pin types in PACE commit cd2cdf77f6c95d5cb061b08e7d04284c6057e640 Author: Peter Marschall Date: Sat May 26 09:46:32 2012 +0200 opensc-explorer: explicitly mention key types commit 2276c7e7e4195e724a1737f031e7d96889bee872 Author: Peter Marschall Date: Sat May 26 09:43:40 2012 +0200 opensc-explorer: do not use hard-coded string length Determine length of prefix dynamically instead of using a hard-coded, common value for all prefixes. commit 2efc9b76dfee4a18ee1c78c780b2710c46132062 Author: Peter Marschall Date: Fri May 25 19:15:32 2012 +0200 opensc-explorer: document new command line parameter SCRIPT commit 458517783c66cb13d8d52540667c3f87cdc1ca51 Author: Peter Marschall Date: Fri May 25 18:56:12 2012 +0200 OpenPGP: fix access conditions for DOs Follow the specs. commit 26aac71c9f99ef55c7b3c2d218efb417218c1e49 Author: Peter Marschall Date: Sun May 20 21:04:57 2012 +0200 OpenPGP: correct PIN type to UTF-8 OpenPGP card spec v1.1 and v2.0 make it absolutely clear: "... The format of the CHVs is UTF-8 (case sensitive) ...". commit 6d4d1b4acaed53df6c5a2e256527fcb383610108 Author: Viktor Tarasov Date: Wed May 30 09:25:57 2012 +0200 win32: do not export 'sc_pkcs15_remove_df' procedure in source it replaced by static 'sc_pkcs15_remove_dfs' commit 177af40535c22840737ee766251197bec8c7da04 Author: Viktor Tarasov Date: Wed May 30 09:18:03 2012 +0200 md: prototype of sc_pkcs15_get_guid() has been changed introduced 'flags' parameter commit 6337149ef7d1817fcdb8a0f45a3421882eeb43b7 Author: Viktor Tarasov Date: Sun May 27 21:20:22 2012 +0200 pkcs15: decode 'seInfo', 'profileIndication', 'lastUpdate' Encode,decode 'lastUpdate', 'seInfo', 'profileIndication' of TokenInfo (CIAInfo). Trailing whitespaces. commit be81263d8e34d84c71e68ed2d39b02f3f3e7bf25 Author: Viktor Tarasov Date: Tue May 29 19:44:54 2012 +0200 log: config option to reopen debug file at every debug log ... To be used in windows: "In Windows, file handles can not be shared between DLL-s, each DLL has a separate file handle table. For that reason reopen debug file before every debug message." sc_context_repair() procedure from Hunter William "Workaround some threading and data lifetime issues when card handle changes and need to re-associate card" http://www.opensc-project.org/pipermail/opensc-devel/2011-December/017445.html commit 954d0b9375eb49a340e2926d26815bc23b8ebb87 Author: Viktor Tarasov Date: Tue May 29 19:19:06 2012 +0200 pkcs15: DIR EF can have EF_LINEAR structure and so, in this case the checking file size is not appropriate method to validate it. ;use short call form for the log messages; commit 9d5404bac60e1d182d535e0c9add95678b73182c Author: Viktor Tarasov Date: Tue May 29 11:29:44 2012 +0200 libopensc: some usefull macros, crc32 calculation procedure Introduce some usefull define macros, error code 'inconsistent configuration'. Introduce procedure to calculate CRC32 digest, to be used in minidriver to calculate the 'freshness' values. commit 9c882ff5c2e8c4e12d98c4a1de2fe8d48694c20c Author: Viktor Tarasov Date: Tue May 29 09:37:26 2012 +0200 AuthentIC: add SM related procedures Added to facilitate future SM merge, for a while disabled by conditional macro. commit 230b782309df5d444d087cd13df3a9945e7ffe6e Author: Viktor Tarasov Date: Mon May 28 23:15:37 2012 +0200 pkcs15: add 'sc_pkcs15_derive' missing for ECDH support also, key path, that has to be selected before crypto operation, can contain an aid. commit 9c5dbea8838904cfa700d502e4ecbf8d834418e8 Author: Viktor Tarasov Date: Sat May 26 21:17:39 2012 +0200 pkcs11: ECHD and secret keys support from Douglas This support were initially proposed by Douglas (https://github.com/dengert/OpenSC/commits/ecdh) and then merged into SM branch (https://github.com/viktorTarasov/OpenSC-SM/tree/secure-messaging). commit 76b8ad8ad6d85bbd00cd60737f94a72d23a88f46 Author: Viktor Tarasov Date: Sat May 26 21:12:22 2012 +0200 pkcs11-spy: timestamp, environment strings in paths Add to the spy logs the timestamp with millisecond resolution. Environment strings are accepted in the key registries paths related to spy module. pkcs11-spy: code formatting commit 388d68fb1a4a4cbb7f67aaf548668f82d46c68cd Merge: c6cae25 38be3c1 Author: viktorTarasov Date: Sat May 26 05:29:24 2012 -0700 Merge pull request #37 from marschap/staging improvements to opensc-explorer & new tool openpgp-tool Usefull improvement: probably could be used in automated tests. I follow Ludovic and attract your attention onto the necessity, in the nearest future, to supply the doc/man for the tool newly introduced. Without it the build of OpenSC package will simply not be possible. commit c6cae254706a49df9f057ee6e22437ae7db87855 Merge: 4b745f5 363e374 Author: viktorTarasov Date: Sat May 26 04:44:20 2012 -0700 Merge pull request #41 from viktorTarasov/pull-request--multi-applications-mixed-slots Multi on-card applications in PKCS#11 OpenSC module commit 363e3746646994ae18fa8d32fb4773687e366ea3 Author: Viktor Tarasov Date: Sat May 26 09:21:35 2012 +0200 pkcs15init: syntax changed for the ias(ecc) profiles commit b432e9767fc190b1b624373523471b03525c3046 Author: Viktor Tarasov Date: Sat May 26 09:17:21 2012 +0200 pkcs15init: add 'minidriver-style' profile option The on-card support of minidriver could need some MD specific pkcs#15 (DATA) objects. There is no standard for these objects. New option will allow to choose one of the possible implementations. commit 10e1ad001dc2eb1e95b49344fa558394a0d07844 Author: Viktor Tarasov Date: Fri May 25 09:56:15 2012 +0200 pkcs15init: change sc_pkcs15init_bind() prototype Add new argument 'application-info', that will allow to select the on-card application to by binded with. pkcs11: use sc_pkcs15init_bind with 'AID' argument Prototype of sc_pkcs15init_bind() has been changed to add argument with AID of the on-card application to be binded with. commit bf752f3c6137d87801e0ecd1fd34b64eb5b09257 Author: Viktor Tarasov Date: Fri May 25 09:13:40 2012 +0200 pkcs15: new procedure to find an Auth PKCS#15 object (PIN) by flags commit 343fa20a005b9c8460b5fd4e76c8bdc8ce2a856a Author: Viktor Tarasov Date: Wed May 23 09:22:41 2012 +0200 pkcs11: create tokens and slots for multiple on-card applications In card detection procedure bind all present applications and create tokens for them. Treatement of the different 'create-slots' configuration cases, joining the objects from different applications into one slot are previewed for the next commits. commit 80266ff466acb28da9ef8088590386321553baa2 Author: Viktor Tarasov Date: Wed May 23 08:50:18 2012 +0200 pkcs15: new procedure to get application by it's symbolic name At the moment symbolic names for the on-card applications are 'generic', 'protected'. This distinction used by pkcs11 and minidriver module to select the 'master' application in the cases when only reduced number of slots (one) can be exposed by module (minidriver) or particular configuration (pkcs11). commit 14049fb806ca1e60c249155472c8b57270f8cce9 Author: Viktor Tarasov Date: Tue May 22 17:18:00 2012 +0200 pkcs11: prepare internal API for multi-application mode - simplify some of framework handles: remove from it's prototype the arguments that can be derived from the other arguments; for exemple: foo(slot, slot->card) --> foo(slot) - add the 'application' argument to the bind, unbind and similar handles; - preview more then one framework data attached to the pkcs11card object. - placehold for the future 'derive' and 'can_do' handles. commit d1cf65754b9326c09213f151b7ee2f19f4037730 Author: Viktor Tarasov Date: Mon May 21 19:19:38 2012 +0200 pkcs11: no more 'hacked' mode and 'onepin' module version 'OnePIN' version of opensc-pkcs11 module is not installed. Instead, in the 'pkcs11' section of OpenSC configuration, there is a possibility to define in a different manner how to create slots for the present PINs and applications. commit 38be3c1a4a74f6fd9bf6e314a06780d14a2aa921 Author: Peter Marschall Date: Sun May 20 18:18:25 2012 +0200 opensc-explorer: better names for some functions commit 1f70902da5bc43d7604925bad0c97ac3788a85bb Author: Peter Marschall Date: Sun May 20 18:05:16 2012 +0200 opensc-explorer: add 'echo' command Add 'echo' command that simply displays its arguments. With the recently committed script interpreter feature and this echo command, nice litte scripts can be written, like e.g. $ cat opengpg-userinfo #!/usr/bin/opensc-explorer cd 0065 echo Name: cat 005B echo Language: cat 5F2D echo Gender: cat 5F35 quit commit f8f02dbd659581c7b8ade221a3ba628691a7436a Author: Peter Marschall Date: Sun May 20 17:12:14 2012 +0200 opensc-explorer: allow acting as script interpreter Take a filename as argument and interpret the commands in it. commit 69e9861dddced0c3e0d982c8fdb9c2fc3f6a7a12 Author: Peter Marschall Date: Sat May 19 20:28:25 2012 +0200 OpenPGP: first go at openpgp-tool commit 4b745f51afdfee10301ab79bbf817c6050fc1237 Author: Viktor Tarasov Date: Wed May 16 23:14:42 2012 +0200 openpgp: 'ushort' type not defined for WIN32 Change-Id: Ifb28730af2d39440721be9d1e38ea1c6106167a2 commit e88c08d13828c6d5235bfa8940e832139855b56d Author: Diego Elio Pettenò Date: Mon May 14 10:14:53 2012 -0700 build: allow cross-compilation build by reordering libraries' order. If the system libraries are set before the locally built libraries, libtool will pick the system copy of OpenSC instead of the local one, and that can make cross-builds fail badly. This patch is already applied in Gentoo for proper building. commit 3c324b8b73e3a9e11bf082bdca1a6f4df17057d9 Author: Diego Elio Pettenò Date: Sun May 13 14:55:35 2012 -0700 build: fix parallel install by creating directory in the rule Relying on the rule that creates the directory is a bad idea to be parallel safe. commit 07d51bea36d2698b9f7925369dc884c8b0124c29 Author: Peter Marschall Date: Sun May 6 21:29:06 2012 +0200 OpenPGP: get PKCS#15 token info from preparsed card data Determine data for sc_pkcs15_card's tokeninfo structure from sc_pkcs15_card's card structure. This makes sure the two stay consistent as much as possible. commit 7b4532736e0c7923b972aa22344d156760b40adf Author: Peter Marschall Date: Sun May 6 21:08:09 2012 +0200 OpenPGP: set version for PKCS#15 commit bc0949140db52da95176ec6027c0a5e6765fd33a Author: Peter Marschall Date: Sun May 6 21:07:01 2012 +0200 OpenPGP: format paths more nicely commit 0283a6f24e247c6f27466a239516566aac0e03d8 Author: Peter Marschall Date: Sun May 6 19:52:58 2012 +0200 OpenPGP: only add keys with legal algorithm identifier commit 671ac54b71d6092d137645cc88f856b3d28278a8 Author: Peter Marschall Date: Sun May 6 18:35:47 2012 +0200 OpenPGP: more data driven logic Tie together in a struct what belongs together instead of relying on the ordering of multiple unrelated arrays. commit 076a4ed2dc6409e7d8cf951637e7e202e16365dc Author: Peter Marschall Date: Sun May 6 18:13:28 2012 +0200 OpenPGP: slight cleanup Use typedef'd types instead of their underlying structs, use helper functions instead of reinventing the wheel, and fix typos. commit 4c09e290e1b2c4d3b6cad9688e1ddabf58a02f57 Author: Peter Marschall Date: Sun May 6 17:59:34 2012 +0200 OpenPGP: make logic more data driven Instead of doing lots of special-cases in code, create data structures decide once which variant of the data to use, and then use it. commit 7535f30738ee6c04e50d40baf2eea6ac84f58635 Author: Martin Paljak Date: Thu Aug 11 13:57:12 2011 +0300 OpenPGP: add the v1.0 spec URL as well to source code comments. Even though it is obsolete, it makes it more complete;) commit 5f84400ef7eb6ad85baa3be9aefd0a9addd54322 Author: Martin Paljak Date: Wed Aug 10 16:45:40 2011 +0300 OpenPGP: more support for CryptoStick/OpenPGP v2.0 Using CryptoStick v1.2 with 4096bit keys now allows to authenticate to SSH with all three keys. commit fb772cccf85577f4fd11ff14fc17cda9e1f2cc13 Author: Martin Paljak Date: Fri Jul 15 13:36:33 2011 +0300 OpenPGP: use actual references in PKCS#15 emulation code for PIN codes. Only v1.1 has 3 PIN codes. commit 63b08786a0e62cb68a3ff6d51117ec883cc84de8 Author: Martin Paljak Date: Thu Jul 14 17:21:34 2011 +0300 OpenPGP: do not hardcode key sizes to 1024 but fetch actual values from the card. OpenPGP 2.0 (especially CryptoStick v1.2) supports key sizes up to 4096 commit 750d2e9121d534b1761fc4ee81974608ae8ed774 Author: Frank Morgner Date: Sat May 5 18:55:54 2012 +0200 cherry-picked from Frank Morgner OpenSC.git and rebased opensc-explorer: documented `find` opensc-explorer: documented `pace` commit fb1aa8ed116b2a4f5e711d8545ad3a03ef96a82d Merge: 20bf3f5 4d9ea43 Author: viktorTarasov Date: Wed May 16 06:17:37 2012 -0700 Merge pull request #32 from frankmorgner/4d9ea43ab24a2e36fc36d1be81b3414cc003786f new commands for opensc-explorer and bugfix for iso7816 driver - opensc-explorer: new find command: tested; nice, useful feature. - opensc-explorer: pace: no means to test -- accepted. - iso driver: really bug, thanks. commit 20bf3f5484ff2dd8a59ed5afa0c6798d7439d402 Author: Viktor Tarasov Date: Sun May 13 00:27:38 2012 +0200 pkcs11: use short form of log messages Change-Id: Ice298552238da9eeb0b646fc1ddfaf4d7a8ee4bb commit 5d1feb305144eb758afbce356d189d6e89c67f6a Author: Viktor Tarasov Date: Sat May 5 14:49:21 2012 +0200 pkcs15: throw an error if there is no valid public key data Thanks to Nguyen Hong Quan. http://www.opensc-project.org/pipermail/opensc-devel/2012-May/017997.html Change-Id: I48bc6664909ca324ef71a2a10d9fddb9096ae598 commit 4d9ea43ab24a2e36fc36d1be81b3414cc003786f Author: Frank Morgner Date: Fri Apr 27 10:14:25 2012 +0200 iso driver: select with p2=0x0c when le is missing commit 59b214ec23b5e263788721a0180ac36e717f15be Author: Frank Morgner Date: Thu Apr 26 03:41:20 2012 +0200 opensc-explorer: implemented `pace` commit 7b630962c179aad8ec2543977ceb59bcea364ea0 Author: Frank Morgner Date: Thu Apr 26 03:40:06 2012 +0200 opensc-explorer: `find [ []]` commit 66aa2b389654146294ba8deb6e2fcccbdea6c61b Author: Viktor Tarasov Date: Mon Apr 9 11:07:15 2012 +0200 pkcs15: throw an error when trying to read from DF Change-Id: Ifac6777436f889393e3d3981d98a78149fd6cb17 commit cb13633634a481560a26278a6284d11d1c21d00e Author: Viktor Tarasov Date: Tue Apr 3 00:00:56 2012 +0200 remove trailing whitespaces inspired by http://www.opensc-project.org/pipermail/opensc-devel/2012-March/017883.html Change-Id: If170e830d8d9587a31742feffb6fff54cfdf75b4 commit e57d443e8614adada3ef316017479696b2403037 Author: Viktor Tarasov Date: Mon Apr 2 23:40:05 2012 +0200 pkcd15init: remove trailing whitespaces inspired by http://www.opensc-project.org/pipermail/opensc-devel/2012-March/017883.html Change-Id: I817f903e67965942d9cc0c30931dbaea0c5f736e commit df8715849d462e617025427ad96a7708bad24445 Author: Alon Bar-Lev Date: Fri Dec 9 22:46:45 2011 +0200 Remove libltdl: Remove ltld references http://www.opensc-project.org/pipermail/opensc-devel/2011-December/017490.html cherry-picked from 'libtool' branch of Alon Barlev's github project git://github.com/alonbl/OpenSC.git and rebased Remove libltdl: Remove ltld references (cherry picked from commit a350326c520c5b0cb185f90946648633f4d0e456) Remove libltdl: Detect libdl (cherry picked from commit 51e7de45c11823460e776492dcbd40e60583a7eb) Remove libltdl: Use libscdl (cherry picked from commit 09f3eadb8a1a47407c011dcd3d5ce461516f3b87) Remove libltdl: Cleanup libscdl (cherry picked from commit 52d5f1be01146079e3a6fad1c88ebb0f577d0a94) Remove libltdl: Cleanup libscdl usage at Microsoft VC build Untested, I don't have the environment, Martin, please test. (cherry picked from commit 7fb18f8d0b0bae6d181981a0c71190440e917c2c) Change-Id: I73c98ccb9365584b12f4b0b97b69316a190b6e45 commit 594427e516bccb82bbe1fec26460a6701688fd54 Merge: 63eb4a4 af559fb Author: Ludovic Rousseau Date: Wed Feb 22 09:38:37 2012 -0800 Merge pull request #26 from viktorTarasov/staging merge 'master' into 'staging' commit af559fbffdd90cbe493688265038156f72385756 Merge: 97e6ac6 fa6c1a1 Author: Viktor Tarasov Date: Wed Feb 22 11:20:17 2012 +0100 Merge branch 'master' into staging commit fa6c1a1aa61ab4432b702ecf33e0e04aed876458 Merge: e6c501f 2b63213 Author: viktorTarasov Date: Wed Feb 22 00:51:21 2012 -0800 Merge pull request #25 from viktorTarasov/master pkcs15-init tool: fix for pin auth_type comparison (thanks to 'joelhockey') commit 2b63213e0a3be22b48bf8a7cb3346222d8c2eabb Author: Viktor Tarasov Date: Wed Feb 22 09:42:30 2012 +0100 pkcs15-init tool: fix for pin auth_type comparison partial merge of pull request https://github.com/OpenSC/OpenSC/pull/8 Thanks to 'joelhockey'. commit 63eb4a4b4bb43e9192feba2e52bd62d4cddfc2d5 Merge: bc8e320 931e913 Author: viktorTarasov Date: Wed Feb 22 00:28:07 2012 -0800 Merge pull request #4 from marschap/iso7816-for-staging iso7816 cleanups for staging commit e6c501f3de48643c0ffd26013de139fbad3b043f Merge: c4a200a 61ea633 Author: viktorTarasov Date: Wed Feb 22 00:20:49 2012 -0800 Merge pull request #13 from mjrider/master 2 small fixes for storing a private key commit bc8e320f845e213c2b24a7df922d6d0e4e5ffc42 Merge: 318408e 3248a6b Author: viktorTarasov Date: Wed Feb 22 00:13:57 2012 -0800 Merge pull request #16 from mjrider/for-opensc-accessflags-fix Agree, it's better to fix the key access flags at the general pkcs15init level. commit 318408ee46cb6046e8b2f1bf040f85697f8402b9 Merge: 1049bca 006a97b Author: Ludovic Rousseau Date: Tue Feb 21 12:25:58 2012 -0800 Merge pull request #24 from LudovicRousseau/staging Staging commit 006a97b8c811cabe6d8125b25ab55a58ab1d42a8 Author: Ludovic Rousseau Date: Tue Feb 21 21:22:54 2012 +0100 Use the short form sc_log() instead of sc_debug() Replace sc_debug(ctx, SC_LOG_DEBUG_NORMAL, ...) by sc_log(ctx, ...) as suggested by Viktor Tarasov commit 1049bca092ee00251bb1a3eea4cc1bef7660fc7b Merge: 97e6ac6 27c6652 Author: Ludovic Rousseau Date: Tue Feb 21 12:16:36 2012 -0800 Merge pull request #23 from LudovicRousseau/staging Fix for Windows compilation commit 27c6652a1df9f1af99859634fd246a0cfdfd445b Author: Ludovic Rousseau Date: Tue Feb 21 21:02:47 2012 +0100 Remove extra spaces and tabs at end of lines commit af9dada86ced0a6d452f928ae089c17b9efdda19 Author: Ludovic Rousseau Date: Tue Feb 21 20:59:07 2012 +0100 Fix compilation error on Windows SC_READER_CAP_PACE has been renamed to SC_READER_CAP_PACE_GENERIC during patch discussion. Thanks to Viktor Tarasov for the bug report commit 7a9b9f81b961683ff47b6df028c2114b6f48b06a Merge: a3a1bf5 97e6ac6 Author: Ludovic Rousseau Date: Tue Feb 21 21:09:46 2012 +0100 Merge remote branch 'upstream/staging' commit 97e6ac6ecb304673e21d5f8489be54adbcd73cdb Merge: c20cd12 1141ae5 Author: Ludovic Rousseau Date: Tue Feb 21 05:39:26 2012 -0800 Merge pull request #22 from frankmorgner/1141ae520201c91b9859b0cd6cbb59f4ebfbd577 Add possibility to execute PACE on the reader (tested with Reiner SCT RFID standard/komfort) commit 1141ae520201c91b9859b0cd6cbb59f4ebfbd577 Author: Frank Morgner Date: Mon Feb 20 12:47:58 2012 +0100 Add support for PACE-enabled readers Implements PC/SC interface to PACE-enabled readers defined in PC/SC pt. 10 AMD 1 and BSI TR-03119. PACE can be started using `sc_perform_pace`. This function currently calls the new `perform_pace` from `struct sc_reader_operations`, if the reader has the needed capabilities. `sc_perform_pace` could also be extended with a stand-alone implementation of PACE (code could be imported from here http://vsmartcard.sourceforge.net/npa/README.html). Note that the reader's PACE capabilities are correctly determined by calling GetReaderPACECapabilities. OpenSC's new PACE capabilities can be tested using the `npa-tool` from the Virtual Smart Card Architecture (see link above). commit c20cd128097953550ac1ef762b0988735c657d6c Author: Ludovic Rousseau Date: Mon Feb 20 14:01:53 2012 +0100 Fix lookup_enum_spec() prototype A change introduced in c0072d16ddc7b52d2b2c3c53991f569dcac45b3a made the compilation fail: pkcs11-display.c:738:1: error: conflicting types for 'lookup_enum_spec' pkcs11-display.h:64:13: note: previous declaration of 'lookup_enum_spec' was here commit e485e3a32fc4d08106a04a7a7b3b02b14c0d5b43 Merge: ec70ee5 c0072d1 Author: Ludovic Rousseau Date: Mon Feb 20 04:50:05 2012 -0800 Merge pull request #21 from viktorTarasov/staging Staging commit c0072d16ddc7b52d2b2c3c53991f569dcac45b3a Author: Viktor Tarasov Date: Mon Feb 20 10:57:57 2012 +0100 pkcs11: coding style Signed-off-by: Viktor Tarasov commit 8f9bbbe780395f5680604a3cb6518aee1ae98399 Author: Viktor Tarasov Date: Mon Feb 20 10:37:50 2012 +0100 pkcs11: win32 compilation error pkcs11-display.c(139) : error C2275: 'CK_BYTE' : illegal use of this type as an expression Signed-off-by: Viktor Tarasov commit ec70ee5c4de97413fcb97c34d24d1d157e3d1841 Merge: 28956b5 0c9717a Author: Ludovic Rousseau Date: Fri Feb 17 01:11:49 2012 -0800 Merge pull request #20 from LudovicRousseau/staging Staging commit 0c9717a82e8cd39c755ce5491cf4c3d77bd08022 Author: Ludovic Rousseau Date: Fri Feb 17 10:06:57 2012 +0100 Reformat: remove extra spaces and tabs commit 00e02359a36a9decfd3ca15a194daa8ecabac8ce Author: Stef Walter Date: Wed Dec 14 13:14:14 2011 +0100 libopensc: Add 'paranoid-memory' setting for behavior when mlock() fails * Setting paranoid-memory to true, and mlock() fails, then allocations which require non-pageable memory will return NULL commit 6ed52a06b8fa6c2ff956a8d5653cc0435384c8be Author: Stef Walter Date: Wed Oct 5 11:20:04 2011 +0200 libopensc: Don't fail to allocate memory when mlock fails * Print out warning when mlock fails, and continue. * The warning required a ctx to be passed in, so that means changing a few function signatures. https://www.opensc-project.org/opensc/ticket/389 commit a3a1bf5eb83dad0a5d2092d277397c9ea08539db Merge: 0918c0e 9252eec Author: Ludovic Rousseau Date: Fri Feb 17 09:44:19 2012 +0100 Merge branch 'master' of github.com:LudovicRousseau/OpenSC commit 28956b5892cd4d5c9fe5a20db44aee0e771b1846 Merge: 6a02adc 48f8f98 Author: Ludovic Rousseau Date: Thu Jan 12 06:54:31 2012 -0800 Merge pull request #18 from LudovicRousseau/spy Display the ASCII equivalent of a hex dump commit 3248a6b122d99ef8ef8e7086b7f9e3395392a438 Author: Robbert Müller Date: Sun Jan 8 15:48:12 2012 +0100 Adding default accessflags to the do_store_private_key function in the same way do_generate_key has those accessflags This seems the right thing to do, when you look at the initial commit which added the flags in do_generate_key and the ticket http://www.opensc-project.org/opensc/ticket/198 Currently when storing a key, the accessflags are not set commit 48f8f982a09e5a484ff4951609ebfcabf8d99bfc Author: Ludovic Rousseau Date: Tue Jan 10 13:44:20 2012 +0100 Display the ASCII equivalent of a hex dump The output format of a hex dump has changed from: 668C045A 1C3A4EF4 CF8550F3 20926525 1E8BF478 to: 00000000 66 8C 04 5A 1C 3A 4E F4 CF 85 50 F3 20 92 65 25 f..Z.:N...P. .e% 00000010 1E 8B F4 78 ...x Is it now possible to find text strings inside a hex dump commit 6a02adca06e29ad6198e0293bbf5542d2d1c832f Merge: 3f7db42 9252eec Author: Ludovic Rousseau Date: Mon Jan 9 02:15:59 2012 -0800 Merge pull request #15 from LudovicRousseau/master Fix commit c4a200a23690b28a57c566032fad123cc99349f6 Merge: 3f7db42 9252eec Author: Ludovic Rousseau Date: Mon Jan 9 02:05:55 2012 -0800 Merge pull request #14 from LudovicRousseau/master Fix compiler warning and real problem commit 9252eec2d3f76b039be00e33a05de072a64c3a56 Author: Ludovic Rousseau Date: Mon Jan 9 10:40:42 2012 +0100 Fix compiler warning and real problem openssl.c: In function 'sc_pkcs11_verify_data': openssl.c:384:19: warning: 'pkey_ctx' may be used uninitialized in this function [-Wuninitialized] openssl.c:325:16: note: 'pkey_ctx' was declared here commit 3f7db4287875887053be2110e4cae2f306100c8e Merge: e2b5603 3b6c985 Author: Ludovic Rousseau Date: Mon Jan 9 01:25:36 2012 -0800 Merge pull request #5 from marschap/doc-for-staging Doc updates for staging commit 61ea63304b2f2779a3145537b5ec2611da9450df Author: Robbert Müller Date: Sun Jan 8 15:04:37 2012 +0100 Changing default usage from sign to verify, because verify is the public key opposite of sign for the private key commit d97f23946876c4e48186868f507c67907ec47c8d Author: Robbert Müller Date: Sun Jan 8 13:47:58 2012 +0100 Setting usage flags for the public key when storing a private key commit e2b5603ff76a83345d67bd8494c38ca2b9ebe528 Merge: 6f8dcc9 51630a8 Author: viktorTarasov Date: Fri Dec 16 00:58:12 2011 -0800 Merge pull request #11 from dengert/staging Note about using 'local'/'global' flags by OpenSC http://www.opensc-project.org/pipermail/opensc-devel/2011-December/017525.html commit 51630a844e8e95e7108cb1966c5f3e21b93a463b Author: Doug Engert Date: Thu Dec 15 14:58:02 2011 -0600 Cleanup PKCS15 PIV Card PIN flags If PIV card Discovery Object is present and Global PIN is preferred, turn off the LOCAL flag. commit 6f8dcc9172277799011d88fdbe2fabfcf3a89774 Author: Martin Paljak Date: Thu Dec 8 11:21:29 2011 +0200 Make the Mac OS X package builder script more resilient. Don't fail if not needed .la files are nout found in the target. This is actually a test commit for github<->opensc-project.org sync commit ed7f0b8f7957dd2877c1b9e79b6d0840a5d61103 Merge: 5180460 c6fcd68 Author: Martin Paljak Date: Wed Dec 7 13:31:26 2011 +0000 Merge "Fix for ticket #400" into staging commit c6fcd68fbcc4d39885568fcca2238f3d99f34382 Author: João Poupino Date: Tue Nov 15 01:39:53 2011 +0000 Fix for ticket #400 Specify the path of the Address PIN for IAS-based cards in order to properly read the Citizen Address Data object. Remove the "Citizen Data" object association with the Address PIN, as it incorrectly describes the card layout. http://www.opensc-project.org/opensc/ticket/400. Change-Id: I7ca81d6d15c5e2b137ff3c9a40b7471eb2fad55c commit 51804601e42be2b7a3c9898dae04defe3339220b Author: Martin Paljak Date: Mon Aug 8 17:16:52 2011 +0300 pkcs15-tool: also print the PIN reference in hex. This eases debugging. commit 4f334b80752314c694e4f2f9c8f87d589d2a9a02 Author: Martin Paljak Date: Wed Aug 31 13:11:21 2011 +0300 MacInstaller: remove cardos-tool on uninstall Thanks to Marek Kusmin for noticing this. commit e0748e2bdb75849d3e6e295750870525075ff301 Author: Martin Paljak Date: Tue Sep 13 22:02:01 2011 +0300 Staging builds must include the new version for PreReleases. commit a4b0daf98cfba7d52ed85727d425ed746b9df0d9 Merge: 10ae35f 5fc31b7 Author: Martin Paljak Date: Tue Sep 13 22:00:38 2011 +0300 Merge branch 'macosx' into staging commit 5fc31b7f6e1a00f8d2eebfdaac87b68758f8fc7d Author: Martin Paljak Date: Tue Sep 13 21:57:22 2011 +0300 MacInstaller: more explicitly support 10.7 Tokend has been discontinued on 10.7 but still works. This fixes #382 commit 10ae35fd5713bd643029f1cbb70f5bf129719b6e Author: Martin Paljak Date: Wed Aug 31 13:16:09 2011 +0300 Add LGPL header with a generic copyright to util.c Individual copyright should be delegated to individual commits. commit 931e91359960361c880f98873da5cabc3350d52d Author: Peter Marschall Date: Sun Aug 28 11:49:28 2011 +0200 iso7816.c: clean up iso7816_restore_security_env() No need for response buffers for APDUs of the APDU_CASE_1 type. This should fix OpenSC Ticket #299. commit 93816b37cd0fb3ac85c6df12ded71c4548ca1cbc Author: Peter Marschall Date: Sun Aug 28 11:15:38 2011 +0200 iso7816.c: slightly clean up iso7816_delete_file() Only set the APDU's data element for the APDU_CASE3_SHORT type; no need to do it for the APDU_CASE_1 type. commit 3b6c985cca934ff30b66e4ec0161d50be11de8db Author: Peter Marschall Date: Sun Aug 21 13:46:02 2011 +0200 westcos-tool.1.xml: sort options alphabetically commit 56a1ab6769fc196a47792d60fea588344007f785 Author: Peter Marschall Date: Sun Aug 21 13:43:15 2011 +0200 pkcs15-tool.1.xml: sort options alphabetically commit 6b989aef577412c72bbcaf94b0978e89d8f6b5f3 Author: Peter Marschall Date: Sun Aug 21 13:36:38 2011 +0200 pkcs15-init.1.xml: sort options alphabetically, slight rewording commit 6a6898a2ea2e8ed2667cace13456b00540a5a269 Author: Peter Marschall Date: Sun Aug 21 13:30:26 2011 +0200 pkcs11-tool.1.xml: sort options alphabetically commit bd85c8f472ccc06efcb585a8b4db146ea20eb6e1 Author: Peter Marschall Date: Sun Aug 21 13:20:28 2011 +0200 opensc-tool.1.xml: sort options alphabetically commit 1df0340f57f447bb343ad8a269e6f0fb8cdaefde Author: Peter Marschall Date: Sun Aug 21 13:15:33 2011 +0200 netkey-tool.1.xml: sort options & commands alphabetically commit 55699b9d666e7dfd1194779fd9e466f43bc7cd60 Author: Peter Marschall Date: Sun Aug 21 13:13:55 2011 +0200 eidenv.1.xml: sort options alphabetically commit 6227e079cffeb84452aacf773107afa2cdf64eaf Author: Peter Marschall Date: Sun Aug 21 13:11:32 2011 +0200 cryptoflex-tool.1.xml: sort options alphabetically commit ea18c4a0d0247fc4e5e2fbc511fc19d5f495ae68 Author: Peter Marschall Date: Sun Aug 21 13:08:52 2011 +0200 cardos-tool.1.xml: sort options alphabetically commit ca0343de429b3cbd6197265c0d1c5e2470f5c996 Author: Peter Marschall Date: Sun Aug 21 13:05:46 2011 +0200 westcos-tool.1.xml: slight rewording commit 9f09113ab7ef1df7c5a1c106396e73f557e60228 Author: Peter Marschall Date: Sun Aug 21 12:45:13 2011 +0200 opensc-explorer.1.xml: sort options & commands alphabetically commit d6a9cda78b6d867fa038cd2a4654d34a2a1e0d67 Author: Peter Marschall Date: Sun Aug 21 12:33:13 2011 +0200 pkcs15-crypt.1.xml: sort options alphabetically commit 77d051ffc57fcf9e2322e8dadc5ad90a71f5bb69 Author: Peter Marschall Date: Sun Aug 21 12:26:20 2011 +0200 piv-tool.1.xml: more harmonization, slight re-wording commit 1bff1c1cc00ef1ecc070e573e3b863dd5f5b3575 Author: Peter Marschall Date: Sat Aug 20 18:09:19 2011 +0200 opensc-explorer.1.xml: fix typos, more harmonization commit 353067d7dbbfb212452ff81eceb76a29d497bdd7 Author: Peter Marschall Date: Sat Aug 20 16:51:45 2011 +0200 tools.xml: use 2 reference sections: tools & file formats Split the contents into two reference sections: on for tools and one for file formats. commit a9c320f8f8b5de380075ba7f5850c24c5ad02be4 Author: Peter Marschall Date: Mon Aug 15 16:58:01 2011 +0200 doc/tools/*.xml: more consistent formatting uUse specific tags: for commands