diff --git a/Makefile b/Makefile index 4f5616a..9378403 100644 --- a/Makefile +++ b/Makefile @@ -1,59 +1,82 @@ -# Copyright 2017 Cedric Mesnil , Ledger SAS -# +#******************************************************************************* +# Ledger App +# (c) 2016-2018 Ledger +# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# +#******************************************************************************* ifeq ($(BOLOS_SDK),) $(error Environment variable BOLOS_SDK is not set) endif include $(BOLOS_SDK)/Makefile.defines -APPNAME = "OpenPGP" -APP_LOAD_PARAMS=--appFlags 0x40 --path "2152157255" --curve secp256k1 $(COMMON_LOAD_PARAMS) +APP_LOAD_PARAMS=--appFlags 0x40 --path "2152157255'" --curve secp256k1 $(COMMON_LOAD_PARAMS) + +APPNAME = OpenPGP + +SPECVERSION="3.3.1" APPVERSION_M=1 -APPVERSION_N=0 -APPVERSION_P=RC7 +APPVERSION_N=2 +APPVERSION_P=1 APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) -ICONNAME=icon_pgp.gif +ifeq ($(TARGET_NAME),TARGET_BLUE) +ICONNAME=images/icon_pgp_blue.gif +else +ICONNAME=images/icon_pgp.gif +endif +DEFINES += $(GPG_CONFIG) GPG_VERSION=$(APPVERSION) GPG_NAME=$(APPNAME) SPEC_VERSION=$(SPECVERSION) ################ # Default rule # ################ + all: default ############ # Platform # ############ +#SCRIPT_LD := script.ld + +ifneq ($(NO_CONSENT),) +DEFINES += NO_CONSENT +endif + +DEFINES += OS_IO_SEPROXYHAL IO_SEPROXYHAL_BUFFER_SIZE_B=300 +DEFINES += HAVE_BAGL HAVE_SPRINTF +#DEFINES += HAVE_PRINTF PRINTF=screen_printf +DEFINES += PRINTF\(...\)= +DEFINES += HAVE_IO_USB HAVE_L4_USBLIB IO_USB_MAX_ENDPOINTS=6 IO_HID_EP_LENGTH=64 HAVE_USB_APDU +#DEFINES += HAVE_BLE +DEFINES += UNUSED\(x\)=\(void\)x +DEFINES += APPVERSION=\"$(APPVERSION)\" +DEFINES += CUSTOM_IO_APDU_BUFFER_SIZE=\(255+5+64\) -DEFINES += OS_IO_SEPROXYHAL IO_SEPROXYHAL_BUFFER_SIZE_B=128 -DEFINES += HAVE_BAGL HAVE_PRINTF HAVE_SPRINTF -DEFINES += HAVE_IO_USB HAVE_L4_USBLIB IO_USB_MAX_ENDPOINTS=7 IO_HID_EP_LENGTH=64 HAVE_USB_APDU DEFINES += HAVE_USB_CLASS_CCID -DEFINES += $(GPG_CONFIG) GPG_VERSION=$(APPVERSION) GPG_NAME=$(APPNAME) - ############## -# Compiler # +# Compiler # ############## #GCCPATH := $(BOLOS_ENV)/gcc-arm-none-eabi-5_3-2016q1/bin/ #CLANGPATH := $(BOLOS_ENV)/clang-arm-fropi/bin/ CC := $(CLANGPATH)clang #CFLAGS += -O0 -gdwarf-2 -gstrict-dwarf -CFLAGS += -O3 -Os +CFLAGS += -O3 -Os +#CFLAGS += -fno-jump-tables -fno-lookup-tables -fsave-optimization-record +#$(info $(CFLAGS)) AS := $(GCCPATH)arm-none-eabi-gcc @@ -65,9 +88,9 @@ LDLIBS += -lm -lgcc -lc # import rules to compile glyphs(/pone) include $(BOLOS_SDK)/Makefile.glyphs -### computed variables +### variables processed by the common makefile.rules of the SDK to grab source files and include dirs APP_SOURCE_PATH += src -SDK_SOURCE_PATH += lib_stusb lib_stusb_impl +SDK_SOURCE_PATH += lib_stusb load: all @@ -77,8 +100,8 @@ delete: python -m ledgerblue.deleteApp $(COMMON_DELETE_PARAMS) # import generic rules from the sdk -include $(BOLOS_SDK)/Makefile.rules +include Makefile.rules #add dependency on custom makefile filename -dep/%.d: %.c Makefile.genericwallet +dep/%.d: %.c Makefile diff --git a/Makefile.rules b/Makefile.rules new file mode 100644 index 0000000..7c2d312 --- /dev/null +++ b/Makefile.rules @@ -0,0 +1,29 @@ +#******************************************************************************* +# Ledger SDK +# (c) 2017 Ledger +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#******************************************************************************* + + +#Make the SDK modified SDK sources prioritar to the original ones + +SOURCE_PATH += $(dir $(foreach libdir, $(APP_SOURCE_PATH), $(dir $(shell find $(libdir) | grep "\.c$$")))) $(BOLOS_SDK)/src $(foreach libdir, $(SDK_SOURCE_PATH), $(dir $(shell find $(BOLOS_SDK)/$(libdir) | grep "\.c$$"))) +SOURCE_FILES := $(foreach path, $(SOURCE_PATH),$(shell find $(path) | grep "\.c$$") ) $(GLYPH_DESTC) +INCLUDES_PATH := $(dir $(foreach libdir, $(APP_SOURCE_PATH), $(dir $(shell find $(libdir) | grep "\.h$$")))) $(dir $(foreach libdir, $(SDK_SOURCE_PATH), $(dir $(shell find $(BOLOS_SDK)/$(libdir) | grep "\.h$$")))) include $(BOLOS_SDK)/include $(BOLOS_SDK)/include/arm + +VPATH := $(dir $(SOURCE_FILES)) +OBJECT_FILES := $(sort $(addprefix obj/, $(addsuffix .o, $(basename $(notdir $(SOURCE_FILES)))))) +DEPEND_FILES := $(sort $(addprefix dep/, $(addsuffix .d, $(basename $(notdir $(SOURCE_FILES)))))) + +include $(BOLOS_SDK)/Makefile.rules_generic \ No newline at end of file diff --git a/README.md b/README.md index 4a621b5..204edee 100644 --- a/README.md +++ b/README.md @@ -11,64 +11,15 @@ The application supports: - ECDH with secp256k1, secp256r1, brainpool 256r1, brainpool 256t1 and curve25519 curves -This release is in beta stage with known missing parts (see also Add-on) : +This release has known missing parts (see also Add-on) : * Ledger Blue support * Seed mode ON/OFF via apdu -## Installation +## Installation and Usage -### NanoS - -For both, source and binary installation, use the 1RC7 tag. - -#### From source - -Building from sources requires the the Nano S SDK 1.3.1.4 on firmware 1.3.1. See -https://github.com/LedgerHQ/nanos-secure-sdk - -The SDK must be slightly modified: - - - replace lib_stusb/STM32_USB_Device_Library/Class/CCID/src/usbd_ccid_if.c and - replace lib_stusb/STM32_USB_Device_Library/Class/CCID/inc/usbd_ccid_if.h by the one provided in sdk/ directory - - edit script.ld and modify the stack size : STACK_SIZE = 832; - -#### From Binary - -Use the Chrome App "Ledger Manager". See https://www.ledgerwallet.com/apps/manager for details. - -As "OpenPGP card" application is not fully released, click on "Show delevoppers items" on the bottom right corner. - -### Host - -#### Linux - -You have to have to add the NanoS to /etc/libccid_Info.plist - - In ifdVendorID add the entry 0x2C97 - In ifdProductID add the entry 0x0001 - In ifdFriendlyName add the entry Ledger Token - -This 3 entries must be added at the end of each list. - -#### MAC - -1. First it is necessary to [disable SIP](https://developer.apple.com/library/mac/documentation/Security/Conceptual/System_Integrity_Protection_Guide/ConfiguringSystemIntegrityProtection/ConfiguringSystemIntegrityProtection.html) That doesn't allow the editing of files in /usr/. - -2. You have to have to add the NanoS to /usr/libexec/SmartCardServices/drivers/ifd-ccid.bundle/Contents/Info.plist - - - In ifdVendorID add the entry 0x2C97 - In ifdProductID add the entry 0x0001 - In ifdFriendlyName add the entry Ledger Token - -This 3 entries must be added at the end of each list. - -3. [Enable SIP](https://developer.apple.com/library/content/documentation/Security/Conceptual/System_Integrity_Protection_Guide/ConfiguringSystemIntegrityProtection/ConfiguringSystemIntegrityProtection.html) - -#### Windows - -TODO +See the full doc at https://github.com/LedgerHQ/blue-app-openpgp-card/blob/master/doc/user/blue-app-openpgp-card.pdf ## Add-on @@ -79,7 +30,7 @@ The GnuPG application implements the following addon: - 3 independent key slots - seeded key generation -Technical specification is available in doc/gpgcard3.0-addon.rst +Technical specification is available at https://github.com/LedgerHQ/blue-app-openpgp-card/blob/master/doc/developper/gpgcard3.0-addon.rst ### Key slot diff --git a/doc/common/LogoLedgerV.png b/doc/common/LogoLedgerV.png new file mode 100644 index 0000000..7e8bbfc Binary files /dev/null and b/doc/common/LogoLedgerV.png differ diff --git a/doc/gpgcard3.0-addon.rst b/doc/developper/gpgcard3.0-addon.rst similarity index 100% rename from doc/gpgcard3.0-addon.rst rename to doc/developper/gpgcard3.0-addon.rst diff --git a/doc/developper/quick-test.txt b/doc/developper/quick-test.txt new file mode 100644 index 0000000..5558ce7 --- /dev/null +++ b/doc/developper/quick-test.txt @@ -0,0 +1,196 @@ +Step1: ... +----- +Jump into any temp dir + + +Step2: install nanos +----- +Do a fresh install of gpg application 1.1.0 from google app manager + + +Step3: setup conf +----- +Create a 'manual-test' directory + $ mkdir manual-test + +Create a 'manual-test/gnupg' + $ mkdir manual-test/gnupg + +Create a 'manual-test/gnupg/scdaemon.conf' file with content: + reader-port "Ledger Token [Nano S] (0001) 01 00" + allow-admin + card-timeout 1 + debug-level expert + debug 11 + log-file /tmp/scdaemon.log + +Jump into manual-test dir + +Step4: change to host pin style +----- +Launch gpg NanoS application and: + $ killall scdaemon gpg-agent + $ gpg2 --homedir `pwd`/gnupg --card-edit + gpg: WARNING: unsafe permissions on homedir '/home/cme/Projects/Git/ledgerblue/blue-app-openpgp-card/manual-test/gnupg' + gpg: keybox '/home/cme/Projects/Git/ledgerblue/blue-app-openpgp-card/manual-test/gnupg/pubring.kbx' created + + Reader ...........: Ledger Token [Nano S] (0001) 01 00 + Application ID ...: D2760001240103002C97DDD38BA90000 + Version ..........: 3.0 + Manufacturer .....: unknown + Serial number ....: DDD38BA9 + Name of cardholder: [not set] + Language prefs ...: [not set] + Sex ..............: unspecified + URL of public key : [not set] + Login data .......: [not set] + Signature PIN ....: not forced + Key attributes ...: rsa2048 rsa2048 rsa2048 + Max. PIN lengths .: 12 12 12 + PIN retry counter : 3 0 3 + Signature counter : 0 + Signature key ....: [none] + Encryption key....: [none] + Authentication key: [none] + General key info..: [none] + + gpg/card> verify + + Reader ...........: Ledger Token [Nano S] (0001) 01 00 + Application ID ...: D2760001240103002C97DDD38BA90000 + Version ..........: 3.0 + Manufacturer .....: unknown + Serial number ....: DDD38BA9 + Name of cardholder: [not set] + Language prefs ...: [not set] + Sex ..............: unspecified + URL of public key : [not set] + Login data .......: [not set] + Signature PIN ....: not forced + Key attributes ...: rsa2048 rsa2048 rsa2048 + Max. PIN lengths .: 12 12 12 + PIN retry counter : 3 0 3 + Signature counter : 0 + Signature key ....: [none] + Encryption key....: [none] + Authentication key: [none] + General key info..: [none] + + gpg/card> + +Then on nanos, goto settings->PIN mode, and select 'Host' +Then on nanos, goto settings->PIN mode, and select 'Set as default' + +unplug and replug the nanos + +relaunch the openpgp application + +Goto settings->PIN mode, and check you have "Host # +" (DASH and PLUS) + + +Step5: create 2048bits RSA keys +----- + + +In 'manual-test' directory, ask key generation. Nota that during this phase PIN has to be validate on Nanos + + $ killall scdaemon gpg-agent + $ gpg2 --homedir `pwd`/gnupg --card-edit + gpg: WARNING: unsafe permissions on homedir '/home/cme/Projects/Git/ledgerblue/blue-app-openpgp-card/manual-test/gnupg' + + Reader ...........: Ledger Token [Nano S] (0001) 01 00 + Application ID ...: D2760001240103002C97DDD38BA90000 + Version ..........: 3.0 + Manufacturer .....: unknown + Serial number ....: DDD38BA9 + Name of cardholder: [not set] + Language prefs ...: [not set] + Sex ..............: unspecified + URL of public key : [not set] + Login data .......: [not set] + Signature PIN ....: not forced + Key attributes ...: rsa2048 rsa2048 rsa2048 + Max. PIN lengths .: 12 12 12 + PIN retry counter : 3 0 3 + Signature counter : 0 + Signature key ....: [none] + Encryption key....: [none] + Authentication key: [none] + General key info..: [none] + + gpg/card> admin + Admin commands are allowed + + gpg/card> generate + Make off-card backup of encryption key? (Y/n) n + + Please note that the factory settings of the PINs are + PIN = '123456' Admin PIN = '12345678' + You should change them using the command --change-pin + + What keysize do you want for the Signature key? (2048) 2048 + What keysize do you want for the Encryption key? (2048) 2048 + What keysize do you want for the Authentication key? (2048) 2048 + Please specify how long the key should be valid. + 0 = key does not expire + = key expires in n days + w = key expires in n weeks + m = key expires in n months + y = key expires in n years + Key is valid for? (0) 0 + Key does not expire at all + Is this correct? (y/N) y + + GnuPG needs to construct a user ID to identify your key. + + Real name: testkey + Email address: + Comment: + You selected this USER-ID: + "testkey" + + Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O + gpg: /home/cme/Projects/Git/ledgerblue/blue-app-openpgp-card/manual-test/gnupg/trustdb.gpg: trustdb created + gpg: key 5ED17DF289C757A2 marked as ultimately trusted + gpg: directory '/home/cme/Projects/Git/ledgerblue/blue-app-openpgp-card/manual-test/gnupg/openpgp-revocs.d' created + gpg: revocation certificate stored as '/home/cme/Projects/Git/ledgerblue/blue-app-openpgp-card/manual-test/gnupg/openpgp-revocs.d/7FDC3D2FCD3558CB06631EAB5ED17DF289C757A2.rev' + public and secret key created and signed. + + + gpg/card> quit + pub rsa2048 2017-10-03 [SC] + 7FDC3D2FCD3558CB06631EAB5ED17DF289C757A2 + uid testkey + sub rsa2048 2017-10-03 [A] + sub rsa2047 2017-10-03 [E] + + + +Step6: encrypt/decrypt +----- +encrypt + + $ killall scdaemon gpg-agent + $ echo CLEAR > foo.txt + $ gpg2 --homedir `pwd`/gnupg -e -r testkey foo.txt + gpg: WARNING: unsafe permissions on homedir '/home/cme/Projects/Git/ledgerblue/blue-app-openpgp-card/manual-test/gnupg' + gpg: checking the trustdb + gpg: marginals needed: 3 completes needed: 1 trust model: pgp + gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u + +Force pin to asked + + $ killall gpg-agent scdaemon + +decrypt + + $ gpg2 --homedir `pwd`/gnupg foo.txt.gpg + gpg: WARNING: unsafe permissions on homedir '/home/cme/Projects/Git/ledgerblue/blue-app-openpgp-card/manual-test/gnupg' + gpg: encrypted with 2047-bit RSA key, ID 602FE5EB7BFA4B00, created 2017-10-03 + "testkey" + File 'foo.txt' exists. Overwrite? (y/N) y + +Step7: pin on screen +------ + +Restart from Step1, but skip step4 diff --git a/doc/openpgp-card-3.0.pdf b/doc/specification/openpgp-card-3.0.pdf similarity index 100% rename from doc/openpgp-card-3.0.pdf rename to doc/specification/openpgp-card-3.0.pdf diff --git a/doc/user/blue-app-openpgp-card.pdf b/doc/user/blue-app-openpgp-card.pdf new file mode 100644 index 0000000..da6874f Binary files /dev/null and b/doc/user/blue-app-openpgp-card.pdf differ diff --git a/doc/user/blue-app-openpgp-card.rst b/doc/user/blue-app-openpgp-card.rst new file mode 100644 index 0000000..97a778d --- /dev/null +++ b/doc/user/blue-app-openpgp-card.rst @@ -0,0 +1,1125 @@ +.. + Copyright 2018 Cedric Mesnil , Ledger SAS + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +.. + ------------------------------------------------------------------------ + LaTex substitution Definition + ------------------------------------------------------------------------ + + + +License +======= + +Author: Cedric Mesnil + +License: + + | Copyright 2017 Cedric Mesnil , Ledger SAS + | + | Licensed under the Apache License, Version 2.0 (the "License"); + | you may not use this file except in compliance with the License. + | You may obtain a copy of the License at + | + | http://www.apache.org/licenses/LICENSE-2.0 + | + | Unless required by applicable law or agreed to in writing, software + | distributed under the License is distributed on an "AS IS" BASIS, + | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + | See the License for the specific language governing permissions and + | limitations under the License. + + + + +Introduction +============ + +GnuPG application for Ledger Blue and Nano S + +This application implements "The OpenPGP card" specification revision 3.0. This specification is available in doc directory and at https://g10code.com/p-card.html . + +The application supports: + + - RSA with key up to 4096 bits + - ECDSA with secp256k1, secp256r1, brainpool 256r1 and brainpool 256t1 curves + - EDDSA with Ed25519 curve + - ECDH with secp256k1, secp256r1, brainpool 256r1, brainpool 256t1 and + curve25519 curves + +This release has known missing parts (see also [GPGADD]) : + + - Ledger Blue support + - Seed mode ON/OFF via apdu + + + +How to install GPG Application +============================== + +Nano S / Blue +------------- + +For both, source and binary installation, use the most recent tag. + +From Binary +~~~~~~~~~~~~~ + +Use the "Ledger Manager" Chrome App. See https://www.ledgerwallet.com/apps/manager for details. + +As the "OpenPGP card" application is not fully compliant with UI and documentation guidelines, the application is in developer section: click on "Show delevopers items" on the bottom right corner to see it. + + - Launch the Ledger Manager. See `Ledger Manager `_ + and ` `_ + for details about installing and using the manager; + - Connect your Nano S or your Blue, enter your PIN, and stay on the + dashboard; + - Click on *show developer items* on the bottom right corner; + - Click on the green bottom arrow icon near the Ledger *Open PGP* logo; + - Confirm the installation when required on your device by pressing the + right button above the checkmark; + - Quit the Ledger Manager + + +The application is ready to use! + + +From source +~~~~~~~~~~~~~ + +Building from sources requires the the Nano S SDK 1.4.2.1 on firmware 1.4.2. See https://github.com/LedgerHQ/nanos-secure-sdk + +Refer to the SDK documentation for the compiling/loading... + +System Configuration +-------------------- + +For Linux and MAC, until version 1.4.27, Ledger CCID interface is not supported +by default by pcscd and must be manually added + +For windows.... + +Linux +~~~~~ + +You have to have to add the NanoS to /etc/libccid_Info.plist + + - In ifdVendorID add the entry 0x2C97 + - In ifdProductID add the entry 0x0001 + - In ifdFriendlyName add the entry Ledger Token + +These 3 entries must be added at the end of each list. + +MAC +~~~ + +1. First it is necessary to [disable SIP](https://developer.apple.com/library/mac/documentation/Security/Conceptual/System_Integrity_Protection_Guide/ConfiguringSystemIntegrityProtection/ConfiguringSystemIntegrityProtection.html) That doesn't allow the editing of files in /usr/. + +2. You have to add the Nano S to /usr/libexec/SmartCardServices/drivers/ifd-ccid.bundle/Contents/Info.plist + + + - In ifdVendorID add the entry 0x2C97 + - In ifdProductID add the entry 0x0001 + - In ifdFriendlyName add the entry Ledger Token + +This 3 entries must be added at the end of each list. + +3. [Enable SIP](https://developer.apple.com/library/content/documentation/Security/Conceptual/System_Integrity_Protection_Guide/ConfiguringSystemIntegrityProtection/ConfiguringSystemIntegrityProtection.html) + +Windows +~~~~~~~ + +TODO + + + +Nano S OpenPGP Card application explained +========================================= + +Menu Overview +------------- + +The full menu layout is : + + +| \ *Device Info* +| Select Slot +| \ *Choose:* +| Slot 1 #+ +| Slot 2 +| Slot 3 +| Set Default +| Settings +| Key Template +| Choose Key... +| Signature +| Decryption +| Authentication +| Choose Type... +| RSA 2048 +| RSA 3072 +| RSA 4096 +| NIST P256 +| Brainpool 256R1 +| ED25519 +| Set Template +| Seed mode +| \ ** +| Set on +| Set off +| PIN mode +| UIF mode +| \ *Choose:* +| Host +| On Screen +| Confirm only #+ +| Trust +| Reset +| About +| \ *OpenPGP Card* +| \ *(c) Ledger SAS* +| \ *Spec 3.0* +| \ *App 1.0.1* + +| Emphasis entries are not selectable and just provide information. +| A "**#**" after the entry label means default value on reset. +| A "**+**" after the entry label means current value. + + +Device Info +------------- + +The *Device Info* provides current user and slot information. The format is: + +| + +with: + + * **name** is the one provided to ``gpg --card-edit``. See [GPGSC]. + * **n** is the current slot, see below. + * **s** is the 32 bits card serial number. Note that the last three bits always +encode the current slot value. + + +Select Slot +------------- + +A Slot is a set of +three key pairs *Signature, Decryption, Authentication* as defined by gnupg +specification. + +Usually a GPG card application only manages a single set. Ledger version enhances +this and allows you to manage three key sets. + +The *Select Slot* menu allows you to select the slot you want to play with, and +to set the default slot when the application start. + +To change the current slot, display the slot you want and select it + +To change the default slot, first select it, and then select the *Set Default* +entry. + + +Settings +-------- + +Key Template +~~~~~~~~~~~~ + +A key template is defined by the OpenGPG card application specification. It +describes the key to be generated with the ``generate`` command in +``gpg --card-edit`` + +The problem is there is no way with the ``gpg --card-edit`` command line +to easily set up the desired template, except for Ed25519. + +To set up a new ECC template you have tow choice: the NanoS menu or the +gpg-connect-agent tools. + + + +**gpg-connect-agent** (recommended) + +This method suppose you have correctly configured your GnuPG tool. +See the dedicated section for that. + +In a terminal launch : + + gpg-connect-agent "SCD SETATTR KEY-ATTR --force 1 " /bye + gpg-connect-agent "SCD SETATTR KEY-ATTR --force 2 18 " /bye + gpg-connect-agent "SCD SETATTR KEY-ATTR --force 3 " /bye + +This 3 commands fix, in that order, the template for Signature, Decryption, Authentication keys. + +Supported curve name are: + +- secp256k1 with tag 19 +- nistp256 with tag 19 +- brainpoolP256r1 with tag 19 +- cv25519 (only for key 2) +- ed25519 with tag 22 (only for key 1 and 3) + + +To show the current template use the ``gpg --card-status`` command. + +**NanoS menu** + +First under *Choose Key* menu, select the one of three keys for which you want to modify +the template. Then under "Choose Type", select the desired key template. +Finally select "Set Template" entry to set it. + +To show the current template use the ``gpg --card-status`` command. + +Seed mode +~~~~~~~~~ + +**WARNING** : SEED MODE IS EXPERIMENTAL + +When generating new keys on NanoS, those keys can be generated randomly +or in a deterministic way. The deterministic way is specified in [GPGADD]. +The current mode is displayed in the first sub menu. To activate the seeded + mode select *ON*, to deactivate the seeded mode select *OFF*. + +When the application starts, the seeded mode is always set to *OFF* + +**WARNING** : SEED MODE IS EXPERIMENTAL + +PIN mode +~~~~~~~~ + +Some operations require the user to enter his PIN code. +The default PIN values are: + + - user: ``123456`` + - admin: ``12345678`` + +The PIN entry can be done using four methods, named +"*Host*", "*On Screen*", "*Confirm only*", "*Trust*". + +After each mode a *+* or *#* symbol may appear to tell which mode is the current one +and which one is the default when the application starts. +The default mode can be changed by first selecting the desired mode and then +selecting the *Set default" menu. Note that *Trust* can not be set as default mode. + + +Note that *On Screen*", "*Confirm only*" and "*Trust*" may not work if the +client application does not support it. In that case the "*Host*" should be +automatically used by the client in a transparent way. + +**Host** + +The PIN is entered on the external computer. + + +**On Screen** + +The PIN is entered on the Nano S or Blue screen. For entering the PIN choose the +next digit by using the left or right button. When the digit you expect is displayed +select it by pressing both buttons ar the same time + +.. image:: pin_entry.png + :align: middle + + +Once all digits are selected, validate the PIN by selecting the **'V'** (Validate) +letter + +.. image:: pin_validate.png + :align: middle + + +If you want to change the previous digit select the **'C'** (Cancel) letter. + +.. image:: pin_cancel.png + :align: middle + + +Finnaly if you want to abort the PIN entry, select the **'A'** (Abort) letter. + +.. image:: pin_abort.png + :align: middle + + +**Confirm only** + +The user is requested, on the NanoS or Blue screen, to confirm +the PIN validation. The PIN value is not required, the user just has +to push the *REJECT* or *OK* button on the device. + +This is the default mode after application installation. + +.. image:: pin_confirm.png + :align: middle + + +**Trust** + +Act as if the PIN is always validated. This is a dangerous mode which should only be +used in a highly secure environment. + +UIF mode +~~~~~~~~ + + +By activating UIF mode for either signature, decryption or authentication, a user validation +will be ask by the device each time the related operation is performed. + +To activate or deactivate the UIF, select the operation to protect and press both button. +When activated, a '+' symbol appears after the operation name. + + +Reset +~~~~~ + +Selecting the menu will erase all OpenPGP Card Application data and will +reset the application in its '*just installed*' state. + + + +Nano S OpenPGP Card application usage +===================================== + + +GPG +--- + +The OpenGPG Card application need at least version 2.1.19 for full support. +A version prior to 2.1.19 will fail when using ECC. + +You should test with a test key and make a backup of your +keyring before starting, except if your are sure about what you do. + + +Configuration +~~~~~~~~~~~~~ + +In order to use a Ledger device with gpg it is needed to explicitly setup +the reader and the delegated PIN support. +Edit the file ~/.gnupg/scdaemon.conf and add the following lines: + + | ``reader-port "Ledger Token [Nano S] (0001) 01 00"`` + | ``allow-admin`` + | ``enable-pinpad-varlen`` + + +If you do not set the ``enable-pinpad-varlen`` option, even if Nano S is +configured in *On Screen* mode, gpg will keep requesting the PIN on the host. + +You can check ``the reader-port`` value by running the command line ``pcsc_scan``: + + | ``$ pcsc_scan `` + | ``PC/SC device scanner`` + | ``V 1.4.27 (c) 2001-2011, Ludovic Rousseau `` + | ``Compiled with PC/SC lite version: 1.8.14`` + | ``Using reader plug'n play mechanism`` + | ``Scanning present readers...`` + | ``0: Alcor Micro AU9560 00 00`` + | ``1: Ledger Token [Nano S] (0001) 01 00`` + | ``Reader 0: Alcor Micro AU9560 00 00`` + | ``Card state: Card removed, `` + | ``Reader 1: Ledger Token [Nano S] (0001) 01 00`` + | ``Card state: Card inserted, `` + | ``ATR: 3B 00`` + | ``+ TS = 3B --> Direct Convention`` + | ``+ T0 = 00, Y(1): 0000, K: 0 (historical bytes)`` + + + +Get/Set basic information +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``gpg --card-status`` command provides default card information. Just after +installation it should look like this: + + | ``$ gpg --card-status`` + | ``Reader ...........: Ledger Token [Nano S] (0001) 01 00`` + | ``Application ID ...: D2760001240103002C97AFB114290000`` + | ``Version ..........: 3.0`` + | ``Manufacturer .....: unknown`` + | ``Serial number ....: AFB11429`` + | ``Name of cardholder: [not set]`` + | ``Language prefs ...: [not set]`` + | ``Sex ..............: unspecified`` + | ``URL of public key : [not set]`` + | ``Login data .......: [not set]`` + | ``Signature PIN ....: not forced`` + | ``Key attributes ...: rsa2048 rsa2048 rsa2048`` + | ``Max. PIN lengths .: 12 12 12`` + | ``PIN retry counter : 3 0 3`` + | ``Signature counter : 0`` + | ``Signature key ....: [none]`` + | ``Encryption key....: [none]`` + | ``Authentication key: [none]`` + | ``General key info..: [none]`` + +You can set the user information with the ``gpg --card-edit`` command and +``name``, ``url``, ``login``, ``lang``, ``sex`` subcommands. For example if you +want to set up your name: + + | ``$ gpg --card-edit`` + | ``gpg/card> admin`` + | ``Admin commands are allowed`` + | ```` + | ``gpg/card> name`` + | ``Cardholder's surname: Mesnil`` + | ``Cardholder's given name: Cedric`` + | ```` + | ``gpg/card> sex`` + | ``Sex ((M)ale, (F)emale or space): M`` + | ```` + | ``gpg/card> list`` + | ```` + | ``Reader ...........: Ledger Token [Nano S] (0001) 01 00`` + | ``Application ID ...: D2760001240103002C97AFB114290000`` + | ``Version ..........: 3.0`` + | ``Manufacturer .....: unknown`` + | ``Serial number ....: AFB11429`` + | ``Name of cardholder: Cedric Mesnil`` + | ``Language prefs ...: [not set]`` + | ``Sex ..............: unspecified`` + | ``URL of public key : [not set]`` + | ``Login data .......: [not set]`` + | ``Signature PIN ....: not forced`` + | ``Key attributes ...: rsa2048 rsa2048 rsa2048`` + | ``Max. PIN lengths .: 12 12 12`` + | ``PIN retry counter : 3 0 3`` + | ``Signature counter : 0`` + | ``Signature key ....: [none]`` + | ``Encryption key....: [none]`` + | ``Authentication key: [none]`` + | ``General key info..: [none]`` + + +Notes: + + * Modifying the user information will prompt you to enter ``User PIN``. + * Setting user information is not required for using gpg client. + + +Generate new key pair +~~~~~~~~~~~~~~~~~~~~~ + +For generating a new key pair follow those steps: + + - Select the desired NanoS OpenPGP Card application slot + - Setup the desired key template for this slot + - Generate the new key set + + +**Step 1** + +Starting from main menu: + + - Select *Select slot* menu + - Scroll to desired slot + - Select it + - Optionally set it as default by selecting *Set Default* menu + - Select *Back* to return to main menu. + + +**Step 2** + +The default template for each three keys (*signature*, *decryption*, +*authentication*) is ``RSA 2048``. If you want another kind of key +you have to set the template before generating keys. + +!WARNING!: changing the current template of a key automatically erases +the associated key. + +Starting from main menu: + + - Select *Settings* menu + - Select *Key template* menu + - Select *Choose Key...* menu (a) + - Scroll and select which key you want to set the new template for + - Select *Choose type...* menu + - Scroll and select among the supported key types and sizes + - Select *Set template* + - Repeat this process from (a) if you want to modify another key + template + - Select *Back* to return to main menu. + + +**Step 3** + +Once the template has been set, it's possible to generate new key pairs +with ``gpg``. + +!WARNING!: gpg will generate the three key pairs and +will overwrite any key already present in the selected slot. + +Here after is a detailed log of key generation of ECC keys, assuming +the three key templates are ``NIST P256``. + +**Edit Card** + + + | ``$ gpg2 --edit-card`` + | ``Reader ...........: Ledger Token [Nano S] (0001) 01 00`` + | ``Application ID ...: D2760001240103002C97AFB1142B0000`` + | ``Version ..........: 3.0`` + | ``Manufacturer .....: unknown`` + | ``Serial number ....: AFB1142B`` + | ``Name of cardholder: Cedric Mesnil`` + | ``Language prefs ...: [not set]`` + | ``Sex ..............: male`` + | ``URL of public key : [not set]`` + | ``Login data .......: [not set]`` + | ``Signature PIN ....: not forced`` + | ``Key attributes ...: nistp256 nistp256 nistp256`` + | ``Max. PIN lengths .: 12 12 12`` + | ``PIN retry counter : 3 0 3`` + | ``Signature counter : 0`` + | ``Signature key ....: [none]`` + | ``Encryption key....: [none]`` + | ``Authentication key: [none]`` + | ``General key info..: [none]`` + +**Switch to admin mode:** + + | ``gpg/card>`` *admin* + | ``Admin commands are allowed`` + +**Request new key generation without backup** + + | ``gpg/card>`` *generate* + | ``Make off-card backup of encryption key? (Y/n)`` **n** + +**Unlock user level ``81``** + + | ``Please unlock the card`` + | ```` + | ``Number: 2C97 AFB1142B`` + | ``Holder: Cedric Mesnil`` + | ```` + | ``Use the reader's pinpad for input.`` + | `` OK`` + | ``Press any key to continue. `` + +**Set key validity** + + | ``Please specify how long the key should be valid.`` + | `` 0 = key does not expire`` + | `` = key expires in n days`` + | `` w = key expires in n weeks`` + | `` m = key expires in n months`` + | `` y = key expires in n years`` + | ``Key is valid for? (0)`` *0* + | ``Key does not expire at all`` + | ``Is this correct? (y/N)``*y* + +**Set user ID** + + | ``GnuPG needs to construct a user ID to identify your key.`` + | ```` + | ``Real name: Cedric Mesnil`` + | ``Email address: cedric@ledger.fr`` + | ``Comment: `` + | ``You selected this USER-ID:`` + | `` "Cedric Mesnil "`` + | ```` + | ``Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit?`` *O* + | ```` + | ``You selected this USER-ID:`` + | `` "Cedric Mesnil "`` + +**Unlock admin level ``83``** + + | ``Please enter the Admin PIN `` + | ```` + | ``Number: 2C97 AFB1142B`` + | ``Holder: Cedric Mesnil`` + | ```` + | ``Use the reader's pinpad for input.`` + | `` OK`` + | ``Press any key to continue.`` + +**Unlock user level ``82``** + + | ``Please unlock the card`` + | ```` + | ``Number: 2C97 AFB1142B`` + | ``Holder: Cedric Mesnil`` + | ``Counter: 8`` + | ```` + | ``Use the reader's pinpad for input.`` + | `` OK`` + | ``Press any key to continue.`` + +**Final confirmation** + + | ``Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit?`` *O* + | ``gpg: key DF3FA4A33EF00E47 marked as ultimately trusted`` + | ``gpg: revocation certificate stored as '/home/gnuk/.gnupg/openpgp-revocs.d/89F772243C9A3E583CB59AB5DF3FA4A33EF00E47.rev'`` + | ``public and secret key created and signed.`` + +**Get information after key generation** + + | ``gpg/card> list`` + | ```` + | ``Reader ...........: Ledger Token [Nano S] (0001) 01 00`` + | ``Application ID ...: D2760001240103002C97AFB1142B0000`` + | ``Version ..........: 3.0`` + | ``Manufacturer .....: unknown`` + | ``Serial number ....: AFB1142B`` + | ``Name of cardholder: Cedric Mesnil`` + | ``Language prefs ...: [not set]`` + | ``Sex ..............: male`` + | ``URL of public key : [not set]`` + | ``Login data .......: [not set]`` + | ``Signature PIN ....: not forced`` + | ``Key attributes ...: nistp256 nistp256 nistp256`` + | ``Max. PIN lengths .: 12 12 12`` + | ``PIN retry counter : 3 0 3`` + | ``Signature counter : 12`` + | ``Signature key ....: F844 38BB CA87 F9A7 6830 F002 F8A4 A353 3CBF CAA5`` + | `` created ....: 2017-08-22 15:59:36`` + | ``Encryption key....: B1D3 C9F2 C3C5 87CA 36A7 F02E E137 28E9 13B8 77E1`` + | `` created ....: 2017-08-22 15:59:36`` + | ``Authentication key: F87D EF02 9C38 C43D 41F0 6872 2345 A677 CE9D 8223`` + | `` created ....: 2017-08-22 15:59:36`` + | ``General key info..: pub nistp256/F8A4A3533CBFCAA5 2017-08-22 cedric mesnilCedric Mesnil `` + | ``sec> nistp256/F8A4A3533CBFCAA5 created: 2017-08-22 expires: never `` + | `` card-no: 2C97 AFB1142B`` + | ``ssb> nistp256/2345A677CE9D8223 created: 2017-08-22 expires: never `` + | `` card-no: 2C97 AFB1142B`` + | ``ssb> nistp256/E13728E913B877E1 created: 2017-08-22 expires: never `` + | `` card-no: 2C97 AFB1142B`` + +**Say goodbye + + | ``gpg/card> quit``** + + + +At this point it's possible to check that the key has been generated on card with the following command: + + | ``$ gpg2 --list-secret-keys cedric@ledger`` + | ``gpg: checking the trustdb`` + | ```` + | ``sec> nistp256 2017-08-22 [SC]`` + | `` F84438BBCA87F9A76830F002F8A4A3533CBFCAA5`` + | `` Card serial no. = 2C97 AFB1142B`` + | ``uid [ultimate] cedric mesnilCedric Mesnil `` + | ``ssb> nistp256 2017-08-22 [A]`` + | ``ssb> nistp256 2017-08-22 [E]`` + + + +Moving existing key pair +~~~~~~~~~~~~~~~~~~~~~~~~ + +This section shows how to move an existing key onto the Ledger device. + +The key to transfer here is a RSA 4096 bits key: + + | ``$ gpg2 --list-secret-keys "RSA 4096"`` + | ``sec rsa4096 2017-04-26 [SC]`` + | `` FB6C6C75FB016635872ED3E49B93CB47F954FB53`` + | ``uid [ultimate] RSA 4096`` + | ``ssb rsa4096 2017-04-26 [E]`` + + +In case of transfer it is not necessary to previously set the template. +It will be automatically changed. +When generating a new key, the three keys (*signature*, *decryption*, +*authentication*)) are automatically generated. +When transferring existing ones, it is possible to choose which one will be +moved. + +**Edit Key** + + | ``$ gpg2 --edit-key "RSA 4096"`` + | ``gpg (GnuPG) 2.1.19; Copyright (C) 2017 Free Software Foundation, Inc.`` + | ``This is free software: you are free to change and redistribute it.`` + | ``There is NO WARRANTY, to the extent permitted by law.`` + | ```` + | ``Secret key is available.`` + | ```` + | ``sec rsa4096/9B93CB47F954FB53`` + | `` created: 2017-04-26 expires: never usage: SC `` + | `` trust: ultimate validity: ultimate`` + | ``ssb rsa4096/49EE12B0F5CBDF26`` + | `` created: 2017-04-26 expires: never usage: E `` + | ``[ultimate] (1). RSA 4096`` + +**Select the key to move, here the *encryption* one.** + + | ``gpg> `` *key 1* + | `` `` + | ``sec rsa4096/9B93CB47F954FB53`` + | `` created: 2017-04-26 expires: never usage: SC `` + | `` trust: ultimate validity: ultimate`` + | ``ssb* rsa4096/49EE12B0F5CBDF26`` + | `` created: 2017-04-26 expires: never usage: E `` + | ``[ultimate] (1). RSA 4096`` + +**Move** + + | ``gpg> `` **keytocard** + | ``Please select where to store the key:`` + | `` (2) Encryption key`` + | ``Your selection?`` *2* + +**Unlock admin level ``83``** + + | ``Please enter the Admin PIN`` + | ```` + | ``Number: 2C97 1D49B409`` + | ``Holder: `` + | ```` + | ``Use the reader's pinpad for input.`` + | `` OK`` + | ``Press any key to continue.`` + +**Unlock admin level ``83`` (maybe twice....)** + + | ``Please enter the Admin PIN`` + | ```` + | ``Number: 2C97 1D49B409`` + | ``Holder: `` + | ```` + | ``Use the reader's pinpad for input.`` + | `` OK`` + | ``Press any key to continue.`` + + | ``sec rsa4096/9B93CB47F954FB53`` + | `` created: 2017-04-26 expires: never usage: SC `` + | `` trust: ultimate validity: ultimate`` + | ``ssb* rsa4096/49EE12B0F5CBDF26`` + | `` created: 2017-04-26 expires: never usage: E `` + | ``[ultimate] (1). RSA 4096`` + +**Say goobye with saving!** + + | ``gpg> `` *save* + +**check** + + | ``$ gpg2 --edit-keys cedric`` + | ``gpg: error reading key: No public key`` + | ``gnuk@Lulu:~$ /opt/gnupg2.1.19/bin/gpg2 --edit-key "RSA 4096"`` + | ``gpg (GnuPG) 2.1.19; Copyright (C) 2017 Free Software Foundation, Inc.`` + | ``This is free software: you are free to change and redistribute it.`` + | ``There is NO WARRANTY, to the extent permitted by law.`` + | ```` + | ``Secret key is available.`` + | ```` + | ``sec rsa4096/9B93CB47F954FB53`` + | `` created: 2017-04-26 expires: never usage: SC `` + | `` trust: ultimate validity: ultimate`` + | ``ssb rsa4096/49EE12B0F5CBDF26`` + | `` created: 2017-04-26 expires: never usage: E `` + | `` card-no: 2C97 7BB895B9`` + | ``[ultimate] (1). RSA 4096`` + | ```` + | ``gpg> `` *quit* + + +The encryption key is now associated with a card. + +Decrypting and Signing +~~~~~~~~~~~~~~~~~~~~~~ + + +Decrypting and Signing will act exactly the same way as if keys were not on +the card. The only difference is ``gpg`` will request the PIN code instead +of the passphrase. + + +SSH +--- + + +Overview +~~~~~~~~ + +In order to use gpg for SSH authentication, an "authentication" is needed. +There are two solutions for that, either generate one on the device +or add an authentication sub-key to your existing master gpg key. + +Once done, it is necessary to configure ssh to point to the right key and +delegate the authentication to *gpg-ssg-agent* instead of *ssh-agent*. + + +Generate new key on device +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The important thing to keep in mind here is there is no way to tell gpg to +only generate the authentication key. So generating this key will also +generate the two other under a new identity and will erase existing keys +on the current slot on the device. + +Nevertheless, if you want to use a different identity for ssh login, you can use +another slot on the device. See `Nano S OpenPGP Card application explained`_ +and `Generate new key pair`_. + + +Add sub-key +~~~~~~~~~~~ + +**Edit pgp key set** + + | ``$ gpg --expert --edit-key cedric`` + | ``gpg (GnuPG) 2.1.15; Copyright (C) 2016 Free Software Foundation, Inc.`` + | ``This is free software: you are free to change and redistribute it.`` + | ``There is NO WARRANTY, to the extent permitted by law.`` + + | ``Secret key is available.`` + + | ``sec rsa2048/831415DA94A9A15C`` + | `` created: 2017-08-25 expires: never usage: SC `` + | `` trust: ultimate validity: ultimate`` + | ``ssb rsa2048/8E95F2999EEC38C4`` + | `` created: 2017-08-25 expires: never usage: E `` + | ``[ultimate] (1). cedric`` + +**Add sub key** + + | ``gpg> *addkey*`` + + | ``Please select what kind of key you want:`` + | `` (3) DSA (sign only)`` + | `` (4) RSA (sign only)`` + | `` (5) Elgamal (encrypt only)`` + | `` (6) RSA (encrypt only)`` + | `` (7) DSA (set your own capabilities)`` + | `` (8) RSA (set your own capabilities)`` + | `` (10) ECC (sign only)`` + | `` (11) ECC (set your own capabilities)`` + | `` (12) ECC (encrypt only)`` + | `` (13) Existing key`` + | ``Your selection? 8`` + +**Toggle sign/encrypt OFF, Toggle authentication ON** + + | ``Possible actions for a RSA key: Sign Encrypt Authenticate `` + | ``Current allowed actions: Sign Encrypt `` + + | `` (S) Toggle the sign capability`` + | `` (E) Toggle the encrypt capability`` + | `` (A) Toggle the authenticate capability`` + | `` (Q) Finished`` + + | ``Your selection?`` *S* + + | ``Possible actions for a RSA key: Sign Encrypt Authenticate `` + | ``Current allowed actions: Encrypt `` + + | `` (S) Toggle the sign capability`` + | `` (E) Toggle the encrypt capability`` + | `` (A) Toggle the authenticate capability`` + | `` (Q) Finished`` + + | ``Your selection?`` *E* + + | ``Possible actions for a RSA key: Sign Encrypt Authenticate `` + | ``Current allowed actions: `` + + | `` (S) Toggle the sign capability`` + | `` (E) Toggle the encrypt capability`` + | `` (A) Toggle the authenticate capability`` + | `` (Q) Finished`` + + | ``Your selection?`` *A* + + | ``Possible actions for a RSA key: Sign Encrypt Authenticate `` + | ``Current allowed actions: Authenticate `` + + | `` (S) Toggle the sign capability`` + | `` (E) Toggle the encrypt capability`` + | `` (A) Toggle the authenticate capability`` + | `` (Q) Finished`` + + | ``Your selection? Q`` + +**Set key options** + + | ``RSA keys may be between 1024 and 4096 bits long.`` + | ``What keysize do you want? (2048)`` *2048* + | ``Requested keysize is 2048 bits`` + | ``Please specify how long the key should be valid.`` + | `` 0 = key does not expire`` + | `` = key expires in n days`` + | `` w = key expires in n weeks`` + | `` m = key expires in n months`` + | `` y = key expires in n years`` + | ``Key is valid for? (0)`` *0* + | ``Key does not expire at all`` + | ``Is this correct? (y/N)`` *y* + | ``Really create? (y/N)`` *y* + | ``We need to generate a lot of random bytes. It is a good idea to perform`` + | ``some other action (type on the keyboard, move the mouse, utilize the`` + | ``disks) during the prime generation; this gives the random number`` + | ``generator a better chance to gain enough entropy.`` + + | ``sec rsa2048/831415DA94A9A15C`` + | `` created: 2017-08-25 expires: never usage: SC `` + | `` trust: ultimate validity: ultimate`` + | ``ssb rsa2048/8E95F2999EEC38C4`` + | `` created: 2017-08-25 expires: never usage: E `` + | ``ssb rsa2048/C20B90E12F68F035`` + | `` created: 2017-08-28 expires: never usage: A `` + | ``[ultimate] (1). cedric`` + +**Select the key and move it** + + | ``gpg> key`` *2* + | ```` + | ``sec rsa2048/831415DA94A9A15C`` + | `` created: 2017-08-25 expires: never usage: SC `` + | `` trust: ultimate validity: ultimate`` + | ``ssb rsa2048/8E95F2999EEC38C4`` + | `` created: 2017-08-25 expires: never usage: E `` + | ``ssb* rsa2048/C20B90E12F68F035`` + | `` created: 2017-08-28 expires: never usage: A `` + | ``[ultimate] (1). cedric`` + | ```` + | ``gpg>`` *keytocard* + | ``Please select where to store the key:`` + | `` (3) Authentication key`` + | ``Your selection?`` *3* + | `` `` + | ``sec rsa2048/831415DA94A9A15C`` + | `` created: 2017-08-25 expires: never usage: SC `` + | `` trust: ultimate validity: ultimate`` + | ``ssb rsa2048/8E95F2999EEC38C4`` + | `` created: 2017-08-25 expires: never usage: E `` + | ``ssb* rsa2048/C20B90E12F68F035`` + | `` created: 2017-08-28 expires: never usage: A `` + | ``[ultimate] (1). cedric`` + + +**Save and Quit** + + | ``gpg>`` save + | ``$ `` + + + + +Configure SSH and GPG +~~~~~~~~~~~~~~~~~~~~~ + + +First, tell gpg-agent to enable ssh-auth feature by adding the following line +to your .gpg-agent.conf: + + ``enable-ssh-support`` + +Starting with gpg2 it necessary to add some configuration options to make the *pinentry* +work properly. Add the following line to ~/.bashrc file: + + | ``export SSH_AUTH_SOCK=`gpgconf --list-dirs agent-ssh-socket` `` + | ``export GPG_TTY=`tty` `` + | ``gpgconf --launch gpg-agent `` + + +It may be also necessary to setup the loopback pinentry options. + +Add the following line to your ~/.gnupg/gpg-agent.conf: + + ``allow-loopback-pinentry`` + +And add the following line to your ~/.gnupg/gpg.conf: + + ``pinentry-mode loopback`` + + +Then export your authentication public key. First execute the +``gpg -k --with-subkey-fingerprint --with-keygrip cedric`` command. + + + | ``pub rsa2048 2017-08-25 [SC]`` + | `` 7886147C4C2E5CE2A4B1546C831415DA94A9A15C`` + | `` Keygrip = DE2B63C13AB92EBD2D05C1021A9DAA2D40ECB564`` + | ``uid [ultimate] cedric`` + | ``sub rsa2048 2017-08-25 [E]`` + | `` 789E56872A0D9A5AC8AF9C2F8E95F2999EEC38C4`` + | `` Keygrip = 9D7C2EF8D84E3B31371A09DFD9A4B3EF72AB4ACE`` + | ``sub rsa2048 2017-08-28 [A]`` + | `` 2D0E4FFFAA448AA2770C7F02C20B90E12F68F035`` + | `` Keygrip = 6D60CB58D9D66EE09804E7FE460E865A91F5E41A`` + +Add the keygrip of the authentication key, the one identified by ``[A]``, +to .gnupg/sshcontrol file: + + | ``$ echo 6D60CB58D9D66EE09804E7FE460E865A91F5E41A > .gnupg/sshcontrol`` + +Export your authentication key, identifier by its fingerprint, in a SSH compliant format. + + | ``$ gpg --export-ssh-key 2D0E4FFFAA448AA2770C7F02C20B90E12F68F035`` + | ``ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDCIARKh0IZTHld+I6oA8nwrgnCUQE8f`` + | ``7X3pmI4ZwryT52fKhpcsQJsd3krodXrM//LiK8+m2ZRMneJ9iGlqqE7SCyZkNBj1GUm9s`` + | ``rK3Q5eoR6nU0s+sq17b/FAtQWHBJTqqaOtyA33hFj5twUtWZ6rokX9cNZrD1ne8kRVHDe`` + | ``3uEBsaY5PR1Tuko/GwywLyZu0SwfEobl/RPjL7P8rUSc7DTHpQMw8fjJFb4BNvIHAlaVC`` + | ``5FwZwkuogygaJdN/44MayHFmOZmzx9CAgYgLpTzen35+CcyhlqCqi+HjNlnHL2DDWd4iR`` + | ``d3Y6pY8LjS3xQkECc3Bhedptp17D+H9AVJt openpgp:0x2F68F035`` + +Finaly copy the above export (``ssh-rsa AAAAB...Jt openpgp:0x2F68F035``) into the +~/.ssh/authorized_keys file on your remote server. + + +Now, if everything is correctly setup and running, an ``ssh-add -l`` should show your key: + + | ``$ ssh-add -l`` + | ``2048 SHA256:sLCzsoi5GAG2kJkG6hSp8gTLPxSvo/zNtsks2kQ7vTU cardno:2C979421A9E1 (RSA)`` + | ``2048 SHA256:sLCzsoi5GAG2kJkG6hSp8gTLPxSvo/zNtsks2kQ7vTU (none) (RSA)`` + +And you should be able to ssh to your remote server with your gpg key! + + +Trouble/FAQ +----------- + +**Q:** pinentry failed with a strange canceled message: + +**R:** there is some problem with gpg2 and pinentry-gnome3. You may update your system +to use pinentry-gtk-2. Under Ubuntu-like OS, use ``update-alternatives --config pinentry`` + + | ```` + +**Q:** gpg-connection agent failed + +**R:** check that you don't have multiple running agents. After setting-up all SSH stuff, try to fully +logout/login + + | ```` + +**Q:** It does not work at all, HELP ME!!! + +**R** Please keep calm and do not cry. +Add the following option to ~/.gnupg/gpg-agent.conf + + | ``debug-level guru`` + | ``log-file /tmp/gpgagent.log`` + +Add the follwing option to ~/.gnupg/scdaemon.conf + + | ``log-file /tmp/scd.log`` + | ``debug-level guru`` + | ``debug-all`` + +Make a nice issue report under github providing log and and command line you run. +**!*WARNING*!** : this may reveal confidential information such as key values. Do your log with a test key. + + +Annexes +======= + +References +---------- + +* [GPG] *The GNU Privacy Guard*, https://gnupg.org/ +* [GPGSC] *The GnuPG Smartcard HOWTO*, https://gnupg.org/howtos/card-howto/en/smartcard-howto.html +* [G10CODE] *The OpenPGP card application*, https://g10code.com/p-card.html +* [GPGADD] *The OpenPGP card application add-on*, https://github.com/LedgerHQ/blue-app-openpgp-card/blob/master/doc/gpgcard3.0-addon.rst diff --git a/doc/user/blue-app-openpgp-card.template b/doc/user/blue-app-openpgp-card.template new file mode 100644 index 0000000..6e2e0f8 --- /dev/null +++ b/doc/user/blue-app-openpgp-card.template @@ -0,0 +1,275 @@ +\documentclass[$if(fontsize)$$fontsize$,$endif$$if(lang)$$babel-lang$,$endif$$if(papersize)$$papersize$paper,$endif$$for(classoption)$$classoption$$sep$,$endfor$,towside]{report} +$if(fontfamily)$ +\usepackage[$for(fontfamilyoptions)$$fontfamilyoptions$$sep$,$endfor$]{$fontfamily$} +$else$ +\usepackage{lmodern} +$endif$ +$if(linestretch)$ +\usepackage{setspace} +\setstretch{$linestretch$} +$endif$ + +\usepackage{amssymb,amsmath} +\usepackage{ifxetex,ifluatex} +\usepackage{fixltx2e} % provides \textsubscript +\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex + \usepackage[$if(fontenc)$$fontenc$$else$T1$endif$]{fontenc} + \usepackage[utf8]{inputenc} +$if(euro)$ + \usepackage{eurosym} +$endif$ +\else % if luatex or xelatex + \ifxetex + \usepackage{mathspec} + \else + \usepackage{fontspec} + \fi + \defaultfontfeatures{Ligatures=TeX,Scale=MatchLowercase} +$for(fontfamilies)$ + \newfontfamily{$fontfamilies.name$}[$fontfamilies.options$]{$fontfamilies.font$} +$endfor$ +$if(euro)$ + \newcommand{\euro}{€} +$endif$ +$if(mainfont)$ + \setmainfont[$for(mainfontoptions)$$mainfontoptions$$sep$,$endfor$]{$mainfont$} +$endif$ +$if(sansfont)$ + \setsansfont[$for(sansfontoptions)$$sansfontoptions$$sep$,$endfor$]{$sansfont$} +$endif$ +$if(monofont)$ + \setmonofont[Mapping=tex-ansi$if(monofontoptions)$,$for(monofontoptions)$$monofontoptions$$sep$,$endfor$$endif$]{$monofont$} +$endif$ +$if(mathfont)$ + \setmathfont(Digits,Latin,Greek)[$for(mathfontoptions)$$mathfontoptions$$sep$,$endfor$]{$mathfont$} +$endif$ +$if(CJKmainfont)$ + \usepackage{xeCJK} + \setCJKmainfont[$for(CJKoptions)$$CJKoptions$$sep$,$endfor$]{$CJKmainfont$} +$endif$ +\fi +% use upquote if available, for straight quotes in verbatim environments +\IfFileExists{upquote.sty}{\usepackage{upquote}}{} +% use microtype if available +\IfFileExists{microtype.sty}{% +\usepackage{microtype} +\UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts +}{} + +$if(geometry)$ +\usepackage[$for(geometry)$$geometry$$sep$,$endfor$]{geometry} +$endif$ + +\usepackage[unicode=true]{hyperref} +$if(colorlinks)$ +\PassOptionsToPackage{usenames,dvipsnames}{color} % color is loaded by hyperref +$endif$ +\hypersetup{ +$if(title-meta)$ + pdftitle={$title-meta$}, +$endif$ +$if(author-meta)$ + pdfauthor={$author-meta$}, +$endif$ +$if(keywords)$ + pdfkeywords={$for(keywords)$$keywords$$sep$; $endfor$}, +$endif$ +$if(colorlinks)$ + colorlinks=true, + linkcolor=$if(linkcolor)$$linkcolor$$else$Maroon$endif$, + citecolor=$if(citecolor)$$citecolor$$else$Blue$endif$, + urlcolor=$if(urlcolor)$$urlcolor$$else$Blue$endif$, +$else$ + pdfborder={0 0 0}, +$endif$ + breaklinks=true} +\urlstyle{same} % don't use monospace font for urls +$if(lang)$ +\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex + \usepackage[shorthands=off,$for(babel-otherlangs)$$babel-otherlangs$,$endfor$main=$babel-lang$]{babel} +$if(babel-newcommands)$ + $babel-newcommands$ +$endif$ +\else + \usepackage{polyglossia} + \setmainlanguage[$polyglossia-lang.options$]{$polyglossia-lang.name$} +$for(polyglossia-otherlangs)$ + \setotherlanguage[$polyglossia-otherlangs.options$]{$polyglossia-otherlangs.name$} +$endfor$ +\fi +$endif$ +$if(natbib)$ +\usepackage{natbib} +\bibliographystyle{$if(biblio-style)$$biblio-style$$else$plainnat$endif$} +$endif$ +$if(biblatex)$ +\usepackage[$if(biblio-style)$style=$biblio-style$,$endif$$for(biblatexoptions)$$biblatexoptions$$sep$,$endfor$]{biblatex} +$for(bibliography)$ +\addbibresource{$bibliography$} +$endfor$ +$endif$ +$if(listings)$ +\usepackage{listings} +$endif$ +$if(lhs)$ +\lstnewenvironment{code}{\lstset{language=Haskell,basicstyle=\small\ttfamily}}{} +$endif$ +$if(highlighting-macros)$ +$highlighting-macros$ +$endif$ +$if(verbatim-in-note)$ +\usepackage{fancyvrb} +\VerbatimFootnotes % allows verbatim text in footnotes +$endif$ +$if(tables)$ +\usepackage{longtable,booktabs} +$endif$ +$if(graphics)$ +\usepackage{graphicx,grffile,float} +\makeatletter +\def\maxwidth{\ifdim\Gin@nat@width>\linewidth\linewidth\else\Gin@nat@width\fi} +\def\maxheight{\ifdim\Gin@nat@height>\textheight\textheight\else\Gin@nat@height\fi} +\makeatother +% Scale images if necessary, so that they will not overflow the page +% margins by default, and it is still possible to overwrite the defaults +% using explicit options in \includegraphics[width, height, ...]{} +\setkeys{Gin}{width=\maxwidth,height=\maxheight,keepaspectratio} +$endif$ +$if(links-as-notes)$ +% Make links footnotes instead of hotlinks: +\renewcommand{\href}[2]{#2\footnote{\url{#1}}} +$endif$ +$if(strikeout)$ +\usepackage[normalem]{ulem} +% avoid problems with \sout in headers with hyperref: +\pdfstringdefDisableCommands{\renewcommand{\sout}{}} +$endif$ +$if(indent)$ +$else$ +\IfFileExists{parskip.sty}{% +\usepackage{parskip} +}{% else +\setlength{\parindent}{0pt} +\setlength{\parskip}{6pt plus 2pt minus 1pt} +} +$endif$ +\setlength{\emergencystretch}{3em} % prevent overfull lines +\providecommand{\tightlist}{% + \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} +$if(numbersections)$ +\setcounter{secnumdepth}{$if(secnumdepth)$$secnumdepth$$else$5$endif$} +$else$ +\setcounter{secnumdepth}{0} +$endif$ +$if(subparagraph)$ +$else$ +% Redefines (sub)paragraphs to behave more like sections +\ifx\paragraph\undefined\else +\let\oldparagraph\paragraph +\renewcommand{\paragraph}[1]{\oldparagraph{#1}\mbox{}} +\fi +\ifx\subparagraph\undefined\else +\let\oldsubparagraph\subparagraph +\renewcommand{\subparagraph}[1]{\oldsubparagraph{#1}\mbox{}} +\fi +$endif$ +$if(dir)$ +\ifxetex + % load bidi as late as possible as it modifies e.g. graphicx + $if(latex-dir-rtl)$ + \usepackage[RTLdocument]{bidi} + $else$ + \usepackage{bidi} + $endif$ +\fi +\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex + \TeXXeTstate=1 + \newcommand{\RL}[1]{\beginR #1\endR} + \newcommand{\LR}[1]{\beginL #1\endL} + \newenvironment{RTL}{\beginR}{\endR} + \newenvironment{LTR}{\beginL}{\endL} +\fi +$endif$ +$for(header-includes)$ +$header-includes$ +$endfor$ + + +\title{OpenPGP Card Application} +\author{Cedric Mesnil cedric@ledger.fr} +\date{$date$} + + +\begin{document} + +\begin{titlepage} + \centering +% \includegraphics[width=0.15\textwidth]{example-image-1x1}\par\vspace{1cm} + {\scshape\LARGE OpenPGP Card Application \par} + {\scshape \LARGE User Guide \par} + \vspace{1cm} + +% {\scshape\Large Ledger SAS \par} + \vspace{1cm} + \begin{figure}[h] + \includegraphics{../common/LogoLedgerV.png} + \centering + \end{figure} + {\Large\itshape Cédric Mesnil (cedric@ledger.fr)\par} + \vfill + +% Bottom of the page + {\large \today\par} +\end{titlepage} + + + +$if(abstract)$ +\begin{abstract} +$abstract$ +\end{abstract} +$endif$ + +$for(include-before)$ +$include-before$ + +$endfor$ +$if(toc)$ +{ +$if(colorlinks)$ +\hypersetup{linkcolor=$if(toccolor)$$toccolor$$else$black$endif$} +$endif$ +\setcounter{tocdepth}{$toc-depth$} +\tableofcontents +} +$endif$ +$if(lot)$ +\listoftables +$endif$ +$if(lof)$ +\listoffigures +$endif$ +$body$ + +$if(natbib)$ +$if(bibliography)$ +$if(biblio-title)$ +$if(book-class)$ +\renewcommand\bibname{$biblio-title$} +$else$ +\renewcommand\refname{$biblio-title$} +$endif$ +$endif$ +\bibliography{$for(bibliography)$$bibliography$$sep$,$endfor$} + +$endif$ +$endif$ +$if(biblatex)$ +\printbibliography$if(biblio-title)$[title=$biblio-title$]$endif$ + +$endif$ +$for(include-after)$ +$include-after$ + +$endfor$ +\end{document} diff --git a/doc/user/generate.sh b/doc/user/generate.sh new file mode 100755 index 0000000..a4c4406 --- /dev/null +++ b/doc/user/generate.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +rm -f blue-app-monero.pdf blue-app-monero.latex + +pandoc -s --template=blue-app-openpgp-card.template -f rst+raw_tex+line_blocks+citations -t latex --toc -N -o blue-app-openpgp-card.pdf blue-app-openpgp-card.rst \ No newline at end of file diff --git a/doc/user/pin_abort.png b/doc/user/pin_abort.png new file mode 100644 index 0000000..c85a273 Binary files /dev/null and b/doc/user/pin_abort.png differ diff --git a/doc/user/pin_cancel.png b/doc/user/pin_cancel.png new file mode 100644 index 0000000..0d6367e Binary files /dev/null and b/doc/user/pin_cancel.png differ diff --git a/doc/user/pin_confirm.png b/doc/user/pin_confirm.png new file mode 100644 index 0000000..2c05db9 Binary files /dev/null and b/doc/user/pin_confirm.png differ diff --git a/doc/user/pin_entry.png b/doc/user/pin_entry.png new file mode 100644 index 0000000..933995c Binary files /dev/null and b/doc/user/pin_entry.png differ diff --git a/doc/user/pin_validate.png b/doc/user/pin_validate.png new file mode 100644 index 0000000..8bd3f19 Binary files /dev/null and b/doc/user/pin_validate.png differ diff --git a/images/LICENSE b/images/LICENSE new file mode 100644 index 0000000..ef84e83 --- /dev/null +++ b/images/LICENSE @@ -0,0 +1,11 @@ + LICENSE + + + +Files "manager_gnupg.xcf" and "manager_gnupg.png" are covered by Creative Commons Attribution-ShareAlike 3.0 Unported License. +See https://creativecommons.org/licenses/by-sa/3.0/legalcode . +Thanks to gnupg.org for the original images. + + +Others under this directory are covered by Apache License Version 2.0, + diff --git a/icon_pgp.gif b/images/icon_pgp.gif similarity index 100% rename from icon_pgp.gif rename to images/icon_pgp.gif diff --git a/images/manager_gnupg.png b/images/manager_gnupg.png new file mode 100644 index 0000000..c5c0a89 Binary files /dev/null and b/images/manager_gnupg.png differ diff --git a/pytools/gpgcard/gpgcard.py b/pytools/gpgcard/gpgcard.py index eaf9109..5d248bc 100644 --- a/pytools/gpgcard/gpgcard.py +++ b/pytools/gpgcard/gpgcard.py @@ -109,6 +109,7 @@ class GPGCard() : if device.startswith("ledger:"): self.token = getDongle(True) self.exchange = self._exchange_ledger + self.disconnect = self._disconnect_ledger elif device.startswith("pcsc:"): allreaders = readers() for r in allreaders: @@ -120,6 +121,7 @@ class GPGCard() : self.connection = r.createConnection() self.connection.connect() self.exchange = self._exchange_pcsc + self.disconnect = self._disconnect_pcsc else: #print("No") pass @@ -127,6 +129,8 @@ class GPGCard() : print("No token") + + ### APDU interface ### def _exchange_ledger(self,cmd,sw=0x9000): resp = b'' @@ -160,6 +164,14 @@ class GPGCard() : #print("xch S resp: %s %.04x"%(binascii.hexlify(resp),sw)) return resp,sw + def _disconnect_ledger(self): + return self.token.close() + + def _disconnect_pcsc(self): + r = self.connection.disconnect() + #self.connection.releaseContext() + return r + def select(self): apdu = binascii.unhexlify(b"00A4040006D27600012401") return self.exchange(apdu) @@ -254,7 +266,7 @@ class GPGCard() : self.sig_date = dates[0:4] self.dec_date = dates[4:8] self.aut_date = dates[8:12] - + self.cardholder_cert = self.get_data(0x7f21) self.UIF_SIG,sw = self.get_data(0xD6) @@ -277,13 +289,13 @@ class GPGCard() : self.put_data(0x0102, self.private_02) self.put_data(0x0103, self.private_03) self.put_data(0x0104, self.private_04) - + self.put_data(0x5b, self.name) self.put_data(0x5e, self.login) self.put_data(0x5f2d, self.lang) self.put_data(0x5f35, self.sex) self.put_data(0x5f50, self.url) - + self.put_data(0xc1, self.sig_attribute) self.put_data(0xc2, self.dec_attribute) self.put_data(0xc3, self.aut_attribute) @@ -314,13 +326,13 @@ class GPGCard() : self.name, self.login, self.sex, self.url, self.sig_attribute, self.dec_attribute, self.aut_attribute, self.PW_status, - self.sig_fingerprints, self.dec_fingerprints, self.aut_fingerprints, - self.sig_CA_fingerprints, self.dec_CA_fingerprints, self.aut_CA_fingerprints, - self.sig_date, self.dec_date, self.aut_date, + self.sig_fingerprints, self.dec_fingerprints, self.aut_fingerprints, + self.sig_CA_fingerprints, self.dec_CA_fingerprints, self.aut_CA_fingerprints, + self.sig_date, self.dec_date, self.aut_date, self.cardholder_cert, self.UIF_SIG, self.UIF_DEC, self.UIF_AUT), f, 2) - + def restore(self, file_name, seed_key=False): f = open(file_name,mode='r+b') @@ -329,9 +341,9 @@ class GPGCard() : self.name, self.login, self.sex, self.url, self.sig_attribute, self.dec_attribute, self.aut_attribute, self.status, - self.sig_fingerprints, self.dec_fingerprints, self.aut_fingerprints, - self.sig_CA_fingerprints, self.dec_CA_fingerprints, self.aut_CA_fingerprints, - self.sig_date, self.dec_date, self.aut_date, + self.sig_fingerprints, self.dec_fingerprints, self.aut_fingerprints, + self.sig_CA_fingerprints, self.dec_CA_fingerprints, self.aut_CA_fingerprints, + self.sig_date, self.dec_date, self.aut_date, self.cardholder_cert, self.UIF_SIG, self.UIF_DEC, self.UIF_AUT) = pickle.load(f) self.set_all() diff --git a/pytools/gpgcard/restore_perso.py b/pytools/gpgcard/restore_perso.py new file mode 100644 index 0000000..9c20560 --- /dev/null +++ b/pytools/gpgcard/restore_perso.py @@ -0,0 +1,35 @@ +import binascii + +from gpgcard import GPGCard + +print("Connecting to device ...") +gpgcard = GPGCard() +gpgcard.connect("pcsc:Ledger") +gpgcard.get_all() + +gpgcard.verify_pin(0x81, "123456") +gpgcard.verify_pin(0x83, "12345678") + +print("Generating key 1/3 ...") +gpgcard.generate_asym_key_pair(0x80, 0xb600) +print("Generating key 2/3 ...") +gpgcard.generate_asym_key_pair(0x80, 0xb800) +print("Generating key 3/3 ...") +gpgcard.generate_asym_key_pair(0x80, 0xa400) + +# Use 'gpg -k --with-subkey-fingerprint' to find fingerprints + +print("Setting fingerprints ...") +sig_fingerprint = b'A3F35A5124D47C3195FF07B7F85D93686A3A9063' +aut_fingerprint = b'9C686F97A39B4A34E0C9D37CDBF45893AB524BBC' +dec_fingerprint = b'E4FE54969060DBF2756FC0EFD8203245E390CAEA' + +sig_fingerprint_bin = binascii.unhexlify(sig_fingerprint) +aut_fingerprint_bin = binascii.unhexlify(aut_fingerprint) +dec_fingerprint_bin = binascii.unhexlify(dec_fingerprint) + +gpgcard.sig_fingerprints = sig_fingerprint_bin +gpgcard.aut_fingerprints = aut_fingerprint_bin +gpgcard.dec_fingerprints = dec_fingerprint_bin + +gpgcard.set_all() diff --git a/script.ld b/script.ld new file mode 100644 index 0000000..60b4ced --- /dev/null +++ b/script.ld @@ -0,0 +1,167 @@ +/******************************************************************************* +* Ledger Blue - Secure firmware +* (c) 2016, 2017 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +/** + * Global chip memory layout and constants + * + */ + +MEMORY +{ + DISCARD (rwx) : ORIGIN = 0xd0000000, LENGTH = 1M + + FLASH (rx) : ORIGIN = 0xc0d00000, LENGTH = 400K + SRAM (rwx) : ORIGIN = 0x20001800, LENGTH = 4K +} + +PAGE_SIZE = 64; +STACK_SIZE = 768; +END_STACK = ORIGIN(SRAM) + LENGTH(SRAM); + +SECTIONS +{ + ENTRY(main) + + /****************************************************************/ + /* This section locates the code in FLASH */ + /****************************************************************/ + + /** put text in Flash memory, VMA will be equal to LMA */ + .text : + { + /* provide start code symbol, shall be zero */ + _text = .; + _nvram = .; + + PROVIDE(_setjmp = setjmp); /*thanks clang*/ + + /* ensure main is always @ 0xC0D00000 */ + *(.boot*) + + /* place the other code and rodata defined BUT nvram variables that are displaced in a r/w area */ + *(.text*) + *(.rodata.[^UN]*) /*.data.rel.ro* not here to detect invalid PIC usage */ + *(.rodata.N[^_]*) + + . = ALIGN(4); + + /* all code placed */ + _etext = .; + + . = ALIGN(PAGE_SIZE); + + _nvram_data = .; + + /* NVM data (ex-filesystem) */ + *(.rodata.USBD_CfgDesc) + *(.bss.N_* .rodata.N_* .rodata.USBD_CfgDesc) + . = ALIGN(PAGE_SIZE); + _install_parameters = .; + PROVIDE(N_install_parameters = .); + _envram = .; + _nvram_data_size = _envram - _nvram_data; + + } > FLASH = 0x00 + + .data (NOLOAD): + { + . = ALIGN(4); + + /** + * Place RAM initialized variables + */ + _data = .; + + *(vtable) + *(.data*) + + _edata = .; + + } > DISCARD /*> SRAM AT>FLASH = 0x00 */ + + .bss : + { + /** + * Place RAM uninitialized variables + */ + _bss = .; + *(.bss*) + _ebss = .; + + + /** + * Reserve stack size + */ + . = ALIGN(4); + app_stack_canary = .; + PROVIDE(app_stack_canary = .); + . += 4; + _stack = .; + . = _stack + STACK_SIZE; + PROVIDE( _stack_size = STACK_SIZE ); + PROVIDE( _estack = ABSOLUTE(END_STACK) ); + + } > SRAM = 0x00 + + /****************************************************************/ + /* DEBUG */ + /****************************************************************/ + + /* remove the debugging information from the standard libraries */ + DEBUG (NOLOAD) : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + +} diff --git a/sdk/usbd_ccid_if.h b/sdk/usbd_ccid_if.h deleted file mode 100755 index 78e042f..0000000 --- a/sdk/usbd_ccid_if.h +++ /dev/null @@ -1,209 +0,0 @@ -/** - ****************************************************************************** - * @file usbd_ccid_if.h - * @author MCD Application Team - * @version V1.0.1 - * @date 31-January-2014 - * @brief This file provides all the functions prototypes for USB CCID - ****************************************************************************** - * @attention - * - *

