diff --git a/Makefile b/Makefile index 69b2b3f..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=RC4 +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 fabd347..204edee 100644 --- a/README.md +++ b/README.md @@ -11,16 +11,17 @@ The application supports: - ECDH with secp256k1, secp256r1, brainpool 256r1, brainpool 256t1 and curve25519 curves -To compile it - - use at least the Nano S SDK 1.3.1.4 on firmware 1.3.1 - - replace lib_stusb/STM32_USB_Device_Library/Class/CCID/src/usbd_ccid_if.c by the one provided in sdk/ directory - -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 and Usage + +See the full doc at https://github.com/LedgerHQ/blue-app-openpgp-card/blob/master/doc/user/blue-app-openpgp-card.pdf + + ## Add-on The GnuPG application implements the following addon: @@ -29,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 94% rename from doc/gpgcard3.0-addon.rst rename to doc/developper/gpgcard3.0-addon.rst index 1e0c95b..a501b0d 100644 --- a/doc/gpgcard3.0-addon.rst +++ b/doc/developper/gpgcard3.0-addon.rst @@ -83,6 +83,15 @@ Deterministic key derivation The deterministic key derivation process relies on the BIP32 scheme. The master install path of GPG-ledger is set to /0x80'GPG', aka /80475047 +Deterministic key derivation maybe activated in: + Settings->Seed Mode->Set on + +This activation remains effective until *set off* is selected or the application +ends. + +The key management remains the same if seed mode is on or off, i.e. key are stored in memory key containers. So their is no perfomance inpact when using seeded keys. + +Seeded keys are generated as follow: **Step1**: @@ -269,6 +278,6 @@ Other minor add-on ------------------ GnuPG use both fingerprints and serial number to identfy key on card. -So, the put data command accept to modify the AID file with '4F' tag. +So, the put data command is able to modify the AID file with '4F' tag. In that case the data field shall be four bytes length and shall contain the new serial number. '4F' is protected by PW3 (admin) PIN. 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/src/gpg_api.h b/src/gpg_api.h index 142a0d3..378fd01 100644 --- a/src/gpg_api.h +++ b/src/gpg_api.h @@ -16,6 +16,11 @@ #ifndef GPG_API_H #define GPG_API_H +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); @@ -27,24 +32,26 @@ 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) ; int gpg_apdu_select(void) ; -int gpg_apdu_verify(int id) ; -int gpg_apdu_change_ref_data(int id) ; +int gpg_apdu_verify(void) ; +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_blocked(gpg_pin_t *pin); +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); -int gpg_oid2curve(unsigned char* oid, unsigned int len); -int gpg_is_pin_verified(int id); -int gpg_is_pin_blocked(int id); -int gpg_set_pin_verified(int id, int verified); -int gpg_check_pin(int id, unsigned char *pin_val, unsigned int pin_len); -void gpg_change_pin(int id, unsigned char *pin_val, unsigned int pin_len); +int gpg_mse_reset(); +int gpg_apdu_mse(); /* ----------------------------------------------------------------------- */ /* --- IO ---- */ @@ -67,6 +74,7 @@ void gpg_io_insert_tlv(unsigned int T, unsigned int L, unsigned char const *V) ; void gpg_io_fetch_buffer(unsigned char * buffer, unsigned int len) ; unsigned int gpg_io_fetch_u32(void) ; +unsigned int gpg_io_fetch_u24(void) ; unsigned int gpg_io_fetch_u16(void) ; unsigned int gpg_io_fetch_u8(void) ; int gpg_io_fetch_t(unsigned int *T) ; diff --git a/src/gpg_challenge.c b/src/gpg_challenge.c index f156920..7144574 100644 --- a/src/gpg_challenge.c +++ b/src/gpg_challenge.c @@ -29,6 +29,7 @@ int gpg_apdu_get_challenge() { } if (olen > GPG_EXT_CHALLENGE_LENTH) { THROW(SW_WRONG_LENGTH); + return SW_WRONG_LENGTH; } if ((G_gpg_vstate.io_p1&0x82) == 0x82) { @@ -43,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 ec09851..8273bf9 100644 --- a/src/gpg_data.c +++ b/src/gpg_data.c @@ -30,6 +30,7 @@ int gpg_apdu_get_data(unsigned int ref) { int sw; + if (G_gpg_vstate.DO_current != ref) { G_gpg_vstate.DO_current = ref; G_gpg_vstate.DO_reccord = 0; @@ -64,6 +65,10 @@ int gpg_apdu_get_data(unsigned int ref) { case 0x01F2: gpg_io_insert_u8(G_gpg_vstate.slot); break; + /* ----------------- Config RSA exponent ----------------- */ + case 0x01F8: + gpg_io_insert(N_gpg_pstate->default_RSA_exponent,4); + break; /* ----------------- Application ----------------- */ /* Full Application identifier */ @@ -176,8 +181,8 @@ int gpg_apdu_get_data(unsigned int ref) { /* WAT */ default: - sw = SW_REFERENCED_DATA_NOT_FOUND; - break; + THROW(SW_REFERENCED_DATA_NOT_FOUND); + return 0; } return sw; @@ -225,6 +230,7 @@ int gpg_apdu_put_data(unsigned int ref) { WRITE_PRIVATE_DO: if (G_gpg_vstate.io_length > GPG_EXT_PRIVATE_DO_LENGTH) { THROW(SW_WRONG_LENGTH); + return 0; } gpg_nvm_write(ptr_v, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, G_gpg_vstate.io_length); gpg_nvm_write(ptr_l, &G_gpg_vstate.io_length, sizeof(unsigned int)); @@ -234,37 +240,50 @@ int gpg_apdu_put_data(unsigned int ref) { case 0x01F1: if (G_gpg_vstate.io_length != 3) { THROW(SW_WRONG_LENGTH); + return 0; } if ((G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset +0] != GPG_KEYS_SLOTS) || (G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset +1] >= GPG_KEYS_SLOTS) || (G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset +2] > 3)) { THROW(SW_WRONG_DATA); + return 0; } gpg_nvm_write(N_gpg_pstate->config_slot, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset,3); - sw = SW_OK; break; case 0x01F2: if ((N_gpg_pstate->config_slot[2] & 2) == 0) { THROW(SW_CONDITIONS_NOT_SATISFIED); + return 0; } if ((G_gpg_vstate.io_length != 1) || (G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset] >= GPG_KEYS_SLOTS)) { THROW(SW_WRONG_DATA); + return 0; } G_gpg_vstate.slot = G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset]; - - sw = SW_OK; break; + /* ----------------- Config RSA exponent ----------------- */ + case 0x01F8: { + unsigned int e; + if (G_gpg_vstate.io_length != 4) { + THROW(SW_WRONG_LENGTH); + return 0; + } + e = gpg_io_fetch_u32(); + nvm_write(&N_gpg_pstate->default_RSA_exponent, &e, sizeof(unsigned int)); + break; + } + + /* ----------------- Serial -----------------*/ case 0x4f: 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); - sw = SW_OK; break; /* ----------------- Extended Header list -----------------*/ @@ -277,6 +296,7 @@ int gpg_apdu_put_data(unsigned int ref) { gpg_io_fetch_tl(&t,&l); if (t!=0x4D) { THROW(SW_REFERENCED_DATA_NOT_FOUND); + return 0; } //fecth B8/B6/A4 gpg_io_fetch_tl(&t,&l); @@ -296,11 +316,13 @@ int gpg_apdu_put_data(unsigned int ref) { break; default: THROW(SW_REFERENCED_DATA_NOT_FOUND); + return 0; } //fecth 7f78 gpg_io_fetch_tl(&t,&l); if (t!=0x7f48) { THROW(SW_REFERENCED_DATA_NOT_FOUND); + return 0; } len_e = 0; len_p = 0; len_q = 0; endof = G_gpg_vstate.io_offset+l; @@ -321,15 +343,18 @@ int gpg_apdu_put_data(unsigned int ref) { case 0x95: case 0x96: case 0x97: + case 0x99: break; default: THROW(SW_REFERENCED_DATA_NOT_FOUND); + return 0; } } //fecth 5f78 gpg_io_fetch_tl(&t,&l); if (t!=0x5f48) { THROW(SW_REFERENCED_DATA_NOT_FOUND); + return 0; } // --- RSA KEY --- @@ -342,63 +367,69 @@ 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.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.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.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.rsa.public4096.n; break; } ksz = ksz>>1; - if ( (len_e>4)||(len_e==0) || - (len_p > ksz )|| - (len_q > ksz)) { - THROW(SW_WRONG_DATA); - } + //fetch e e = 0; switch(len_e) { case 4: - e = (e<<8) | gpg_io_fetch_u8(); + e = gpg_io_fetch_u32(); + break; case 3: - e = (e<<8) | gpg_io_fetch_u8(); + e = gpg_io_fetch_u24(); + break; case 2: - e = (e<<8) | gpg_io_fetch_u8(); + e = gpg_io_fetch_u16(); + break; case 1: - e = (e<<8) | gpg_io_fetch_u8(); + e = gpg_io_fetch_u8(); + break; + default: + THROW(SW_WRONG_DATA); + return 0; } //move p,q over pub key, this only work because adr < adr