© COPYRIGHT 2014 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/software_license_agreement_liberty_v2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __USBD_CCID_IF_H -#define __USBD_CCID_IF_H - -#include "usbd_core.h" - -#ifdef HAVE_USB_CLASS_CCID - - -/* Exported defines ----------------------------------------------------------*/ -/* Bulk-only Command Block Wrapper */ -#define ABDATA_SIZE 261 -#define CCID_CMD_HEADER_SIZE 10 -#define CCID_RESPONSE_HEADER_SIZE 10 - - -#define CCID_INT_BUFF_SIZ 2 - -#define CARD_SLOT_FITTED 1 -#define CARD_SLOT_REMOVED 0 - -#define BULK_MAX_PACKET_SIZE 0x40 -#define CCID_IN_EP_SIZE 0x40 -#define INTR_MAX_PACKET_SIZE 8 -#define CCID_MESSAGE_HEADER_SIZE 10 -#define CCID_NUMBER_OF_SLOTS 1 - /* Number of SLOTS. For single card, this value is 1 */ - -/* Following Parameters used in PC_to_RDR_IccPowerOn */ -#define VOLTAGE_SELECTION_AUTOMATIC 0xFF -#define VOLTAGE_SELECTION_3V 0x02 -#define VOLTAGE_SELECTION_5V 0x01 -#define VOLTAGE_SELECTION_1V8 0x03 - -#define PC_TO_RDR_ICCPOWERON 0x62 -#define PC_TO_RDR_ICCPOWEROFF 0x63 -#define PC_TO_RDR_GETSLOTSTATUS 0x65 -#define PC_TO_RDR_XFRBLOCK 0x6F -#define PC_TO_RDR_GETPARAMETERS 0x6C -#define PC_TO_RDR_RESETPARAMETERS 0x6D -#define PC_TO_RDR_SETPARAMETERS 0x61 -#define PC_TO_RDR_ESCAPE 0x6B -#define PC_TO_RDR_ICCCLOCK 0x6E -#define PC_TO_RDR_T0APDU 0x6A -#define PC_TO_RDR_SECURE 0x69 -#define PC_TO_RDR_MECHANICAL 0x71 -#define PC_TO_RDR_ABORT 0x72 -#define PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY 0x73 - -#define RDR_TO_PC_DATABLOCK 0x80 -#define RDR_TO_PC_SLOTSTATUS 0x81 -#define RDR_TO_PC_PARAMETERS 0x82 -#define RDR_TO_PC_ESCAPE 0x83 -#define RDR_TO_PC_DATARATEANDCLOCKFREQUENCY 0x84 - -#define RDR_TO_PC_NOTIFYSLOTCHANGE 0x50 -#define RDR_TO_PC_HARDWAREERROR 0x51 - -#define OFFSET_INT_BMESSAGETYPE 0 -#define OFFSET_INT_BMSLOTICCSTATE 1 -#define SLOT_ICC_PRESENT 0x01 - /* LSb : (0b = no ICC present, 1b = ICC present) */ - -#define SLOT_ICC_CHANGE 0x02 /* MSb : (0b = no change, 1b = change) */ -/*****************************************************************************/ -/*********************** CCID Bulk Transfer State machine ********************/ -/*****************************************************************************/ -#define CCID_STATE_IDLE 0 /* Idle state */ -#define CCID_STATE_DATA_OUT 1 /* Data Out state */ -#define CCID_STATE_RECEIVE_DATA 2 -#define CCID_STATE_SEND_RESP 3 -#define CCID_STATE_DATAIN 4 -#define CCID_STATE_UNCORRECT_LENGTH 5 - -#define DIR_IN 0 -#define DIR_OUT 1 -#define BOTH_DIR 2 - -/* Exported types ------------------------------------------------------------*/ -#pragma pack(1) -typedef struct -{ - #pragma pack(1) - union { - #pragma pack(1) - struct { - uint8_t bMessageType; /* Offset = 0*/ - uint32_t dwLength; /* Offset = 1, The length field (dwLength) is the length - of the message not including the 10-byte header.*/ - uint8_t bSlot; /* Offset = 5*/ - uint8_t bSeq; /* Offset = 6*/ - uint8_t bSpecific_0; /* Offset = 7*/ - uint8_t bSpecific_1; /* Offset = 8*/ - uint8_t bSpecific_2; /* Offset = 9*/ - } bulkout; - #pragma pack(1) - struct { - uint8_t bMessageType; /* Offset = 0*/ - uint32_t dwLength; /* Offset = 1*/ - uint8_t bSlot; /* Offset = 5, Same as Bulk-OUT message */ - uint8_t bSeq; /* Offset = 6, Same as Bulk-OUT message */ - uint8_t bStatus; /* Offset = 7, Slot status as defined in § 6.2.6*/ - uint8_t bError; /* Offset = 8, Slot error as defined in § 6.2.6*/ - uint8_t bSpecific; /* Offset = 9*/ - } bulkin; - } header; - uint8_t abData [ABDATA_SIZE]; /* Offset = 10, For reference, the absolute - maximum block size for a TPDU T=0 block is 260 bytes - (5 bytes command; 255 bytes data), - or for a TPDU T=1 block is 259 bytes, - or for a short APDU T=1 block is 261 bytes, - or for an extended APDU T=1 block is 65544 bytes.*/ -} Ccid_bulk_data_t; -#pragma pack() - - -#pragma pack() - -typedef struct -{ - __IO uint8_t SlotStatus; - __IO uint8_t SlotStatusChange; -} Ccid_SlotStatus_t; - - -typedef struct -{ - __IO uint8_t bAbortRequestFlag; - __IO uint8_t bSeq; - __IO uint8_t bSlot; -} usb_ccid_param_t; - - -#pragma pack(1) -typedef struct _Protocol0_DataStructure_t -{ - uint8_t bmFindexDindex; - uint8_t bmTCCKST0; - uint8_t bGuardTimeT0; - uint8_t bWaitingIntegerT0; - uint8_t bClockStop; -} Protocol0_DataStructure_t; -#pragma pack() - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_ccid_core.h" - -extern usb_ccid_param_t usb_ccid_param; -extern Ccid_bulk_data_t Ccid_bulk_data; /* Buffer for the Out Data */ -extern Ccid_SlotStatus_t Ccid_SlotStatus; -extern uint8_t UsbIntMessageBuffer[]; - -extern Protocol0_DataStructure_t Protocol0_DataStructure; - -/* Exported macros -----------------------------------------------------------*/ -/* Exported variables --------------------------------------------------------*/ -/* Exported functions ------------------------------------------------------- */ -void CCID_BulkMessage_In (USBD_HandleTypeDef *pdev, - uint8_t epnum); - -void CCID_BulkMessage_Out (USBD_HandleTypeDef *pdev, - uint8_t epnum, uint8_t* buffer, uint16_t buflen); - -void CCID_ReceiveCmdHeader(uint8_t* pDst, uint8_t u8length); -void CCID_CmdDecode(USBD_HandleTypeDef *pdev); - -void CCID_IntMessage(USBD_HandleTypeDef *pdev); -void CCID_Init(USBD_HandleTypeDef *pdev); -void CCID_DeInit(USBD_HandleTypeDef *pdev); - -uint8_t CCID_IsIntrTransferComplete(void); -void CCID_SetIntrTransferStatus (uint8_t ); -void Transfer_Data_Request(void); -void Set_CSW (uint8_t CSW_Status, uint8_t Send_Permission); - -void io_usb_ccid_set_card_inserted(unsigned int inserted); - -#endif // HAVE_USB_CLASS_CCID - -#endif /* __USBD_CCID_IF_H */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/src/gpg_api.h b/src/gpg_api.h index dfc4a44..378fd01 100644 --- a/src/gpg_api.h +++ b/src/gpg_api.h @@ -16,7 +16,11 @@ #ifndef GPG_API_H #define GPG_API_H -int gpg_oid2curve(unsigned char* oid, unsigned int len); +void USBD_CCID_activate_pinpad(int enabled); + +unsigned int gpg_oid2curve(unsigned char* oid, unsigned int len); +unsigned char* gpg_curve2oid(unsigned int cv, unsigned int *len); +unsigned int gpg_curve2domainlen(unsigned int cv); void gpg_init(void); void gpg_init_ux(void); @@ -28,7 +32,7 @@ int gpg_apdu_get_data(unsigned int ref) ; int gpg_apdu_get_next_data(unsigned int ref) ; int gpg_apdu_put_data(unsigned int ref) ; -int gpg_apdu_pso(unsigned int ref); +int gpg_apdu_pso(void); int gpg_apdu_internal_authenticate(void); int gpg_apdu_gen(void ); int gpg_apdu_get_challenge(void) ; @@ -40,12 +44,14 @@ int gpg_apdu_change_ref_data(void) ; int gpg_apdu_reset_retry_counter(void) ; gpg_pin_t *gpg_pin_get_pin(int id); -int gpg_pin_is_verified(gpg_pin_t *pin); int gpg_pin_is_blocked(gpg_pin_t *pin); -int gpg_pin_set_verified(gpg_pin_t *pin, int verified); -int gpg_pin_check(gpg_pin_t *pin, unsigned char *pin_val, unsigned int pin_len); +int gpg_pin_is_verified(int pinID); +int gpg_pin_set_verified(int pinID, int verified); +int gpg_pin_check(gpg_pin_t *pin, int pinID,unsigned char *pin_val, unsigned int pin_len); void gpg_pin_set(gpg_pin_t *pin, unsigned char *pin_val, unsigned int pin_len); -void gpg_pin_sync12(void) ; + +int gpg_mse_reset(); +int gpg_apdu_mse(); /* ----------------------------------------------------------------------- */ /* --- IO ---- */ diff --git a/src/gpg_challenge.c b/src/gpg_challenge.c index 1a655f7..7144574 100644 --- a/src/gpg_challenge.c +++ b/src/gpg_challenge.c @@ -44,16 +44,16 @@ int gpg_apdu_get_challenge() { chain[0] = 'r'; chain[1]='n'; chain[2] = 'd'; cx_sha256_init(&G_gpg_vstate.work.md.sha256); - cx_hash((cx_hash_t *)&G_gpg_vstate.work.md.sha256, 0, Sr, 32, NULL); - cx_hash((cx_hash_t *)&G_gpg_vstate.work.md.sha256, 0, chain, 3, NULL); + cx_hash((cx_hash_t *)&G_gpg_vstate.work.md.sha256, 0, Sr, 32, NULL, 0); + cx_hash((cx_hash_t *)&G_gpg_vstate.work.md.sha256, 0, chain, 3, NULL, 0); hlen=cx_hash((cx_hash_t *)&G_gpg_vstate.work.md.sha256, CX_LAST, G_gpg_vstate.work.io_buffer, G_gpg_vstate.io_length, - G_gpg_vstate.work.io_buffer); + G_gpg_vstate.work.io_buffer, 32); cx_sha3_xof_init(&G_gpg_vstate.work.md.sha3, 256, olen); cx_hash((cx_hash_t *)&G_gpg_vstate.work.md.sha3, CX_LAST, G_gpg_vstate.work.io_buffer, hlen, - G_gpg_vstate.work.io_buffer); + G_gpg_vstate.work.io_buffer,olen); } else { cx_rng(G_gpg_vstate.work.io_buffer, olen); } diff --git a/src/gpg_data.c b/src/gpg_data.c index 492f626..8273bf9 100644 --- a/src/gpg_data.c +++ b/src/gpg_data.c @@ -67,7 +67,7 @@ int gpg_apdu_get_data(unsigned int ref) { break; /* ----------------- Config RSA exponent ----------------- */ case 0x01F8: - gpg_io_insert_u32(N_gpg_pstate->default_RSA_exponent); + gpg_io_insert(N_gpg_pstate->default_RSA_exponent,4); break; /* ----------------- Application ----------------- */ @@ -282,7 +282,7 @@ int gpg_apdu_put_data(unsigned int ref) { if (G_gpg_vstate.io_length != 4) { THROW(SW_WRONG_LENGTH); } - G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset] &= ~0x07; + G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+3] &= ~0x07; nvm_write(&N_gpg_pstate->AID[10], &G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset], 4); break; @@ -316,7 +316,7 @@ int gpg_apdu_put_data(unsigned int ref) { break; default: THROW(SW_REFERENCED_DATA_NOT_FOUND); - break; + return 0; } //fecth 7f78 gpg_io_fetch_tl(&t,&l); @@ -343,6 +343,7 @@ int gpg_apdu_put_data(unsigned int ref) { case 0x95: case 0x96: case 0x97: + case 0x99: break; default: THROW(SW_REFERENCED_DATA_NOT_FOUND); @@ -366,34 +367,25 @@ int gpg_apdu_put_data(unsigned int ref) { //check length ksz = (keygpg->attributes.value[1]<<8)|keygpg->attributes.value[2]; ksz = ksz >> 3; + rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa.public; + rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa.private; + pkey = &keygpg->priv_key.rsa; switch(ksz) { case 1024/8: - rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa1024.public; - rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa1024.private; - pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa1024; pkey_size = sizeof(cx_rsa_1024_private_key_t); - pq = G_gpg_vstate.work.rsa1024.public.n; + pq = G_gpg_vstate.work.rsa.public1024.n; break; case 2048/8: - rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa2048.public; - rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa2048.private; - pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa2048; pkey_size = sizeof(cx_rsa_2048_private_key_t); - pq = G_gpg_vstate.work.rsa2048.public.n; + pq = G_gpg_vstate.work.rsa.public2048.n; break; case 3072/8: - rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa3072.public; - rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa3072.private; - pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa3072; pkey_size = sizeof(cx_rsa_3072_private_key_t); - pq = G_gpg_vstate.work.rsa3072.public.n; + pq = G_gpg_vstate.work.rsa.public3072.n; break; case 4096/8: - rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa4096.public; - rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa4096.private; - pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa4096; pkey_size = sizeof(cx_rsa_4096_private_key_t); - pq = G_gpg_vstate.work.rsa4096.public.n; + pq = G_gpg_vstate.work.rsa.public4096.n; break; } ksz = ksz>>1; @@ -432,7 +424,12 @@ int gpg_apdu_put_data(unsigned int ref) { os_memset(pq+ksz, 0, ksz-len_q); //regenerate RSA private key - cx_rsa_generate_pair(ksz<<1, rsa_pub, rsa_priv, e, pq); + unsigned char _e[4]; + _e[0] = e>>24; + _e[1] = e>>16; + _e[2] = e>>8; + _e[3] = e>>0; + cx_rsa_generate_pair(ksz<<1, rsa_pub, rsa_priv, _e, 4, pq); //write keys nvm_write(&keygpg->pub_key.rsa, rsa_pub->e, 4); @@ -454,14 +451,14 @@ int gpg_apdu_put_data(unsigned int ref) { THROW(SW_WRONG_DATA); return 0; } - ksz = 32; - if (ksz == 32) { - G_gpg_vstate.work.ecfp256.private.curve = curve; - G_gpg_vstate.work.ecfp256.private.d_len = ksz; - os_memmove(G_gpg_vstate.work.ecfp256.private.d, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset,ksz); - cx_ecfp_generate_pair(curve, &G_gpg_vstate.work.ecfp256.public, &G_gpg_vstate.work.ecfp256.private, 1); - nvm_write(&keygpg->pub_key.ecfp256, &G_gpg_vstate.work.ecfp256.public, sizeof(cx_ecfp_public_key_t)); - nvm_write(&keygpg->key.ecfp256, &G_gpg_vstate.work.ecfp256.private, sizeof(cx_ecfp_private_key_t)); + ksz = gpg_curve2domainlen(curve); + if (ksz == len_p) { + G_gpg_vstate.work.ecfp.private.curve = curve; + G_gpg_vstate.work.ecfp.private.d_len = ksz; + os_memmove(G_gpg_vstate.work.ecfp.private.d, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset,ksz); + cx_ecfp_generate_pair(curve, &G_gpg_vstate.work.ecfp.public, &G_gpg_vstate.work.ecfp.private, 1); + nvm_write(&keygpg->pub_key.ecfp, &G_gpg_vstate.work.ecfp.public, sizeof(cx_ecfp_public_key_t)); + nvm_write(&keygpg->priv_key.ecfp, &G_gpg_vstate.work.ecfp.private, sizeof(cx_ecfp_private_key_t)); if (reset_cnt) { reset_cnt = 0; nvm_write(&G_gpg_vstate.kslot->sig_count,&reset_cnt,sizeof(unsigned int)); @@ -653,10 +650,27 @@ int gpg_apdu_put_data(unsigned int ref) { } /* ----------------- RC ----------------- */ - case 0xD3: - sw = gpg_apdu_change_ref_data(); - break; + case 0xD3: { + gpg_pin_t *pin; + pin = gpg_pin_get_pin(PIN_ID_RC); + if (G_gpg_vstate.io_length == 0) { + gpg_nvm_write(pin, NULL, sizeof(gpg_pin_t)); + + } + else if ((G_gpg_vstate.io_length > GPG_MAX_PW_LENGTH) || + (G_gpg_vstate.io_length < 8)) { + THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; + } else { + gpg_pin_set(pin, + G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, + G_gpg_vstate.io_length); + } + sw = SW_OK; + break; + } + /* ----------------- UIF ----------------- */ case 0xD6: ptr_v = G_gpg_vstate.kslot->sig.UIF; diff --git a/src/gpg_dispatch.c b/src/gpg_dispatch.c index 81267c3..9640a36 100644 --- a/src/gpg_dispatch.c +++ b/src/gpg_dispatch.c @@ -23,12 +23,7 @@ void gpg_check_access_ins() { unsigned int ref; - gpg_pin_t *pin_pw1, *pin_pw2, *pin_pw3, *pin_rc; - - pin_pw1 = gpg_pin_get_pin(PIN_ID_PW1); - pin_pw2 = gpg_pin_get_pin(PIN_ID_PW2); - pin_pw3 = gpg_pin_get_pin(PIN_ID_PW3); - pin_rc = gpg_pin_get_pin(PIN_ID_RC); + ref = (G_gpg_vstate.io_p1 << 8) | G_gpg_vstate.io_p2 ; switch (G_gpg_vstate.io_ins) { @@ -45,7 +40,7 @@ void gpg_check_access_ins() { return; case INS_RESET_RETRY_COUNTER: - if (gpg_pin_is_verified(pin_pw3) || gpg_pin_is_verified(pin_rc)) { + if (gpg_pin_is_verified(PIN_ID_PW3) || gpg_pin_is_verified(PIN_ID_RC)) { return; } @@ -57,27 +52,30 @@ void gpg_check_access_ins() { if (G_gpg_vstate.io_p1 == 0x81) { return; } - if (gpg_pin_is_verified(pin_pw3)) { + if (gpg_pin_is_verified(PIN_ID_PW3)) { return; } break; + case INS_MSE: + return ; + case INS_PSO: - if ((ref == 0x9e9a) && gpg_pin_is_verified(pin_pw1)) { - //pso:sign + if ((ref == 0x9e9a) && gpg_pin_is_verified(PIN_ID_PW1)) { + //pso:sign if (N_gpg_pstate->PW_status[0] == 0) { - gpg_pin_set_verified(pin_pw1, 0); + gpg_pin_set_verified(PIN_ID_PW1, 0); } return; } - if ((ref == 0x8086 ) && gpg_pin_is_verified(pin_pw2)) { - //pso:dec + if (((ref == 0x8086 )||(ref == 0x8680)) && gpg_pin_is_verified(PIN_ID_PW2)) { + //pso:dec/enc return; } break; case INS_INTERNAL_AUTHENTICATE: - if (gpg_pin_is_verified(pin_pw2)) { + if (gpg_pin_is_verified(PIN_ID_PW2)) { return; } break; @@ -86,7 +84,7 @@ void gpg_check_access_ins() { return; case INS_TERMINATE_DF: - if (gpg_pin_is_verified(pin_pw3)) { + if (gpg_pin_is_verified(PIN_ID_PW3)) { return; } break; @@ -99,10 +97,6 @@ void gpg_check_access_ins() { void gpg_check_access_read_DO() { unsigned int ref; - gpg_pin_t *pin_pw2, *pin_pw3; - - pin_pw2 = gpg_pin_get_pin(PIN_ID_PW2); - pin_pw3 = gpg_pin_get_pin(PIN_ID_PW3); ref = (G_gpg_vstate.io_p1 << 8) | G_gpg_vstate.io_p2 ; @@ -152,14 +146,14 @@ void gpg_check_access_read_DO() { //PW1 case 0x0103: - if (gpg_pin_is_verified(pin_pw2)) { + if (gpg_pin_is_verified(PIN_ID_PW2)) { return; } break; //PW3 case 0x0104: - if (gpg_pin_is_verified(pin_pw3)) { + if (gpg_pin_is_verified(PIN_ID_PW3)) { return; } break; @@ -183,7 +177,7 @@ void gpg_check_access_write_DO() { case 0x0101: case 0x0103: case 0x01F2: - if (gpg_pin_is_verified(pin_pw2)) { + if (gpg_pin_is_verified(PIN_ID_PW2)) { return; } break; @@ -225,7 +219,7 @@ void gpg_check_access_write_DO() { case 0x00D6: case 0x00D7: case 0x00D8: - if (gpg_pin_is_verified(pin_pw3)) { + if (gpg_pin_is_verified(PIN_ID_PW3)) { return; } break; @@ -242,7 +236,12 @@ int gpg_dispatch() { unsigned int tag,t,l; int sw; - + if ((G_gpg_vstate.io_cla != 0x00) && + (G_gpg_vstate.io_cla != 0x10) && + (G_gpg_vstate.io_cla != 0xEF)) { + THROW(SW_CLA_NOT_SUPPORTED); + return SW_CLA_NOT_SUPPORTED; + } tag = (G_gpg_vstate.io_p1 << 8) | G_gpg_vstate.io_p2 ; @@ -265,7 +264,7 @@ int gpg_dispatch() { case INS_TERMINATE_DF: gpg_io_discard(0); - if (gpg_pin_is_verified(gpg_pin_get_pin(PIN_ID_PW3)) || (N_gpg_pstate->PW3.counter == 0)) { + if (gpg_pin_is_verified(PIN_ID_PW3) || (N_gpg_pstate->PW3.counter == 0)) { gpg_install(STATE_TERMINATE); return(SW_OK); break; @@ -274,7 +273,6 @@ int gpg_dispatch() { } - /* Other commands allowed if not terminated */ if (N_gpg_pstate->histo[7] != 0x07) { THROW(SW_STATE_TERMINATED); @@ -361,7 +359,7 @@ int gpg_dispatch() { THROW(SW_INCORRECT_P1P2); case INS_RESET_RETRY_COUNTER: - if ((G_gpg_vstate.io_p2 == 0x81) && + if ((G_gpg_vstate.io_p2 == 0x81) && ( (G_gpg_vstate.io_p1 == 0) || (G_gpg_vstate.io_p1 == 2) ) ) { @@ -375,10 +373,16 @@ int gpg_dispatch() { sw = gpg_apdu_gen(); break; + /* --- MSE --- */ + case INS_MSE: + sw = gpg_apdu_mse(tag); + break; + /* --- PSO --- */ case INS_PSO: - sw = gpg_apdu_pso(tag); + sw = gpg_apdu_pso(); break; + case INS_INTERNAL_AUTHENTICATE: sw = gpg_apdu_internal_authenticate(); break; diff --git a/src/gpg_gen.c b/src/gpg_gen.c index e7de95d..22564f4 100644 --- a/src/gpg_gen.c +++ b/src/gpg_gen.c @@ -46,12 +46,12 @@ static void gpg_pso_derive_key_seed(unsigned char *Sn, unsigned char* key_name, h[1] = idx; cx_sha256_init(&G_gpg_vstate.work.md.sha256); - cx_hash((cx_hash_t*)&G_gpg_vstate.work.md.sha256, 0, Sn, 32, NULL); - cx_hash((cx_hash_t*)&G_gpg_vstate.work.md.sha256, 0, (unsigned char *)key_name, 4, NULL); - cx_hash((cx_hash_t*)&G_gpg_vstate.work.md.sha256, CX_LAST, h , 2, h); + cx_hash((cx_hash_t*)&G_gpg_vstate.work.md.sha256, 0, Sn, 32, NULL, 0); + cx_hash((cx_hash_t*)&G_gpg_vstate.work.md.sha256, 0, (unsigned char *)key_name, 4, NULL, 0); + cx_hash((cx_hash_t*)&G_gpg_vstate.work.md.sha256, CX_LAST, h , 2, h,32); cx_sha3_xof_init(&G_gpg_vstate.work.md.sha3, 256, Ski_len); - cx_hash((cx_hash_t*)&G_gpg_vstate.work.md.sha3, CX_LAST, h, 32, Ski); + cx_hash((cx_hash_t*)&G_gpg_vstate.work.md.sha3, CX_LAST, h, 32, Ski, Ski_len); } @@ -113,29 +113,20 @@ int gpg_apdu_gen() { ksz = (keygpg->attributes.value[1]<<8)|keygpg->attributes.value[2]; ksz = ksz >> 3; + rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa.public; + rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa.private; + pkey = &keygpg->priv_key.rsa; switch(ksz) { case 1024/8: - rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa1024.public; - rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa1024.private; - pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa1024; pkey_size = sizeof(cx_rsa_1024_private_key_t); break; case 2048/8: - rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa2048.public; - rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa2048.private; - pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa2048; pkey_size = sizeof(cx_rsa_2048_private_key_t); break; case 3072/8: - rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa3072.public; - rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa3072.private; - pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa3072; pkey_size = sizeof(cx_rsa_3072_private_key_t); break; case 4096/8: - rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa4096.public; - rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa4096.private; - pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa4096; pkey_size = sizeof(cx_rsa_4096_private_key_t); break; } @@ -153,8 +144,7 @@ int gpg_apdu_gen() { cx_math_next_prime(pq+size,size); } - - cx_rsa_generate_pair(ksz, rsa_pub, rsa_priv, N_gpg_pstate->default_RSA_exponent, pq); + cx_rsa_generate_pair(ksz, rsa_pub, rsa_priv, N_gpg_pstate->default_RSA_exponent, 4, pq); nvm_write(pkey, rsa_priv, pkey_size); nvm_write(&keygpg->pub_key.rsa[0], rsa_pub->e, 4); @@ -174,19 +164,24 @@ int gpg_apdu_gen() { unsigned int curve,keepprivate; keepprivate = 0; curve = gpg_oid2curve(keygpg->attributes.value+1, keygpg->attributes.length-1); + if (curve == CX_CURVE_NONE) { + THROW(SW_REFERENCED_DATA_NOT_FOUND); + return SW_REFERENCED_DATA_NOT_FOUND; + } if ((G_gpg_vstate.io_p2 == 0x01) | (G_gpg_vstate.seed_mode)) { + ksz = gpg_curve2domainlen(curve); gpg_pso_derive_slot_seed(G_gpg_vstate.slot, seed); - gpg_pso_derive_key_seed(seed, name, 1, seed, 32); - cx_ecfp_init_private_key(curve,seed, 32, &G_gpg_vstate.work.ecfp256.private); + gpg_pso_derive_key_seed(seed, name, 1, seed, ksz); + cx_ecfp_init_private_key(curve,seed, ksz, &G_gpg_vstate.work.ecfp.private); keepprivate = 1; } cx_ecfp_generate_pair(curve, - &G_gpg_vstate.work.ecfp256.public, - &G_gpg_vstate.work.ecfp256.private, + &G_gpg_vstate.work.ecfp.public, + &G_gpg_vstate.work.ecfp.private, keepprivate); - nvm_write(&keygpg->key.ecfp256, &G_gpg_vstate.work.ecfp256.private, sizeof(cx_ecfp_private_key_t)); - nvm_write(&keygpg->pub_key.ecfp256, &G_gpg_vstate.work.ecfp256.public, sizeof(cx_ecfp_public_key_t)); + nvm_write(&keygpg->priv_key.ecfp, &G_gpg_vstate.work.ecfp.private, sizeof(cx_ecfp_private_key_t)); + nvm_write(&keygpg->pub_key.ecfp, &G_gpg_vstate.work.ecfp.public, sizeof(cx_ecfp_public_key_t)); if (reset_cnt) { reset_cnt = 0; nvm_write(&G_gpg_vstate.kslot->sig_count,&reset_cnt,sizeof(unsigned int)); @@ -208,32 +203,32 @@ int gpg_apdu_gen() { gpg_io_mark(); switch(ksz) { case 1024/8: - if (keygpg->key.rsa1024.size == 0) { + if (keygpg->priv_key.rsa1024.size == 0) { THROW (SW_REFERENCED_DATA_NOT_FOUND); return SW_REFERENCED_DATA_NOT_FOUND; } - gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->key.rsa1024.n); + gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->priv_key.rsa1024.n); break; case 2048/8: - if (keygpg->key.rsa2048.size == 0) { + if (keygpg->priv_key.rsa2048.size == 0) { THROW (SW_REFERENCED_DATA_NOT_FOUND); return SW_REFERENCED_DATA_NOT_FOUND; } - gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->key.rsa2048.n); + gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->priv_key.rsa2048.n); break; case 3072/8: - if (keygpg->key.rsa3072.size == 0) { + if (keygpg->priv_key.rsa3072.size == 0) { THROW (SW_REFERENCED_DATA_NOT_FOUND); return SW_REFERENCED_DATA_NOT_FOUND; } - gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->key.rsa3072.n); + gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->priv_key.rsa3072.n); break; case 4096/8: - if (keygpg->key.rsa4096.size == 0) { + if (keygpg->priv_key.rsa4096.size == 0) { THROW (SW_REFERENCED_DATA_NOT_FOUND); return SW_REFERENCED_DATA_NOT_FOUND; } - gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->key.rsa4096.n); + gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->priv_key.rsa4096.n); break; } gpg_io_insert_tlv(0x82, 4, keygpg->pub_key.rsa); @@ -262,7 +257,7 @@ int gpg_apdu_gen() { curve = gpg_oid2curve(keygpg->attributes.value+1, keygpg->attributes.length-1); if (curve == CX_CURVE_Ed25519) { os_memmove(G_gpg_vstate.work.io_buffer+128, keygpg->pub_key.ecfp256.W,keygpg->pub_key.ecfp256.W_len); - cx_edward_compress_point(CX_CURVE_Ed25519, G_gpg_vstate.work.io_buffer+128); + cx_edward_compress_point(CX_CURVE_Ed25519, G_gpg_vstate.work.io_buffer+128, 65); gpg_io_insert_tlv(0x86, 32, G_gpg_vstate.work.io_buffer+129); //129: discard 02 } else if (curve == CX_CURVE_Curve25519) { unsigned int i,len; diff --git a/src/gpg_init.c b/src/gpg_init.c index 0aaf2c9..07e4d85 100644 --- a/src/gpg_init.c +++ b/src/gpg_init.c @@ -18,6 +18,7 @@ #include "gpg_types.h" #include "gpg_api.h" #include "gpg_vars.h" +#include "usbd_impl.h" #define SHORT(x) ((x)>>8)&0xFF, (x)&0xFF /* ----------------------*/ @@ -31,24 +32,47 @@ const unsigned char C_MAGIC[8] = { /* --ECC OID -- */ /* ----------------------*/ +/* +//brainpool 256t1: 1.3.36.3.3.2.8.1.1.8 +const unsigned char C_OID_BRAINPOOL256T1[9] = { + 0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x07 +}; +*/ + +//secp256r1 / NIST P256 /ansi-x9.62 : 1.2.840.10045.3.1.7 +const unsigned char C_OID_SECP256R1[8] = { + 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 +}; +//secp384r1 / NIST P384 /ansi-x9.62 :1.3.132.0.34 +const unsigned char C_OID_SECP384R1[5] = { + 0x2B, 0x81, 0x04, 0x00 , 0x22 +}; +//secp521r1 / NIST P521 /ansi-x9.62 : 1.3.132.0.35 +const unsigned char C_OID_SECP521R1[5] = { + 0x2B, 0x81, 0x04, 0x00, 0x23 +}; + + //secp256k1: 1.3.132.0.10 const unsigned char C_OID_SECP256K1[5] = { 0x2B, 0x81, 0x04, 0x00, 0x0A }; -//secp256r1 / NIST P256 /ansi-x9.62 : 1.2.840.10045.3.1.7 -const unsigned char C_OID_SECP256R1[8] = { - 0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x07 -}; //brainpool 256r1: 1.3.36.3.3.2.8.1.1.7 const unsigned char C_OID_BRAINPOOL256R1[9] = { - 0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x08 + 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x08 }; -//brainpool 256t1: 1.3.36.3.3.2.8.1.1.8 -const unsigned char C_OID_BRAINPOOL256T1[9] = { - 0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x07 +//brainpool 284r1: 1.3.36.3.3.2.8.1.1.11 +const unsigned char C_OID_BRAINPOOL384R1[9] = { + 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B }; +//brainpool 512r1: 1.3.36.3.3.2.8.1.1.13 +const unsigned char C_OID_BRAINPOOL512R1[9] = { + 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D +}; + + //Ed25519/curve25519: 1.3.6.1.4.1.11591.15.1 const unsigned char C_OID_Ed25519[9] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01, @@ -59,10 +83,29 @@ const unsigned char C_OID_cv25519[10] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01, }; -int gpg_oid2curve(unsigned char* oid, unsigned int len) { +unsigned int gpg_oid2curve(unsigned char* oid, unsigned int len) { + if ( (len == sizeof(C_OID_SECP256K1)) && (os_memcmp(oid, C_OID_SECP256K1, len)==0) ) { + return CX_CURVE_SECP256K1; + } if ( (len == sizeof(C_OID_SECP256R1)) && (os_memcmp(oid, C_OID_SECP256R1, len)==0) ) { - return CX_CURVE_256R1; + return CX_CURVE_SECP256R1; + } + if ( (len == sizeof(C_OID_SECP384R1)) && (os_memcmp(oid, C_OID_SECP384R1, len)==0) ) { + return CX_CURVE_SECP384R1; + } + if ( (len == sizeof(C_OID_SECP521R1)) && (os_memcmp(oid, C_OID_SECP521R1, len)==0) ) { + return CX_CURVE_SECP521R1; + } + + if ( (len == sizeof(C_OID_BRAINPOOL256R1)) && (os_memcmp(oid, C_OID_BRAINPOOL256R1, len)==0) ) { + return CX_CURVE_BrainPoolP256R1; + } + if ( (len == sizeof(C_OID_BRAINPOOL384R1)) && (os_memcmp(oid, C_OID_BRAINPOOL384R1, len)==0) ) { + return CX_CURVE_BrainPoolP384R1; + } + if ( (len == sizeof(C_OID_BRAINPOOL512R1)) && (os_memcmp(oid, C_OID_BRAINPOOL512R1, len)==0) ) { + return CX_CURVE_BrainPoolP512R1; } if ( (len == sizeof(C_OID_Ed25519)) && (os_memcmp(oid, C_OID_Ed25519, len)==0) ) { @@ -72,27 +115,93 @@ int gpg_oid2curve(unsigned char* oid, unsigned int len) { if ( (len == sizeof(C_OID_cv25519)) && (os_memcmp(oid, C_OID_cv25519, len)==0) ) { return CX_CURVE_Curve25519; } - + + /* if ( (len == sizeof(C_OID_SECP256K1)) && (os_memcmp(oid, C_OID_SECP256K1, len)==0) ) { return CX_CURVE_256K1; } - - if ( (len == sizeof(C_OID_BRAINPOOL256R1)) && (os_memcmp(oid, C_OID_BRAINPOOL256R1, len)==0) ) { - return CX_CURVE_BrainPoolP256R1; - } - if ( (len == sizeof(C_OID_BRAINPOOL256T1)) && (os_memcmp(oid, C_OID_BRAINPOOL256T1, len)==0) ) { return CX_CURVE_BrainPoolP256T1; } + */ return CX_CURVE_NONE; } + +unsigned char* gpg_curve2oid(unsigned int cv, unsigned int *len) { + switch (cv) { + + case CX_CURVE_SECP256K1: + *len = sizeof(C_OID_SECP256K1); + return (unsigned char*)PIC(C_OID_SECP256K1); + + case CX_CURVE_SECP256R1: + *len = sizeof(C_OID_SECP256R1); + return (unsigned char*)PIC(C_OID_SECP256R1); + + case CX_CURVE_SECP384R1: + *len = sizeof(C_OID_SECP384R1); + return (unsigned char*)PIC(C_OID_SECP384R1); + + case CX_CURVE_SECP521R1: + *len = sizeof(C_OID_SECP521R1); + return (unsigned char*)PIC(C_OID_SECP521R1); + + case CX_CURVE_BrainPoolP256R1: + *len = sizeof(C_OID_SECP256R1); + return (unsigned char*)PIC(C_OID_SECP256R1); + + case CX_CURVE_BrainPoolP384R1: + *len = sizeof(C_OID_SECP384R1); + return (unsigned char*)PIC(C_OID_SECP384R1); + + case CX_CURVE_BrainPoolP512R1: + *len = sizeof(C_OID_SECP521R1); + return (unsigned char*)PIC(C_OID_SECP521R1); + + case CX_CURVE_Ed25519: + *len = sizeof(C_OID_Ed25519); + return (unsigned char*)PIC(C_OID_Ed25519); + + case CX_CURVE_Curve25519: + *len = sizeof(C_OID_cv25519); + return (unsigned char*)PIC(C_OID_cv25519); + } + + *len = 0; + return NULL; +} + +unsigned int gpg_curve2domainlen(unsigned int cv) { + switch (cv) { + + case CX_CURVE_SECP256K1: + case CX_CURVE_SECP256R1: + case CX_CURVE_BrainPoolP256R1: + case CX_CURVE_Ed25519: + case CX_CURVE_Curve25519: + return 32; + + case CX_CURVE_SECP384R1: + case CX_CURVE_BrainPoolP384R1: + return 48; + + case CX_CURVE_BrainPoolP512R1: + return 64; + + case CX_CURVE_SECP521R1: + return 66; + } + + return 0; +} + /* -------------------------------*/ /* -- Non Mutable Capabilities -- */ /* -------------------------------*/ const unsigned char C_ext_capabilities[10] = { - //-SM, +getchallenge, +keyimport, +PWchangeable, +privateDO, +algAttrChangeable, +AES, -RFU + //-SM, +getchallenge, +keyimport, +PWchangeable, +privateDO, +algAttrChangeable, +AES, -KDF 0x7E, // No SM, 0x00, @@ -104,8 +213,8 @@ const unsigned char C_ext_capabilities[10] = { SHORT(GPG_EXT_PRIVATE_DO_LENGTH), //PIN block formart 2 not supported 0x00, - //RFU - 0x00 + //MSE + 0x01 }; @@ -122,7 +231,7 @@ const unsigned char C_ext_length[8] = { const unsigned char C_default_AID[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01, //version - 0x03, 0x00, + 0x03, 0x03, //manufacturer 0x2C, 0x97, //serial @@ -154,7 +263,7 @@ const unsigned char C_default_AlgoAttrRSA[] = { 0x01 }; -#if 1 +#if 0 const unsigned char C_default_AlgoAttrECC_sig[] = { // ecdsa 0x13, @@ -167,17 +276,29 @@ const unsigned char C_default_AlgoAttrECC_dec[] = { // NIST-P256 0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x07 }; - -#else +#elif 0 +const unsigned char C_default_AlgoAttrECC_sig[] = { + // ecdsa + 0x13, + // NIST-P384 + 0x2B, 0x81, 0x04, 0x00 , 0x22 +}; +const unsigned char C_default_AlgoAttrECC_dec[] = { + // ecdh + 0x12, + // NIST-P384 + 0x2B, 0x81, 0x04, 0x00 , 0x22 +}; +#elif 1 const unsigned char C_default_AlgoAttrECC_sig[] = { // eddsa - 22, + 0x16, // ed25519 0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01, }; const unsigned char C_default_AlgoAttrECC_dec[] = { // ecdh - 18, + 0x12, //cv25519 0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01, }; @@ -211,11 +332,11 @@ void gpg_init() { gpg_nvm_write(N_gpg_pstate->magic, (void*)C_MAGIC, sizeof(C_MAGIC)); os_memset(&G_gpg_vstate, 0, sizeof(gpg_v_state_t)); } - //ensure pin1 and pin2 are sync in case of powerloss - gpg_pin_sync12(); + //key conf G_gpg_vstate.slot = N_gpg_pstate->config_slot[1]; G_gpg_vstate.kslot = &N_gpg_pstate->keys[G_gpg_vstate.slot]; + gpg_mse_reset(); //pin conf G_gpg_vstate.pinmode = N_gpg_pstate->config_pin[0]; //ux conf @@ -260,8 +381,6 @@ int gpg_install(unsigned char app_state) { pin.counter = 3; pin.ref = PIN_ID_PW1; gpg_nvm_write(&N_gpg_pstate->PW1, &pin, sizeof(gpg_pin_t)); - pin.ref = PIN_ID_PW2; - gpg_nvm_write(&N_gpg_pstate->PW2, &pin, sizeof(gpg_pin_t)); //default PW3: 1 2 3 4 5 6 7 8 os_memmove(pin.value, C_sha256_PW2, sizeof(C_sha256_PW2)); @@ -284,16 +403,29 @@ int gpg_install(unsigned char app_state) { gpg_nvm_write(&N_gpg_pstate->config_slot, G_gpg_vstate.work.io_buffer, 3); //config rsa pub - l = GPG_RSA_DEFAULT_PUB; - nvm_write(&N_gpg_pstate->default_RSA_exponent, &l, sizeof(unsigned int)); + G_gpg_vstate.work.io_buffer[0] = (GPG_RSA_DEFAULT_PUB>>24)&0xFF; + G_gpg_vstate.work.io_buffer[1] = (GPG_RSA_DEFAULT_PUB>>16)&0xFF; + G_gpg_vstate.work.io_buffer[2] = (GPG_RSA_DEFAULT_PUB>>8)&0xFF; + G_gpg_vstate.work.io_buffer[3] = (GPG_RSA_DEFAULT_PUB>>0)&0xFF; + nvm_write(&N_gpg_pstate->default_RSA_exponent, G_gpg_vstate.work.io_buffer, 4); //config pin + #if 1 G_gpg_vstate.work.io_buffer[0] = PIN_MODE_CONFIRM; gpg_nvm_write(&N_gpg_pstate->config_pin, G_gpg_vstate.work.io_buffer, 1); - + USBD_CCID_activate_pinpad(3); + #else + G_gpg_vstate.work.io_buffer[0] = PIN_MODE_HOST; + gpg_nvm_write(&N_gpg_pstate->config_pin, G_gpg_vstate.work.io_buffer, 1); + USBD_CCID_activate_pinpad(0); + #endif + //default key template: RSA 2048) for (int s = 0; s< GPG_KEYS_SLOTS; s++) { + unsigned char uif[2]; + uif[0] = 0x00; + uif[1] = 0x20; #if 1 l = sizeof(C_default_AlgoAttrRSA); gpg_nvm_write(&N_gpg_pstate->keys[s].sig.attributes.value, (void*)C_default_AlgoAttrRSA, l); @@ -312,8 +444,25 @@ int gpg_install(unsigned char app_state) { gpg_nvm_write(&N_gpg_pstate->keys[s].dec.attributes.value, (void*)C_default_AlgoAttrECC_dec, l); gpg_nvm_write(&N_gpg_pstate->keys[s].dec.attributes.length, &l, sizeof(unsigned int)); #endif + gpg_nvm_write(&N_gpg_pstate->keys[s].sig.UIF, &uif, 2); + gpg_nvm_write(&N_gpg_pstate->keys[s].dec.UIF, &uif, 2); + gpg_nvm_write(&N_gpg_pstate->keys[s].aut.UIF, &uif, 2); + + } } return 0; } + + +#define USBD_OFFSET_CfgDesc_bPINSupport (sizeof(USBD_CfgDesc)-16) +void USBD_CCID_activate_pinpad(int enabled) { + unsigned short length; + uint8_t *cfgDesc; + unsigned char e; + e = enabled?3:0; + length = 0; + cfgDesc = USBD_GetCfgDesc_impl(&length); + nvm_write(cfgDesc+(length-16), &e,1); +} diff --git a/src/gpg_io.c b/src/gpg_io.c index 50a46cf..9a36db2 100644 --- a/src/gpg_io.c +++ b/src/gpg_io.c @@ -149,10 +149,7 @@ void gpg_io_insert_tlv(unsigned int T, unsigned int L, unsigned char const *V) { /* ----------------------------------------------------------------------- */ /* FECTH data from received buffer */ /* ----------------------------------------------------------------------- */ -void gpg_io_fetch_buffer(unsigned char* buffer, unsigned int len) { - os_memmove(buffer, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, len); - G_gpg_vstate.io_offset += len; -} + unsigned int gpg_io_fetch_u32() { unsigned int v32; @@ -247,7 +244,7 @@ int gpg_io_fetch(unsigned char* buffer, int len) { int gpg_io_do(unsigned int io_flags) { //if pending input chaining - if (G_gpg_vstate.io_cla & 0x01) { + if (G_gpg_vstate.io_cla & 0x10) { goto in_chaining; } diff --git a/src/gpg_main.c b/src/gpg_main.c index 9390543..78bb5e2 100644 --- a/src/gpg_main.c +++ b/src/gpg_main.c @@ -165,7 +165,7 @@ __attribute__((section(".boot"))) int main(void) { //start communication with MCU io_seproxyhal_init(); - USB_CCID_power(1); + USB_power(1); io_usb_ccid_set_card_inserted(1); diff --git a/src/gpg_mse.c b/src/gpg_mse.c new file mode 100644 index 0000000..d3cc88c --- /dev/null +++ b/src/gpg_mse.c @@ -0,0 +1,78 @@ +/* Copyright 2017 Cedric Mesnil , Ledger SAS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "os.h" +#include "cx.h" +#include "gpg_types.h" +#include "gpg_api.h" +#include "gpg_vars.h" + +static int gpg_mse_set(int crt, int ref) { + + if (crt == 0xA4) { + if (ref == 0x02) { + G_gpg_vstate.mse_aut = &G_gpg_vstate.kslot->dec; + } + if (ref == 0x03) { + G_gpg_vstate.mse_aut = &G_gpg_vstate.kslot->aut; + } + } + + if (crt == 0xB8) { + if (ref == 0x02) { + G_gpg_vstate.mse_dec = &G_gpg_vstate.kslot->dec; + } + if (ref == 0x03) { + G_gpg_vstate.mse_dec = &G_gpg_vstate.kslot->aut; + } + } + return 0; +} + + + +int gpg_mse_reset() { + gpg_mse_set(0xA4, 0x03); + gpg_mse_set(0xB8, 0x02); + return 0; +} + +int gpg_apdu_mse() { + int crt,ref; + + + if ((G_gpg_vstate.io_p1 != 0x41) || + ((G_gpg_vstate.io_p2 != 0xA4)&&(G_gpg_vstate.io_p2 != 0xB8))) { + THROW(SW_INCORRECT_P1P2); + return SW_INCORRECT_P1P2; + } + + crt = gpg_io_fetch_u16(); + if (crt != 0x8301) { + THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; + } + + ref = gpg_io_fetch_u8(); + if ((ref != 0x02) && (ref != 0x03)) { + THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; + } + + gpg_mse_set(crt,ref); + gpg_io_discard(1); + return SW_OK; + +} \ No newline at end of file diff --git a/src/gpg_pin.c b/src/gpg_pin.c index 1495227..4ed7aa2 100644 --- a/src/gpg_pin.c +++ b/src/gpg_pin.c @@ -21,12 +21,11 @@ #include "gpg_ux_nanos.h" -gpg_pin_t *gpg_pin_get_pin(int id) { - switch (id) { +gpg_pin_t *gpg_pin_get_pin(int pinref) { + switch (pinref) { case PIN_ID_PW1 : - return &N_gpg_pstate->PW1; case PIN_ID_PW2 : - return &N_gpg_pstate->PW2; + return &N_gpg_pstate->PW1; case PIN_ID_PW3: return &N_gpg_pstate->PW3; case PIN_ID_RC: @@ -37,8 +36,8 @@ gpg_pin_t *gpg_pin_get_pin(int id) { -static int gpg_pin_get_index(unsigned int id) { - switch (id) { +static int gpg_pin_get_state_index(unsigned int pinref) { + switch (pinref) { case PIN_ID_PW1 : return 1; case PIN_ID_PW2 : @@ -52,28 +51,10 @@ static int gpg_pin_get_index(unsigned int id) { return -1; } -void gpg_pin_sync12() { - gpg_pin_t *pin1, *pin2; - pin1 = gpg_pin_get_pin(PIN_ID_PW1); - pin2 = gpg_pin_get_pin(PIN_ID_PW2); - if (os_memcmp(pin1, pin2, sizeof(gpg_pin_t))) { - pin1 = gpg_pin_get_pin(PIN_ID_PW1); - pin2 = gpg_pin_get_pin(PIN_ID_PW2); - gpg_nvm_write(pin2, pin1, sizeof(gpg_pin_t)); - } - -} -static int gpg_pin_check_internal(gpg_pin_t *pin, unsigned char *pin_val, int pin_len) { +static int gpg_pin_check_internal(gpg_pin_t *pin, unsigned char *pin_val, int pin_len) { cx_sha256_t sha256; unsigned int counter; - gpg_pin_t *brother; - brother = NULL; - if (pin->ref == PIN_ID_PW1) { - brother = gpg_pin_get_pin(PIN_ID_PW2); - } else if (pin->ref == PIN_ID_PW2) { - brother = gpg_pin_get_pin(PIN_ID_PW1); - } if (pin->counter == 0) { return SW_PIN_BLOCKED; @@ -81,40 +62,37 @@ static int gpg_pin_check_internal(gpg_pin_t *pin, unsigned char *pin_val, int pi counter = pin->counter-1; gpg_nvm_write(&(pin->counter), &counter, sizeof(int)); - if (brother) { - gpg_nvm_write(&(brother->counter), &counter, sizeof(int)); - } cx_sha256_init(&sha256); - cx_hash((cx_hash_t*)&sha256, CX_LAST, pin_val, pin_len, NULL); + cx_hash((cx_hash_t*)&sha256, CX_LAST, pin_val, pin_len, NULL, 0); if (os_memcmp(sha256.acc, pin->value, 32)) { return SW_SECURITY_STATUS_NOT_SATISFIED; } counter = 3; gpg_nvm_write(&(pin->counter), &counter, sizeof(int)); - if (brother) { - gpg_nvm_write(&(brother->counter), &counter, sizeof(int)); - } return SW_OK; } -static void gpg_pin_check_throw(gpg_pin_t *pin, unsigned char *pin_val, int pin_len) { +static void gpg_pin_check_throw(gpg_pin_t *pin, int pinID, + unsigned char *pin_val, int pin_len) { int sw; - gpg_pin_set_verified(pin,0); + gpg_pin_set_verified(pinID,0); sw = gpg_pin_check_internal(pin,pin_val,pin_len); if (sw == SW_OK) { - gpg_pin_set_verified(pin,1); + gpg_pin_set_verified(pinID,1); return; } THROW(sw); + return; } -int gpg_pin_check(gpg_pin_t *pin, unsigned char *pin_val, unsigned int pin_len) { +int gpg_pin_check(gpg_pin_t *pin, int pinID, + unsigned char *pin_val, unsigned int pin_len) { int sw; + gpg_pin_set_verified(pinID,0); sw = gpg_pin_check_internal(pin,pin_val,pin_len); - gpg_pin_set_verified(pin,0); if (sw == SW_OK) { - gpg_pin_set_verified(pin,1); + gpg_pin_set_verified(pinID,1); } return sw; } @@ -125,17 +103,16 @@ void gpg_pin_set(gpg_pin_t *pin, unsigned char *pin_val, unsigned int pin_len) { gpg_pin_t newpin; cx_sha256_init(&sha256); - cx_hash((cx_hash_t*)&sha256, CX_LAST, pin_val, pin_len, newpin.value); + cx_hash((cx_hash_t*)&sha256, CX_LAST, pin_val, pin_len, newpin.value, 32); newpin.length = pin_len; newpin.counter = 3; gpg_nvm_write(pin, &newpin, sizeof(gpg_pin_t)); - gpg_pin_sync12(); } -int gpg_pin_set_verified(gpg_pin_t *pin, int verified) { +int gpg_pin_set_verified(int pinID, int verified) { int idx; - idx = gpg_pin_get_index(pin->ref); + idx = gpg_pin_get_state_index(pinID); if (idx >= 0) { G_gpg_vstate.verified_pin[idx]=verified; return verified; @@ -143,9 +120,9 @@ int gpg_pin_set_verified(gpg_pin_t *pin, int verified) { return 0; } -int gpg_pin_is_verified(gpg_pin_t *pin) { +int gpg_pin_is_verified(int pinID) { int idx; - idx = gpg_pin_get_index(pin->ref); + idx = gpg_pin_get_state_index(pinID); if (idx >= 0) { return G_gpg_vstate.verified_pin[idx]; } @@ -167,12 +144,13 @@ int gpg_apdu_verify() { return SW_WRONG_DATA; } - gpg_pin_set_verified(pin,0); - if (gpg_pin_is_blocked(pin)) { - THROW(SW_PIN_BLOCKED); - return SW_PIN_BLOCKED; - } - if (G_gpg_vstate.io_length == 0) { + //PINPAD + if (G_gpg_vstate.io_cla == 0xEF) { + if (gpg_pin_is_blocked(pin)) { + THROW(SW_PIN_BLOCKED); + return SW_PIN_BLOCKED; + } + if (G_gpg_vstate.pinmode == PIN_MODE_SCREEN) { //Delegate pin check to ui gpg_io_discard(1); @@ -186,16 +164,46 @@ int gpg_apdu_verify() { return 0; } if (G_gpg_vstate.pinmode == PIN_MODE_TRUST) { - gpg_pin_set_verified(pin,1); + gpg_pin_set_verified(G_gpg_vstate.io_p2,1); gpg_io_discard(1); return SW_OK; } + THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; } - gpg_pin_check_throw(pin, - G_gpg_vstate.work.io_buffer+ G_gpg_vstate.io_offset, - G_gpg_vstate.io_length); + + //NORMAL CHECK + if ((G_gpg_vstate.io_p1==0) && G_gpg_vstate.io_length) { + if (gpg_pin_is_blocked(pin)) { + THROW(SW_PIN_BLOCKED); + return SW_PIN_BLOCKED; + } + gpg_pin_check_throw(pin, G_gpg_vstate.io_p2, + G_gpg_vstate.work.io_buffer+ G_gpg_vstate.io_offset, + G_gpg_vstate.io_length); + gpg_io_discard(1); + return SW_OK; + } + gpg_io_discard(1); - return SW_OK; + + //STATUS REQUEST + if ((G_gpg_vstate.io_p1==0) && G_gpg_vstate.io_length==0) { + if (gpg_pin_is_verified(G_gpg_vstate.io_p2)) { + return SW_OK; + } + return 0x63C0 | pin->counter; + } + + //RESET REQUEST + if ((G_gpg_vstate.io_p1==0xFF) && G_gpg_vstate.io_length==0) { + gpg_pin_set_verified(G_gpg_vstate.io_p2,0); + return SW_OK; + } + + THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; + } int gpg_apdu_change_ref_data() { @@ -208,28 +216,10 @@ int gpg_apdu_change_ref_data() { return SW_WRONG_DATA; } - gpg_pin_set_verified(pin,0); + gpg_pin_set_verified(pin->ref,0); + - // --- RC pin --- - if (pin->ref == PIN_ID_RC) { - newlen = G_gpg_vstate.io_length; - if (newlen == 0) { - gpg_nvm_write(pin, NULL, sizeof(gpg_pin_t)); - - } - else if ((newlen > GPG_MAX_PW_LENGTH) || - (newlen < 8)) { - THROW(SW_WRONG_DATA); - return SW_WRONG_DATA; - } else { - gpg_pin_set(pin, - G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, - newlen); - } - gpg_io_discard(1); - return SW_OK; - } // --- PW1/PW3 pin --- if (gpg_pin_is_blocked(pin)) { @@ -252,7 +242,7 @@ int gpg_apdu_change_ref_data() { len = pin->length; } - gpg_pin_check_throw(pin, + gpg_pin_check_throw(pin, pin->ref, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, len); @@ -281,7 +271,7 @@ int gpg_apdu_reset_retry_counter() { pin_rc = gpg_pin_get_pin(PIN_ID_RC); if (G_gpg_vstate.io_p1 == 2) { - if (!gpg_pin_is_verified(pin_pw3)) { + if (!gpg_pin_is_verified(PIN_ID_PW3)) { THROW(SW_SECURITY_STATUS_NOT_SATISFIED); return SW_SECURITY_STATUS_NOT_SATISFIED; } @@ -296,7 +286,7 @@ int gpg_apdu_reset_retry_counter() { rc_len = pin_rc->length; } pw1_len = G_gpg_vstate.io_length-rc_len; - gpg_pin_check_throw(pin_rc, + gpg_pin_check_throw(pin_rc,pin_rc->ref, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, rc_len); } diff --git a/src/gpg_pso.c b/src/gpg_pso.c index b74aa3d..1d389b0 100644 --- a/src/gpg_pso.c +++ b/src/gpg_pso.c @@ -18,6 +18,7 @@ #include "gpg_types.h" #include "gpg_api.h" #include "gpg_vars.h" +#include "gpg_ux_nanos.h" const unsigned char gpg_oid_sha256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 @@ -28,7 +29,7 @@ const unsigned char gpg_oid_sha512[] = { static void gpg_pso_reset_PW1() { if (N_gpg_pstate->PW_status[0] ==0) { - gpg_pin_set_verified(gpg_pin_get_pin(PIN_ID_PW1),0); + gpg_pin_set_verified(PIN_ID_PW1,0); } } @@ -41,16 +42,16 @@ static int gpg_sign(gpg_key_t *sigkey) { ksz = ksz>>3; switch(ksz) { case 1024/8: - key = (cx_rsa_private_key_t *)&sigkey->key.rsa1024; + key = (cx_rsa_private_key_t *)&sigkey->priv_key.rsa1024; break; case 2048/8: - key = (cx_rsa_private_key_t *)&sigkey->key.rsa2048; + key = (cx_rsa_private_key_t *)&sigkey->priv_key.rsa2048; break; case 3072/8: - key = (cx_rsa_private_key_t *)&sigkey->key.rsa3072; + key = (cx_rsa_private_key_t *)&sigkey->priv_key.rsa3072; break; case 4096/8: - key = (cx_rsa_private_key_t *)&sigkey->key.rsa4096; + key = (cx_rsa_private_key_t *)&sigkey->priv_key.rsa4096; break; } if (key->size != ksz) { @@ -86,23 +87,27 @@ static int gpg_sign(gpg_key_t *sigkey) { unsigned int sz,i,rs_len; unsigned char *rs; - key = &sigkey->key.ecfp256; - if (key->d_len != 32) { - THROW(SW_CONDITIONS_NOT_SATISFIED); - return SW_CONDITIONS_NOT_SATISFIED; - } + key = &sigkey->priv_key.ecfp; + //sign + #define RS (G_gpg_vstate.work.io_buffer+(GPG_IO_BUFFER_LENGTH-256)) if (sigkey->attributes.value[0] == 19) { + sz = gpg_curve2domainlen(key->curve); + if ((sz == 0) || (key->d_len != sz)) { + THROW(SW_CONDITIONS_NOT_SATISFIED); + return SW_CONDITIONS_NOT_SATISFIED; + } sz = cx_ecdsa_sign(key, CX_RND_TRNG, CX_NONE, - G_gpg_vstate.work.io_buffer, G_gpg_vstate.io_length, - G_gpg_vstate.work.io_buffer); + G_gpg_vstate.work.io_buffer, sz, + RS, 256, + NULL); //reencode r,s in MPI format gpg_io_discard(0); - rs_len = G_gpg_vstate.work.io_buffer[3]; - rs = &G_gpg_vstate.work.io_buffer[4]; + rs_len = RS[3]; + rs = &RS[4]; for (i = 0; i<2; i++) { if (*rs == 0) { @@ -116,15 +121,18 @@ static int gpg_sign(gpg_key_t *sigkey) { rs += 2; } } else{ - sz = cx_eddsa_sign(key, NULL, + sz = cx_eddsa_sign(key, CX_NONE, - CX_SHA512, + CX_SHA512, G_gpg_vstate.work.io_buffer, G_gpg_vstate.io_length, - G_gpg_vstate.work.io_buffer+128); + NULL, 0, + RS, 256, + NULL); gpg_io_discard(0); - gpg_io_insert(G_gpg_vstate.work.io_buffer+128, sz); + gpg_io_insert(RS, sz); } - + #undef RS + //send gpg_pso_reset_PW1(); return SW_OK; @@ -134,9 +142,38 @@ static int gpg_sign(gpg_key_t *sigkey) { return SW_REFERENCED_DATA_NOT_FOUND; } - -int gpg_apdu_pso(unsigned int pso) { +int gpg_apdu_pso() { unsigned int t,l,ksz; + + unsigned int pso; + + pso = (G_gpg_vstate.io_p1 << 8) | G_gpg_vstate.io_p2 ; + + //UIF HANDLE + switch(pso) { + // --- PSO:CDS --- + case 0x9e9a: + if (G_gpg_vstate.kslot->sig.UIF[0]) { + if ((G_gpg_vstate.UIF_flags)==0) { + ui_menu_uifconfirm_display(0); + return 0; + } + G_gpg_vstate.UIF_flags = 0; + } + break; + // --- PSO:DEC --- + case 0x8096: + if (G_gpg_vstate.kslot->dec.UIF[0]) { + if ((G_gpg_vstate.UIF_flags)==0) { + ui_menu_uifconfirm_display(0); + return 0; + } + G_gpg_vstate.UIF_flags = 0; + } + break; + } + + // --- PSO:ENC --- switch(pso) { // --- PSO:CDS --- case 0x9e9a: { @@ -147,6 +184,27 @@ int gpg_apdu_pso(unsigned int pso) { nvm_write(&G_gpg_vstate.kslot->sig_count,&cnt,sizeof(unsigned int)); return sw; } + // --- PSO:ENC --- + case 0x8680: { + unsigned int msg_len; + cx_aes_key_t *key; + unsigned int sz; + key = &G_gpg_vstate.kslot->AES_dec; + if (!(key->size != 16)) { + THROW(SW_CONDITIONS_NOT_SATISFIED+5); + return SW_CONDITIONS_NOT_SATISFIED; + } + msg_len = G_gpg_vstate.io_length - G_gpg_vstate.io_offset; + sz = cx_aes(key, + CX_ENCRYPT|CX_CHAIN_CBC|CX_LAST, + G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, msg_len, + G_gpg_vstate.work.io_buffer+1, GPG_IO_BUFFER_LENGTH-1); + //send + gpg_io_discard(0); + G_gpg_vstate.work.io_buffer[0] = 0x02; + gpg_io_inserted(1+sz); + return SW_OK; + } // --- PSO:DEC --- case 0x8086: { @@ -159,31 +217,31 @@ int gpg_apdu_pso(unsigned int pso) { // --- PSO:DEC:RSA case 0x00: { cx_rsa_private_key_t *key; - if (G_gpg_vstate.kslot->dec.attributes.value[0] != 0x01) { + if (G_gpg_vstate.mse_dec->attributes.value[0] != 0x01) { THROW(SW_CONDITIONS_NOT_SATISFIED); return SW_CONDITIONS_NOT_SATISFIED; } - ksz = (G_gpg_vstate.kslot->dec.attributes.value[1]<<8) | G_gpg_vstate.kslot->dec.attributes.value[2]; + ksz = (G_gpg_vstate.mse_dec->attributes.value[1]<<8) | G_gpg_vstate.mse_dec->attributes.value[2]; ksz = ksz>>3; key = NULL; switch(ksz) { case 1024/8: - key = (cx_rsa_private_key_t *)&G_gpg_vstate.kslot->dec.key.rsa1024; + key = (cx_rsa_private_key_t *)&G_gpg_vstate.mse_dec->priv_key.rsa1024; break; case 2048/8: - key = (cx_rsa_private_key_t *)&G_gpg_vstate.kslot->dec.key.rsa2048; + key = (cx_rsa_private_key_t *)&G_gpg_vstate.mse_dec->priv_key.rsa2048; break; case 3072/8: - key = (cx_rsa_private_key_t *)&G_gpg_vstate.kslot->dec.key.rsa3072; + key = (cx_rsa_private_key_t *)&G_gpg_vstate.mse_dec->priv_key.rsa3072; break; case 4096/8: - key = (cx_rsa_private_key_t *)&G_gpg_vstate.kslot->dec.key.rsa4096; + key = (cx_rsa_private_key_t *)&G_gpg_vstate.mse_dec->priv_key.rsa4096; break; } if ((key == NULL) || (key->size != ksz)) { THROW(SW_CONDITIONS_NOT_SATISFIED); - return 0; + return SW_CONDITIONS_NOT_SATISFIED; } msg_len = G_gpg_vstate.io_length - G_gpg_vstate.io_offset; sz = cx_rsa_decrypt(key, @@ -208,9 +266,9 @@ int gpg_apdu_pso(unsigned int pso) { } msg_len = G_gpg_vstate.io_length - G_gpg_vstate.io_offset; sz = cx_aes(key, - CX_DECRYPT|CX_LAST, + CX_DECRYPT|CX_CHAIN_CBC|CX_LAST, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, msg_len, - G_gpg_vstate.work.io_buffer); + G_gpg_vstate.work.io_buffer, GPG_IO_BUFFER_LENGTH); //send gpg_io_discard(0); gpg_io_inserted(sz); @@ -222,15 +280,11 @@ int gpg_apdu_pso(unsigned int pso) { cx_ecfp_private_key_t *key; unsigned int sz; unsigned int curve; - if (G_gpg_vstate.kslot->dec.attributes.value[0] != 18) { + if (G_gpg_vstate.mse_dec->attributes.value[0] != 0x12) { THROW(SW_CONDITIONS_NOT_SATISFIED); return SW_CONDITIONS_NOT_SATISFIED; } - key = &G_gpg_vstate.kslot->dec.key.ecfp256; - if (key->d_len != 32) { - THROW(SW_CONDITIONS_NOT_SATISFIED); - return SW_CONDITIONS_NOT_SATISFIED; - } + key = &G_gpg_vstate.mse_dec->priv_key.ecfp; gpg_io_fetch_l(&l); gpg_io_fetch_tl(&t, &l); if (t != 0x7f49) { @@ -243,7 +297,11 @@ int gpg_apdu_pso(unsigned int pso) { return SW_WRONG_DATA; } - curve = gpg_oid2curve(G_gpg_vstate.kslot->dec.attributes.value+1, G_gpg_vstate.kslot->dec.attributes.length-1); + curve = gpg_oid2curve(G_gpg_vstate.mse_dec->attributes.value+1, G_gpg_vstate.mse_dec->attributes.length-1); + if (key->curve != curve) { + THROW(SW_CONDITIONS_NOT_SATISFIED); + return SW_CONDITIONS_NOT_SATISFIED; + } if (curve == CX_CURVE_Curve25519) { unsigned int i; @@ -253,8 +311,8 @@ int gpg_apdu_pso(unsigned int pso) { G_gpg_vstate.work.io_buffer[511] = 0x02; sz = cx_ecdh(key, CX_ECDH_X, - G_gpg_vstate.work.io_buffer+511, - G_gpg_vstate.work.io_buffer+256); + G_gpg_vstate.work.io_buffer+511, 65, + G_gpg_vstate.work.io_buffer+256, 160); for (i = 0; i <=31; i++) { G_gpg_vstate.work.io_buffer[128+i] = G_gpg_vstate.work.io_buffer[287-i]; } @@ -262,8 +320,8 @@ int gpg_apdu_pso(unsigned int pso) { } else { sz = cx_ecdh(key, CX_ECDH_X, - G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, - G_gpg_vstate.work.io_buffer+128); + G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, 65, + G_gpg_vstate.work.io_buffer+128, 160); } //send gpg_io_discard(0); @@ -276,7 +334,6 @@ int gpg_apdu_pso(unsigned int pso) { THROW(SW_REFERENCED_DATA_NOT_FOUND); return SW_REFERENCED_DATA_NOT_FOUND; } - } //--- PSO:yy NOT SUPPPORTED --- @@ -290,14 +347,20 @@ int gpg_apdu_pso(unsigned int pso) { int gpg_apdu_internal_authenticate() { - gpg_key_t *sigkey; - sigkey = &G_gpg_vstate.kslot->aut; + // --- PSO:AUTH --- + if (G_gpg_vstate.kslot->aut.UIF[0]) { + if ((G_gpg_vstate.UIF_flags)==0) { + ui_menu_uifconfirm_display(0); + return 0; + } + G_gpg_vstate.UIF_flags = 0; + } - if (G_gpg_vstate.kslot->aut.attributes.value[0] == 1) { - if ( G_gpg_vstate.io_length > ((G_gpg_vstate.kslot->aut.attributes.value[1]<<8)|G_gpg_vstate.kslot->aut.attributes.value[2])*40/100) { + if (G_gpg_vstate.mse_aut->attributes.value[0] == 1) { + if ( G_gpg_vstate.io_length > ((G_gpg_vstate.mse_aut->attributes.value[1]<<8)|G_gpg_vstate.mse_aut->attributes.value[2])*40/100) { THROW(SW_WRONG_LENGTH); return SW_WRONG_LENGTH; } } - return gpg_sign(&G_gpg_vstate.kslot->aut); + return gpg_sign(G_gpg_vstate.mse_aut); } diff --git a/src/gpg_ram.c b/src/gpg_ram.c index eb07678..d536d5e 100644 --- a/src/gpg_ram.c +++ b/src/gpg_ram.c @@ -24,6 +24,7 @@ #ifndef GPG_DEBUG_MAIN unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; ux_state_t ux; + #else extern unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; int apdu_n; diff --git a/src/gpg_select.c b/src/gpg_select.c index d88f672..6c36ac5 100644 --- a/src/gpg_select.c +++ b/src/gpg_select.c @@ -19,10 +19,19 @@ #include "gpg_types.h" #include "gpg_api.h" #include "gpg_vars.h" +const unsigned char C_MF[] = {0x3F, 0x00}; int gpg_apdu_select() { int sw; - if ( (G_gpg_vstate.io_length == 6) && + + //MF + if ( (G_gpg_vstate.io_length == 2) && + (os_memcmp(G_gpg_vstate.work.io_buffer, C_MF, G_gpg_vstate.io_length) == 0) ) { + gpg_io_discard(0); + sw = SW_OK; + } + //AID APP + else if ( (G_gpg_vstate.io_length == 6) && (os_memcmp(G_gpg_vstate.work.io_buffer, N_gpg_pstate->AID, G_gpg_vstate.io_length) == 0) ) { G_gpg_vstate.DO_current = 0; G_gpg_vstate.DO_reccord = 0; @@ -36,8 +45,13 @@ int gpg_apdu_select() { } gpg_io_discard(0); + if (N_gpg_pstate->histo[7] != 0x07) { + THROW(SW_STATE_TERMINATED); + } sw = SW_OK; - } else { + } + //NOT FOUND + else { THROW(SW_FILE_NOT_FOUND); return SW_FILE_NOT_FOUND; } diff --git a/src/gpg_types.h b/src/gpg_types.h index 707f045..c98b766 100644 --- a/src/gpg_types.h +++ b/src/gpg_types.h @@ -36,7 +36,7 @@ #define GPG_KEY_ATTRIBUTES_LENGTH 12 -#define GPG_RSA_DEFAULT_PUB 0x010001U +#define GPG_RSA_DEFAULT_PUB 0x00010001U struct gpg_pin_s { unsigned int ref; @@ -66,15 +66,25 @@ typedef struct gpg_key_s { LV(attributes,GPG_KEY_ATTRIBUTES_LENGTH); /* key value */ union { + cx_rsa_private_key_t rsa; cx_rsa_1024_private_key_t rsa1024; cx_rsa_2048_private_key_t rsa2048; cx_rsa_3072_private_key_t rsa3072; cx_rsa_4096_private_key_t rsa4096; - cx_ecfp_private_key_t ecfp256; - } key; + + cx_ecfp_private_key_t ecfp; + cx_ecfp_256_private_key_t ecfp256; + cx_ecfp_384_private_key_t ecfp384; + cx_ecfp_512_private_key_t ecfp512; + cx_ecfp_640_private_key_t ecfp640; + } priv_key; union { - unsigned char rsa[4]; - cx_ecfp_public_key_t ecfp256; + unsigned char rsa[4]; + cx_ecfp_public_key_t ecfp; + cx_ecfp_256_public_key_t ecfp256; + cx_ecfp_384_public_key_t ecfp384; + cx_ecfp_512_public_key_t ecfp512; + cx_ecfp_640_public_key_t ecfp640; } pub_key; /* C7 C8 C9 , C5 = C7|C8|C9*/ unsigned char fingerprints[20]; @@ -116,7 +126,7 @@ struct gpg_nv_state_s { /* 01F1 (01F2 is volatile)*/ unsigned char config_slot[3]; /* RSA exponent */ - unsigned int default_RSA_exponent; + unsigned char default_RSA_exponent[4]; /* 0101 0102 0103 0104 */ LV(private_DO1, GPG_EXT_PRIVATE_DO_LENGTH); @@ -152,7 +162,6 @@ struct gpg_nv_state_s { /* PINs */ gpg_pin_t PW1; - gpg_pin_t PW2; gpg_pin_t PW3; gpg_pin_t RC; @@ -174,10 +183,15 @@ struct gpg_v_state_s { /* app state */ unsigned char selected; unsigned char slot; /* DO 01F2 */ - gpg_key_slot_t *kslot; + gpg_key_slot_t *kslot; + gpg_key_t *mse_aut; + gpg_key_t *mse_dec; unsigned char seed_mode; + unsigned char UIF_flags; + /* io state*/ + unsigned char io_cla; unsigned char io_ins; unsigned char io_p1; @@ -189,26 +203,41 @@ struct gpg_v_state_s { unsigned short io_mark; union { unsigned char io_buffer[GPG_IO_BUFFER_LENGTH]; + struct { - cx_rsa_1024_public_key_t public; - cx_rsa_1024_private_key_t private; - }rsa1024; + union { + cx_rsa_public_key_t public; + cx_rsa_1024_public_key_t public1024; + cx_rsa_2048_public_key_t public2048; + cx_rsa_3072_public_key_t public3072; + cx_rsa_4096_public_key_t public4096; + }; + union { + cx_rsa_private_key_t private; + cx_rsa_1024_private_key_t private1024; + cx_rsa_2048_private_key_t private2048; + cx_rsa_3072_private_key_t private3072; + cx_rsa_4096_private_key_t private4096; + }; + } rsa; + struct { - cx_rsa_2048_public_key_t public; - cx_rsa_2048_private_key_t private; - }rsa2048; - struct { - cx_rsa_3072_public_key_t public; - cx_rsa_3072_private_key_t private; - }rsa3072; - struct { - cx_rsa_4096_public_key_t public; - cx_rsa_4096_private_key_t private; - }rsa4096; - struct { - cx_ecfp_public_key_t public; - cx_ecfp_private_key_t private; - }ecfp256; + union{ + cx_ecfp_public_key_t public; + cx_ecfp_256_public_key_t public256; + cx_ecfp_384_public_key_t public384; + cx_ecfp_512_public_key_t public512; + cx_ecfp_640_public_key_t public640; + }; + union { + cx_ecfp_private_key_t private; + cx_ecfp_256_private_key_t private256; + cx_ecfp_384_private_key_t private384; + cx_ecfp_512_private_key_t private512; + cx_ecfp_640_private_key_t private640; + }; + }ecfp; + struct { unsigned char md_buffer[GPG_IO_BUFFER_LENGTH-MAX(sizeof(cx_sha3_t),sizeof(cx_sha256_t))]; union { @@ -228,7 +257,7 @@ struct gpg_v_state_s { unsigned char pinmode; /* ux menus */ - char menu[64]; + char menu[112]; unsigned char ux_pinentry[12]; unsigned int ux_key; unsigned int ux_type; @@ -285,6 +314,7 @@ typedef struct gpg_v_state_s gpg_v_state_t; #define INS_PUT_DATA_ODD 0xdb #define INS_VERIFY 0x20 +#define INS_MSE 0x22 #define INS_CHANGE_REFERENCE_DATA 0x24 #define INS_RESET_RETRY_COUNTER 0x2c diff --git a/src/gpg_ux_msg.c b/src/gpg_ux_msg.c index e455438..c684890 100644 --- a/src/gpg_ux_msg.c +++ b/src/gpg_ux_msg.c @@ -34,3 +34,5 @@ const char * const C_NOT_ALLOWED = "Not Allowed "; const char * const C_DEFAULT_MODE = "Default mode"; + +const char * const C_UIF_LOCKED = "UIF locked"; diff --git a/src/gpg_ux_msg.h b/src/gpg_ux_msg.h index 1090930..38847a6 100644 --- a/src/gpg_ux_msg.h +++ b/src/gpg_ux_msg.h @@ -36,6 +36,9 @@ extern const char * const C_NOT_ALLOWED; extern const char * const C_DEFAULT_MODE; +extern const char * const C_UIF_LOCKED; +extern const char * const C_UIF_INVALID; + #define PICSTR(x) ((char*)PIC(x)) #define TEMPLATE_TYPE PICSTR(C_TEMPLATE_TYPE) @@ -54,5 +57,7 @@ extern const char * const C_DEFAULT_MODE; #define ALLOWED PICSTR(C_ALLOWED) #define NOT_ALLOWED PICSTR(C_NOT_ALLOWED) #define DEFAULT_MODE PICSTR(C_DEFAULT_MODE) +#define UIF_LOCKED PICSTR(C_UIF_LOCKED) +#define UIF_INVALID PICSTR(C_UIF_INVALID) #endif diff --git a/src/gpg_ux_nanos.c b/src/gpg_ux_nanos.c index 780c79c..d9e1808 100644 --- a/src/gpg_ux_nanos.c +++ b/src/gpg_ux_nanos.c @@ -91,6 +91,122 @@ void ui_info(const char* msg1, const char* msg2, const void *menu_display, unsig UX_MENU_DISPLAY(0, G_gpg_vstate.ui_dogsays, NULL); }; + +/* ------------------------------ UIF CONFIRM UX ----------------------------- */ +unsigned int ui_uifconfirm_nanos_button(unsigned int button_mask, unsigned int button_mask_counter); +unsigned int ui_uifconfirm_prepro(const bagl_element_t* element); + +const bagl_element_t ui_uifconfirm_nanos[] = { + // type userid x y w h str rad fill fg bg font_id icon_id + { {BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, + NULL, + 0, + 0, 0, + NULL, NULL, NULL}, + + { {BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CROSS }, + NULL, + 0, + 0, 0, + NULL, NULL, NULL }, + + { {BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CHECK }, + NULL, + 0, + 0, 0, + NULL, NULL, NULL }, + + { {BAGL_LABELINE, 0x01, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, + G_gpg_vstate.menu, + 0, + 0, 0, + NULL, NULL, NULL }, + { {BAGL_LABELINE, 0x02, 0, 26, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, + G_gpg_vstate.menu, + 0, + 0, 0, + NULL, NULL, NULL }, +}; + +void ui_menu_uifconfirm_display(unsigned int value) { + UX_DISPLAY(ui_uifconfirm_nanos, (void*)ui_uifconfirm_prepro); +} + +unsigned int ui_uifconfirm_prepro(const bagl_element_t* element) { + if (element->component.userid == 1) { + snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "Confirm:"); + return 1; + } + if (element->component.userid == 2) { + unsigned int uif_case = (G_gpg_vstate.io_ins<<16)|(G_gpg_vstate.io_p1<<8)|(G_gpg_vstate.io_p2); + switch (uif_case) { + case 0x002A9E9A: + snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "Signature"); + return 1; + case 0x002A8680: + snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "Encryption"); + return 1; + case 0x002A8086: + snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "Decryption"); + return 1; + case 0x0088000: + snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "Authentication"); + return 1; + } + } + snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "Please Cancel"); + return 1; +} + +unsigned int ui_uifconfirm_nanos_button(unsigned int button_mask, unsigned int button_mask_counter) { + unsigned int sw; + + sw = 0x6985; + switch(button_mask) { + case BUTTON_EVT_RELEASED|BUTTON_LEFT: // CANCEL + gpg_io_discard(1); + gpg_io_insert_u16(sw); + gpg_io_do(IO_RETURN_AFTER_TX); + ui_menu_main_display(0); + sw = 0x6985; + break; + + case BUTTON_EVT_RELEASED|BUTTON_RIGHT: // OK + BEGIN_TRY { + TRY { + G_gpg_vstate.UIF_flags = 1; + if (G_gpg_vstate.io_ins == INS_PSO) { + sw = gpg_apdu_pso(); + } else if (G_gpg_vstate.io_ins == INS_INTERNAL_AUTHENTICATE) { + sw = gpg_apdu_internal_authenticate(); + } else { + gpg_io_discard(1); + sw = 0x6985; + } + } + CATCH_OTHER(e) { + gpg_io_discard(1); + if ( (e & 0xFFFF0000) || + ( ((e&0xF000)!=0x6000) && ((e&0xF000)!=0x9000) ) ) { + gpg_io_insert_u32(e); + sw = 0x6f42; + } else { + sw = e; + } + } + FINALLY { + G_gpg_vstate.UIF_flags = 0; + gpg_io_insert_u16(sw); + gpg_io_do(IO_RETURN_AFTER_TX); + ui_menu_main_display(0); + } + break; + + } END_TRY; + } + return 0; +} + /* ------------------------------ PIN CONFIRM UX ----------------------------- */ const bagl_element_t ui_pinconfirm_nanos[] = { @@ -152,12 +268,12 @@ unsigned int ui_pinconfirm_nanos_button(unsigned int button_mask, unsigned int b sw = 0x6985; switch(button_mask) { case BUTTON_EVT_RELEASED|BUTTON_LEFT: // CANCEL - gpg_pin_set_verified(gpg_pin_get_pin(G_gpg_vstate.io_p2),0); + gpg_pin_set_verified(G_gpg_vstate.io_p2,0); sw = 0x6985; break; case BUTTON_EVT_RELEASED|BUTTON_RIGHT: // OK - gpg_pin_set_verified(gpg_pin_get_pin(G_gpg_vstate.io_p2),1); + gpg_pin_set_verified(G_gpg_vstate.io_p2,1); sw = 0x9000; break; default: @@ -240,9 +356,17 @@ unsigned int ui_pinentry_prepro(const bagl_element_t* element) { else if (element->component.userid == 2) { unsigned int i; G_gpg_vstate.menu[0] = ' '; + #if 1 for (i = 1; i<= G_gpg_vstate.ux_pinentry[0]; i++) { - G_gpg_vstate.menu[i] = C_pin_digit[G_gpg_vstate.ux_pinentry[i]]; + G_gpg_vstate.menu[i] = C_pin_digit[G_gpg_vstate.ux_pinentry[i]]; } + #else + for (i = 1; i< G_gpg_vstate.ux_pinentry[0]; i++) { + G_gpg_vstate.menu[i] = '*'; + } + G_gpg_vstate.menu[i] = C_pin_digit[G_gpg_vstate.ux_pinentry[i]]; + i++; + #endif for (; i<= GPG_MAX_PW_LENGTH;i++) { G_gpg_vstate.menu[i] = '-'; } @@ -325,15 +449,16 @@ static unsigned int validate_pin() { if (G_gpg_vstate.io_ins == 0x20) { pin = gpg_pin_get_pin(G_gpg_vstate.io_p2); - sw = gpg_pin_check(pin, (unsigned char*)(G_gpg_vstate.menu+1), G_gpg_vstate.ux_pinentry[0]); + sw = gpg_pin_check(pin, G_gpg_vstate.io_p2, (unsigned char*)(G_gpg_vstate.menu+1), G_gpg_vstate.ux_pinentry[0]); gpg_io_discard(1); gpg_io_insert_u16(sw); gpg_io_do(IO_RETURN_AFTER_TX); - if (sw == SW_CONDITIONS_NOT_SATISFIED) { + if (sw != SW_OK) { snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), " %d tries remaining", pin->counter ); ui_info(WRONG_PIN, G_gpg_vstate.menu, ui_menu_main_display, 0); - } - return 0; + } else { + ui_menu_main_display(0); + } } if (G_gpg_vstate.io_ins == 0x24) { @@ -344,7 +469,7 @@ static unsigned int validate_pin() { } if (G_gpg_vstate.io_p1 == 3) { pin = gpg_pin_get_pin(G_gpg_vstate.io_p2); - if (gpg_pin_check(pin, G_gpg_vstate.work.io_buffer+1, G_gpg_vstate.work.io_buffer[0])!=SW_OK) { + if (gpg_pin_check(pin, G_gpg_vstate.io_p2, G_gpg_vstate.work.io_buffer+1, G_gpg_vstate.work.io_buffer[0])!=SW_OK) { gpg_io_discard(1); gpg_io_insert_u16(SW_CONDITIONS_NOT_SATISFIED); gpg_io_do(IO_RETURN_AFTER_TX); @@ -381,12 +506,17 @@ static unsigned int validate_pin() { #define LABEL_AUT "Authentication" #define LABEL_DEC "Decryption" -#define LABEL_RSA2048 "RSA 2048" -#define LABEL_RSA3072 "RSA 3072" -#define LABEL_RSA4096 "RSA 4096" -#define LABEL_NISTP256 "NIST P256" -#define LABEL_BPOOLR1 "Brainpool R1" -#define LABEL_Ed25519 "Ed25519" +#define LABEL_RSA2048 "RSA 2048" +#define LABEL_RSA3072 "RSA 3072" +#define LABEL_RSA4096 "RSA 4096" +#define LABEL_NISTP256 "NIST P256" +//#define LABEL_NISTP384 "NIST P384" +//#define LABEL_NISTP521 "NIST P521" +#define LABEL_SECP256K1 "SEPC 256K1" +#define LABEL_BPOOL256R1 "Brainpool 256R1" +//#define LABEL_BPOOL384R1 "Brainpool 384R1" +//#define LABEL_BPOOL512R1 "Brainpool 512R1" +#define LABEL_Ed25519 "Ed25519" const ux_menu_entry_t ui_menu_template[] = { {ui_menu_tmpl_key, NULL, -1, NULL, "Choose key...", NULL, 0, 0}, @@ -430,12 +560,34 @@ const bagl_element_t* ui_menu_template_preprocessor(const ux_menu_entry_t* entry case 4096: snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_RSA4096); break; + case CX_CURVE_SECP256R1: snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_NISTP256); break; - case CX_CURVE_BrainPoolP256R1: - snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_BPOOLR1); + /* + case CX_CURVE_SECP384R1: + snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_NISTP384); break; + case CX_CURVE_SECP521R1: + snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_NISTP521); + break; + */ + case CX_CURVE_SECP256K1: + snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_SECP256K1); + break; + + case CX_CURVE_BrainPoolP256R1: + snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_BPOOL256R1); + break; + /* + case CX_CURVE_BrainPoolP384R1: + snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_BPOOL384R1); + break; + case CX_CURVE_BrainPoolP512R1: + snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_BPOOL512R1); + break; + */ + case CX_CURVE_Ed25519: snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu)," %s", LABEL_Ed25519); break; @@ -456,7 +608,8 @@ void ui_menu_tmpl_set_action(unsigned int value) { LV(attributes,GPG_KEY_ATTRIBUTES_LENGTH); gpg_key_t* dest; const char* err; - + const unsigned char *oid; + unsigned int oid_len; err = NULL; os_memset(&attributes, 0, sizeof(attributes)); @@ -473,24 +626,21 @@ void ui_menu_tmpl_set_action(unsigned int value) { attributes.length = 6; break; + case CX_CURVE_SECP256K1: case CX_CURVE_SECP256R1: - if (G_gpg_vstate.ux_key == 2) { - attributes.value[0] = 18; //ecdh - } else { - attributes.value[0] = 19; //ecdsa - } - os_memmove(attributes.value+1, C_OID_SECP256R1, sizeof(C_OID_SECP256R1)); - attributes.length = 1+sizeof(C_OID_SECP256R1); - break; - + //case CX_CURVE_SECP384R1: + //case CX_CURVE_SECP521R1: case CX_CURVE_BrainPoolP256R1: - if (G_gpg_vstate.ux_key == 2) { + //case CX_CURVE_BrainPoolP384R1: + //case CX_CURVE_BrainPoolP512R1: + if (G_gpg_vstate.ux_key == 2) { attributes.value[0] = 18; //ecdh } else { attributes.value[0] = 19; //ecdsa } - os_memmove(attributes.value+1, C_OID_BRAINPOOL256R1, sizeof(C_OID_BRAINPOOL256R1)); - attributes.length = 1+sizeof(C_OID_BRAINPOOL256R1); + oid = gpg_curve2oid(G_gpg_vstate.ux_type, &oid_len); + os_memmove(attributes.value+1, oid, sizeof(oid_len)); + attributes.length = 1+oid_len; break; case CX_CURVE_Ed25519: @@ -505,7 +655,7 @@ void ui_menu_tmpl_set_action(unsigned int value) { } break; - default: + default: err = TEMPLATE_TYPE; goto ERROR; } @@ -552,13 +702,18 @@ void ui_menu_tmpl_key_action(unsigned int value) { const ux_menu_entry_t ui_menu_tmpl_type[] = { - {NULL, ui_menu_tmpl_type_action, 2048, NULL, LABEL_RSA2048, NULL, 0, 0}, - {NULL, ui_menu_tmpl_type_action, 3072, NULL, LABEL_RSA3072, NULL, 0, 0}, - {NULL, ui_menu_tmpl_type_action, 4096, NULL, LABEL_RSA4096, NULL, 0, 0}, - {NULL, ui_menu_tmpl_type_action, CX_CURVE_SECP256R1, NULL, LABEL_NISTP256, NULL, 0, 0}, - {NULL, ui_menu_tmpl_type_action, CX_CURVE_BrainPoolP256R1, NULL, LABEL_BPOOLR1, NULL, 0, 0}, - {NULL, ui_menu_tmpl_type_action, CX_CURVE_Ed25519, NULL, LABEL_Ed25519, NULL, 0, 0}, - {ui_menu_template, NULL, 1, &C_badge_back, "Back", NULL, 61, 40}, + {NULL, ui_menu_tmpl_type_action, 2048, NULL, LABEL_RSA2048, NULL, 0, 0}, + {NULL, ui_menu_tmpl_type_action, 3072, NULL, LABEL_RSA3072, NULL, 0, 0}, + {NULL, ui_menu_tmpl_type_action, 4096, NULL, LABEL_RSA4096, NULL, 0, 0}, + {NULL, ui_menu_tmpl_type_action, CX_CURVE_SECP256R1, NULL, LABEL_NISTP256, NULL, 0, 0}, +// {NULL, ui_menu_tmpl_type_action, CX_CURVE_SECP384R1, NULL, LABEL_NISTP384, NULL, 0, 0}, +// {NULL, ui_menu_tmpl_type_action, CX_CURVE_SECP521R1, NULL, LABEL_NISTP521, NULL, 0, 0}, + {NULL, ui_menu_tmpl_type_action, CX_CURVE_SECP256K1, NULL, LABEL_SECP256K1, NULL, 0, 0}, + {NULL, ui_menu_tmpl_type_action, CX_CURVE_BrainPoolP256R1, NULL, LABEL_BPOOL256R1, NULL, 0, 0}, +// {NULL, ui_menu_tmpl_type_action, CX_CURVE_BrainPoolP384R1, NULL, LABEL_BPOOL384R1, NULL, 0, 0}, +// {NULL, ui_menu_tmpl_type_action, CX_CURVE_BrainPoolP512R1, NULL, LABEL_BPOOL512R1, NULL, 0, 0}, + {NULL, ui_menu_tmpl_type_action, CX_CURVE_Ed25519, NULL, LABEL_Ed25519, NULL, 0, 0}, + {ui_menu_template, NULL, 1, &C_badge_back, "Back", NULL, 61, 40}, UX_MENU_END }; @@ -654,6 +809,7 @@ void ui_menu_pinmode_action(unsigned int value) { } else { s = 3; } + //#warning USBD_CCID_activate_pinpad commented USBD_CCID_activate_pinpad(s); } } @@ -661,15 +817,16 @@ void ui_menu_pinmode_action(unsigned int value) { switch (value) { case PIN_MODE_HOST: case PIN_MODE_SCREEN: - if (!gpg_pin_is_verified(gpg_pin_get_pin(PIN_ID_PW2))) { + case PIN_MODE_CONFIRM: + if (!gpg_pin_is_verified(PIN_ID_PW2)) { ui_info(PIN_USER, NOT_VERIFIED, ui_menu_pinmode_display,0); return; } break; - case PIN_MODE_CONFIRM: + case PIN_MODE_TRUST: - if (!gpg_pin_is_verified(gpg_pin_get_pin(PIN_ID_PW3))) { + if (!gpg_pin_is_verified(PIN_ID_PW3)) { ui_info(PIN_ADMIN, NOT_VERIFIED, ui_menu_pinmode_display,0); return; } @@ -683,6 +840,84 @@ void ui_menu_pinmode_action(unsigned int value) { // redisplay first entry of the idle menu ui_menu_pinmode_display(0); } + + + +/* ------------------------------- UIF MODE UX ------------------------------ */ +const ux_menu_entry_t ui_menu_uifmode[]; +void ui_menu_uifmode_display(unsigned int value); +const bagl_element_t* ui_menu_uifmode_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element); +void ui_menu_uifmode_action(unsigned int value); + +const ux_menu_entry_t ui_menu_uifmode[] = { + {NULL, NULL, -1, NULL, "Activate (+) for:", NULL, 0, 0}, + {NULL, ui_menu_uifmode_action, 1, NULL, "Signature", NULL, 0, 0}, + {NULL, ui_menu_uifmode_action, 2, NULL, "Decryption", NULL, 0, 0}, + {NULL, ui_menu_uifmode_action, 3, NULL, "Authentication", NULL, 0, 0}, + {ui_menu_settings, NULL, 1, &C_badge_back, "Back", NULL, 61, 40}, + UX_MENU_END +}; + +void ui_menu_uifmode_display(unsigned int value) { + UX_MENU_DISPLAY(value, ui_menu_uifmode, ui_menu_uifmode_preprocessor); +} + +const bagl_element_t* ui_menu_uifmode_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element) { + if (element->component.userid==0x20) { + if ((entry->userid >= 1) && (entry->userid<=3)) { + unsigned char uif[2] ; + uif[0] = 0; + uif[1] = 0; + switch (entry->userid) { + case 1: + *uif = G_gpg_vstate.kslot->sig.UIF[0]?'+':' '; + break; + case 2: + *uif = G_gpg_vstate.kslot->dec.UIF[0]?'+':' '; + break; + case 3: + *uif = G_gpg_vstate.kslot->aut.UIF[0]?'+':' '; + break; + } + snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "%s %s", + (char*)PIC(entry->line1), uif); + element->text = G_gpg_vstate.menu; + element->component.height = 32; + } + } + return element; +} + +void ui_menu_uifmode_action(unsigned int value) { + unsigned char *uif; + unsigned char new_uif; + switch (value) { + case 1: + uif = &G_gpg_vstate.kslot->sig.UIF[0]; + break; + case 2: + uif = &G_gpg_vstate.kslot->dec.UIF[0]; + break; + case 3: + uif = &G_gpg_vstate.kslot->aut.UIF[0]; + break; + default: + ui_info(INVALID_SELECTION, NULL, ui_menu_uifmode_display,0); + return; + } + if (uif[0] == 0) { + new_uif = 1; + gpg_nvm_write(&uif[0], &new_uif, 1); + } else if (uif[0] == 1) { + new_uif = 0; + gpg_nvm_write(&uif[0], &new_uif, 1) ; + } else /*if (uif[0] == 2 )*/ { + ui_info(UIF_LOCKED, NULL, ui_menu_uifmode_display,0); + return; + } + ui_menu_uifmode_display(value); +} + /* -------------------------------- RESET UX --------------------------------- */ const ux_menu_entry_t ui_menu_reset[] = { @@ -709,6 +944,7 @@ const ux_menu_entry_t ui_menu_settings[] = { {NULL, ui_menu_template_display, 0, NULL, "Key template", NULL, 0, 0}, {NULL, ui_menu_seed_display, 0, NULL, "Seed mode", NULL, 0, 0}, {NULL, ui_menu_pinmode_display, 0, NULL, "PIN mode", NULL, 0, 0}, + {NULL, ui_menu_uifmode_display, 0, NULL, "UIF mode", NULL, 0, 0}, {ui_menu_reset, NULL, 0, NULL, "Reset", NULL, 0, 0}, {NULL, ui_menu_main_display, 2, &C_badge_back, "Back", NULL, 61, 40}, UX_MENU_END @@ -766,6 +1002,7 @@ void ui_menu_slot_action(unsigned int value) { if (s!= G_gpg_vstate.slot) { G_gpg_vstate.slot = s; G_gpg_vstate.kslot = &N_gpg_pstate->keys[G_gpg_vstate.slot]; + gpg_mse_reset(); ui_CCID_reset(); } } @@ -784,7 +1021,7 @@ void ui_menu_slot_action(unsigned int value) { const ux_menu_entry_t ui_menu_info[] = { {NULL, NULL, -1, NULL, "OpenPGP Card", NULL, 0, 0}, {NULL, NULL, -1, NULL, "(c) Ledger SAS", NULL, 0, 0}, - {NULL, NULL, -1, NULL, "Spec 3.0", NULL, 0, 0}, + {NULL, NULL, -1, NULL, "Spec " XSTR(SPEC_VERSION),NULL, 0, 0}, {NULL, NULL, -1, NULL, "App " XSTR(GPG_VERSION), NULL, 0, 0}, {NULL, ui_menu_main_display, 3, &C_badge_back, "Back", NULL, 61, 40}, UX_MENU_END diff --git a/src/gpg_ux_nanos.h b/src/gpg_ux_nanos.h index 499c86b..ba41bf4 100644 --- a/src/gpg_ux_nanos.h +++ b/src/gpg_ux_nanos.h @@ -22,4 +22,5 @@ void ui_init(void); void ui_main_display(unsigned int value); void ui_menu_pinconfirm_display(unsigned int value); void ui_menu_pinentry_display(unsigned int value); +void ui_menu_uifconfirm_display(unsigned int value); #endif \ No newline at end of file diff --git a/src/sdk/usbd_ccid_cmd.c b/src/sdk/usbd_ccid_cmd.c new file mode 100755 index 0000000..1558d3d --- /dev/null +++ b/src/sdk/usbd_ccid_cmd.c @@ -0,0 +1,1057 @@ +/** + ****************************************************************************** + * @file usbd_ccid_cmd.c + * @author MCD Application Team + * @version V1.0.1 + * @date 31-January-2014 + * @brief CCID Commands handling + ****************************************************************************** + * @attention + * + *