+ if ( (len_p > ksz )|| (len_q > ksz)) { + THROW(SW_WRONG_DATA); + return 0; + } p = G_gpg_vstate.work.io_buffer + G_gpg_vstate.io_offset; q = p + len_p; - pq = (unsigned char*)rsa_pub; - os_memmove(pq+ksz-len_p, p, len_p); - os_memmove(pq+2*ksz-len_q, q, len_q); os_memset(pq, 0, ksz-len_p); + os_memmove(pq+2*ksz-len_q, q, len_q); 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); @@ -418,15 +449,16 @@ int gpg_apdu_put_data(unsigned int ref) { curve = gpg_oid2curve(&keygpg->attributes.value[1], keygpg->attributes.length-1); if (curve == 0) { 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)); @@ -437,6 +469,7 @@ int gpg_apdu_put_data(unsigned int ref) { // --- UNSUPPORTED KEY --- else { THROW(SW_REFERENCED_DATA_NOT_FOUND); + return 0; } break; } //endof of 3fff @@ -447,45 +480,45 @@ int gpg_apdu_put_data(unsigned int ref) { case 0x5B: if (G_gpg_vstate.io_length > sizeof(N_gpg_pstate->name.value)) { THROW(SW_WRONG_LENGTH); + return 0; } gpg_nvm_write(N_gpg_pstate->name.value, G_gpg_vstate.work.io_buffer, G_gpg_vstate.io_length); gpg_nvm_write(&N_gpg_pstate->name.length, &G_gpg_vstate.io_length, sizeof(unsigned int)); - sw = SW_OK; break; /* Login data */ case 0x5E: if (G_gpg_vstate.io_length > sizeof(N_gpg_pstate->login.value)) { THROW(SW_WRONG_LENGTH); + return 0; } gpg_nvm_write(N_gpg_pstate->login.value, G_gpg_vstate.work.io_buffer, G_gpg_vstate.io_length); gpg_nvm_write(&N_gpg_pstate->login.length, &G_gpg_vstate.io_length, sizeof(unsigned int)); - sw = SW_OK; break; /* Language preferences */ case 0x5F2D: if (G_gpg_vstate.io_length > sizeof(N_gpg_pstate->lang.value)) { THROW(SW_WRONG_LENGTH); + return 0; } gpg_nvm_write(N_gpg_pstate->lang.value, G_gpg_vstate.work.io_buffer, G_gpg_vstate.io_length); gpg_nvm_write(&N_gpg_pstate->lang.length, &G_gpg_vstate.io_length, sizeof(unsigned int)); - sw = SW_OK; break; /* Sex */ case 0x5F35: if (G_gpg_vstate.io_length != sizeof(N_gpg_pstate->sex)) { THROW(SW_WRONG_LENGTH); + return 0; } gpg_nvm_write(N_gpg_pstate->sex, G_gpg_vstate.work.io_buffer, G_gpg_vstate.io_length); - sw = SW_OK; break; /* Uniform resource locator */ case 0x5F50: if (G_gpg_vstate.io_length > sizeof(N_gpg_pstate->url.value)) { THROW(SW_WRONG_LENGTH); + return 0; } gpg_nvm_write(N_gpg_pstate->url.value, G_gpg_vstate.work.io_buffer, G_gpg_vstate.io_length); gpg_nvm_write(&N_gpg_pstate->url.length, &G_gpg_vstate.io_length, sizeof(unsigned int)); - sw = SW_OK; break; /* ----------------- Cardholder certificate ----------------- */ @@ -505,8 +538,8 @@ int gpg_apdu_put_data(unsigned int ref) { ptr_v = G_gpg_vstate.kslot->dec.CA.value; goto WRITE_CA; default: - sw = SW_REFERENCED_DATA_NOT_FOUND; - break; + THROW(SW_REFERENCED_DATA_NOT_FOUND); + return 0; } WRITE_CA: if (G_gpg_vstate.io_length > GPG_EXT_CARD_HOLDER_CERT_LENTH) { @@ -514,7 +547,6 @@ int gpg_apdu_put_data(unsigned int ref) { } gpg_nvm_write(ptr_v, G_gpg_vstate.work.io_buffer, G_gpg_vstate.io_length); gpg_nvm_write(ptr_l, &G_gpg_vstate.io_length, sizeof(unsigned int)); - sw = SW_OK; break; /* ----------------- Algorithm attributes ----------------- */ @@ -533,10 +565,10 @@ int gpg_apdu_put_data(unsigned int ref) { WRITE_ATTRIBUTES: if (G_gpg_vstate.io_length > 12) { THROW(SW_WRONG_LENGTH); + return 0; } gpg_nvm_write(ptr_v, G_gpg_vstate.work.io_buffer, G_gpg_vstate.io_length); gpg_nvm_write(ptr_l, &G_gpg_vstate.io_length, sizeof(unsigned int)); - sw = SW_OK; break; /* ----------------- PWS status ----------------- */ @@ -566,9 +598,9 @@ int gpg_apdu_put_data(unsigned int ref) { WRITE_FINGERPRINTS: if (G_gpg_vstate.io_length != 20) { THROW(SW_WRONG_LENGTH); + return 0; } gpg_nvm_write(ptr_v, G_gpg_vstate.work.io_buffer, 20); - sw = SW_OK; break; /* ----------------- Generation date/time ----------------- */ @@ -584,9 +616,9 @@ int gpg_apdu_put_data(unsigned int ref) { WRITE_DATE: if (G_gpg_vstate.io_length != 4) { THROW(SW_WRONG_LENGTH); + return 0; } gpg_nvm_write(ptr_v, G_gpg_vstate.work.io_buffer, 4); - sw = SW_OK; break; /* ----------------- AES key ----------------- */ @@ -618,10 +650,27 @@ int gpg_apdu_put_data(unsigned int ref) { } /* ----------------- RC ----------------- */ - case 0xD3: - sw = gpg_apdu_change_ref_data(PIN_ID_RC); - 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; @@ -635,9 +684,9 @@ int gpg_apdu_put_data(unsigned int ref) { WRITE_UIF: if (G_gpg_vstate.io_length != 2) { THROW(SW_WRONG_LENGTH); + return 0; } gpg_nvm_write(ptr_v, G_gpg_vstate.work.io_buffer, 2); - sw = SW_OK; break; /* ----------------- WAT ----------------- */ @@ -645,6 +694,7 @@ int gpg_apdu_put_data(unsigned int ref) { sw = SW_REFERENCED_DATA_NOT_FOUND; break; } + gpg_io_discard(1); return sw; diff --git a/src/gpg_dispatch.c b/src/gpg_dispatch.c index 23a4614..9640a36 100644 --- a/src/gpg_dispatch.c +++ b/src/gpg_dispatch.c @@ -20,12 +20,10 @@ #include "gpg_vars.h" -int gpg_is_verified(id) { - return G_gpg_vstate.verified_pin[id] ; -} void gpg_check_access_ins() { unsigned int ref; + ref = (G_gpg_vstate.io_p1 << 8) | G_gpg_vstate.io_p2 ; switch (G_gpg_vstate.io_ins) { @@ -42,7 +40,7 @@ void gpg_check_access_ins() { return; case INS_RESET_RETRY_COUNTER: - if (gpg_is_verified(PIN_ID_PW3) || gpg_is_verified(PIN_ID_RC)) { + if (gpg_pin_is_verified(PIN_ID_PW3) || gpg_pin_is_verified(PIN_ID_RC)) { return; } @@ -54,27 +52,30 @@ void gpg_check_access_ins() { if (G_gpg_vstate.io_p1 == 0x81) { return; } - if (gpg_is_verified(PIN_ID_PW3)) { + if (gpg_pin_is_verified(PIN_ID_PW3)) { return; } break; + case INS_MSE: + return ; + case INS_PSO: - if ((ref == 0x9e9a) && gpg_is_verified(PIN_ID_PW1)) { - //pso:sign + if ((ref == 0x9e9a) && gpg_pin_is_verified(PIN_ID_PW1)) { + //pso:sign if (N_gpg_pstate->PW_status[0] == 0) { - gpg_set_pin_verified(PIN_ID_PW1,0); + gpg_pin_set_verified(PIN_ID_PW1, 0); } return; } - if ((ref == 0x8086 ) && gpg_is_verified(PIN_ID_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_is_verified(PIN_ID_PW2)) { + if (gpg_pin_is_verified(PIN_ID_PW2)) { return; } break; @@ -83,7 +84,7 @@ void gpg_check_access_ins() { return; case INS_TERMINATE_DF: - if (gpg_is_pin_verified(PIN_ID_PW3) || gpg_is_pin_blocked(PIN_ID_PW3)) { + if (gpg_pin_is_verified(PIN_ID_PW3)) { return; } break; @@ -96,12 +97,17 @@ void gpg_check_access_ins() { void gpg_check_access_read_DO() { unsigned int ref; + ref = (G_gpg_vstate.io_p1 << 8) | G_gpg_vstate.io_p2 ; switch(ref) { //ALWAYS case 0x0101: case 0x0102: + case 0x01F0: + case 0x01F1: + case 0x01F2: + case 0x01F8: case 0x006E: case 0x0065: case 0x0073: @@ -140,14 +146,14 @@ void gpg_check_access_read_DO() { //PW1 case 0x0103: - if (gpg_is_verified(PIN_ID_PW2)) { + if (gpg_pin_is_verified(PIN_ID_PW2)) { return; } break; //PW3 case 0x0104: - if (gpg_is_verified(PIN_ID_PW3)) { + if (gpg_pin_is_verified(PIN_ID_PW3)) { return; } break; @@ -160,15 +166,18 @@ char debugbuff[5]; void gpg_check_access_write_DO() { unsigned int ref; - ref = (G_gpg_vstate.io_p1 << 8) | G_gpg_vstate.io_p2 ; + 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 ; switch(ref) { //PW1 case 0x0101: case 0x0103: case 0x01F2: - if (gpg_is_verified(PIN_ID_PW2)) { + if (gpg_pin_is_verified(PIN_ID_PW2)) { return; } break; @@ -179,6 +188,7 @@ void gpg_check_access_write_DO() { case 0x0102: case 0x0104: case 0x01F1: + case 0x01F8: case 0x005E: case 0x005B: case 0x5F2D: @@ -209,7 +219,7 @@ void gpg_check_access_write_DO() { case 0x00D6: case 0x00D7: case 0x00D8: - if (gpg_is_verified(PIN_ID_PW3)) { + if (gpg_pin_is_verified(PIN_ID_PW3)) { return; } break; @@ -226,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 ; @@ -249,7 +264,7 @@ int gpg_dispatch() { case INS_TERMINATE_DF: gpg_io_discard(0); - if (G_gpg_vstate.verified_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; @@ -258,7 +273,6 @@ int gpg_dispatch() { } - /* Other commands allowed if not terminated */ if (N_gpg_pstate->histo[7] != 0x07) { THROW(SW_STATE_TERMINATED); @@ -330,23 +344,22 @@ int gpg_dispatch() { (G_gpg_vstate.io_p2 == 0x82) || (G_gpg_vstate.io_p2 == 0x83) ) { - sw = gpg_apdu_verify(G_gpg_vstate.io_p2&0x0F); + sw = gpg_apdu_verify(); break; } - THROW(0x9BF0); - //THROW(SW_INCORRECT_P1P2); + THROW(SW_INCORRECT_P1P2); case INS_CHANGE_REFERENCE_DATA: if ((G_gpg_vstate.io_p2 == 0x81) || (G_gpg_vstate.io_p2 == 0x83) ) { - sw = gpg_apdu_change_ref_data(G_gpg_vstate.io_p2&0x0F); + sw = gpg_apdu_change_ref_data(); break; } 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) ) ) { @@ -360,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 703d27f..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); } @@ -69,10 +69,12 @@ int gpg_apdu_gen() { break; default: THROW(SW_INCORRECT_P1P2); + return SW_INCORRECT_P1P2; } if (G_gpg_vstate.io_lc != 2){ THROW(SW_WRONG_LENGTH); + return SW_WRONG_LENGTH; } gpg_io_fetch_tl(&t,&l); @@ -94,6 +96,7 @@ int gpg_apdu_gen() { break; default: THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; } switch ((G_gpg_vstate.io_p1<<8)|G_gpg_vstate.io_p2) { @@ -102,7 +105,7 @@ int gpg_apdu_gen() { case 0x8001: // RSA if (keygpg->attributes.value[0] == 0x01) { -#define GPG_RSA_DEFAULT_PUB 0x010001U + unsigned char *pq; cx_rsa_public_key_t *rsa_pub; cx_rsa_private_key_t *rsa_priv, *pkey; @@ -110,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; } @@ -150,8 +144,7 @@ int gpg_apdu_gen() { cx_math_next_prime(pq+size,size); } - - cx_rsa_generate_pair(ksz, rsa_pub, rsa_priv, GPG_RSA_DEFAULT_PUB, 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); @@ -162,7 +155,7 @@ int gpg_apdu_gen() { gpg_io_clear(); goto send_rsa_pub; -#undef GPG_RSA_DEFAULT_PUB + } // ECC if ((keygpg->attributes.value[0] == 18) || @@ -171,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)); @@ -205,28 +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); @@ -248,13 +250,14 @@ int gpg_apdu_gen() { if (keygpg->pub_key.ecfp256.W_len == 0) { THROW (SW_REFERENCED_DATA_NOT_FOUND); + return 0; } gpg_io_discard(1); gpg_io_mark(); 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; @@ -277,4 +280,5 @@ int gpg_apdu_gen() { } THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; } diff --git a/src/gpg_init.c b/src/gpg_init.c index d56ff27..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,9 +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)); } + //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 @@ -252,16 +375,18 @@ int gpg_install(unsigned char app_state) { G_gpg_vstate.work.io_buffer[0] = 0x39; gpg_nvm_write(&N_gpg_pstate->sex, G_gpg_vstate.work.io_buffer, 1); - //default PW1: 1 2 3 4 5 6 + //default PW1/PW2: 1 2 3 4 5 6 os_memmove(pin.value, C_sha256_PW1, sizeof(C_sha256_PW1)); pin.length = 6; pin.counter = 3; + pin.ref = PIN_ID_PW1; gpg_nvm_write(&N_gpg_pstate->PW1, &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)); pin.length = 8; pin.counter = 3; + pin.ref = PIN_ID_PW3; gpg_nvm_write(&N_gpg_pstate->PW3, &pin, sizeof(gpg_pin_t)); //PWs status @@ -269,21 +394,38 @@ int gpg_install(unsigned char app_state) { G_gpg_vstate.work.io_buffer[1] = GPG_MAX_PW_LENGTH; G_gpg_vstate.work.io_buffer[2] = GPG_MAX_PW_LENGTH; G_gpg_vstate.work.io_buffer[3] = GPG_MAX_PW_LENGTH; - gpg_nvm_write(&N_gpg_pstate->config_slot, G_gpg_vstate.work.io_buffer, 4); + gpg_nvm_write(&N_gpg_pstate->PW_status, G_gpg_vstate.work.io_buffer, 4); //config slot G_gpg_vstate.work.io_buffer[0] = GPG_KEYS_SLOTS; G_gpg_vstate.work.io_buffer[1] = 0; G_gpg_vstate.work.io_buffer[2] = 3; // 3: selection by APDU and screen gpg_nvm_write(&N_gpg_pstate->config_slot, G_gpg_vstate.work.io_buffer, 3); - + + //config rsa pub + 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 - G_gpg_vstate.work.io_buffer[0] = PIN_MODE_SCREEN; + #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); @@ -302,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 21f2582..9a36db2 100644 --- a/src/gpg_io.c +++ b/src/gpg_io.c @@ -42,6 +42,7 @@ void gpg_io_set_offset(unsigned int offset) { } else { THROW(ERROR_IO_OFFSET); + return ; } } @@ -76,6 +77,7 @@ void gpg_io_clear() { void gpg_io_hole(unsigned int sz) { if ((G_gpg_vstate.io_length + sz) > GPG_IO_BUFFER_LENGTH) { THROW(ERROR_IO_FULL); + return ; } os_memmove(G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset+sz, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, @@ -147,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; @@ -162,6 +161,15 @@ unsigned int gpg_io_fetch_u32() { return v32; } +unsigned int gpg_io_fetch_u24() { + unsigned int v24; + v24 = ( (G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+0] << 16) | + (G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+1] << 8) | + (G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+2] << 0) ); + G_gpg_vstate.io_offset += 3; + return v24; +} + unsigned int gpg_io_fetch_u16() { unsigned int v16; v16 = ( (G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+0] << 8) | @@ -236,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; } @@ -269,6 +277,7 @@ int gpg_io_do(unsigned int io_flags) { (G_io_apdu_buffer[2] != 0x00) || (G_io_apdu_buffer[3] != 0x00) ) { THROW(SW_COMMAND_NOT_ALLOWED); + return 0; } } os_memmove(G_io_apdu_buffer, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, G_gpg_vstate.io_length); @@ -329,7 +338,8 @@ int gpg_io_do(unsigned int io_flags) { (G_io_apdu_buffer[1] != G_gpg_vstate.io_ins) || (G_io_apdu_buffer[2] != G_gpg_vstate.io_p1) || (G_io_apdu_buffer[3] != G_gpg_vstate.io_p2) ) { - THROW(SW_COMMAND_NOT_ALLOWED); + THROW(SW_COMMAND_NOT_ALLOWED); + return SW_COMMAND_NOT_ALLOWED; } G_gpg_vstate.io_cla = G_io_apdu_buffer[0]; G_gpg_vstate.io_lc = G_io_apdu_buffer[4]; diff --git a/src/gpg_main.c b/src/gpg_main.c index 40a84c5..78bb5e2 100644 --- a/src/gpg_main.c +++ b/src/gpg_main.c @@ -131,6 +131,7 @@ unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) { default: THROW(INVALID_PARAMETER); + return 0; } return 0; } @@ -164,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 421ea45..4ed7aa2 100644 --- a/src/gpg_pin.c +++ b/src/gpg_pin.c @@ -21,8 +21,8 @@ #include "gpg_ux_nanos.h" -static gpg_pin_t *gpg_get_pin(int id) { - switch (id) { +gpg_pin_t *gpg_pin_get_pin(int pinref) { + switch (pinref) { case PIN_ID_PW1 : case PIN_ID_PW2 : return &N_gpg_pstate->PW1; @@ -36,117 +36,121 @@ static gpg_pin_t *gpg_get_pin(int id) { +static int gpg_pin_get_state_index(unsigned int pinref) { + switch (pinref) { + case PIN_ID_PW1 : + return 1; + case PIN_ID_PW2 : + return 2; + case PIN_ID_PW3 : + return 3; + case PIN_ID_RC : + return 4; + + } + return -1; +} -static int gpg_check_pin_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; + if (pin->counter == 0) { - THROW(SW_PIN_BLOCKED); + return SW_PIN_BLOCKED; } counter = pin->counter-1; gpg_nvm_write(&(pin->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 0; + return SW_SECURITY_STATUS_NOT_SATISFIED; } counter = 3; gpg_nvm_write(&(pin->counter), &counter, sizeof(int)); - return 1; + return SW_OK; } -static void gpg_checkthrow_pin(gpg_pin_t *pin, unsigned char *pin_val, int pin_len) { - - if (gpg_check_pin_internal(pin,pin_val,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(pinID,0); + sw = gpg_pin_check_internal(pin,pin_val,pin_len); + if (sw == SW_OK) { + gpg_pin_set_verified(pinID,1); return; } - THROW(SW_SECURITY_STATUS_NOT_SATISFIED); + THROW(sw); + return; } -static void gpg_set_pin(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); + if (sw == SW_OK) { + gpg_pin_set_verified(pinID,1); + } + return sw; +} + +void gpg_pin_set(gpg_pin_t *pin, unsigned char *pin_val, unsigned int pin_len) { cx_sha256_t sha256; 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)); } -/* -static void gpg_unblock_pin(int id) { - gpg_pin_t *pin; - int counter; - pin = gpg_get_pin(id); - counter = 3; - gpg_nvm_write(&(pin->counter), &counter, sizeof(int)); +int gpg_pin_set_verified(int pinID, int verified) { + int idx; + idx = gpg_pin_get_state_index(pinID); + if (idx >= 0) { + G_gpg_vstate.verified_pin[idx]=verified; + return verified; + } + return 0; } -*/ - -int gpg_set_pin_verified(int id, int verified) { - G_gpg_vstate.verified_pin[id] = verified; - return verified; +int gpg_pin_is_verified(int pinID) { + int idx; + idx = gpg_pin_get_state_index(pinID); + if (idx >= 0) { + return G_gpg_vstate.verified_pin[idx]; + } + return 0; } -int gpg_check_pin(int id, unsigned char *pin_val, unsigned int pin_len) { - gpg_pin_t *pin; - pin = gpg_get_pin(id); - return gpg_set_pin_verified(id, gpg_check_pin_internal(pin,pin_val,pin_len)?1:0); -} - -/* - * - */ -void gpg_change_pin(int id, unsigned char *pin_val, unsigned int pin_len) { - gpg_pin_t *pin; - pin = gpg_get_pin(id); - gpg_set_pin(pin, pin_val, pin_len); -} - -/* - * - */ -int gpg_is_pin_verified(int id) { - return G_gpg_vstate.verified_pin[id] != 0; -} - -/* - * - */ -int gpg_is_pin_blocked(int id) { - gpg_pin_t *pin; - pin = gpg_get_pin(id); +int gpg_pin_is_blocked(gpg_pin_t *pin) { return pin->counter == 0; } -/* @return: 1 verified - * 0 not verified - * -1 blocked - */ -int gpg_apdu_verify(int id) { +int gpg_apdu_verify() { gpg_pin_t *pin; - pin = gpg_get_pin(id); + pin = gpg_pin_get_pin(G_gpg_vstate.io_p2); if (pin == NULL) { THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; } - gpg_set_pin_verified(id,0); - if (gpg_is_pin_blocked(id)) { - THROW(SW_PIN_BLOCKED); - return 0; - } - 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); @@ -160,54 +164,67 @@ int gpg_apdu_verify(int id) { return 0; } if (G_gpg_vstate.pinmode == PIN_MODE_TRUST) { - gpg_set_pin_verified(id,1); + gpg_pin_set_verified(G_gpg_vstate.io_p2,1); gpg_io_discard(1); return SW_OK; } - } - gpg_checkthrow_pin(pin, - G_gpg_vstate.work.io_buffer+ G_gpg_vstate.io_offset, - G_gpg_vstate.io_length); - gpg_set_pin_verified(id,1); - gpg_io_discard(1); - return SW_OK; -} - -int gpg_apdu_change_ref_data(int id) { - gpg_pin_t *pin; - int len, newlen; - - pin = gpg_get_pin(id); - if (pin == NULL) { THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; } - gpg_set_pin_verified(id,0); - - - // --- RC pin --- - if (id == 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); - } else { - gpg_set_pin(pin, - G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, - newlen); + //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); + + //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() { + gpg_pin_t *pin; + int len, newlen; + + pin = gpg_pin_get_pin(G_gpg_vstate.io_p2); + if (pin == NULL) { + THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; + } + + gpg_pin_set_verified(pin->ref,0); + + + + // --- PW1/PW3 pin --- - if (gpg_is_pin_blocked(id)) { + if (gpg_pin_is_blocked(pin)) { THROW(SW_PIN_BLOCKED); - return 0; + return SW_PIN_BLOCKED; } //avoid any-overflow whitout giving info if (G_gpg_vstate.io_length == 0) { @@ -225,17 +242,18 @@ int gpg_apdu_change_ref_data(int id) { len = pin->length; } - gpg_checkthrow_pin(pin, - G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, - len); + gpg_pin_check_throw(pin, pin->ref, + G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, + len); newlen = G_gpg_vstate.io_length-len; if ( (newlen > GPG_MAX_PW_LENGTH) || - ((id == PIN_ID_PW1) && (newlen < 6)) || - ((id == PIN_ID_PW3) && (newlen < 8)) ) { + ((pin->ref == PIN_ID_PW1) && (newlen < 6)) || + ((pin->ref == PIN_ID_PW3) && (newlen < 8)) ) { THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; } - gpg_set_pin(pin, + gpg_pin_set(pin, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset+len, newlen); gpg_io_discard(1); @@ -244,19 +262,23 @@ int gpg_apdu_change_ref_data(int id) { int gpg_apdu_reset_retry_counter() { gpg_pin_t *pin_pw1; + gpg_pin_t *pin_pw3; gpg_pin_t *pin_rc; int rc_len, pw1_len; - pin_pw1 = gpg_get_pin(PIN_ID_PW1); + pin_pw1 = gpg_pin_get_pin(PIN_ID_PW1); + pin_pw3 = gpg_pin_get_pin(PIN_ID_PW3); + pin_rc = gpg_pin_get_pin(PIN_ID_RC); if (G_gpg_vstate.io_p1 == 2) { - if (!G_gpg_vstate.verified_pin[PIN_ID_PW3]) { + if (!gpg_pin_is_verified(PIN_ID_PW3)) { THROW(SW_SECURITY_STATUS_NOT_SATISFIED); + return SW_SECURITY_STATUS_NOT_SATISFIED; } rc_len = 0; pw1_len = G_gpg_vstate.io_length; } else { - pin_rc = gpg_get_pin(PIN_ID_RC); + //avoid any-overflow whitout giving info if (pin_rc->length > G_gpg_vstate.io_length) { rc_len = G_gpg_vstate.io_length; @@ -264,15 +286,16 @@ int gpg_apdu_reset_retry_counter() { rc_len = pin_rc->length; } pw1_len = G_gpg_vstate.io_length-rc_len; - gpg_checkthrow_pin(pin_rc, - G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, - rc_len); + gpg_pin_check_throw(pin_rc,pin_rc->ref, + G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, + rc_len); } if ((pw1_len > GPG_MAX_PW_LENGTH) ||(pw1_len < 6)) { THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; } - gpg_set_pin(pin_pw1, + gpg_pin_set(pin_pw1, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset+rc_len, pw1_len); gpg_io_discard(1); diff --git a/src/gpg_pso.c b/src/gpg_pso.c index 916dc44..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 @@ -26,6 +27,11 @@ const unsigned char gpg_oid_sha512[] = { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 }; +static void gpg_pso_reset_PW1() { + if (N_gpg_pstate->PW_status[0] ==0) { + gpg_pin_set_verified(PIN_ID_PW1,0); + } +} static int gpg_sign(gpg_key_t *sigkey) { // --- RSA @@ -36,20 +42,21 @@ 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) { THROW(SW_CONDITIONS_NOT_SATISFIED); + return SW_CONDITIONS_NOT_SATISFIED; } //sign @@ -70,6 +77,7 @@ static int gpg_sign(gpg_key_t *sigkey) { //send gpg_io_discard(0); gpg_io_inserted(ksz); + gpg_pso_reset_PW1(); return SW_OK; } // --- ECDSA/EdDSA @@ -79,22 +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); - } + 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) { @@ -108,31 +121,91 @@ 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; } // --- PSO:CDS NOT SUPPORTED THROW(SW_REFERENCED_DATA_NOT_FOUND); - + 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: { - return gpg_sign(&G_gpg_vstate.kslot->sig); + unsigned int cnt; + int sw; + sw = gpg_sign(&G_gpg_vstate.kslot->sig); + cnt = G_gpg_vstate.kslot->sig_count+1; + 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: { unsigned int msg_len; @@ -141,32 +214,34 @@ int gpg_apdu_pso(unsigned int pso) { pad_byte = gpg_io_fetch_u8(); switch(pad_byte) { - // --- RSA + // --- 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 SW_CONDITIONS_NOT_SATISFIED; } msg_len = G_gpg_vstate.io_length - G_gpg_vstate.io_offset; sz = cx_rsa_decrypt(key, @@ -179,47 +254,54 @@ int gpg_apdu_pso(unsigned int pso) { gpg_io_inserted(sz); return SW_OK; } - // --- AES + + // --- PSO:DEC:AES case 0x02: { 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_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); return SW_OK; } - // --- ECDH + + // --- PSO:DEC:ECDH case 0xA6: { cx_ecfp_private_key_t *key; unsigned int sz; unsigned int curve; - if (G_gpg_vstate.kslot->dec.attributes.value[0] != 18) { - THROW(SW_CONDITIONS_NOT_SATISFIED); - } - key = &G_gpg_vstate.kslot->dec.key.ecfp256; - if (key->d_len != 32) { + if (G_gpg_vstate.mse_dec->attributes.value[0] != 0x12) { 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) { THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; } gpg_io_fetch_tl(&t, &l); if (t != 0x86) { THROW(SW_WRONG_DATA); + 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; @@ -229,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]; } @@ -238,36 +320,47 @@ 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); gpg_io_insert( G_gpg_vstate.work.io_buffer+128,sz); return SW_OK; } - // --- PSO:DEC NOT SUPPORTDED - default: - THROW(SW_REFERENCED_DATA_NOT_FOUND); - } - //--- PSO NOT SUPPPORTED --- + // --- PSO:DEC:xx NOT SUPPORTDED + default: + THROW(SW_REFERENCED_DATA_NOT_FOUND); + return SW_REFERENCED_DATA_NOT_FOUND; + } + } + + //--- PSO:yy NOT SUPPPORTED --- default: THROW(SW_REFERENCED_DATA_NOT_FOUND); - + return SW_REFERENCED_DATA_NOT_FOUND; } - THROW(SW_WRONG_DATA); - }} + THROW(SW_REFERENCED_DATA_NOT_FOUND); + return SW_REFERENCED_DATA_NOT_FOUND; +} 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 ccfe145..6c36ac5 100644 --- a/src/gpg_select.c +++ b/src/gpg_select.c @@ -19,24 +19,41 @@ #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; G_gpg_vstate.DO_offset = 0; if ( G_gpg_vstate.selected == 0) { - G_gpg_vstate.verified_pin[PIN_ID_PW1] = 0; - G_gpg_vstate.verified_pin[PIN_ID_PW2] = 0; - G_gpg_vstate.verified_pin[PIN_ID_PW3] = 0; - G_gpg_vstate.verified_pin[PIN_ID_RC] = 0; + G_gpg_vstate.verified_pin[0] = 0; + G_gpg_vstate.verified_pin[1] = 0; + G_gpg_vstate.verified_pin[2] = 0; + G_gpg_vstate.verified_pin[3] = 0; + G_gpg_vstate.verified_pin[4] = 0; } + 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; } return sw; } diff --git a/src/gpg_types.h b/src/gpg_types.h index f9b4451..c98b766 100644 --- a/src/gpg_types.h +++ b/src/gpg_types.h @@ -36,11 +36,13 @@ #define GPG_KEY_ATTRIBUTES_LENGTH 12 +#define GPG_RSA_DEFAULT_PUB 0x00010001U struct gpg_pin_s { + unsigned int ref; //initial pin length, 0 means not set - unsigned int length; - unsigned int counter; + unsigned int length; + unsigned int counter; //only store sha256 of PIN/RC unsigned char value[32]; }; @@ -64,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]; @@ -113,6 +125,8 @@ struct gpg_nv_state_s { /* 01F1 (01F2 is volatile)*/ unsigned char config_slot[3]; + /* RSA exponent */ + unsigned char default_RSA_exponent[4]; /* 0101 0102 0103 0104 */ LV(private_DO1, GPG_EXT_PRIVATE_DO_LENGTH); @@ -169,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; @@ -184,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 { @@ -223,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; @@ -256,10 +290,10 @@ typedef struct gpg_v_state_s gpg_v_state_t; #define IO_OFFSET_END (unsigned int)-1 #define IO_OFFSET_MARK (unsigned int)-2 -#define PIN_ID_PW1 1 -#define PIN_ID_PW2 2 -#define PIN_ID_PW3 3 -#define PIN_ID_RC 4 +#define PIN_ID_PW1 0x81 +#define PIN_ID_PW2 0x82 +#define PIN_ID_PW3 0x83 +#define PIN_ID_RC 0x84 #define PIN_MODE_HOST 1 #define PIN_MODE_SCREEN 2 @@ -280,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 3b58e1a..d9e1808 100644 --- a/src/gpg_ux_nanos.c +++ b/src/gpg_ux_nanos.c @@ -32,26 +32,6 @@ /* --- NanoS UI layout --- */ /* ----------------------------------------------------------------------- */ -#define PICSTR(x) ((char*)PIC(x)) - -#define TEMPLATE_TYPE PICSTR(C_TEMPLATE_TYPE) -#define TEMPLATE_KEY PICSTR(C_TEMPLATE_KEY) -#define INVALID_SELECTION PICSTR(C_INVALID_SELECTION) -#define OK PICSTR(C_OK) -#define NOK PICSTR(C_NOK) -#define WRONG_PIN PICSTR(C_WRONG_PIN) -#define RIGHT_PIN PICSTR(C_RIGHT_PIN) -#define PIN_CHANGED PICSTR(C_PIN_CHANGED) -#define PIN_DIFFERS PICSTR(C_PIN_DIFFERS) -#define PIN_USER PICSTR(C_PIN_USER) -#define PIN_ADMIN PICSTR(C_PIN_ADMIN) -#define VERIFIED PICSTR(C_VERIFIED) -#define NOT_VERIFIED PICSTR(C_NOT_VERIFIED) -#define ALLOWED PICSTR(C_ALLOWED) -#define NOT_ALLOWED PICSTR(C_NOT_ALLOWED) -#define DEFAULT_MODE PICSTR(C_DEFAULT_MODE) - - const ux_menu_entry_t ui_menu_template[]; void ui_menu_template_display(unsigned int value); @@ -111,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[] = { @@ -172,13 +268,13 @@ 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_set_pin_verified(G_gpg_vstate.io_p2&0x0F,0); + gpg_pin_set_verified(G_gpg_vstate.io_p2,0); sw = 0x6985; break; case BUTTON_EVT_RELEASED|BUTTON_RIGHT: // OK - gpg_set_pin_verified(G_gpg_vstate.io_p2&0x0F,1); - sw = 0x9000; + gpg_pin_set_verified(G_gpg_vstate.io_p2,1); + sw = 0x9000; break; default: return 0; @@ -249,7 +345,7 @@ unsigned int ui_pinentry_prepro(const bagl_element_t* element) { case 2: snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "Confirm %s PIN", (G_gpg_vstate.io_p2 == 0x83)?"Admin":"User"); break; - default: + default: snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "WAT %s PIN", (G_gpg_vstate.io_p2 == 0x83)?"Admin":"User"); break; } @@ -260,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] = '-'; } @@ -336,26 +440,25 @@ unsigned int ui_pinentry_nanos_button(unsigned int button_mask, unsigned int but // >= 0 static unsigned int validate_pin() { unsigned int offset, len, sw; + gpg_pin_t *pin; + for (offset = 1; offset< G_gpg_vstate.ux_pinentry[0];offset++) { G_gpg_vstate.menu[offset] = C_pin_digit[G_gpg_vstate.ux_pinentry[offset]]; } if (G_gpg_vstate.io_ins == 0x20) { - if (gpg_check_pin(G_gpg_vstate.io_p2&0x0F, (unsigned char*)(G_gpg_vstate.menu+1), G_gpg_vstate.ux_pinentry[0])) { - sw = SW_OK; - } else { - sw = SW_CONDITIONS_NOT_SATISFIED; - } + pin = gpg_pin_get_pin(G_gpg_vstate.io_p2); + 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) { - ui_info(WRONG_PIN, NULL, ui_menu_main_display, 0); + 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); } else { - ui_info(RIGHT_PIN, NULL, ui_menu_main_display, 0); + ui_menu_main_display(0); } - return 0; } if (G_gpg_vstate.io_ins == 0x24) { @@ -365,10 +468,12 @@ static unsigned int validate_pin() { G_gpg_vstate.io_p1++; } if (G_gpg_vstate.io_p1 == 3) { - if (!gpg_check_pin(G_gpg_vstate.io_p2&0x0F, G_gpg_vstate.work.io_buffer+1, G_gpg_vstate.work.io_buffer[0])) { + pin = gpg_pin_get_pin(G_gpg_vstate.io_p2); + 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); + snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), " %d tries remaining", pin->counter ); ui_info(WRONG_PIN, NULL, ui_menu_main_display, 0); return 0; } @@ -381,11 +486,12 @@ static unsigned int validate_pin() { gpg_io_do(IO_RETURN_AFTER_TX); ui_info(PIN_DIFFERS, NULL, ui_menu_main_display, 0); } else { - gpg_change_pin(G_gpg_vstate.io_p2&0x0F, G_gpg_vstate.work.io_buffer+offset+ 1, len); + gpg_pin_set(gpg_pin_get_pin(G_gpg_vstate.io_p2), G_gpg_vstate.work.io_buffer+offset+ 1, len); gpg_io_discard(1); gpg_io_insert_u16(SW_OK); gpg_io_do(IO_RETURN_AFTER_TX); - ui_info(PIN_CHANGED, NULL, ui_menu_main_display, 0); + //ui_info(PIN_CHANGED, NULL, ui_menu_main_display, 0); + ui_menu_main_display(0); } return 0; } else { @@ -400,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}, @@ -449,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; @@ -475,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)); @@ -492,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: @@ -524,7 +655,7 @@ void ui_menu_tmpl_set_action(unsigned int value) { } break; - default: + default: err = TEMPLATE_TYPE; goto ERROR; } @@ -571,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 }; @@ -673,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); } } @@ -680,15 +817,16 @@ void ui_menu_pinmode_action(unsigned int value) { switch (value) { case PIN_MODE_HOST: case PIN_MODE_SCREEN: - if (!gpg_is_pin_verified(PIN_ID_PW1)) { + 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_is_pin_verified(PIN_ID_PW3)) { + if (!gpg_pin_is_verified(PIN_ID_PW3)) { ui_info(PIN_ADMIN, NOT_VERIFIED, ui_menu_pinmode_display,0); return; } @@ -702,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,8 +925,8 @@ const ux_menu_entry_t ui_menu_reset[] = { #error menu definition not correct for current value of GPG_KEYS_SLOTS #endif {NULL, NULL, 0, NULL, "Really Reset ?", NULL, 0, 0}, - {NULL, ui_menu_main_display, 0, &C_badge_back, "Oh No!", NULL, 61, 40}, - {NULL, ui_menu_reset_action, 0, NULL, "Yes!", NULL, 0, 0}, + {NULL, ui_menu_main_display, 0, &C_badge_back, "No", NULL, 61, 40}, + {NULL, ui_menu_reset_action, 0, NULL, "Yes", NULL, 0, 0}, UX_MENU_END }; @@ -728,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 @@ -785,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(); } } @@ -803,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