© COPYRIGHT 2014 STMicroelectronics

+ * + * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.st.com/software_license_agreement_liberty_v2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#pragma message "Override SDK source file :" __FILE__ + + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_ccid_cmd.h" + +#ifdef HAVE_USB_CLASS_CCID + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ +/* Private macro -------------------------------------------------------------*/ +#define CCID_UpdateCommandStatus(cmd_status,icc_status)\ + G_io_ccid.bulk_header.bulkin.bStatus=(cmd_status|icc_status) + /* + The Above Macro can take any of following Values + #define BM_ICC_PRESENT_ACTIVE 0x00 + #define BM_ICC_PRESENT_INACTIVE 0x01 + #define BM_ICC_NO_ICC_PRESENT 0x02 + + #define BM_COMMAND_STATUS_OFFSET 0x06 + #define BM_COMMAND_STATUS_NO_ERROR 0x00 + #define BM_COMMAND_STATUS_FAILED (0x01 << BM_COMMAND_STATUS_OFFSET) + #define BM_COMMAND_STATUS_TIME_EXTN (0x02 << BM_COMMAND_STATUS_OFFSET) + */ + +/* Private function prototypes -----------------------------------------------*/ +static uint8_t CCID_CheckCommandParams (uint32_t param_type); + +/* Private functions ---------------------------------------------------------*/ + +/** + * @brief PC_to_RDR_IccPowerOn + * PC_TO_RDR_ICCPOWERON message execution, apply voltage and get ATR + * @param None + * @retval uint8_t status of the command execution + */ +uint8_t PC_to_RDR_IccPowerOn(void) +{ + /* Apply the ICC VCC + Fills the Response buffer with ICC ATR + This Command is returned with RDR_to_PC_DataBlock(); + */ + + uint8_t voltage; + uint8_t sc_voltage = 0; + uint8_t error; + + G_io_ccid.bulk_header.bulkin.dwLength = 0; /* Reset Number of Bytes in abData */ + + error = CCID_CheckCommandParams(CHK_PARAM_SLOT |\ + CHK_PARAM_DWLENGTH | \ + CHK_PARAM_abRFU2 |\ + CHK_PARAM_CARD_PRESENT |\ + CHK_PARAM_ABORT ); + if (error != 0) + { + return error; + } + + /* Voltage that is applied to the ICC + 00h – Automatic Voltage Selection + 01h – 5.0 volts + 02h – 3.0 volts + 03h – 1.8 volts + */ + /* G_io_ccid.bulk_header.bulkout.bSpecific_0 Contains bPowerSelect */ + voltage = G_io_ccid.bulk_header.bulkout.bSpecific_0; + if (voltage >= VOLTAGE_SELECTION_1V8) + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_ACTIVE); + return SLOTERROR_BAD_POWERSELECT; /* The Voltage specified is out of Spec */ + } + + /* Correct Voltage Requested by the Host */ + if ((voltage == VOLTAGE_SELECTION_AUTOMATIC) || + (voltage == VOLTAGE_SELECTION_3V)) + { + /* voltage == 00 Voltage Automatic + voltage == 01 Voltage Automatic = 5.0V + voltage == 02 Voltage Automatic = 3V + voltage == 03 Voltage Automatic = 1.8V + */ + sc_voltage = SC_VOLTAGE_3V; + } + else if (voltage == VOLTAGE_SELECTION_5V) + { + sc_voltage = SC_VOLTAGE_5V; + } + + G_io_ccid.bulk_header.bulkin.dwLength = SC_AnswerToReset(sc_voltage, G_io_ccid_data_buffer); + + /* Check if the Card has come to Active State*/ + error = CCID_CheckCommandParams(CHK_ACTIVE_STATE); + if (error != 0) + { + /* Check if Voltage is not Automatic */ + if (voltage != 0) + { /* If Specific Voltage requested by Host i.e 3V or 5V*/ + return error; + } + else + {/* Automatic Voltage selection requested by Host */ + + if (sc_voltage != SC_VOLTAGE_5V) + { /* If voltage selected was Automatic and 5V is not yet tried */ + sc_voltage = SC_VOLTAGE_5V; + G_io_ccid.bulk_header.bulkin.dwLength = SC_AnswerToReset(sc_voltage, G_io_ccid_data_buffer); + + /* Check again the State */ + error = CCID_CheckCommandParams(CHK_ACTIVE_STATE); + if (error != 0) + return error; + + } + else + { /* Voltage requested from Host was 5V already*/ + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_INACTIVE); + return error; + } + } /* Voltage Selection was automatic */ + } /* If Active State */ + + /* ATR is received, No Error Condition Found */ + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_NO_ERROR, BM_ICC_PRESENT_ACTIVE); + + return SLOT_NO_ERROR; +} + +/** + * @brief PC_to_RDR_IccPowerOff + * Icc VCC is switched Off + * @param None + * @retval uint8_t error: status of the command execution + */ +uint8_t PC_to_RDR_IccPowerOff(void) +{ + /* The response to this command message is the RDR_to_PC_SlotStatus + response message. */ + uint8_t error; + + error = CCID_CheckCommandParams(CHK_PARAM_SLOT |\ + CHK_PARAM_abRFU3 |\ + CHK_PARAM_DWLENGTH ); + if (error != 0) + { + return error; + } + + /* Command is ok, Check for Card Presence */ + if (SC_Detect()) + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_NO_ERROR,BM_ICC_PRESENT_INACTIVE); + } + else + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_NO_ERROR,BM_ICC_NO_ICC_PRESENT); + } + + /* Power OFF the card */ + SC_Poweroff(); + + return SLOT_NO_ERROR; +} + +/** + * @brief PC_to_RDR_GetSlotStatus + * Provides the Slot status to the host + * @param None + * @retval uint8_t status of the command execution + */ +uint8_t PC_to_RDR_GetSlotStatus(void) +{ + uint8_t error; + + error = CCID_CheckCommandParams(CHK_PARAM_SLOT |\ + CHK_PARAM_DWLENGTH |\ + CHK_PARAM_CARD_PRESENT |\ + CHK_PARAM_abRFU3 ); + if (error != 0) + { + return error; + } + + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_NO_ERROR,BM_ICC_PRESENT_ACTIVE); + return SLOT_NO_ERROR; +} + + +/** + * @brief PC_to_RDR_XfrBlock + * Handles the Block transfer from Host. + * Response to this command message is the RDR_to_PC_DataBlock + * @param None + * @retval uint8_t status of the command execution + */ +uint8_t PC_to_RDR_XfrBlock(void) +{ + uint16_t expectedLength, reqlen; + + uint8_t error; + + error = CCID_CheckCommandParams(CHK_PARAM_SLOT |\ + CHK_PARAM_CARD_PRESENT |\ + CHK_PARAM_abRFU3 |\ + CHK_PARAM_ABORT |\ + CHK_ACTIVE_STATE ); + if (error != 0) + return error; + + if (G_io_ccid.bulk_header.bulkout.dwLength > IO_CCID_DATA_BUFFER_SIZE) + { /* Check amount of Data Sent by Host is > than memory allocated ? */ + + return SLOTERROR_BAD_DWLENGTH; + } + + + /* wLevelParameter = Size of expected data to be returned by the + bulk-IN endpoint */ + expectedLength = (G_io_ccid.bulk_header.bulkout.bSpecific_2 << 8) | + G_io_ccid.bulk_header.bulkout.bSpecific_1; + + reqlen = G_io_ccid.bulk_header.bulkout.dwLength; + + G_io_ccid.bulk_header.bulkin.dwLength = (uint16_t)expectedLength; + + + error = SC_XferBlock(&G_io_ccid_data_buffer[0], + reqlen, + &expectedLength); + + if (error != SLOT_NO_ERROR) + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_ACTIVE); + } + else + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_NO_ERROR, BM_ICC_PRESENT_ACTIVE); + error = SLOT_NO_ERROR; + } + + return error; +} + + +/** + * @brief PC_to_RDR_GetParameters + * Provides the ICC parameters to the host + * Response to this command message is the RDR_to_PC_Parameters + * @param None + * @retval uint8_t status of the command execution + */ +uint8_t PC_to_RDR_GetParameters(void) +{ + uint8_t error; + + error = CCID_CheckCommandParams(CHK_PARAM_SLOT |\ + CHK_PARAM_DWLENGTH |\ + CHK_PARAM_CARD_PRESENT |\ + CHK_PARAM_abRFU3 ); + if (error != 0) + return error; + + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_NO_ERROR, BM_ICC_PRESENT_ACTIVE); + + return SLOT_NO_ERROR; +} + + +/** + * @brief PC_to_RDR_ResetParameters + * Set the ICC parameters to the default + * Response to this command message is the RDR_to_PC_Parameters + * @param None + * @retval uint8_t status of the command execution + */ +uint8_t PC_to_RDR_ResetParameters(void) +{ + uint8_t error; + + error = CCID_CheckCommandParams(CHK_PARAM_SLOT |\ + CHK_PARAM_DWLENGTH |\ + CHK_PARAM_CARD_PRESENT |\ + CHK_PARAM_abRFU3 |\ + CHK_ACTIVE_STATE); + if (error != 0) + return error; + + /* This command resets the slot parameters to their default values */ + G_io_ccid.Protocol0_DataStructure.bmFindexDindex = DEFAULT_FIDI; + G_io_ccid.Protocol0_DataStructure.bmTCCKST0 = DEFAULT_T01CONVCHECKSUM; + G_io_ccid.Protocol0_DataStructure.bGuardTimeT0 = DEFAULT_EXTRA_GUARDTIME; + G_io_ccid.Protocol0_DataStructure.bWaitingIntegerT0 = DEFAULT_WAITINGINTEGER; + G_io_ccid.Protocol0_DataStructure.bClockStop = DEFAULT_CLOCKSTOP; + + error = SC_SetParams(&G_io_ccid.Protocol0_DataStructure); + + if (error != SLOT_NO_ERROR) + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_ACTIVE); + } + else + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_NO_ERROR, BM_ICC_PRESENT_ACTIVE); + error = SLOT_NO_ERROR; + } + + return error; +} + + +/** + * @brief PC_to_RDR_SetParameters + * Set the ICC parameters to the host defined parameters + * Response to this command message is the RDR_to_PC_Parameters + * @param None + * @retval uint8_t status of the command execution + */ +uint8_t PC_to_RDR_SetParameters(void) +{ + uint8_t error; + + error = CCID_CheckCommandParams(CHK_PARAM_SLOT |\ + CHK_PARAM_CARD_PRESENT |\ + CHK_PARAM_abRFU2 |\ + CHK_ACTIVE_STATE); + if (error != 0) + return error; + + error = SLOT_NO_ERROR; + + /* for Protocol T=0 (bProtocolNum=0) (dwLength=00000005h) */ + if ( (G_io_ccid.bulk_header.bulkout.dwLength == 5) && + (G_io_ccid.bulk_header.bulkout.bSpecific_0 != 0)) + error = SLOTERROR_BAD_PROTOCOLNUM; + + /* for Protocol T=1 (bProtocolNum=1) (dwLength=00000007h) */ + if ( (G_io_ccid.bulk_header.bulkout.dwLength == 7) && + (G_io_ccid.bulk_header.bulkout.bSpecific_0 != 1)) + error = SLOTERROR_CMD_NOT_SUPPORTED; + + /* For T0, Waiting Integer 0 supported */ + if (G_io_ccid_data_buffer[3] != 0) + error = SLOTERROR_BAD_WAITINGINTEGER; + + if (G_io_ccid_data_buffer[4] != DEFAULT_CLOCKSTOP) + error = SLOTERROR_BAD_CLOCKSTOP; + + if (error != SLOT_NO_ERROR) + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_ACTIVE); + } + + os_memmove(&G_io_ccid.Protocol0_DataStructure, (Protocol0_DataStructure_t*)(&(G_io_ccid_data_buffer[0])), sizeof(Protocol0_DataStructure_t)); + error = SC_SetParams(&G_io_ccid.Protocol0_DataStructure); + + if (error != SLOT_NO_ERROR) + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_ACTIVE); + } + else + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_NO_ERROR, BM_ICC_PRESENT_ACTIVE); + error = SLOT_NO_ERROR; + } + + return error; +} + + +/** + * @brief PC_to_RDR_Escape + * Execute the Escape command. This is user specific Implementation + * Response to this command message is the RDR_to_PC_Escape + * @param None + * @retval uint8_t status of the command execution + */ +uint8_t PC_to_RDR_Escape(void) +{ + uint8_t error; + uint16_t size; + + error = CCID_CheckCommandParams(CHK_PARAM_SLOT |\ + CHK_PARAM_CARD_PRESENT |\ + CHK_PARAM_abRFU3 |\ + CHK_PARAM_ABORT |\ + CHK_ACTIVE_STATE); + + if (error != 0) + return error; + + error = SC_ExecuteEscape(&G_io_ccid_data_buffer[0], + G_io_ccid.bulk_header.bulkout.dwLength, + &G_io_ccid_data_buffer[0], + &size); + + G_io_ccid.bulk_header.bulkin.dwLength = size; + + if (error != SLOT_NO_ERROR) + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_ACTIVE); + } + else + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_NO_ERROR, BM_ICC_PRESENT_ACTIVE); + } + + return error; +} + + +/** + * @brief PC_to_RDR_IccClock + * Execute the Clock specific command from host + * Response to this command message is the RDR_to_PC_SlotStatus + * @param None + * @retval uint8_t status of the command execution + */ +uint8_t PC_to_RDR_IccClock(void) +{ + uint8_t error; + + error = CCID_CheckCommandParams(CHK_PARAM_SLOT |\ + CHK_PARAM_CARD_PRESENT |\ + CHK_PARAM_abRFU2 |\ + CHK_PARAM_DWLENGTH|\ + CHK_ACTIVE_STATE); + if (error != 0) + return error; + + /* bClockCommand • 00h restarts Clock + • 01h Stops Clock in the state shown in the bClockStop + field of the PC_to_RDR_SetParameters command + and RDR_to_PC_Parameters message.*/ + if (G_io_ccid.bulk_header.bulkout.bSpecific_0 > 1) + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_ACTIVE); + return SLOTERROR_BAD_CLOCKCOMMAND; + } + + error = SC_SetClock(G_io_ccid.bulk_header.bulkout.bSpecific_0); + + if (error != SLOT_NO_ERROR) + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_ACTIVE); + } + else + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_NO_ERROR, BM_ICC_PRESENT_ACTIVE); + } + + return error; +} + + +/** + * @brief PC_to_RDR_Abort + * Execute the Abort command from host, This stops all Bulk transfers + * from host and ICC + * Response to this command message is the RDR_to_PC_SlotStatus + * @param None + * @retval uint8_t status of the command execution + */ +uint8_t PC_to_RDR_Abort(void) +{ + uint8_t error; + + error = CCID_CheckCommandParams(CHK_PARAM_SLOT |\ + CHK_PARAM_abRFU3 |\ + CHK_PARAM_DWLENGTH); + if (error != 0) + return error; + + CCID_CmdAbort (G_io_ccid.bulk_header.bulkout.bSlot, G_io_ccid.bulk_header.bulkout.bSeq); + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_NO_ERROR,BM_ICC_PRESENT_ACTIVE); + return SLOT_NO_ERROR; +} + +/** + * @brief CCID_CmdAbort + * Execute the Abort command from Bulk EP or from Control EP, + * This stops all Bulk transfers from host and ICC + * @param uint8_t slot: slot number that host wants to abort + * @param uint8_t seq : Seq number for PC_to_RDR_Abort + * @retval uint8_t status of the command execution + */ +uint8_t CCID_CmdAbort(uint8_t slot, uint8_t seq) +{ + /* This function is called for REQUEST_ABORT & PC_to_RDR_Abort */ + + if (slot >= CCID_NUMBER_OF_SLOTS) + { /* This error condition is possible only from CLASS_REQUEST, otherwise + Slot is already checked in parameters from PC_to_RDR_Abort request */ + /* Slot requested is more than supported by Firmware */ + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED,BM_ICC_NO_ICC_PRESENT); + return SLOTERROR_BAD_SLOT; + } + + if ( G_io_ccid.usb_ccid_param.bAbortRequestFlag == 1) + { /* Abort Command was already received from ClassReq or PC_to_RDR */ + if (( G_io_ccid.usb_ccid_param.bSeq == seq) && (G_io_ccid.usb_ccid_param.bSlot == slot)) + { + /* CLASS Specific request is already Received, Reset the abort flag */ + G_io_ccid.usb_ccid_param.bAbortRequestFlag = 0; + } + } + else + { + /* Abort Command was NOT received from ClassReq or PC_to_RDR, + so save them for next ABORT command to verify */ + G_io_ccid.usb_ccid_param.bAbortRequestFlag = 1; + G_io_ccid.usb_ccid_param.bSeq = seq ; + G_io_ccid.usb_ccid_param.bSlot = slot; + } + + return 0; +} + +/** + * @brief PC_TO_RDR_T0Apdu + * Execute the PC_TO_RDR_T0APDU command from host + * Response to this command message is the RDR_to_PC_SlotStatus + * @param None + * @retval uint8_t status of the command execution + */ +uint8_t PC_TO_RDR_T0Apdu(void) +{ + uint8_t error; + + error = CCID_CheckCommandParams(CHK_PARAM_SLOT |\ + CHK_PARAM_CARD_PRESENT |\ + CHK_PARAM_DWLENGTH | + CHK_PARAM_ABORT ); + if (error != 0) + return error; + + if (G_io_ccid.bulk_header.bulkout.bSpecific_0 > 0x03) + {/* Bit 0 is associated with field bClassGetResponse + Bit 1 is associated with field bClassEnvelope + Other bits are RFU.*/ + + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_ACTIVE); + return SLOTERROR_BAD_BMCHANGES; + } + + error = SC_T0Apdu(G_io_ccid.bulk_header.bulkout.bSpecific_0, + G_io_ccid.bulk_header.bulkout.bSpecific_1, + G_io_ccid.bulk_header.bulkout.bSpecific_2); + + if (error != SLOT_NO_ERROR) + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_ACTIVE); + } + else + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_NO_ERROR, BM_ICC_PRESENT_ACTIVE); + } + + return error; +} + +/** + * @brief PC_TO_RDR_Mechanical + * Execute the PC_TO_RDR_MECHANICAL command from host + * Response to this command message is the RDR_to_PC_SlotStatus + * @param None + * @retval uint8_t status of the command execution + */ +uint8_t PC_TO_RDR_Mechanical(void) +{ + uint8_t error; + + error = CCID_CheckCommandParams(CHK_PARAM_SLOT |\ + CHK_PARAM_CARD_PRESENT |\ + CHK_PARAM_abRFU2 |\ + CHK_PARAM_DWLENGTH + ); + if (error != 0) + return error; + + if (G_io_ccid.bulk_header.bulkout.bSpecific_0 > 0x05) + {/* 01h – Accept Card + 02h – Eject Card + 03h – Capture Card + 04h – Lock Card + 05h – Unlock Card*/ + + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_ACTIVE); + return SLOTERROR_BAD_BFUNCTION_MECHANICAL; + } + + error = SC_Mechanical(G_io_ccid.bulk_header.bulkout.bSpecific_0); + + if (error != SLOT_NO_ERROR) + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_ACTIVE); + } + else + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_NO_ERROR, BM_ICC_PRESENT_ACTIVE); + } + + return error; +} + +/** + * @brief PC_TO_RDR_SetDataRateAndClockFrequency + * Set the required Card Frequency and Data rate from the host. + * Response to this command message is the + * RDR_to_PC_DataRateAndClockFrequency + * @param None + * @retval uint8_t status of the command execution + */ +uint8_t PC_TO_RDR_SetDataRateAndClockFrequency(void) +{ + uint8_t error; + uint32_t clockFrequency; + uint32_t dataRate; + uint32_t temp =0; + + error = CCID_CheckCommandParams(CHK_PARAM_SLOT |\ + CHK_PARAM_CARD_PRESENT |\ + CHK_PARAM_abRFU3); + if (error != 0) + return error; + + if (G_io_ccid.bulk_header.bulkout.dwLength != 0x08) + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED,BM_ICC_PRESENT_ACTIVE); + return SLOTERROR_BAD_LENTGH; + } + + /* HERE we avoiding to an unaligned memory access*/ + clockFrequency = U4LE(G_io_ccid_data_buffer, 0); + dataRate = U4LE(G_io_ccid_data_buffer, 4); + + error = SC_SetDataRateAndClockFrequency(clockFrequency, dataRate); + G_io_ccid.bulk_header.bulkin.bError = error; + + if (error != SLOT_NO_ERROR) + { + G_io_ccid.bulk_header.bulkin.dwLength = 0; + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_ACTIVE); + } + else + { + G_io_ccid.bulk_header.bulkin.dwLength = 8; + + (G_io_ccid_data_buffer[0]) = clockFrequency & 0x000000FF ; + (G_io_ccid_data_buffer[1]) = (clockFrequency & 0x0000FF00) >> 8; + (G_io_ccid_data_buffer[2]) = (clockFrequency & 0x00FF0000) >> 16; + (G_io_ccid_data_buffer[3]) = (clockFrequency & 0xFF000000) >> 24; + (G_io_ccid_data_buffer[4]) = dataRate & 0x000000FF ; + (G_io_ccid_data_buffer[5]) = (dataRate & 0x0000FF00) >> 8; + (G_io_ccid_data_buffer[6]) = (dataRate & 0x00FF0000) >> 16; + (G_io_ccid_data_buffer[7]) = (dataRate & 0xFF000000) >> 24; + + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_NO_ERROR, BM_ICC_PRESENT_ACTIVE); + } + + return error; +} + +/** + * @brief PC_TO_RDR_Secure + * Execute the Secure Command from the host. + * Response to this command message is the RDR_to_PC_DataBlock + * @param None + * @retval uint8_t status of the command execution + */ +uint8_t PC_TO_RDR_Secure(void) +{ + uint8_t error; + uint8_t bBWI; + uint16_t wLevelParameter; + uint32_t responseLen; + + + error = CCID_CheckCommandParams(CHK_PARAM_SLOT |\ + CHK_PARAM_CARD_PRESENT |\ + CHK_PARAM_ABORT ); + + if (error != 0) { + G_io_ccid.bulk_header.bulkin.dwLength = 0; + return error; + } + + bBWI = G_io_ccid.bulk_header.bulkout.bSpecific_0; + wLevelParameter = (G_io_ccid.bulk_header.bulkout.bSpecific_1 + ((uint16_t)G_io_ccid.bulk_header.bulkout.bSpecific_2<<8)); + + if ((EXCHANGE_LEVEL_FEATURE == TPDU_EXCHANGE) || + (EXCHANGE_LEVEL_FEATURE == SHORT_APDU_EXCHANGE)) + { + /* TPDU level & short APDU level, wLevelParameter is RFU, = 0000h */ + if (wLevelParameter != 0 ) + { + G_io_ccid.bulk_header.bulkin.dwLength = 0; + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_ACTIVE); + error = SLOTERROR_BAD_LEVELPARAMETER; + return error; + } + } + + error = SC_Secure(G_io_ccid.bulk_header.bulkout.dwLength - CCID_HEADER_SIZE, bBWI, wLevelParameter, + &G_io_ccid_data_buffer[0], &responseLen); + + G_io_ccid.bulk_header.bulkin.dwLength = responseLen; + + if (error != SLOT_NO_ERROR) + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_ACTIVE); + } + else + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_NO_ERROR, BM_ICC_PRESENT_ACTIVE); + } + + return error; +} + +/******************************************************************************/ +/* BULK IN ROUTINES */ +/******************************************************************************/ + +/** + * @brief RDR_to_PC_DataBlock + * Provide the data block response to the host + * Response for PC_to_RDR_IccPowerOn, PC_to_RDR_XfrBlock + * @param uint8_t errorCode: code to be returned to the host + * @retval None + */ +void RDR_to_PC_DataBlock(uint8_t errorCode) +{ + G_io_ccid.bulk_header.bulkin.bMessageType = RDR_TO_PC_DATABLOCK; + G_io_ccid.bulk_header.bulkin.bError = errorCode; + G_io_ccid.bulk_header.bulkin.bSpecific=0; /* bChainParameter */ + + /* void the Length Specified in Command */ + if(errorCode != SLOT_NO_ERROR) + { + G_io_ccid.bulk_header.bulkin.dwLength = 0; + } + + Transfer_Data_Request(); + +} + + +/** + * @brief RDR_to_PC_SlotStatus + * Provide the Slot status response to the host + * Response for PC_to_RDR_IccPowerOff + * PC_to_RDR_GetSlotStatus + * PC_to_RDR_IccClock + * PC_to_RDR_T0APDU + * PC_to_RDR_Mechanical + * Also the device sends this response message when it has completed + * aborting a slot after receiving both the Class Specific ABORT request + * and PC_to_RDR_Abort command message. + * @param uint8_t errorCode: code to be returned to the host + * @retval None + */ +void RDR_to_PC_SlotStatus(uint8_t errorCode) +{ + + G_io_ccid.bulk_header.bulkin.bMessageType = RDR_TO_PC_SLOTSTATUS; + G_io_ccid.bulk_header.bulkin.dwLength =0; + G_io_ccid.bulk_header.bulkin.bError = errorCode; + G_io_ccid.bulk_header.bulkin.bSpecific=0; /* bClockStatus = 00h Clock running + 01h Clock stopped in state L + 02h Clock stopped in state H + 03h Clock stopped in an unknown state + All other values are RFU. */ + + Transfer_Data_Request(); + +} + +/** + * @brief RDR_to_PC_Parameters + * Provide the data block response to the host + * Response for PC_to_RDR_GetParameters, PC_to_RDR_ResetParameters + * PC_to_RDR_SetParameters + * @param uint8_t errorCode: code to be returned to the host + * @retval None + */ +void RDR_to_PC_Parameters(uint8_t errorCode) +{ + + G_io_ccid.bulk_header.bulkin.bMessageType = RDR_TO_PC_PARAMETERS; + G_io_ccid.bulk_header.bulkin.bError = errorCode; + + if(errorCode == SLOT_NO_ERROR) + { + G_io_ccid.bulk_header.bulkin.dwLength = LEN_PROTOCOL_STRUCT_T0; + } + else + { + G_io_ccid.bulk_header.bulkin.dwLength = 0; + } + + os_memmove(G_io_ccid_data_buffer, &G_io_ccid.Protocol0_DataStructure, sizeof(G_io_ccid.Protocol0_DataStructure)); + + /* bProtocolNum */ + G_io_ccid.bulk_header.bulkin.bSpecific = BPROTOCOL_NUM_T0; + + Transfer_Data_Request(); +} + +/** + * @brief RDR_to_PC_Escape + * Provide the Escaped data block response to the host + * Response for PC_to_RDR_Escape + * @param uint8_t errorCode: code to be returned to the host + * @retval None + */ +void RDR_to_PC_Escape(uint8_t errorCode) +{ + G_io_ccid.bulk_header.bulkin.bMessageType = RDR_TO_PC_ESCAPE; + + G_io_ccid.bulk_header.bulkin.bSpecific=0; /* Reserved for Future Use */ + G_io_ccid.bulk_header.bulkin.bError = errorCode; + + /* void the Length Specified in Command */ + if(errorCode != SLOT_NO_ERROR) + { + G_io_ccid.bulk_header.bulkin.dwLength = 0; + } + + Transfer_Data_Request(); +} + + + +/** + * @brief RDR_to_PC_DataRateAndClockFrequency + * Provide the Clock and Data Rate information to host + * Response for PC_TO_RDR_SetDataRateAndClockFrequency + * @param uint8_t errorCode: code to be returned to the host + * @retval None + */ +void RDR_to_PC_DataRateAndClockFrequency(uint8_t errorCode) +{ + /* + uint16_t length = CCID_RESPONSE_HEADER_SIZE; + */ + + G_io_ccid.bulk_header.bulkin.bMessageType = RDR_TO_PC_DATARATEANDCLOCKFREQUENCY; + G_io_ccid.bulk_header.bulkin.bError = errorCode; + G_io_ccid.bulk_header.bulkin.bSpecific=0; /* Reserved for Future Use */ + + /* void the Length Specified in Command */ + if(errorCode != SLOT_NO_ERROR) + { + G_io_ccid.bulk_header.bulkin.dwLength = 0; + } + + Transfer_Data_Request(); +} + +#ifdef HAVE_CCID_INTERRUPT +/** + * @brief RDR_to_PC_NotifySlotChange + * Interrupt message to be sent to the host, Checks the card presence + * status and update the buffer accordingly + * @param None + * @retval None + */ +void RDR_to_PC_NotifySlotChange(void) +{ + G_io_ccid.UsbIntMessageBuffer[OFFSET_INT_BMESSAGETYPE] = RDR_TO_PC_NOTIFYSLOTCHANGE; + + if (SC_Detect() ) + { + /* + SLOT_ICC_PRESENT 0x01 : LSb : (0b = no ICC present, 1b = ICC present) + SLOT_ICC_CHANGE 0x02 : MSb : (0b = no change, 1b = change). + */ + G_io_ccid.UsbIntMessageBuffer[OFFSET_INT_BMSLOTICCSTATE] = SLOT_ICC_PRESENT | + SLOT_ICC_CHANGE; + } + else + { + G_io_ccid.UsbIntMessageBuffer[OFFSET_INT_BMSLOTICCSTATE] = SLOT_ICC_CHANGE; + + /* Power OFF the card */ + SC_Poweroff(); + } +} +#endif // HAVE_CCID_INTERRUPT + + +/** + * @brief CCID_UpdSlotStatus + * Updates the variable for the slot status + * @param uint8_t slotStatus : slot status from the calling function + * @retval None + */ +void CCID_UpdSlotStatus (uint8_t slotStatus) +{ + G_io_ccid.Ccid_SlotStatus.SlotStatus = slotStatus; +} + +/** + * @brief CCID_UpdSlotChange + * Updates the variable for the slot change status + * @param uint8_t changeStatus : slot change status from the calling function + * @retval None + */ +void CCID_UpdSlotChange (uint8_t changeStatus) +{ + G_io_ccid.Ccid_SlotStatus.SlotStatusChange = changeStatus; +} + +/** + * @brief CCID_IsSlotStatusChange + * Provides the value of the variable for the slot change status + * @param None + * @retval uint8_t slot change status + */ +uint8_t CCID_IsSlotStatusChange (void) +{ + return G_io_ccid.Ccid_SlotStatus.SlotStatusChange; +} + +/** + * @brief CCID_CheckCommandParams + * Checks the specific parameters requested by the function and update + * status accordingly. This function is called from all + * PC_to_RDR functions + * @param uint32_t param_type : Parameter enum to be checked by calling function + * @retval uint8_t status + */ +static uint8_t CCID_CheckCommandParams (uint32_t param_type) +{ + uint32_t parameter; + + G_io_ccid.bulk_header.bulkin.bStatus = BM_ICC_PRESENT_ACTIVE | BM_COMMAND_STATUS_NO_ERROR ; + + parameter = (uint32_t)param_type; + + if (parameter & CHK_PARAM_SLOT) + { + /* + The slot number (bSlot) identifies which ICC slot is being addressed + by the message, if the CCID supports multiple slots. + The slot number is zero-relative, and is in the range of zero to FFh. + */ + + /* SLOT Number is 0 onwards, so always < CCID_NUMBER_OF_SLOTs */ + /* Error Condition !!! */ + if (G_io_ccid.bulk_header.bulkout.bSlot >= CCID_NUMBER_OF_SLOTS) + { /* Slot requested is more than supported by Firmware */ + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED,BM_ICC_NO_ICC_PRESENT); + return SLOTERROR_BAD_SLOT; + } + } + + if (parameter & CHK_PARAM_CARD_PRESENT) + { + /* Commands Parameters ok, Check the Card Status */ + if (SC_Detect() == 0) + { /* Card is Not detected */ + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED,BM_ICC_NO_ICC_PRESENT); + return SLOTERROR_ICC_MUTE; + } + } + + /* Check that DwLength is 0 */ + if (parameter & CHK_PARAM_DWLENGTH) + { + if (G_io_ccid.bulk_header.bulkout.dwLength != 0) + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED,BM_ICC_PRESENT_ACTIVE); + return SLOTERROR_BAD_LENTGH; + } + } + + /* abRFU 2 : Reserved for Future Use*/ + if (parameter & CHK_PARAM_abRFU2) + { + + if ((G_io_ccid.bulk_header.bulkout.bSpecific_1 != 0) || + (G_io_ccid.bulk_header.bulkout.bSpecific_2 != 0)) + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED,BM_ICC_PRESENT_ACTIVE); + return SLOTERROR_BAD_ABRFU_2B; /* bSpecific_1 */ + } + } + + if (parameter & CHK_PARAM_abRFU3) + { + /* abRFU 3 : Reserved for Future Use*/ + if ((G_io_ccid.bulk_header.bulkout.bSpecific_0 != 0) || + (G_io_ccid.bulk_header.bulkout.bSpecific_1 != 0) || + (G_io_ccid.bulk_header.bulkout.bSpecific_2 != 0)) + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED,BM_ICC_PRESENT_ACTIVE); + return SLOTERROR_BAD_ABRFU_3B; + } + } + + + if (parameter & CHK_PARAM_ABORT) + { + if( G_io_ccid.usb_ccid_param.bAbortRequestFlag ) + { + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED,BM_ICC_PRESENT_INACTIVE); + return SLOTERROR_CMD_ABORTED; + } + } + + if (parameter & CHK_ACTIVE_STATE) + { + /* Commands Parameters ok, Check the Card Status */ + /* Card is detected */ + if (! SC_Detect()) + { + /* Check that from Lower Layers, the SmartCard come to known state */ + CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED,BM_ICC_PRESENT_INACTIVE); + return SLOTERROR_HW_ERROR; + } + } + + return 0; +} + +#endif // HAVE_USB_CLASS_CCID + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/sdk/usbd_ccid_if.c b/src/sdk/usbd_ccid_if.c old mode 100644 new mode 100755 similarity index 58% rename from sdk/usbd_ccid_if.c rename to src/sdk/usbd_ccid_if.c index fd07035..a709032 --- a/sdk/usbd_ccid_if.c +++ b/src/sdk/usbd_ccid_if.c @@ -25,27 +25,23 @@ ****************************************************************************** */ +#pragma message "Override SDK source file :" __FILE__ + /* Includes ------------------------------------------------------------------*/ +#include "os.h" #include "usbd_ccid_if.h" #ifdef HAVE_USB_CLASS_CCID +#if CCID_BULK_EPIN_SIZE > USB_SEGMENT_SIZE + #error configuration error, the USB MAX SEGMENT SIZE does not support the CCID endpoint (CCID_BULK_EPIN_SIZE vs USB_SEGMENT_SIZE) +#endif + /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ -uint8_t Ccid_BulkState; -uint8_t ccid_card_inserted; -uint8_t UsbIntMessageBuffer[INTR_MAX_PACKET_SIZE]; /* data buffer*/ -__IO uint8_t PrevXferComplete_IntrIn; -usb_ccid_param_t usb_ccid_param; - -uint8_t* pUsbMessageBuffer; -static uint32_t UsbMessageLength; -Ccid_SlotStatus_t Ccid_SlotStatus; -Protocol0_DataStructure_t Protocol0_DataStructure; - -Ccid_bulk_data_t Ccid_bulk_data; +usb_class_ccid_t G_io_ccid; /* Private function prototypes -----------------------------------------------*/ static void CCID_Response_SendData (USBD_HandleTypeDef *pdev, @@ -60,23 +56,17 @@ static void CCID_Response_SendData (USBD_HandleTypeDef *pdev, */ void CCID_Init (USBD_HandleTypeDef *pdev) { - memset(&Ccid_BulkState, 0, sizeof(Ccid_BulkState)); - memset(&UsbIntMessageBuffer, 0, sizeof(UsbIntMessageBuffer)); - memset(&PrevXferComplete_IntrIn, 0, sizeof(PrevXferComplete_IntrIn)); - memset(&usb_ccid_param, 0, sizeof(usb_ccid_param)); - memset(&pUsbMessageBuffer, 0, sizeof(pUsbMessageBuffer)); - memset(&UsbMessageLength, 0, sizeof(UsbMessageLength)); - memset(&Ccid_SlotStatus, 0, sizeof(Ccid_SlotStatus)); - memset(&Protocol0_DataStructure, 0, sizeof(Protocol0_DataStructure)); - memset(&Ccid_bulk_data, 0, sizeof(Ccid_bulk_data)); - ccid_card_inserted = 0; + memset(&G_io_ccid, 0, sizeof(G_io_ccid)); + /* CCID Related Initialization */ +#ifdef HAVE_CCID_INTERRUPT CCID_SetIntrTransferStatus(1); /* Transfer Complete Status */ +#endif // HAVE_CCID_INTERRUPT CCID_UpdSlotChange(1); SC_InitParams(); /* Prepare Out endpoint to receive 1st packet */ - Ccid_BulkState = CCID_STATE_IDLE; + G_io_ccid.Ccid_BulkState = CCID_STATE_IDLE; USBD_LL_PrepareReceive(pdev, CCID_BULK_OUT_EP, CCID_BULK_EPOUT_SIZE); // send the smartcard as inserted state at boot time @@ -92,7 +82,7 @@ void CCID_Init (USBD_HandleTypeDef *pdev) void CCID_DeInit (USBD_HandleTypeDef *pdev) { UNUSED(pdev); - Ccid_BulkState = CCID_STATE_IDLE; + G_io_ccid.Ccid_BulkState = CCID_STATE_IDLE; } /** @@ -110,43 +100,50 @@ void CCID_BulkMessage_In (USBD_HandleTypeDef *pdev, /*************** Handle Bulk Transfer IN data completion *****************/ - switch (Ccid_BulkState) + switch (G_io_ccid.Ccid_BulkState) { case CCID_STATE_SEND_RESP: { - unsigned int remLen = UsbMessageLength; + unsigned int remLen = G_io_ccid.UsbMessageLength; // advance with acknowledged sent chunk - pUsbMessageBuffer += MIN(CCID_BULK_EPIN_SIZE, UsbMessageLength); - UsbMessageLength -= MIN(CCID_BULK_EPIN_SIZE, UsbMessageLength); + if (G_io_ccid.pUsbMessageBuffer == &G_io_ccid.bulk_header) { + // first part of the bulk in sent. + // advance in the data buffer to transmit. (mixed source leap) + G_io_ccid.pUsbMessageBuffer = G_io_ccid_data_buffer+MIN(CCID_BULK_EPIN_SIZE, G_io_ccid.UsbMessageLength)-CCID_HEADER_SIZE; + } + else { + G_io_ccid.pUsbMessageBuffer += MIN(CCID_BULK_EPIN_SIZE, G_io_ccid.UsbMessageLength); + } + G_io_ccid.UsbMessageLength -= MIN(CCID_BULK_EPIN_SIZE, G_io_ccid.UsbMessageLength); // if remaining length is > EPIN_SIZE: send a filled bulk packet - if (UsbMessageLength >= CCID_BULK_EPIN_SIZE) { - CCID_Response_SendData(pdev, pUsbMessageBuffer, + if (G_io_ccid.UsbMessageLength >= CCID_BULK_EPIN_SIZE) { + CCID_Response_SendData(pdev, G_io_ccid.pUsbMessageBuffer, // use the header declared size packet must be well formed CCID_BULK_EPIN_SIZE); } // if remaining length is 0; send an empty packet and prepare to receive a new command - else if (UsbMessageLength == 0 && remLen == CCID_BULK_EPIN_SIZE) { - CCID_Response_SendData(pdev, pUsbMessageBuffer, + else if (G_io_ccid.UsbMessageLength == 0 && remLen == CCID_BULK_EPIN_SIZE) { + CCID_Response_SendData(pdev, G_io_ccid.pUsbMessageBuffer, // use the header declared size packet must be well formed 0); goto last_xfer; // won't wait ack to avoid missing a command } // else if no more data, then last packet sent, go back to idle (done on transfer ack) - else if (UsbMessageLength == 0) { // robustness only + else if (G_io_ccid.UsbMessageLength == 0) { // robustness only last_xfer: - Ccid_BulkState = CCID_STATE_IDLE; + G_io_ccid.Ccid_BulkState = CCID_STATE_IDLE; /* Prepare EP to Receive First Cmd */ - USBD_LL_PrepareReceive(pdev, CCID_BULK_OUT_EP, CCID_BULK_EPOUT_SIZE); + // not timeout compliant // USBD_LL_PrepareReceive(pdev, CCID_BULK_OUT_EP, CCID_BULK_EPOUT_SIZE); } // if remaining length is < EPIN_SIZE: send packet and prepare to receive a new command - else if (UsbMessageLength < CCID_BULK_EPIN_SIZE) { - CCID_Response_SendData(pdev, pUsbMessageBuffer, + else if (G_io_ccid.UsbMessageLength < CCID_BULK_EPIN_SIZE) { + CCID_Response_SendData(pdev, G_io_ccid.pUsbMessageBuffer, // use the header declared size packet must be well formed - UsbMessageLength); + G_io_ccid.UsbMessageLength); goto last_xfer; // won't wait ack to avoid missing a command } @@ -157,11 +154,35 @@ void CCID_BulkMessage_In (USBD_HandleTypeDef *pdev, break; } } +#ifdef HAVE_CCID_INTERRUPT else if (epnum == (CCID_INTR_IN_EP & 0x7F)) { /* Filter the epnum by masking with 0x7f (mask of IN Direction) */ CCID_SetIntrTransferStatus(1); /* Transfer Complete Status */ } +#endif // HAVE_CCID_INTERRUPT +} + +void CCID_Send_Reply(USBD_HandleTypeDef *pdev) { + /********** Decide for all commands ***************/ + if (G_io_ccid.Ccid_BulkState == CCID_STATE_SEND_RESP) + { + G_io_ccid.UsbMessageLength = G_io_ccid.bulk_header.bulkin.dwLength+CCID_HEADER_SIZE; /* Store for future use */ + + /* Expected Data Length Packet Received */ + G_io_ccid.pUsbMessageBuffer = (uint8_t*) &G_io_ccid.bulk_header; + + // send bulk header and first pat of the message at once + os_memmove(G_io_usb_ep_buffer, &G_io_ccid.bulk_header, CCID_HEADER_SIZE); + if (G_io_ccid.UsbMessageLength>CCID_HEADER_SIZE) { + // copy start of data if bigger size than a header + os_memmove(G_io_usb_ep_buffer+CCID_HEADER_SIZE, G_io_ccid_data_buffer, MIN(CCID_BULK_EPIN_SIZE, G_io_ccid.UsbMessageLength)-CCID_HEADER_SIZE); + } + // send the first mixed source chunk + CCID_Response_SendData(pdev, G_io_usb_ep_buffer, + // use the header declared size packet must be well formed + MIN(CCID_BULK_EPIN_SIZE, G_io_ccid.UsbMessageLength)); + } } /** @@ -174,123 +195,113 @@ void CCID_BulkMessage_In (USBD_HandleTypeDef *pdev, void CCID_BulkMessage_Out (USBD_HandleTypeDef *pdev, uint8_t epnum, uint8_t* buffer, uint16_t dataLen) { - - switch (Ccid_BulkState) - { - case CCID_STATE_IDLE: - if (dataLen == 0x00) - { /* Zero Length Packet Received */ - Ccid_BulkState = CCID_STATE_IDLE; - } - else if (dataLen >= CCID_MESSAGE_HEADER_SIZE) + if (epnum == (CCID_BULK_OUT_EP & 0x7F)) { + switch (G_io_ccid.Ccid_BulkState) { - UsbMessageLength = dataLen; /* Store for future use */ - - /* Expected Data Length Packet Received */ - pUsbMessageBuffer = (uint8_t*) &Ccid_bulk_data; - - /* Fill CCID_BulkOut Data Buffer from USB Buffer */ - memmove(pUsbMessageBuffer, buffer, dataLen); - - /* - Refer : 6 CCID Messages - The response messages always contain the exact same slot number, - and sequence number fields from the header that was contained in - the Bulk-OUT command message. - */ - Ccid_bulk_data.header.bulkin.bSlot = Ccid_bulk_data.header.bulkout.bSlot; - Ccid_bulk_data.header.bulkin.bSeq = Ccid_bulk_data.header.bulkout.bSeq; - - if (dataLen < CCID_BULK_EPOUT_SIZE) - {/* Short message, less than the EP Out Size, execute the command, - if parameter like dwLength is too big, the appropriate command will - give an error */ - CCID_CmdDecode(pdev); + + // after a timeout, could be in almost any state :) therefore, clean it and process the newly received command + default: + G_io_ccid.Ccid_BulkState = CCID_STATE_IDLE; + // no break is intentional + + case CCID_STATE_IDLE: + // prepare to receive another packet later on (to avoid troubles with timeout due to other hid command timeouting the ccid endpoint reply) + USBD_LL_PrepareReceive(pdev, CCID_BULK_OUT_EP, CCID_BULK_EPOUT_SIZE); + + if (dataLen == 0x00) + { /* Zero Length Packet Received, end of transfer */ + G_io_ccid.Ccid_BulkState = CCID_STATE_IDLE; } - else - { /* Long message, receive additional data with command */ - /* (u8dataLen == CCID_BULK_EPOUT_SIZE) */ + else if (dataLen >= CCID_HEADER_SIZE) + { + G_io_ccid.UsbMessageLength = dataLen; /* Store for future use */ - if (Ccid_bulk_data.header.bulkout.dwLength > ABDATA_SIZE) + /* Expected Data Length Packet Received */ + // endianness is little :) useful for our ARM convention + G_io_ccid.pUsbMessageBuffer = (uint8_t*) &G_io_ccid.bulk_header; + + // copy the ccid bulk header only + os_memmove(G_io_ccid.pUsbMessageBuffer, buffer, CCID_HEADER_SIZE); + // copy remaining part in the data buffer (split from the ccid to allow for overlaying with another ressource buffer) + if (dataLen>CCID_HEADER_SIZE) { + os_memmove(G_io_ccid_data_buffer, buffer+CCID_HEADER_SIZE, dataLen-CCID_HEADER_SIZE); + // we're now receiving in the data buffer (all subsequent calls) + G_io_ccid.pUsbMessageBuffer = G_io_ccid_data_buffer; + } + + if (G_io_ccid.bulk_header.bulkout.dwLength > IO_CCID_DATA_BUFFER_SIZE) { /* Check if length of data to be sent by host is > buffer size */ /* Too long data received.... Error ! */ - Ccid_BulkState = CCID_STATE_UNCORRECT_LENGTH; + G_io_ccid.Ccid_BulkState = CCID_STATE_UNCORRECT_LENGTH; } + else + // everything received in the first packet + if (G_io_ccid.UsbMessageLength == (G_io_ccid.bulk_header.bulkout.dwLength + CCID_HEADER_SIZE)) { + /* Short message, less than the EP Out Size, execute the command, + if parameter like dwLength is too big, the appropriate command will + give an error */ + CCID_CmdDecode(pdev); + } + else + { /* Long message, receive additional data with command */ + G_io_ccid.Ccid_BulkState = CCID_STATE_RECEIVE_DATA; + G_io_ccid.pUsbMessageBuffer += dataLen-CCID_HEADER_SIZE; /* Point to new offset */ + } + } + break; + + case CCID_STATE_RECEIVE_DATA: + + USBD_LL_PrepareReceive(pdev, CCID_BULK_OUT_EP, CCID_BULK_EPOUT_SIZE); + + G_io_ccid.UsbMessageLength += dataLen; + + if (dataLen < CCID_BULK_EPOUT_SIZE) + {/* Short message, less than the EP Out Size, execute the command, + if parameter like dwLength is too big, the appropriate command will + give an error */ - else - { /* Expect more data on OUT EP */ - Ccid_BulkState = CCID_STATE_RECEIVE_DATA; - pUsbMessageBuffer += dataLen; /* Point to new offset */ + /* Full command is received, process the Command */ + os_memmove(G_io_ccid.pUsbMessageBuffer, buffer, dataLen); + CCID_CmdDecode(pdev); + } + else //if (dataLen == CCID_BULK_EPOUT_SIZE) + { + if (G_io_ccid.UsbMessageLength < (G_io_ccid.bulk_header.bulkout.dwLength + CCID_HEADER_SIZE)) + { + os_memmove(G_io_ccid.pUsbMessageBuffer, buffer, dataLen); + G_io_ccid.pUsbMessageBuffer += dataLen; + /* Increment the pointer to receive more data */ /* Prepare EP to Receive next Cmd */ - USBD_LL_PrepareReceive(pdev, CCID_BULK_OUT_EP, CCID_BULK_EPOUT_SIZE); - - } /* if (dataLen == CCID_BULK_EPOUT_SIZE) ends */ - } /* if (dataLen >= CCID_BULK_EPOUT_SIZE) ends */ - } /* if (dataLen >= CCID_MESSAGE_HEADER_SIZE) ends */ - break; - - case CCID_STATE_RECEIVE_DATA: - - UsbMessageLength += dataLen; - - if (dataLen < CCID_BULK_EPOUT_SIZE) - {/* Short message, less than the EP Out Size, execute the command, - if parameter like dwLength is too big, the appropriate command will - give an error */ + // not timeout compliant // USBD_LL_PrepareReceive(pdev, CCID_BULK_OUT_EP, CCID_BULK_EPOUT_SIZE); + } + else if (G_io_ccid.UsbMessageLength == (G_io_ccid.bulk_header.bulkout.dwLength + CCID_HEADER_SIZE)) + { + /* Full command is received, process the Command */ + os_memmove(G_io_ccid.pUsbMessageBuffer, buffer, dataLen); + CCID_CmdDecode(pdev); + } + else + { + /* Too long data received.... Error ! */ + G_io_ccid.Ccid_BulkState = CCID_STATE_UNCORRECT_LENGTH; + } + } - /* Full command is received, process the Command */ - memmove(pUsbMessageBuffer, buffer, dataLen); - CCID_CmdDecode(pdev); - } - else if (dataLen == CCID_BULK_EPOUT_SIZE) - { - if (UsbMessageLength < (Ccid_bulk_data.header.bulkout.dwLength + CCID_CMD_HEADER_SIZE)) - { - memmove(pUsbMessageBuffer, buffer, dataLen); - pUsbMessageBuffer += dataLen; - /* Increment the pointer to receive more data */ - - /* Prepare EP to Receive next Cmd */ - USBD_LL_PrepareReceive(pdev, CCID_BULK_OUT_EP, CCID_BULK_EPOUT_SIZE); - } - else if (UsbMessageLength == (Ccid_bulk_data.header.bulkout.dwLength + CCID_CMD_HEADER_SIZE)) - { - /* Full command is received, process the Command */ - memmove(pUsbMessageBuffer, buffer, dataLen); - CCID_CmdDecode(pdev); - } - else - { - /* Too long data received.... Error ! */ - Ccid_BulkState = CCID_STATE_UNCORRECT_LENGTH; - } - } + break; - break; - - case CCID_STATE_UNCORRECT_LENGTH: - Ccid_BulkState = CCID_STATE_IDLE; - break; - - default: - break; - } -} + /* + case CCID_STATE_UNCORRECT_LENGTH: + G_io_ccid.Ccid_BulkState = CCID_STATE_IDLE; + break; + + default: -void CCID_Send_Reply(USBD_HandleTypeDef *pdev) { - /********** Decide for all commands ***************/ - if (Ccid_BulkState == CCID_STATE_SEND_RESP) - { - UsbMessageLength = Ccid_bulk_data.header.bulkin.dwLength+CCID_MESSAGE_HEADER_SIZE; /* Store for future use */ - - /* Expected Data Length Packet Received */ - pUsbMessageBuffer = (uint8_t*) &Ccid_bulk_data; - - CCID_Response_SendData(pdev, pUsbMessageBuffer, - // use the header declared size packet must be well formed - MIN(CCID_BULK_EPIN_SIZE, UsbMessageLength)); + break; + */ + } } } @@ -304,7 +315,7 @@ void CCID_CmdDecode(USBD_HandleTypeDef *pdev) { uint8_t errorCode; - switch (Ccid_bulk_data.header.bulkout.bMessageType) + switch (G_io_ccid.bulk_header.bulkout.bMessageType) { case PC_TO_RDR_ICCPOWERON: errorCode = PC_to_RDR_IccPowerOn(); @@ -380,7 +391,7 @@ void CCID_CmdDecode(USBD_HandleTypeDef *pdev) void Transfer_Data_Request(void) { /********** Update Global Variables ***************/ - Ccid_BulkState = CCID_STATE_SEND_RESP; + G_io_ccid.Ccid_BulkState = CCID_STATE_SEND_RESP; } @@ -396,6 +407,7 @@ static void CCID_Response_SendData(USBD_HandleTypeDef *pdev, uint8_t* buf, uint16_t len) { + UNUSED(pdev); // don't ask the MCU to perform bulk split, we could quickly get into a buffer overflow if (len > CCID_BULK_EPIN_SIZE) { THROW(EXCEPTION_IO_OVERFLOW); @@ -411,6 +423,7 @@ static void CCID_Response_SendData(USBD_HandleTypeDef *pdev, io_seproxyhal_spi_send(buf, len); } +#ifdef HAVE_CCID_INTERRUPT /** * @brief CCID_IntMessage * Send the Interrupt-IN data to the host @@ -419,11 +432,14 @@ static void CCID_Response_SendData(USBD_HandleTypeDef *pdev, */ void CCID_IntMessage(USBD_HandleTypeDef *pdev) { + UNUSED(pdev); /* Check if there us change in Smartcard Slot status */ if ( CCID_IsSlotStatusChange() && CCID_IsIntrTransferComplete() ) { +#ifdef HAVE_CCID_INTERRUPT /* Check Slot Status is changed. Card is Removed/ Fitted */ RDR_to_PC_NotifySlotChange(); +#endif // HAVE_CCID_INTERRUPT CCID_SetIntrTransferStatus(0); /* Reset the Status */ CCID_UpdSlotChange(0); /* Reset the Status of Slot Change */ @@ -435,7 +451,7 @@ void CCID_IntMessage(USBD_HandleTypeDef *pdev) G_io_seproxyhal_spi_buffer[4] = SEPROXYHAL_TAG_USB_EP_PREPARE_DIR_IN; G_io_seproxyhal_spi_buffer[5] = 2; io_seproxyhal_spi_send(G_io_seproxyhal_spi_buffer, 6); - io_seproxyhal_spi_send(UsbIntMessageBuffer, 2); + io_seproxyhal_spi_send(G_io_ccid.UsbIntMessageBuffer, 2); } } @@ -447,7 +463,7 @@ void CCID_IntMessage(USBD_HandleTypeDef *pdev) */ uint8_t CCID_IsIntrTransferComplete (void) { - return PrevXferComplete_IntrIn; + return G_io_ccid.PrevXferComplete_IntrIn; } /** @@ -458,8 +474,9 @@ uint8_t CCID_IsIntrTransferComplete (void) */ void CCID_SetIntrTransferStatus (uint8_t xfer_Status) { - PrevXferComplete_IntrIn = xfer_Status; + G_io_ccid.PrevXferComplete_IntrIn = xfer_Status; } +#endif // HAVE_CCID_INTERRUPT @@ -467,12 +484,7 @@ void CCID_SetIntrTransferStatus (uint8_t xfer_Status) uint8_t SC_Detect(void) { - return ccid_card_inserted; -} - -void SC_Poweroff(void) { - // nothing to do - + return G_io_ccid.ccid_card_inserted; } void SC_InitParams (void) { @@ -480,78 +492,89 @@ void SC_InitParams (void) { } uint8_t SC_SetParams (Protocol0_DataStructure_t* pt0) { + UNUSED(pt0); return SLOT_NO_ERROR; } -uint8_t SC_ExecuteEscape (uint8_t* escapePtr, uint32_t escapeLen, - uint8_t* responseBuff, - uint16_t* responseLen) { - io_seproxyhal_se_reset(); -} + uint8_t SC_SetClock (uint8_t bClockCommand) { + UNUSED(bClockCommand); return SLOT_NO_ERROR; } + uint8_t SC_Request_GetClockFrequencies(uint8_t* pbuf, uint16_t* len); uint8_t SC_Request_GetDataRates(uint8_t* pbuf, uint16_t* len); uint8_t SC_T0Apdu(uint8_t bmChanges, uint8_t bClassGetResponse, uint8_t bClassEnvelope) { + UNUSED(bmChanges); + UNUSED(bClassGetResponse); + UNUSED(bClassEnvelope); return SLOTERROR_CMD_NOT_SUPPORTED; } uint8_t SC_Mechanical(uint8_t bFunction) { + UNUSED(bFunction); return SLOTERROR_CMD_NOT_SUPPORTED; } uint8_t SC_SetDataRateAndClockFrequency(uint32_t dwClockFrequency, uint32_t dwDataRate) { + UNUSED(dwClockFrequency); + UNUSED(dwDataRate); return SLOT_NO_ERROR; } - uint8_t SC_Secure(uint32_t dwLength, uint8_t bBWI, uint16_t wLevelParameter, uint8_t* pbuf, uint32_t* returnLen ) { + UNUSED(bBWI); + UNUSED(wLevelParameter); + UNUSED(returnLen); // return SLOTERROR_CMD_NOT_SUPPORTED; uint16_t ret_len,off; - switch(pbuf[0]) { - case 0: // verify pin - //ret_len = dwLength - 15; - ret_len = 5; - os_memmove(G_io_apdu_buffer, pbuf+15, 5); + switch(pbuf[0]) + { + case 0: // verify pin + off = 15; + //ret_len = dwLength - 15; + ret_len = 5; + break; + case 1: // modify pin + switch(pbuf[11]) + { + case 3: + off = 20; break; - - case 1: // modify pin - switch(pbuf[11]) { - case 3: - off = 20; - break; - case 2: - case 1: - off = 19; - break; - default: - off = 18; - break; - } - //ret_len = dwLength - off; - ret_len = 5; - os_memmove(G_io_apdu_buffer, pbuf+off, 5); + case 2: + case 1: + off = 19; break; - - default: // unsupported - Ccid_bulk_data.header.bulkin.dwLength = 0; - RDR_to_PC_DataBlock(SLOTERROR_CMD_NOT_SUPPORTED); - CCID_Send_Reply(&USBD_Device); - return SLOTERROR_CMD_NOT_SUPPORTED; + // 0 and 4-0xFF + default: + off = 18; + break; + } + //ret_len = dwLength - off; + ret_len = 5; + break; + default: // unsupported + G_io_ccid.bulk_header.bulkin.dwLength = 0; + RDR_to_PC_DataBlock(SLOTERROR_CMD_NOT_SUPPORTED); + CCID_Send_Reply(&USBD_Device); + return SLOTERROR_CMD_NOT_SUPPORTED; } - return SC_XferBlock(G_io_apdu_buffer, ret_len, &ret_len); + pbuf += off; + pbuf[0] = 0xEF; + return SC_XferBlock(pbuf, ret_len, &ret_len); } // prepare the apdu to be processed by the application uint8_t SC_XferBlock (uint8_t* ptrBlock, uint32_t blockLen, uint16_t* expectedLen) { + UNUSED(expectedLen); + // check for overflow if (blockLen > IO_APDU_BUFFER_SIZE) { return SLOTERROR_BAD_LENTGH; } - // copy received apdu - memmove(G_io_apdu_buffer, ptrBlock, blockLen); + // copy received apdu // if G_io_ccid_data_buffer is the buffer apdu, then the memmove will do nothing + os_memmove(G_io_apdu_buffer, ptrBlock, blockLen); G_io_apdu_length = blockLen; G_io_apdu_media = IO_APDU_MEDIA_USB_CCID; // for application code G_io_apdu_state = APDU_USB_CCID; // for next call to io_exchange @@ -561,23 +584,26 @@ uint8_t SC_XferBlock (uint8_t* ptrBlock, uint32_t blockLen, uint16_t* expectedLe void io_usb_ccid_reply(unsigned char* buffer, unsigned short length) { // avoid memory overflow - if (length > sizeof(Ccid_bulk_data.abData)) { + if (length > IO_CCID_DATA_BUFFER_SIZE) { THROW(EXCEPTION_IO_OVERFLOW); } // copy the responde apdu - memmove(Ccid_bulk_data.abData, buffer, length); - Ccid_bulk_data.header.bulkin.dwLength = length; + os_memmove(G_io_ccid_data_buffer, buffer, length); + G_io_ccid.bulk_header.bulkin.dwLength = length; // forge reply RDR_to_PC_DataBlock(SLOT_NO_ERROR); // start sending rpely CCID_Send_Reply(&USBD_Device); } + // ask for power on void io_usb_ccid_set_card_inserted(unsigned int inserted) { - ccid_card_inserted = inserted; + G_io_ccid.ccid_card_inserted = inserted; CCID_UpdSlotChange(1); +#ifdef HAVE_CCID_INTERRUPT CCID_IntMessage(&USBD_Device); +#endif // HAVE_CCID_INTERRUPT } @@ -585,6 +611,7 @@ void io_usb_ccid_set_card_inserted(unsigned int inserted) { + #endif // HAVE_USB_CLASS_CCID /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/src/sdk/usbd_ccid_impl.h b/src/sdk/usbd_ccid_impl.h new file mode 100644 index 0000000..eb8dfe0 --- /dev/null +++ b/src/sdk/usbd_ccid_impl.h @@ -0,0 +1,32 @@ +#ifndef USBD_CCID_IMPL_H +#define USBD_CCID_IMPL_H + +#ifdef HAVE_USB_CLASS_CCID + +// ================================================ +// CCID + +#define TPDU_EXCHANGE 0x01 +#define SHORT_APDU_EXCHANGE 0x02 +#define EXTENDED_APDU_EXCHANGE 0x04 +#define CHARACTER_EXCHANGE 0x00 + +#define EXCHANGE_LEVEL_FEATURE SHORT_APDU_EXCHANGE + +#define CCID_INTF 2 +#define CCID_BULK_IN_EP 0x83 +#define CCID_BULK_EPIN_SIZE 64 +#define CCID_BULK_OUT_EP 0x03 +#define CCID_BULK_EPOUT_SIZE 64 + +#ifdef HAVE_CCID_INTERRUPT +#define CCID_INTR_IN_EP 0x84 +#define CCID_INTR_EPIN_SIZE 16 +#endif // HAVE_CCID_INTERRUPT + +#define IO_CCID_DATA_BUFFER_SIZE IO_APDU_BUFFER_SIZE +#define G_io_ccid_data_buffer G_io_apdu_buffer + +#endif // HAVE_USB_CLASS_CCID + +#endif // USBD_CCID_IMPL_H diff --git a/src/sdk/usbd_hid_impl.h b/src/sdk/usbd_hid_impl.h new file mode 100644 index 0000000..8d69655 --- /dev/null +++ b/src/sdk/usbd_hid_impl.h @@ -0,0 +1,29 @@ +#ifndef USBD_HID_IMPL_H +#define USBD_HID_IMPL_H + +// ================================================ +// HIDGEN + +#define HID_INTF 0 + +#define HID_EPIN_ADDR 0x82 +#define HID_EPIN_SIZE 0x40 + +#define HID_EPOUT_ADDR 0x02 +#define HID_EPOUT_SIZE 0x40 + +#ifdef HAVE_IO_U2F +// ================================================ +// HID U2F + +#define U2F_INTF 1 + +#define U2F_EPIN_ADDR 0x81 +#define U2F_EPIN_SIZE 0x40 + +#define U2F_EPOUT_ADDR 0x01 +#define U2F_EPOUT_SIZE 0x40 +#endif // HAVE_IO_U2F + +#endif // USBD_HID_IMPL_H + diff --git a/src/sdk/usbd_impl.c b/src/sdk/usbd_impl.c new file mode 100644 index 0000000..9c82900 --- /dev/null +++ b/src/sdk/usbd_impl.c @@ -0,0 +1,916 @@ +/** + ****************************************************************************** + * @file usbd_hid.c + * @author MCD Application Team + * @version V2.2.0 + * @date 13-June-2014 + * @brief This file provides the HID core functions. + * + * @verbatim + * + * =================================================================== + * HID Class Description + * =================================================================== + * This module manages the HID class V1.11 following the "Device Class Definition + * for Human Interface Devices (HID) Version 1.11 Jun 27, 2001". + * This driver implements the following aspects of the specification: + * - The Boot Interface Subclass + * - Usage Page : Generic Desktop + * - Usage : Vendor + * - Collection : Application + * + * @note In HS mode and when the DMA is used, all variables and data structures + * dealing with the DMA during the transaction process should be 32-bit aligned. + * + * + * @endverbatim + * + ****************************************************************************** + * @attention + * + *

© COPYRIGHT 2014 STMicroelectronics

+ * + * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.st.com/software_license_agreement_liberty_v2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ +#pragma message "Override SDK source file :" __FILE__ + +#include "os.h" + + +/* Includes ------------------------------------------------------------------*/ + +#include "usbd_hid.h" +#include "usbd_hid_impl.h" + +#include "usbd_ctlreq.h" + +#include "usbd_core.h" +#include "usbd_conf.h" + +#include "usbd_def.h" +#include "os_io_seproxyhal.h" + +#ifdef HAVE_IO_U2F +#include "u2f_transport.h" +#include "u2f_impl.h" +#endif // HAVE_IO_U2F + +#ifdef HAVE_USB_CLASS_CCID +#include "usbd_ccid_core.h" +#endif // HAVE_USB_CLASS_CCID + + +/** @addtogroup STM32_USB_DEVICE_LIBRARY + * @{ + */ + + +/** @defgroup USBD_HID + * @brief usbd core module + * @{ + */ + +/** @defgroup USBD_HID_Private_TypesDefinitions + * @{ + */ +/** + * @} + */ + + +/** @defgroup USBD_HID_Private_Defines + * @{ + */ + +/** + * @} + */ + + +/** @defgroup USBD_HID_Private_Macros + * @{ + */ +/** + * @} + */ +/** @defgroup USBD_HID_Private_FunctionPrototypes + * @{ + */ + + +/** + * @} + */ + +/** @defgroup USBD_HID_Private_Variables + * @{ + */ + +#define HID_EPIN_ADDR 0x82 +#define HID_EPIN_SIZE 0x40 + +#define HID_EPOUT_ADDR 0x02 +#define HID_EPOUT_SIZE 0x40 + +#define USBD_LANGID_STRING 0x409 + +#ifdef HAVE_VID_PID_PROBER +#define USBD_VID 0x2581 +#define USBD_PID 0xf1d1 +#else +#define USBD_VID 0x2C97 +#if defined(TARGET_BLUE) // blue +#define USBD_PID 0x0000 +static const uint8_t const USBD_PRODUCT_FS_STRING[] = { + 4*2+2, + USB_DESC_TYPE_STRING, + 'B', 0, + 'l', 0, + 'u', 0, + 'e', 0, +}; + +#elif defined(TARGET_NANOS) // nano s +#define USBD_PID 0x0001 +static const uint8_t const USBD_PRODUCT_FS_STRING[] = { + 6*2+2, + USB_DESC_TYPE_STRING, + 'N', 0, + 'a', 0, + 'n', 0, + 'o', 0, + ' ', 0, + 'S', 0, +}; +#elif defined(TARGET_ARAMIS) // aramis +#define USBD_PID 0x0002 +static const uint8_t const USBD_PRODUCT_FS_STRING[] = { + 6*2+2, + USB_DESC_TYPE_STRING, + 'A', 0, + 'r', 0, + 'a', 0, + 'm', 0, + 'i', 0, + 's', 0, +}; +#else +#error unknown TARGET_ID +#endif +#endif + +/* USB Standard Device Descriptor */ +static const uint8_t const USBD_LangIDDesc[]= +{ + USB_LEN_LANGID_STR_DESC, + USB_DESC_TYPE_STRING, + LOBYTE(USBD_LANGID_STRING), + HIBYTE(USBD_LANGID_STRING), +}; + +static const uint8_t const USB_SERIAL_STRING[] = +{ + 4*2+2, + USB_DESC_TYPE_STRING, + '0', 0, + '0', 0, + '0', 0, + '1', 0, +}; + +static const uint8_t const USBD_MANUFACTURER_STRING[] = { + 6*2+2, + USB_DESC_TYPE_STRING, + 'L', 0, + 'e', 0, + 'd', 0, + 'g', 0, + 'e', 0, + 'r', 0, +}; + +#define USBD_INTERFACE_FS_STRING USBD_PRODUCT_FS_STRING +#define USBD_CONFIGURATION_FS_STRING USBD_PRODUCT_FS_STRING + +static const uint8_t const HID_ReportDesc[] = { + 0x06, 0xA0, 0xFF, // Usage page (vendor defined) + 0x09, 0x01, // Usage ID (vendor defined) + 0xA1, 0x01, // Collection (application) + + // The Input report + 0x09, 0x03, // Usage ID - vendor defined + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8 bits) + 0x95, HID_EPIN_SIZE, // Report Count (64 fields) + 0x81, 0x08, // Input (Data, Variable, Absolute) + + // The Output report + 0x09, 0x04, // Usage ID - vendor defined + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8 bits) + 0x95, HID_EPOUT_SIZE, // Report Count (64 fields) + 0x91, 0x08, // Output (Data, Variable, Absolute) + 0xC0 +}; + +#ifdef HAVE_IO_U2F +static const uint8_t const HID_ReportDesc_fido[] = { + 0x06, 0xD0, 0xF1, // Usage page (vendor defined) + 0x09, 0x01, // Usage ID (vendor defined) + 0xA1, 0x01, // Collection (application) + + // The Input report + 0x09, 0x03, // Usage ID - vendor defined + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8 bits) + 0x95, U2F_EPIN_SIZE, // Report Count (64 fields) + 0x81, 0x08, // Input (Data, Variable, Absolute) + + // The Output report + 0x09, 0x04, // Usage ID - vendor defined + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8 bits) + 0x95, U2F_EPOUT_SIZE, // Report Count (64 fields) + 0x91, 0x08, // Output (Data, Variable, Absolute) + 0xC0 +}; +#endif // HAVE_IO_U2F + +#define ARRAY_U2LE(l) (l)&0xFF, (l)>>8 + +/* USB HID device Configuration Descriptor */ +static __ALIGN_BEGIN const uint8_t const N_USBD_CfgDesc[] __ALIGN_END = +{ + 0x09, /* bLength: Configuration Descriptor size */ + USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */ + ARRAY_U2LE(0x9 /* wTotalLength: Bytes returned */ + +0x9+0x9+0x7+0x7 +#ifdef HAVE_IO_U2F + +0x9+0x9+0x7+0x7 +#endif // HAVE_IO_U2F +#ifdef HAVE_USB_CLASS_CCID + +0x9+0x36+0x7+0x7 +#endif // HAVE_USB_CLASS_CCID + ), + 1 +#ifdef HAVE_IO_U2F + +1 +#endif // HAVE_IO_U2F +#ifdef HAVE_USB_CLASS_CCID + +1 +#endif // HAVE_USB_CLASS_CCID + , /*bNumInterfaces */ + 0x01, /*bConfigurationValue: Configuration value*/ + USBD_IDX_PRODUCT_STR, /*iConfiguration: Index of string descriptor describing the configuration*/ + 0xC0, /*bmAttributes: bus powered */ + 0x32, /*MaxPower 100 mA: this current is used for detecting Vbus*/ + + /* HIDGEN ################################################################################################ */ + + /************** Descriptor of KBD HID interface ****************/ + 0x09, /*bLength: Interface Descriptor size*/ + USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/ + HID_INTF, /*bInterfaceNumber: Number of Interface*/ + 0x00, /*bAlternateSetting: Alternate setting*/ + 0x02, /*bNumEndpoints*/ + 0x03, /*bInterfaceClass: HID*/ + 0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/ + 0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/ + USBD_IDX_PRODUCT_STR, /*iInterface: Index of string descriptor*/ + + /******************** Descriptor of HID *************************/ + 0x09, /*bLength: HID Descriptor size*/ + HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/ + 0x11, /*bHIDUSTOM_HID: HID Class Spec release number*/ + 0x01, + 0x00, /*bCountryCode: Hardware target country*/ + 0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/ + 0x22, /*bDescriptorType*/ + sizeof(HID_ReportDesc),/*wItemLength: Total length of Report descriptor*/ + 0x00, + + /******************** Descriptor of Custom HID endpoints ********************/ + 0x07, /*bLength: Endpoint Descriptor size*/ + USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/ + HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/ + 0x03, /*bmAttributes: Interrupt endpoint*/ + HID_EPIN_SIZE, /*wMaxPacketSize: 2 Byte max */ + 0x00, + 0x01, /*bInterval: Polling Interval (20 ms)*/ + + 0x07, /* bLength: Endpoint Descriptor size */ + USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: */ + HID_EPOUT_ADDR, /*bEndpointAddress: Endpoint Address (OUT)*/ + 0x03, /* bmAttributes: Interrupt endpoint */ + HID_EPOUT_SIZE, /* wMaxPacketSize: 2 Bytes max */ + 0x00, + 0x01, /* bInterval: Polling Interval (20 ms) */ + +#ifdef HAVE_IO_U2F + /* HID FIDO ################################################################################################ */ + + /************** Descriptor of HID FIDO interface ****************/ + 0x09, /*bLength: Interface Descriptor size*/ + USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/ + U2F_INTF, /*bInterfaceNumber: Number of Interface*/ + 0x00, /*bAlternateSetting: Alternate setting*/ + 0x02, /*bNumEndpoints*/ + 0x03, /*bInterfaceClass: HID*/ + 0x01, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/ + 0x01, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/ + USBD_IDX_PRODUCT_STR, /*iInterface: Index of string descriptor*/ + + /******************** Descriptor of HID *************************/ + 0x09, /*bLength: HID Descriptor size*/ + HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/ + 0x11, /*bHIDUSTOM_HID: HID Class Spec release number*/ + 0x01, + 0x21, /*bCountryCode: Hardware target country*/ // 0x21: US, 0x08: FR, 0x0D: ISO Intl + 0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/ + 0x22, /*bDescriptorType*/ + sizeof(HID_ReportDesc_fido),/*wItemLength: Total length of Report descriptor*/ + 0x00, + /******************** Descriptor of Custom HID endpoints ********************/ + 0x07, /*bLength: Endpoint Descriptor size*/ + USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/ + U2F_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/ + 0x03, /*bmAttributes: Interrupt endpoint*/ + U2F_EPIN_SIZE, /*wMaxPacketSize: */ + 0x00, + 0x01, /*bInterval: Polling Interval */ + + 0x07, /* bLength: Endpoint Descriptor size */ + USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: */ + U2F_EPOUT_ADDR, /*bEndpointAddress: Endpoint Address (OUT)*/ + 0x03, /* bmAttributes: Interrupt endpoint */ + U2F_EPOUT_SIZE, /* wMaxPacketSize: */ + 0x00, + 0x01,/* bInterval: Polling Interval */ +#endif // HAVE_IO_U2F + +#ifdef HAVE_USB_CLASS_CCID + /* CCID ################################################################################################ */ + + /******************** CCID **** interface ********************/ + 0x09, /* bLength: Interface Descriptor size */ + 0x04, /* bDescriptorType: */ + CCID_INTF, /* bInterfaceNumber: Number of Interface */ + 0x00, /* bAlternateSetting: Alternate setting */ + 0x02, /* bNumEndpoints: endpoints used */ + 0x0B, /* bInterfaceClass: user's interface for CCID */ + 0x00, /* bInterfaceSubClass : */ + 0x00, /* nInterfaceProtocol : None */ + 0x05, /* iInterface: */ + + /******************* CCID class descriptor ********************/ + 0x36, /* bLength: CCID Descriptor size */ + 0x21, /* bDescriptorType: Functional Descriptor type. */ + 0x10, /* bcdCCID(LSB): CCID Class Spec release number (1.00) */ + 0x01, /* bcdCCID(MSB) */ + + 0x00, /* bMaxSlotIndex :highest available slot on this device */ + 0x03, /* bVoltageSupport: bit Wise OR for 01h-5.0V 02h-3.0V + 04h 1.8V*/ + + 0x01,0x00,0x00,0x00, /* dwProtocols: 0001h = Protocol T=0 */ + 0x10,0x0E,0x00,0x00, /* dwDefaultClock: 3.6Mhz = 3600kHz = 0x0E10, + for 4 Mhz the value is (0x00000FA0) : + This is used in ETU and waiting time calculations*/ + 0x10,0x0E,0x00,0x00, /* dwMaximumClock: Maximum supported ICC clock frequency + in KHz. So, 3.6Mhz = 3600kHz = 0x0E10, + 4 Mhz (0x00000FA0) : */ + 0x00, /* bNumClockSupported : no setting from PC + If the value is 00h, the + supported clock frequencies are assumed to be the + default clock frequency defined by dwDefaultClock + and the maximum clock frequency defined by + dwMaximumClock */ + + 0xCD,0x25,0x00,0x00, /* dwDataRate: Default ICC I/O data rate in bps + 9677 bps = 0x25CD + for example 10752 bps (0x00002A00) */ + + 0xCD,0x25,0x00,0x00, /* dwMaxDataRate: Maximum supported ICC I/O data + rate in bps */ + 0x00, /* bNumDataRatesSupported : + The number of data rates that are supported by the CCID + If the value is 00h, all data rates between the default + data rate dwDataRate and the maximum data rate + dwMaxDataRate are supported. + Dont support GET_CLOCK_FREQUENCIES + */ + //46 + 0x00,0x00,0x00,0x00, /* dwMaxIFSD: 0 (T=0 only) */ + 0x00,0x00,0x00,0x00, /* dwSynchProtocols */ + 0x00,0x00,0x00,0x00, /* dwMechanical: no special characteristics */ + + 0xBA, 0x06, 0x02, 0x00, + //0x38,0x00,EXCHANGE_LEVEL_FEATURE,0x00, + /* dwFeatures: clk, baud rate, voltage : automatic */ + /* 00000008h Automatic ICC voltage selection + 00000010h Automatic ICC clock frequency change + 00000020h Automatic baud rate change according to + active parameters provided by the Host or self + determined 00000100h CCID can set + ICC in clock stop mode + + Only one of the following values may be present to + select a level of exchange: + 00010000h TPDU level exchanges with CCID + 00020000h Short APDU level exchange with CCID + 00040000h Short and Extended APDU level exchange + If none of those values : character level of exchange*/ + 0x0F,0x01,0x00,0x00, /* dwMaxCCIDMessageLength: Maximum block size + header*/ + /* 261 + 10 */ + + 0x00, /* bClassGetResponse*/ + 0x00, /* bClassEnvelope */ + 0x00,0x00, /* wLcdLayout : 0000h no LCD. */ + 0x00, /* bPINSupport : no PIN verif and modif */ + 0x01, /* bMaxCCIDBusySlots */ + + /******************** CCID Endpoints ********************/ + 0x07, /*Endpoint descriptor length = 7*/ + 0x05, /*Endpoint descriptor type */ + CCID_BULK_IN_EP, /*Endpoint address (IN, address 1) */ + 0x02, /*Bulk endpoint type */ + LOBYTE(CCID_BULK_EPIN_SIZE), + HIBYTE(CCID_BULK_EPIN_SIZE), + 0x00, /*Polling interval in milliseconds */ + + 0x07, /*Endpoint descriptor length = 7 */ + 0x05, /*Endpoint descriptor type */ + CCID_BULK_OUT_EP, /*Endpoint address (OUT, address 1) */ + 0x02, /*Bulk endpoint type */ + LOBYTE(CCID_BULK_EPOUT_SIZE), + HIBYTE(CCID_BULK_EPOUT_SIZE), + 0x00, /*Polling interval in milliseconds*/ +#endif // HAVE_USB_CLASS_CCID +} ; + +#ifdef HAVE_IO_U2F +/* USB HID device Configuration Descriptor */ +__ALIGN_BEGIN const uint8_t const USBD_HID_Desc_fido[] __ALIGN_END = +{ + /******************** Descriptor of HID *************************/ + 0x09, /*bLength: HID Descriptor size*/ + HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/ + 0x11, /*bHIDUSTOM_HID: HID Class Spec release number*/ + 0x01, + 0x21, /*bCountryCode: Hardware target country*/ // 0x21: US, 0x08: FR, 0x0D: ISO Intl + 0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/ + 0x22, /*bDescriptorType*/ + sizeof(HID_ReportDesc_fido),/*wItemLength: Total length of Report descriptor*/ + 0x00, +}; +#endif // HAVE_IO_U2F + +/* USB HID device Configuration Descriptor */ +__ALIGN_BEGIN const uint8_t const USBD_HID_Desc[] __ALIGN_END = +{ + /* 18 */ + 0x09, /*bLength: HID Descriptor size*/ + HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/ + 0x11, /*bHIDUSTOM_HID: HID Class Spec release number*/ + 0x01, + 0x00, /*bCountryCode: Hardware target country*/ + 0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/ + 0x22, /*bDescriptorType*/ + sizeof(HID_ReportDesc),/*wItemLength: Total length of Report descriptor*/ + 0x00, +}; + +/* USB Standard Device Descriptor */ +static __ALIGN_BEGIN const uint8_t const USBD_DeviceQualifierDesc[] __ALIGN_END = +{ + USB_LEN_DEV_QUALIFIER_DESC, + USB_DESC_TYPE_DEVICE_QUALIFIER, + 0x00, + 0x02, + 0x00, + 0x00, + 0x00, + 0x40, + 0x01, + 0x00, +}; + +/* USB Standard Device Descriptor */ +static const uint8_t const USBD_DeviceDesc[]= { + 0x12, /* bLength */ + USB_DESC_TYPE_DEVICE, /* bDescriptorType */ + 0x00, /* bcdUSB */ + 0x02, + 0x00, /* bDeviceClass */ + 0x00, /* bDeviceSubClass */ + 0x00, /* bDeviceProtocol */ + USB_MAX_EP0_SIZE, /* bMaxPacketSize */ + LOBYTE(USBD_VID), /* idVendor */ + HIBYTE(USBD_VID), /* idVendor */ + LOBYTE(USBD_PID), /* idVendor */ + HIBYTE(USBD_PID), /* idVendor */ + 0x00, /* bcdDevice rel. 2.00 */ + 0x02, + USBD_IDX_MFC_STR, /* Index of manufacturer string */ + USBD_IDX_PRODUCT_STR, /* Index of product string */ + USBD_IDX_SERIAL_STR, /* Index of serial number string */ + 1 /* bNumConfigurations */ +}; /* USB_DeviceDescriptor */ + + +/** + * @brief Returns the device descriptor. + * @param speed: Current device speed + * @param length: Pointer to data length variable + * @retval Pointer to descriptor buffer + */ +uint8_t *USBD_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) +{ + UNUSED(speed); + *length = sizeof(USBD_DeviceDesc); + return (uint8_t*)USBD_DeviceDesc; +} + +/** + * @brief Returns the LangID string descriptor. + * @param speed: Current device speed + * @param length: Pointer to data length variable + * @retval Pointer to descriptor buffer + */ +uint8_t *USBD_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) +{ + UNUSED(speed); + *length = sizeof(USBD_LangIDDesc); + return (uint8_t*)USBD_LangIDDesc; +} + +/** + * @brief Returns the product string descriptor. + * @param speed: Current device speed + * @param length: Pointer to data length variable + * @retval Pointer to descriptor buffer + */ +uint8_t *USBD_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) +{ + UNUSED(speed); + *length = sizeof(USBD_PRODUCT_FS_STRING); + return (uint8_t*)USBD_PRODUCT_FS_STRING; +} + +/** + * @brief Returns the manufacturer string descriptor. + * @param speed: Current device speed + * @param length: Pointer to data length variable + * @retval Pointer to descriptor buffer + */ +uint8_t *USBD_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) +{ + UNUSED(speed); + *length = sizeof(USBD_MANUFACTURER_STRING); + return (uint8_t*)USBD_MANUFACTURER_STRING; +} + +/** + * @brief Returns the serial number string descriptor. + * @param speed: Current device speed + * @param length: Pointer to data length variable + * @retval Pointer to descriptor buffer + */ +uint8_t *USBD_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) +{ + UNUSED(speed); + *length = sizeof(USB_SERIAL_STRING); + return (uint8_t*)USB_SERIAL_STRING; +} + +/** + * @brief Returns the configuration string descriptor. + * @param speed: Current device speed + * @param length: Pointer to data length variable + * @retval Pointer to descriptor buffer + */ +uint8_t *USBD_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) +{ + UNUSED(speed); + *length = sizeof(USBD_CONFIGURATION_FS_STRING); + return (uint8_t*)USBD_CONFIGURATION_FS_STRING; +} + +/** + * @brief Returns the interface string descriptor. + * @param speed: Current device speed + * @param length: Pointer to data length variable + * @retval Pointer to descriptor buffer + */ +uint8_t *USBD_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) +{ + UNUSED(speed); + *length = sizeof(USBD_INTERFACE_FS_STRING); + return (uint8_t*)USBD_INTERFACE_FS_STRING; +} + +/** +* @brief DeviceQualifierDescriptor +* return Device Qualifier descriptor +* @param length : pointer data length +* @retval pointer to descriptor buffer +*/ +uint8_t *USBD_GetDeviceQualifierDesc_impl (uint16_t *length) +{ + *length = sizeof (USBD_DeviceQualifierDesc); + return (uint8_t*)USBD_DeviceQualifierDesc; +} + +/** + * @brief USBD_CUSTOM_HID_GetCfgDesc + * return configuration descriptor + * @param speed : current device speed + * @param length : pointer data length + * @retval pointer to descriptor buffer + */ +uint8_t *USBD_GetCfgDesc_impl (uint16_t *length) +{ + *length = sizeof (N_USBD_CfgDesc); + return (uint8_t*)N_USBD_CfgDesc; +} + +uint8_t* USBD_HID_GetHidDescriptor_impl(uint16_t* len) { + switch (USBD_Device.request.wIndex&0xFF) { +#ifdef HAVE_IO_U2F + case U2F_INTF: + *len = sizeof(USBD_HID_Desc_fido); + return (uint8_t*)USBD_HID_Desc_fido; +#endif // HAVE_IO_U2F + case HID_INTF: + *len = sizeof(USBD_HID_Desc); + return (uint8_t*)USBD_HID_Desc; + } + *len = 0; + return 0; +} + +uint8_t* USBD_HID_GetReportDescriptor_impl(uint16_t* len) { + switch (USBD_Device.request.wIndex&0xFF) { +#ifdef HAVE_IO_U2F + case U2F_INTF: + + // very dirty work due to lack of callback when USB_HID_Init is called + USBD_LL_OpenEP(&USBD_Device, + U2F_EPIN_ADDR, + USBD_EP_TYPE_INTR, + U2F_EPIN_SIZE); + + USBD_LL_OpenEP(&USBD_Device, + U2F_EPOUT_ADDR, + USBD_EP_TYPE_INTR, + U2F_EPOUT_SIZE); + + /* Prepare Out endpoint to receive 1st packet */ + USBD_LL_PrepareReceive(&USBD_Device, U2F_EPOUT_ADDR, U2F_EPOUT_SIZE); + + + *len = sizeof(HID_ReportDesc_fido); + return (uint8_t*)HID_ReportDesc_fido; +#endif // HAVE_IO_U2F + case HID_INTF: + *len = sizeof(HID_ReportDesc); + return (uint8_t*)HID_ReportDesc; + } + *len = 0; + return 0; +} + +/** + * @} + */ + + +/** + * @brief USBD_HID_DataOut + * handle data OUT Stage + * @param pdev: device instance + * @param epnum: endpoint index + * @retval status + * + * This function is the default behavior for our implementation when data are sent over the out hid endpoint + */ +extern volatile unsigned short G_io_apdu_length; + +#ifdef HAVE_IO_U2F +uint8_t USBD_U2F_DataIn_impl (USBD_HandleTypeDef *pdev, + uint8_t epnum) +{ + UNUSED(pdev); + // only the data hid endpoint will receive data + switch (epnum) { + // FIDO endpoint + case (U2F_EPIN_ADDR&0x7F): + // advance the u2f sending machine state + u2f_transport_sent(&G_io_u2f, U2F_MEDIA_USB); + break; + } + return USBD_OK; +} + +uint8_t USBD_U2F_DataOut_impl (USBD_HandleTypeDef *pdev, + uint8_t epnum, uint8_t* buffer) +{ + switch (epnum) { + // FIDO endpoint + case (U2F_EPOUT_ADDR&0x7F): + USBD_LL_PrepareReceive(pdev, U2F_EPOUT_ADDR , U2F_EPOUT_SIZE); + u2f_transport_received(&G_io_u2f, buffer, io_seproxyhal_get_ep_rx_size(U2F_EPOUT_ADDR), U2F_MEDIA_USB); + break; + } + + return USBD_OK; +} +#endif // HAVE_IO_U2F + +uint8_t USBD_HID_DataOut_impl (USBD_HandleTypeDef *pdev, + uint8_t epnum, uint8_t* buffer) +{ + // only the data hid endpoint will receive data + switch (epnum) { + + // HID gen endpoint + case (HID_EPOUT_ADDR&0x7F): + // prepare receiving the next chunk (masked time) + USBD_LL_PrepareReceive(pdev, HID_EPOUT_ADDR , HID_EPOUT_SIZE); + + // avoid troubles when an apdu has not been replied yet + if (G_io_apdu_media == IO_APDU_MEDIA_NONE) { + // add to the hid transport + switch(io_usb_hid_receive(io_usb_send_apdu_data, buffer, io_seproxyhal_get_ep_rx_size(HID_EPOUT_ADDR))) { + default: + break; + + case IO_USB_APDU_RECEIVED: + G_io_apdu_media = IO_APDU_MEDIA_USB_HID; // for application code + G_io_apdu_state = APDU_USB_HID; // for next call to io_exchange + G_io_apdu_length = G_io_usb_hid_total_length; + break; + } + } + break; + } + + return USBD_OK; +} + +/** @defgroup USBD_HID_Private_Functions + * @{ + */ + +// note: how core lib usb calls the hid class +const USBD_DescriptorsTypeDef const HID_Desc = { + USBD_DeviceDescriptor, + USBD_LangIDStrDescriptor, + USBD_ManufacturerStrDescriptor, + USBD_ProductStrDescriptor, + USBD_SerialStrDescriptor, + USBD_ConfigStrDescriptor, + USBD_InterfaceStrDescriptor, + NULL, +}; + +#ifdef HAVE_IO_U2F +static const USBD_ClassTypeDef const USBD_U2F = +{ + USBD_HID_Init, + USBD_HID_DeInit, + USBD_HID_Setup, + NULL, /*EP0_TxSent*/ + NULL, /*EP0_RxReady*/ /* STATUS STAGE IN */ + USBD_U2F_DataIn_impl, /*DataIn*/ + USBD_U2F_DataOut_impl, /*DataOut*/ + NULL, /*SOF */ + NULL, + NULL, + USBD_GetCfgDesc_impl, + USBD_GetCfgDesc_impl, + USBD_GetCfgDesc_impl, + USBD_GetDeviceQualifierDesc_impl, +}; +#endif // HAVE_IO_U2F + +static const USBD_ClassTypeDef const USBD_HID = +{ + USBD_HID_Init, + USBD_HID_DeInit, + USBD_HID_Setup, + NULL, /*EP0_TxSent*/ + NULL, /*EP0_RxReady*/ /* STATUS STAGE IN */ + NULL, /*DataIn*/ + USBD_HID_DataOut_impl, /*DataOut*/ + NULL, /*SOF */ + NULL, + NULL, + USBD_GetCfgDesc_impl, + USBD_GetCfgDesc_impl, + USBD_GetCfgDesc_impl, + USBD_GetDeviceQualifierDesc_impl, +}; + +#ifdef HAVE_USB_CLASS_CCID +static const USBD_ClassTypeDef USBD_CCID = +{ + USBD_CCID_Init, + USBD_CCID_DeInit, + USBD_CCID_Setup, + NULL, /*EP0_TxSent*/ + NULL, /*EP0_RxReady*/ + USBD_CCID_DataIn, + USBD_CCID_DataOut, + NULL, /*SOF */ + NULL, /*ISOIn*/ + NULL, /*ISOOut*/ + USBD_GetCfgDesc_impl, + USBD_GetCfgDesc_impl, + USBD_GetCfgDesc_impl, + USBD_GetDeviceQualifierDesc_impl, +}; + +uint8_t SC_AnswerToReset (uint8_t voltage, uint8_t* atr_buffer) { + UNUSED(voltage); + // return the atr length + atr_buffer[0] = 0x3B; + atr_buffer[1] = 0; + return 2; +} + +void SC_Poweroff(void) { + // nothing to do ? +} + +uint8_t SC_ExecuteEscape (uint8_t* escapePtr, uint32_t escapeLen, + uint8_t* responseBuff, + uint16_t* responseLen) { + UNUSED(escapePtr); + UNUSED(escapeLen); + UNUSED(responseBuff); + UNUSED(responseLen); + // nothing to do ? + return 0; +} +#endif // HAVE_USB_CLASS_CCID + +void USB_power(unsigned char enabled) { + os_memset(&USBD_Device, 0, sizeof(USBD_Device)); + + if (enabled) { + os_memset(&USBD_Device, 0, sizeof(USBD_Device)); + /* Init Device Library */ + USBD_Init(&USBD_Device, (USBD_DescriptorsTypeDef*)&HID_Desc, 0); + + /* Register the HID class */ + USBD_RegisterClassForInterface(HID_INTF, &USBD_Device, (USBD_ClassTypeDef*)&USBD_HID); +#ifdef HAVE_IO_U2F + USBD_RegisterClassForInterface(U2F_INTF, &USBD_Device, (USBD_ClassTypeDef*)&USBD_U2F); + // initialize the U2F tunnel transport + u2f_transport_init(&G_io_u2f, G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); +#endif // HAVE_IO_U2F +#ifdef HAVE_USB_CLASS_CCID + USBD_RegisterClassForInterface(CCID_INTF, &USBD_Device, (USBD_ClassTypeDef*)&USBD_CCID); +#endif // HAVE_USB_CLASS_CCID + + + /* Start Device Process */ + USBD_Start(&USBD_Device); + } + else { + USBD_DeInit(&USBD_Device); + } +} + +/** + * @} + */ + + +/** + * @} + */ + + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/src/sdk/usbd_impl.h b/src/sdk/usbd_impl.h new file mode 100644 index 0000000..a20edef --- /dev/null +++ b/src/sdk/usbd_impl.h @@ -0,0 +1,7 @@ +#ifndef USBD_IMPL_H +#define USBD_IMPL_H + + +uint8_t *USBD_GetCfgDesc_impl (uint16_t *length); + +#endif //USBD_IMPL_H \ No newline at end of file diff --git a/src/usbd_ccid_impl.c b/src/usbd_ccid_impl.c deleted file mode 100644 index 2a54750..0000000 --- a/src/usbd_ccid_impl.c +++ /dev/null @@ -1,463 +0,0 @@ -/** - ****************************************************************************** - * @file usbd_ccid_core.c - * @author MCD Application Team - * @version V1.0.1 - * @date 31-January-2014 - * @brief This file provides all the CCID core functions. - * - * @verbatim - * - * =================================================================== - * CCID Class Description - * =================================================================== - * This module manages the Specification for Integrated Circuit(s) - * Cards Interface Revision 1.1 - * This driver implements the following aspects of the specification: - * - Bulk Transfers - * - * @endverbatim - * - ****************************************************************************** - * @attention - * - *

© COPYRIGHT 2014 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/software_license_agreement_liberty_v2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - ****************************************************************************** - */ - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_ccid_core.h" - -#ifdef HAVE_USB_CLASS_CCID - -#define USBD_LANGID_STRING 0x409 - -#define USBD_VID 0x2C97 -#if TARGET_ID == 0x31000002 // blue -#define USBD_PID 0x0000 -static const uint8_t const USBD_PRODUCT_FS_STRING[] = { - 4*2+2, - USB_DESC_TYPE_STRING, - 'B', 0, - 'l', 0, - 'u', 0, - 'e', 0, -}; - -#elif TARGET_ID == 0x31100002 // nano s -#define USBD_PID 0x0001 -static const uint8_t const USBD_PRODUCT_FS_STRING[] = { - 6*2+2, - USB_DESC_TYPE_STRING, - 'N', 0, - 'a', 0, - 'n', 0, - 'o', 0, - ' ', 0, - 'S', 0, -}; -#elif TARGET_ID == 0x31200002 // aramis -#define USBD_PID 0x0002 -static const uint8_t const USBD_PRODUCT_FS_STRING[] = { - 6*2+2, - USB_DESC_TYPE_STRING, - 'A', 0, - 'r', 0, - 'a', 0, - 'm', 0, - 'i', 0, - 's', 0, -}; -#else -#error unknown TARGET_ID -#endif - - -/* USB Standard Device Descriptor */ -static __ALIGN_BEGIN const uint8_t const USBD_DeviceQualifierDesc[] __ALIGN_END = -{ - USB_LEN_DEV_QUALIFIER_DESC, - USB_DESC_TYPE_DEVICE_QUALIFIER, - 0x00, - 0x02, - 0x00, - 0x00, - 0x00, - 0x40, - 0x01, - 0x00, -}; - -/* USB Standard Device Descriptor */ -static const uint8_t const USBD_LangIDDesc[]= -{ - USB_LEN_LANGID_STR_DESC, - USB_DESC_TYPE_STRING, - LOBYTE(USBD_LANGID_STRING), - HIBYTE(USBD_LANGID_STRING), -}; - -static const uint8_t const USB_SERIAL_STRING[] = -{ - 4*2+2, - USB_DESC_TYPE_STRING, - '0', 0, - '0', 0, - '0', 0, - '1', 0, -}; - -static const uint8_t const USBD_MANUFACTURER_STRING[] = { - 6*2+2, - USB_DESC_TYPE_STRING, - 'L', 0, - 'e', 0, - 'd', 0, - 'g', 0, - 'e', 0, - 'r', 0, -}; - -#define USBD_INTERFACE_FS_STRING USBD_PRODUCT_FS_STRING -#define USBD_CONFIGURATION_FS_STRING USBD_PRODUCT_FS_STRING - - -/* USB Standard Device Descriptor */ -static const uint8_t const USBD_DeviceDesc[]= { - 0x12, /* bLength */ - USB_DESC_TYPE_DEVICE, /* bDescriptorType */ - 0x00, /* bcdUSB */ - 0x02, - 0x00, /* bDeviceClass */ - 0x00, /* bDeviceSubClass */ - 0x00, /* bDeviceProtocol */ - USB_MAX_EP0_SIZE, /* bMaxPacketSize */ - LOBYTE(USBD_VID), /* idVendor */ - HIBYTE(USBD_VID), /* idVendor */ - LOBYTE(USBD_PID), /* idVendor */ - HIBYTE(USBD_PID), /* idVendor */ - 0x00, /* bcdDevice rel. 2.00 */ - 0x02, - USBD_IDX_MFC_STR, /* Index of manufacturer string */ - USBD_IDX_PRODUCT_STR, /* Index of product string */ - USBD_IDX_SERIAL_STR, /* Index of serial number string */ - USBD_MAX_NUM_CONFIGURATION /* bNumConfigurations */ -}; /* USB_DeviceDescriptor */ - - -/* USB Mass storage device Configuration Descriptor */ -/* All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */ -#define USBD_OFFSET_CfgDesc_bPINSupport 70 -const uint8_t N_USBD_CfgDesc[] = -{ - - 0x09, /* bLength: Configuration Descriptor size */ - USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */ - 0x9+0x9+0x36+0x7+0x7+0x7, - - 0x00, - 0x01, /* bNumInterfaces: 1 interface */ - 0x01, /* bConfigurationValue: */ - 0x04, /* iConfiguration: */ - 0x80, /*bmAttributes: bus powered */ - 0x32, /* MaxPower 100 mA */ - - /******************** CCID **** interface ********************/ - 0x09, /* bLength: Interface Descriptor size */ - 0x04, /* bDescriptorType: */ - 0x00, /* bInterfaceNumber: Number of Interface */ - 0x00, /* bAlternateSetting: Alternate setting */ - 0x03, /* bNumEndpoints: 3 endpoints used */ - 0x0B, /* bInterfaceClass: user's interface for CCID */ - 0x00, /* bInterfaceSubClass : */ - 0x00, /* nInterfaceProtocol : None */ - 0x05, /* iInterface: */ - - /******************* CCID class descriptor ********************/ - 0x36, /* bLength: CCID Descriptor size */ - 0x21, /* bDescriptorType: Functional Descriptor type. */ - 0x10, /* bcdCCID(LSB): CCID Class Spec release number (1.00) */ - 0x01, /* bcdCCID(MSB) */ - - 0x00, /* bMaxSlotIndex :highest available slot on this device */ - 0x03, /* bVoltageSupport: bit Wise OR for 01h-5.0V 02h-3.0V - 04h 1.8V*/ - - 0x01,0x00,0x00,0x00, /* dwProtocols: 0001h = Protocol T=0 */ - 0x10,0x0E,0x00,0x00, /* dwDefaultClock: 3.6Mhz = 3600kHz = 0x0E10, - for 4 Mhz the value is (0x00000FA0) : - This is used in ETU and waiting time calculations*/ - 0x10,0x0E,0x00,0x00, /* dwMaximumClock: Maximum supported ICC clock frequency - in KHz. So, 3.6Mhz = 3600kHz = 0x0E10, - 4 Mhz (0x00000FA0) : */ - 0x00, /* bNumClockSupported : no setting from PC - If the value is 00h, the - supported clock frequencies are assumed to be the - default clock frequency defined by dwDefaultClock - and the maximum clock frequency defined by - dwMaximumClock */ - - 0xCD,0x25,0x00,0x00, /* dwDataRate: Default ICC I/O data rate in bps - 9677 bps = 0x25CD - for example 10752 bps (0x00002A00) */ - - 0xCD,0x25,0x00,0x00, /* dwMaxDataRate: Maximum supported ICC I/O data - rate in bps */ - 0x00, /* bNumDataRatesSupported : - The number of data rates that are supported by the CCID - If the value is 00h, all data rates between the default - data rate dwDataRate and the maximum data rate - dwMaxDataRate are supported. - Dont support GET_CLOCK_FREQUENCIES - */ - //46 - 0x00,0x00,0x00,0x00, /* dwMaxIFSD: 0 (T=0 only) */ - 0x00,0x00,0x00,0x00, /* dwSynchProtocols */ - 0x00,0x00,0x00,0x00, /* dwMechanical: no special characteristics */ - - 0x38,0x00,EXCHANGE_LEVEL_FEATURE,0x00, - /* dwFeatures: clk, baud rate, voltage : automatic */ - /* 00000008h Automatic ICC voltage selection - 00000010h Automatic ICC clock frequency change - 00000020h Automatic baud rate change according to - active parameters provided by the Host or self - determined 00000100h CCID can set - ICC in clock stop mode - - Only one of the following values may be present to - select a level of exchange: - 00010000h TPDU level exchanges with CCID - 00020000h Short APDU level exchange with CCID - 00040000h Short and Extended APDU level exchange - If none of those values : character level of exchange*/ - #if 1 - 0x0F,0x01,0x00,0x00, /* dwMaxCCIDMessageLength: Maximum block size + header*/ - /* 261 + 10 */ - #else - 0xF8,0x00,0x00,0x00, /* dwMaxCCIDMessageLength: Maximum block size + header*/ - /* EEh + 10 */ - #endif - 0x00, /* bClassGetResponse*/ - 0x00, /* bClassEnvelope */ - 0x00,0x00, /* wLcdLayout : 0000h no LCD. */ - 0x03, /* bPINSupport : no PIN verif and modif */ //<= offset: 70 - 0x01, /* bMaxCCIDBusySlots */ - - //72 - /******************** CCID Endpoints ********************/ - 0x07, /*Endpoint descriptor length = 7*/ - 0x05, /*Endpoint descriptor type */ - CCID_BULK_IN_EP, /*Endpoint address (IN, address 1) */ - 0x02, /*Bulk endpoint type */ - LOBYTE(CCID_BULK_EPIN_SIZE), - HIBYTE(CCID_BULK_EPIN_SIZE), - 0x00, /*Polling interval in milliseconds */ - - 0x07, /*Endpoint descriptor length = 7 */ - 0x05, /*Endpoint descriptor type */ - CCID_BULK_OUT_EP, /*Endpoint address (OUT, address 1) */ - 0x02, /*Bulk endpoint type */ - LOBYTE(CCID_BULK_EPOUT_SIZE), - HIBYTE(CCID_BULK_EPOUT_SIZE), - 0x00, /*Polling interval in milliseconds*/ - - - 0x07, /*bLength: Endpoint Descriptor size*/ - 0x05, /*bDescriptorType:*/ - CCID_INTR_IN_EP, /*bEndpointAddress: Endpoint Address (IN)*/ - 0x03, /* bmAttributes: Interrupt endpoint */ - LOBYTE(CCID_INTR_EPIN_SIZE), - HIBYTE(CCID_INTR_EPIN_SIZE), - 0x18 /*Polling interval in milliseconds */ -}; - - - -static uint8_t *USBD_GetCfgDesc_impl (uint16_t *length) -{ - *length = sizeof (N_USBD_CfgDesc); - return (uint8_t*)(N_USBD_CfgDesc); -} - -/** - * @brief Returns the device descriptor. - * @param speed: Current device speed - * @param length: Pointer to data length variable - * @retval Pointer to descriptor buffer - */ -static uint8_t *USBD_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) -{ - UNUSED(speed); - *length = sizeof(USBD_DeviceDesc); - return (uint8_t*)USBD_DeviceDesc; -} - -/** - * @brief Returns the LangID string descriptor. - * @param speed: Current device speed - * @param length: Pointer to data length variable - * @retval Pointer to descriptor buffer - */ -static uint8_t *USBD_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) -{ - UNUSED(speed); - *length = sizeof(USBD_LangIDDesc); - return (uint8_t*)USBD_LangIDDesc; -} - -/** - * @brief Returns the product string descriptor. - * @param speed: Current device speed - * @param length: Pointer to data length variable - * @retval Pointer to descriptor buffer - */ -static uint8_t *USBD_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) -{ - UNUSED(speed); - *length = sizeof(USBD_PRODUCT_FS_STRING); - return (uint8_t*)USBD_PRODUCT_FS_STRING; -} - -/** - * @brief Returns the manufacturer string descriptor. - * @param speed: Current device speed - * @param length: Pointer to data length variable - * @retval Pointer to descriptor buffer - */ -static uint8_t *USBD_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) -{ - UNUSED(speed); - *length = sizeof(USBD_MANUFACTURER_STRING); - return (uint8_t*)USBD_MANUFACTURER_STRING; -} - -/** - * @brief Returns the serial number string descriptor. - * @param speed: Current device speed - * @param length: Pointer to data length variable - * @retval Pointer to descriptor buffer - */ -static uint8_t *USBD_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) -{ - UNUSED(speed); - *length = sizeof(USB_SERIAL_STRING); - return (uint8_t*)USB_SERIAL_STRING; -} - -/** - * @brief Returns the configuration string descriptor. - * @param speed: Current device speed - * @param length: Pointer to data length variable - * @retval Pointer to descriptor buffer - */ -static uint8_t *USBD_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) -{ - UNUSED(speed); - *length = sizeof(USBD_CONFIGURATION_FS_STRING); - return (uint8_t*)USBD_CONFIGURATION_FS_STRING; -} - -/** - * @brief Returns the interface string descriptor. - * @param speed: Current device speed - * @param length: Pointer to data length variable - * @retval Pointer to descriptor buffer - */ -static uint8_t *USBD_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) -{ - UNUSED(speed); - *length = sizeof(USBD_INTERFACE_FS_STRING); - return (uint8_t*)USBD_INTERFACE_FS_STRING; -} - -/** -* @brief DeviceQualifierDescriptor -* return Device Qualifier descriptor -* @param length : pointer data length -* @retval pointer to descriptor buffer -*/ -static uint8_t *USBD_GetDeviceQualifierDesc_impl (uint16_t *length) -{ - *length = sizeof (USBD_DeviceQualifierDesc); - return (uint8_t*)USBD_DeviceQualifierDesc; -} - - -uint8_t SC_AnswerToReset (uint8_t voltage, uint8_t* atr_buffer) { - // return the atr length - atr_buffer[0] = 0x3B; - atr_buffer[1] = 0; - return 2; -} - - -// note: how core lib usb calls the hid class -static const USBD_DescriptorsTypeDef const CCID_Desc = { - USBD_DeviceDescriptor, - USBD_LangIDStrDescriptor, - USBD_ManufacturerStrDescriptor, - USBD_ProductStrDescriptor, - USBD_SerialStrDescriptor, - USBD_ConfigStrDescriptor, - USBD_InterfaceStrDescriptor, - NULL, -}; - -static const USBD_ClassTypeDef USBD_CCID = -{ - USBD_CCID_Init, - USBD_CCID_DeInit, - USBD_CCID_Setup, - NULL, /*EP0_TxSent*/ - NULL, /*EP0_RxReady*/ - USBD_CCID_DataIn, - USBD_CCID_DataOut, - NULL, /*SOF */ - NULL, /*ISOIn*/ - NULL, /*ISOOut*/ - USBD_GetCfgDesc_impl, - USBD_GetCfgDesc_impl, - USBD_GetCfgDesc_impl, - USBD_GetDeviceQualifierDesc_impl, -}; - - -void USBD_CCID_activate_pinpad(int enabled) { - unsigned char e; - e = enabled?3:0; - nvm_write(((char*)PIC(N_USBD_CfgDesc))+USBD_OFFSET_CfgDesc_bPINSupport, &e,1); -} - -void USB_CCID_power(unsigned char enabled) { - os_memset(&USBD_Device, 0, sizeof(USBD_Device)); - - if (enabled) { - os_memset(&USBD_Device, 0, sizeof(USBD_Device)); - /* Init Device Library */ - USBD_Init(&USBD_Device, (USBD_DescriptorsTypeDef*)&CCID_Desc, 0); - - /* Register the HID class */ - USBD_RegisterClass(&USBD_Device, (USBD_ClassTypeDef*)&USBD_CCID); - - /* Start Device Process */ - USBD_Start(&USBD_Device); - } - else { - USBD_DeInit(&USBD_Device); - } -} - -#endif // HAVE_USB_CLASS_CCID diff --git a/src/usbd_ccid_impl.h b/src/usbd_ccid_impl.h deleted file mode 100644 index b254b71..0000000 --- a/src/usbd_ccid_impl.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef USBD_CCID_IMPL_H -#define USBD_CCID_IMPL_H - -#define TPDU_EXCHANGE 0x01 -#define SHORT_APDU_EXCHANGE 0x02 -#define EXTENDED_APDU_EXCHANGE 0x04 -#define CHARACTER_EXCHANGE 0x00 - -#define EXCHANGE_LEVEL_FEATURE SHORT_APDU_EXCHANGE - - -#define CCID_BULK_IN_EP 0x82 -#define CCID_BULK_EPIN_SIZE 64 -#define CCID_BULK_OUT_EP 0x02 -#define CCID_BULK_EPOUT_SIZE 64 -#define CCID_INTR_IN_EP 0x81 -#define CCID_INTR_EPIN_SIZE 16 - -#define CCID_EP0_BUFF_SIZ 64 - -void USB_CCID_power(unsigned char enabled); -void USBD_CCID_activate_pinpad(int enabled); - -#endif // USBD_CCID_IMPL_H \ No newline at end of file