1.2.0 code
SDK 1.4.2.x port Add uif support Refactor memory layout Try to add more curve gpg 2.2.x handle curves other than ed25519 in a such strange way that it is very difficult to do such support. So secp256k1, secp256r1 and brainpoolp256 seems works, but according to gpg code it works by side effects :-/ Update user documentation for UIF There is still an issue with ssh authentication with Ed25519. It works with NIST-P256, Brainpool256 curves
This commit is contained in:
parent
a0d537dcec
commit
281ea42cbb
17
Makefile
17
Makefile
@ -15,13 +15,6 @@
|
||||
# limitations under the License.
|
||||
#*******************************************************************************
|
||||
|
||||
BOLOS_SDK=/home/cme/Projects/Git/ledger/nanos-secure-sdk-cslashm
|
||||
|
||||
|
||||
CLANGPATH=/home/cme/Projects/Git/ledger/compilers/clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-16.10/bin/
|
||||
GCCPATH=/home/cme/Projects/Git/ledger/compilers/gcc-arm-none-eabi-5_3-2016q1/bin/
|
||||
|
||||
|
||||
ifeq ($(BOLOS_SDK),)
|
||||
$(error Environment variable BOLOS_SDK is not set)
|
||||
endif
|
||||
@ -34,8 +27,8 @@ APPNAME = OpenPGP
|
||||
SPECVERSION="3.3.1"
|
||||
|
||||
APPVERSION_M=1
|
||||
APPVERSION_N=1
|
||||
APPVERSION_P=1
|
||||
APPVERSION_N=2
|
||||
APPVERSION_P=0
|
||||
APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)
|
||||
|
||||
ifeq ($(TARGET_NAME),TARGET_BLUE)
|
||||
@ -81,7 +74,7 @@ DEFINES += HAVE_USB_CLASS_CCID
|
||||
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))
|
||||
|
||||
@ -96,7 +89,7 @@ LDLIBS += -lm -lgcc -lc
|
||||
include $(BOLOS_SDK)/Makefile.glyphs
|
||||
|
||||
### variables processed by the common makefile.rules of the SDK to grab source files and include dirs
|
||||
APP_SOURCE_PATH += src src/lib_stusb_impl
|
||||
APP_SOURCE_PATH += src
|
||||
SDK_SOURCE_PATH += lib_stusb
|
||||
|
||||
|
||||
@ -107,7 +100,7 @@ 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
|
||||
|
29
Makefile.rules
Normal file
29
Makefile.rules
Normal file
@ -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
|
Binary file not shown.
@ -185,6 +185,7 @@ The full menu layout is :
|
||||
| Set on
|
||||
| Set off
|
||||
| PIN mode
|
||||
| UIF mode
|
||||
| \ *Choose:*
|
||||
| Host
|
||||
| On Screen
|
||||
@ -246,8 +247,39 @@ 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`` command line to easily set
|
||||
up the desired template. The menu fixes that.
|
||||
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 <tag> <curvename>" /bye
|
||||
gpg-connect-agent "SCD SETATTR KEY-ATTR --force 2 18 <curvename>" /bye
|
||||
gpg-connect-agent "SCD SETATTR KEY-ATTR --force 3 <tag> <curvename>" /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.
|
||||
@ -255,10 +287,11 @@ 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
|
||||
@ -266,6 +299,7 @@ The current mode is displayed in the first sub menu. To activate the seeded
|
||||
|
||||
When the application starts, the seeded mode is always set to *OFF*
|
||||
|
||||
**WARNING** : SEED MODE IS EXPERIMENTAL
|
||||
|
||||
PIN mode
|
||||
~~~~~~~~
|
||||
@ -340,6 +374,17 @@ This is the default mode after application installation.
|
||||
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
|
||||
~~~~~
|
||||
|
||||
|
@ -18,7 +18,9 @@
|
||||
|
||||
void USBD_CCID_activate_pinpad(int enabled);
|
||||
|
||||
int gpg_oid2curve(unsigned char* oid, unsigned int len);
|
||||
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);
|
||||
|
@ -367,34 +367,25 @@ int gpg_apdu_put_data(unsigned int ref) {
|
||||
//check length
|
||||
ksz = (keygpg->attributes.value[1]<<8)|keygpg->attributes.value[2];
|
||||
ksz = ksz >> 3;
|
||||
rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa.public;
|
||||
rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa.private;
|
||||
pkey = &keygpg->priv_key.rsa;
|
||||
switch(ksz) {
|
||||
case 1024/8:
|
||||
rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa1024.public;
|
||||
rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa1024.private;
|
||||
pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa1024;
|
||||
pkey_size = sizeof(cx_rsa_1024_private_key_t);
|
||||
pq = G_gpg_vstate.work.rsa1024.public.n;
|
||||
pq = G_gpg_vstate.work.rsa.public1024.n;
|
||||
break;
|
||||
case 2048/8:
|
||||
rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa2048.public;
|
||||
rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa2048.private;
|
||||
pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa2048;
|
||||
pkey_size = sizeof(cx_rsa_2048_private_key_t);
|
||||
pq = G_gpg_vstate.work.rsa2048.public.n;
|
||||
pq = G_gpg_vstate.work.rsa.public2048.n;
|
||||
break;
|
||||
case 3072/8:
|
||||
rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa3072.public;
|
||||
rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa3072.private;
|
||||
pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa3072;
|
||||
pkey_size = sizeof(cx_rsa_3072_private_key_t);
|
||||
pq = G_gpg_vstate.work.rsa3072.public.n;
|
||||
pq = G_gpg_vstate.work.rsa.public3072.n;
|
||||
break;
|
||||
case 4096/8:
|
||||
rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa4096.public;
|
||||
rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa4096.private;
|
||||
pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa4096;
|
||||
pkey_size = sizeof(cx_rsa_4096_private_key_t);
|
||||
pq = G_gpg_vstate.work.rsa4096.public.n;
|
||||
pq = G_gpg_vstate.work.rsa.public4096.n;
|
||||
break;
|
||||
}
|
||||
ksz = ksz>>1;
|
||||
@ -460,14 +451,14 @@ int gpg_apdu_put_data(unsigned int ref) {
|
||||
THROW(SW_WRONG_DATA);
|
||||
return 0;
|
||||
}
|
||||
ksz = 32;
|
||||
if (ksz == 32) {
|
||||
G_gpg_vstate.work.ecfp256.private.curve = curve;
|
||||
G_gpg_vstate.work.ecfp256.private.d_len = ksz;
|
||||
os_memmove(G_gpg_vstate.work.ecfp256.private.d, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset,ksz);
|
||||
cx_ecfp_generate_pair(curve, &G_gpg_vstate.work.ecfp256.public, &G_gpg_vstate.work.ecfp256.private, 1);
|
||||
nvm_write(&keygpg->pub_key.ecfp256, &G_gpg_vstate.work.ecfp256.public, sizeof(cx_ecfp_public_key_t));
|
||||
nvm_write(&keygpg->key.ecfp256, &G_gpg_vstate.work.ecfp256.private, sizeof(cx_ecfp_private_key_t));
|
||||
ksz = gpg_curve2domainlen(curve);
|
||||
if (ksz == len_p) {
|
||||
G_gpg_vstate.work.ecfp.private.curve = curve;
|
||||
G_gpg_vstate.work.ecfp.private.d_len = ksz;
|
||||
os_memmove(G_gpg_vstate.work.ecfp.private.d, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset,ksz);
|
||||
cx_ecfp_generate_pair(curve, &G_gpg_vstate.work.ecfp.public, &G_gpg_vstate.work.ecfp.private, 1);
|
||||
nvm_write(&keygpg->pub_key.ecfp, &G_gpg_vstate.work.ecfp.public, sizeof(cx_ecfp_public_key_t));
|
||||
nvm_write(&keygpg->priv_key.ecfp, &G_gpg_vstate.work.ecfp.private, sizeof(cx_ecfp_private_key_t));
|
||||
if (reset_cnt) {
|
||||
reset_cnt = 0;
|
||||
nvm_write(&G_gpg_vstate.kslot->sig_count,&reset_cnt,sizeof(unsigned int));
|
||||
|
@ -113,29 +113,20 @@ int gpg_apdu_gen() {
|
||||
|
||||
ksz = (keygpg->attributes.value[1]<<8)|keygpg->attributes.value[2];
|
||||
ksz = ksz >> 3;
|
||||
rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa.public;
|
||||
rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa.private;
|
||||
pkey = &keygpg->priv_key.rsa;
|
||||
switch(ksz) {
|
||||
case 1024/8:
|
||||
rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa1024.public;
|
||||
rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa1024.private;
|
||||
pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa1024;
|
||||
pkey_size = sizeof(cx_rsa_1024_private_key_t);
|
||||
break;
|
||||
case 2048/8:
|
||||
rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa2048.public;
|
||||
rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa2048.private;
|
||||
pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa2048;
|
||||
pkey_size = sizeof(cx_rsa_2048_private_key_t);
|
||||
break;
|
||||
case 3072/8:
|
||||
rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa3072.public;
|
||||
rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa3072.private;
|
||||
pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa3072;
|
||||
pkey_size = sizeof(cx_rsa_3072_private_key_t);
|
||||
break;
|
||||
case 4096/8:
|
||||
rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa4096.public;
|
||||
rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa4096.private;
|
||||
pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa4096;
|
||||
pkey_size = sizeof(cx_rsa_4096_private_key_t);
|
||||
break;
|
||||
}
|
||||
@ -173,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));
|
||||
@ -207,32 +203,32 @@ int gpg_apdu_gen() {
|
||||
gpg_io_mark();
|
||||
switch(ksz) {
|
||||
case 1024/8:
|
||||
if (keygpg->key.rsa1024.size == 0) {
|
||||
if (keygpg->priv_key.rsa1024.size == 0) {
|
||||
THROW (SW_REFERENCED_DATA_NOT_FOUND);
|
||||
return SW_REFERENCED_DATA_NOT_FOUND;
|
||||
}
|
||||
gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->key.rsa1024.n);
|
||||
gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->priv_key.rsa1024.n);
|
||||
break;
|
||||
case 2048/8:
|
||||
if (keygpg->key.rsa2048.size == 0) {
|
||||
if (keygpg->priv_key.rsa2048.size == 0) {
|
||||
THROW (SW_REFERENCED_DATA_NOT_FOUND);
|
||||
return SW_REFERENCED_DATA_NOT_FOUND;
|
||||
}
|
||||
gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->key.rsa2048.n);
|
||||
gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->priv_key.rsa2048.n);
|
||||
break;
|
||||
case 3072/8:
|
||||
if (keygpg->key.rsa3072.size == 0) {
|
||||
if (keygpg->priv_key.rsa3072.size == 0) {
|
||||
THROW (SW_REFERENCED_DATA_NOT_FOUND);
|
||||
return SW_REFERENCED_DATA_NOT_FOUND;
|
||||
}
|
||||
gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->key.rsa3072.n);
|
||||
gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->priv_key.rsa3072.n);
|
||||
break;
|
||||
case 4096/8:
|
||||
if (keygpg->key.rsa4096.size == 0) {
|
||||
if (keygpg->priv_key.rsa4096.size == 0) {
|
||||
THROW (SW_REFERENCED_DATA_NOT_FOUND);
|
||||
return SW_REFERENCED_DATA_NOT_FOUND;
|
||||
}
|
||||
gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->key.rsa4096.n);
|
||||
gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->priv_key.rsa4096.n);
|
||||
break;
|
||||
}
|
||||
gpg_io_insert_tlv(0x82, 4, keygpg->pub_key.rsa);
|
||||
|
171
src/gpg_init.c
171
src/gpg_init.c
@ -32,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,
|
||||
@ -60,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) ) {
|
||||
@ -73,21 +115,87 @@ 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 -- */
|
||||
/* -------------------------------*/
|
||||
@ -123,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
|
||||
@ -155,7 +263,7 @@ const unsigned char C_default_AlgoAttrRSA[] = {
|
||||
0x01
|
||||
};
|
||||
|
||||
#if 1
|
||||
#if 0
|
||||
const unsigned char C_default_AlgoAttrECC_sig[] = {
|
||||
// ecdsa
|
||||
0x13,
|
||||
@ -168,8 +276,20 @@ 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
|
||||
0x16,
|
||||
@ -283,7 +403,6 @@ int gpg_install(unsigned char app_state) {
|
||||
gpg_nvm_write(&N_gpg_pstate->config_slot, G_gpg_vstate.work.io_buffer, 3);
|
||||
|
||||
//config rsa pub
|
||||
#define GPG_RSA_DEFAULT_PUB 0x00010001
|
||||
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;
|
||||
@ -304,7 +423,10 @@ int gpg_install(unsigned char app_state) {
|
||||
//default key template: RSA 2048)
|
||||
|
||||
for (int s = 0; s< GPG_KEYS_SLOTS; s++) {
|
||||
#if 0
|
||||
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);
|
||||
gpg_nvm_write(&N_gpg_pstate->keys[s].sig.attributes.length, &l, sizeof(unsigned int));
|
||||
@ -322,6 +444,11 @@ 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);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
@ -41,16 +42,16 @@ static int gpg_sign(gpg_key_t *sigkey) {
|
||||
ksz = ksz>>3;
|
||||
switch(ksz) {
|
||||
case 1024/8:
|
||||
key = (cx_rsa_private_key_t *)&sigkey->key.rsa1024;
|
||||
key = (cx_rsa_private_key_t *)&sigkey->priv_key.rsa1024;
|
||||
break;
|
||||
case 2048/8:
|
||||
key = (cx_rsa_private_key_t *)&sigkey->key.rsa2048;
|
||||
key = (cx_rsa_private_key_t *)&sigkey->priv_key.rsa2048;
|
||||
break;
|
||||
case 3072/8:
|
||||
key = (cx_rsa_private_key_t *)&sigkey->key.rsa3072;
|
||||
key = (cx_rsa_private_key_t *)&sigkey->priv_key.rsa3072;
|
||||
break;
|
||||
case 4096/8:
|
||||
key = (cx_rsa_private_key_t *)&sigkey->key.rsa4096;
|
||||
key = (cx_rsa_private_key_t *)&sigkey->priv_key.rsa4096;
|
||||
break;
|
||||
}
|
||||
if (key->size != ksz) {
|
||||
@ -83,21 +84,21 @@ static int gpg_sign(gpg_key_t *sigkey) {
|
||||
if ((sigkey->attributes.value[0] == 19) ||
|
||||
(sigkey->attributes.value[0] == 22)) {
|
||||
cx_ecfp_private_key_t *key;
|
||||
unsigned int sz,i,rs_len,info;
|
||||
unsigned int sz,i,rs_len;
|
||||
unsigned char *rs;
|
||||
|
||||
key = &sigkey->key.ecfp256;
|
||||
if (key->d_len != 32) {
|
||||
THROW(SW_CONDITIONS_NOT_SATISFIED);
|
||||
return SW_CONDITIONS_NOT_SATISFIED;
|
||||
}
|
||||
key = &sigkey->priv_key.ecfp;
|
||||
//sign
|
||||
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, 32/*G_gpg_vstate.io_length*/,
|
||||
G_gpg_vstate.work.io_buffer, sz,
|
||||
G_gpg_vstate.work.io_buffer, GPG_IO_BUFFER_LENGTH,
|
||||
NULL);
|
||||
//reencode r,s in MPI format
|
||||
@ -149,19 +150,27 @@ int gpg_apdu_pso() {
|
||||
switch(pso) {
|
||||
// --- PSO:CDS ---
|
||||
case 0x9e9a:
|
||||
if ((G_gpg_vstate.kslot->sig.UIF[0]) && ((G_gpg_vstate.UIF_flags)==0)) {
|
||||
ui_menu_uifconfirm_display(0);
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
|
||||
case 0x8680:
|
||||
if ((G_gpg_vstate.kslot->dec.UIF[0]) && ((G_gpg_vstate.UIF_flags)==0)) {
|
||||
ui_menu_uifconfirm_display(0);
|
||||
return 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: {
|
||||
@ -214,16 +223,16 @@ int gpg_apdu_pso() {
|
||||
key = NULL;
|
||||
switch(ksz) {
|
||||
case 1024/8:
|
||||
key = (cx_rsa_private_key_t *)&G_gpg_vstate.mse_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.mse_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.mse_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.mse_dec->key.rsa4096;
|
||||
key = (cx_rsa_private_key_t *)&G_gpg_vstate.mse_dec->priv_key.rsa4096;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -272,11 +281,7 @@ int gpg_apdu_pso() {
|
||||
THROW(SW_CONDITIONS_NOT_SATISFIED);
|
||||
return SW_CONDITIONS_NOT_SATISFIED;
|
||||
}
|
||||
key = &G_gpg_vstate.mse_dec->key.ecfp256;
|
||||
if (key->d_len != 32) {
|
||||
THROW(SW_CONDITIONS_NOT_SATISFIED);
|
||||
return SW_CONDITIONS_NOT_SATISFIED;
|
||||
}
|
||||
key = &G_gpg_vstate.mse_dec->priv_key.ecfp;
|
||||
gpg_io_fetch_l(&l);
|
||||
gpg_io_fetch_tl(&t, &l);
|
||||
if (t != 0x7f49) {
|
||||
@ -290,6 +295,10 @@ int gpg_apdu_pso() {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -335,9 +344,13 @@ int gpg_apdu_pso() {
|
||||
|
||||
|
||||
int gpg_apdu_internal_authenticate() {
|
||||
if ((G_gpg_vstate.kslot->aut.UIF[0]) && ((G_gpg_vstate.UIF_flags)==0)) {
|
||||
ui_menu_uifconfirm_display(0);
|
||||
return 0;
|
||||
// --- 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.mse_aut->attributes.value[0] == 1) {
|
||||
|
@ -36,7 +36,7 @@
|
||||
|
||||
#define GPG_KEY_ATTRIBUTES_LENGTH 12
|
||||
|
||||
#define GPG_RSA_DEFAULT_PUB 0x010001U
|
||||
#define GPG_RSA_DEFAULT_PUB 0x00010001U
|
||||
|
||||
struct gpg_pin_s {
|
||||
unsigned int ref;
|
||||
@ -66,15 +66,25 @@ typedef struct gpg_key_s {
|
||||
LV(attributes,GPG_KEY_ATTRIBUTES_LENGTH);
|
||||
/* key value */
|
||||
union {
|
||||
cx_rsa_private_key_t rsa;
|
||||
cx_rsa_1024_private_key_t rsa1024;
|
||||
cx_rsa_2048_private_key_t rsa2048;
|
||||
cx_rsa_3072_private_key_t rsa3072;
|
||||
cx_rsa_4096_private_key_t rsa4096;
|
||||
cx_ecfp_private_key_t ecfp256;
|
||||
} key;
|
||||
|
||||
cx_ecfp_private_key_t ecfp;
|
||||
cx_ecfp_256_private_key_t ecfp256;
|
||||
cx_ecfp_384_private_key_t ecfp384;
|
||||
cx_ecfp_512_private_key_t ecfp512;
|
||||
cx_ecfp_640_private_key_t ecfp640;
|
||||
} priv_key;
|
||||
union {
|
||||
unsigned char rsa[4];
|
||||
cx_ecfp_public_key_t ecfp256;
|
||||
unsigned char rsa[4];
|
||||
cx_ecfp_public_key_t ecfp;
|
||||
cx_ecfp_256_public_key_t ecfp256;
|
||||
cx_ecfp_384_public_key_t ecfp384;
|
||||
cx_ecfp_512_public_key_t ecfp512;
|
||||
cx_ecfp_640_public_key_t ecfp640;
|
||||
} pub_key;
|
||||
/* C7 C8 C9 , C5 = C7|C8|C9*/
|
||||
unsigned char fingerprints[20];
|
||||
@ -193,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 {
|
||||
|
@ -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";
|
||||
|
@ -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
|
||||
|
@ -134,7 +134,7 @@ void ui_menu_uifconfirm_display(unsigned int value) {
|
||||
|
||||
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 Operation:");
|
||||
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "Confirm:");
|
||||
return 1;
|
||||
}
|
||||
if (element->component.userid == 2) {
|
||||
@ -175,7 +175,14 @@ unsigned int ui_uifconfirm_nanos_button(unsigned int button_mask, unsigned int b
|
||||
BEGIN_TRY {
|
||||
TRY {
|
||||
G_gpg_vstate.UIF_flags = 1;
|
||||
sw = gpg_apdu_pso();
|
||||
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);
|
||||
@ -499,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},
|
||||
@ -548,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;
|
||||
@ -574,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));
|
||||
@ -591,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:
|
||||
@ -623,7 +655,7 @@ void ui_menu_tmpl_set_action(unsigned int value) {
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
default:
|
||||
err = TEMPLATE_TYPE;
|
||||
goto ERROR;
|
||||
}
|
||||
@ -670,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
|
||||
};
|
||||
|
||||
@ -803,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[] = {
|
||||
@ -829,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
|
||||
|
@ -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
|
1057
src/sdk/usbd_ccid_cmd.c
Executable file
1057
src/sdk/usbd_ccid_cmd.c
Executable file
File diff suppressed because it is too large
Load Diff
617
src/sdk/usbd_ccid_if.c
Executable file
617
src/sdk/usbd_ccid_if.c
Executable file
@ -0,0 +1,617 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file usbd_ccid_if.c
|
||||
* @author MCD Application Team
|
||||
* @version V1.0.1
|
||||
* @date 31-January-2014
|
||||
* @brief This file provides all the functions for USB Interface for CCID
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© COPYRIGHT 2014 STMicroelectronics</center></h2>
|
||||
*
|
||||
* 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 "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 ---------------------------------------------------------*/
|
||||
usb_class_ccid_t G_io_ccid;
|
||||
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
static void CCID_Response_SendData (USBD_HandleTypeDef *pdev,
|
||||
uint8_t* pbuf,
|
||||
uint16_t len);
|
||||
/* Private function ----------------------------------------------------------*/
|
||||
/**
|
||||
* @brief CCID_Init
|
||||
* Initialize the CCID USB Layer
|
||||
* @param pdev: device instance
|
||||
* @retval None
|
||||
*/
|
||||
void CCID_Init (USBD_HandleTypeDef *pdev)
|
||||
{
|
||||
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 */
|
||||
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
|
||||
io_usb_ccid_set_card_inserted(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief CCID_DeInit
|
||||
* Uninitialize the CCID Machine
|
||||
* @param pdev: device instance
|
||||
* @retval None
|
||||
*/
|
||||
void CCID_DeInit (USBD_HandleTypeDef *pdev)
|
||||
{
|
||||
UNUSED(pdev);
|
||||
G_io_ccid.Ccid_BulkState = CCID_STATE_IDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief CCID_Message_In
|
||||
* Handle Bulk IN & Intr IN data stage
|
||||
* @param pdev: device instance
|
||||
* @param uint8_t epnum: endpoint index
|
||||
* @retval None
|
||||
*/
|
||||
void CCID_BulkMessage_In (USBD_HandleTypeDef *pdev,
|
||||
uint8_t epnum)
|
||||
{
|
||||
if (epnum == (CCID_BULK_IN_EP & 0x7F))
|
||||
{/* Filter the epnum by masking with 0x7f (mask of IN Direction) */
|
||||
|
||||
/*************** Handle Bulk Transfer IN data completion *****************/
|
||||
|
||||
switch (G_io_ccid.Ccid_BulkState)
|
||||
{
|
||||
case CCID_STATE_SEND_RESP: {
|
||||
unsigned int remLen = G_io_ccid.UsbMessageLength;
|
||||
|
||||
// advance with acknowledged sent chunk
|
||||
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 (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 (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 (G_io_ccid.UsbMessageLength == 0) { // robustness only
|
||||
last_xfer:
|
||||
G_io_ccid.Ccid_BulkState = CCID_STATE_IDLE;
|
||||
|
||||
/* Prepare EP to Receive First Cmd */
|
||||
// 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 (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
|
||||
G_io_ccid.UsbMessageLength);
|
||||
goto last_xfer; // won't wait ack to avoid missing a command
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief CCID_BulkMessage_Out
|
||||
* Proccess CCID OUT data
|
||||
* @param pdev: device instance
|
||||
* @param uint8_t epnum: endpoint index
|
||||
* @retval None
|
||||
*/
|
||||
void CCID_BulkMessage_Out (USBD_HandleTypeDef *pdev,
|
||||
uint8_t epnum, uint8_t* buffer, uint16_t dataLen)
|
||||
{
|
||||
if (epnum == (CCID_BULK_OUT_EP & 0x7F)) {
|
||||
switch (G_io_ccid.Ccid_BulkState)
|
||||
{
|
||||
|
||||
// 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 if (dataLen >= CCID_HEADER_SIZE)
|
||||
{
|
||||
G_io_ccid.UsbMessageLength = dataLen; /* Store for future use */
|
||||
|
||||
/* 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 ! */
|
||||
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 */
|
||||
|
||||
/* 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 */
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
/*
|
||||
case CCID_STATE_UNCORRECT_LENGTH:
|
||||
G_io_ccid.Ccid_BulkState = CCID_STATE_IDLE;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
break;
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief CCID_CmdDecode
|
||||
* Parse the commands and Proccess command
|
||||
* @param pdev: device instance
|
||||
* @retval None
|
||||
*/
|
||||
void CCID_CmdDecode(USBD_HandleTypeDef *pdev)
|
||||
{
|
||||
uint8_t errorCode;
|
||||
|
||||
switch (G_io_ccid.bulk_header.bulkout.bMessageType)
|
||||
{
|
||||
case PC_TO_RDR_ICCPOWERON:
|
||||
errorCode = PC_to_RDR_IccPowerOn();
|
||||
RDR_to_PC_DataBlock(errorCode);
|
||||
break;
|
||||
case PC_TO_RDR_ICCPOWEROFF:
|
||||
errorCode = PC_to_RDR_IccPowerOff();
|
||||
RDR_to_PC_SlotStatus(errorCode);
|
||||
break;
|
||||
case PC_TO_RDR_GETSLOTSTATUS:
|
||||
errorCode = PC_to_RDR_GetSlotStatus();
|
||||
RDR_to_PC_SlotStatus(errorCode);
|
||||
break;
|
||||
case PC_TO_RDR_XFRBLOCK:
|
||||
errorCode = PC_to_RDR_XfrBlock();
|
||||
// asynchronous // RDR_to_PC_DataBlock(errorCode);
|
||||
break;
|
||||
case PC_TO_RDR_GETPARAMETERS:
|
||||
errorCode = PC_to_RDR_GetParameters();
|
||||
RDR_to_PC_Parameters(errorCode);
|
||||
break;
|
||||
case PC_TO_RDR_RESETPARAMETERS:
|
||||
errorCode = PC_to_RDR_ResetParameters();
|
||||
RDR_to_PC_Parameters(errorCode);
|
||||
break;
|
||||
case PC_TO_RDR_SETPARAMETERS:
|
||||
errorCode = PC_to_RDR_SetParameters();
|
||||
RDR_to_PC_Parameters(errorCode);
|
||||
break;
|
||||
case PC_TO_RDR_ESCAPE:
|
||||
errorCode = PC_to_RDR_Escape();
|
||||
RDR_to_PC_Escape(errorCode);
|
||||
break;
|
||||
case PC_TO_RDR_ICCCLOCK:
|
||||
errorCode = PC_to_RDR_IccClock();
|
||||
RDR_to_PC_SlotStatus(errorCode);
|
||||
break;
|
||||
case PC_TO_RDR_ABORT:
|
||||
errorCode = PC_to_RDR_Abort();
|
||||
RDR_to_PC_SlotStatus(errorCode);
|
||||
break;
|
||||
case PC_TO_RDR_T0APDU:
|
||||
errorCode = PC_TO_RDR_T0Apdu();
|
||||
RDR_to_PC_SlotStatus(errorCode);
|
||||
break;
|
||||
case PC_TO_RDR_MECHANICAL:
|
||||
errorCode = PC_TO_RDR_Mechanical();
|
||||
RDR_to_PC_SlotStatus(errorCode);
|
||||
break;
|
||||
case PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY:
|
||||
errorCode = PC_TO_RDR_SetDataRateAndClockFrequency();
|
||||
RDR_to_PC_DataRateAndClockFrequency(errorCode);
|
||||
break;
|
||||
case PC_TO_RDR_SECURE:
|
||||
errorCode = PC_TO_RDR_Secure();
|
||||
// asynchronous // RDR_to_PC_DataBlock(errorCode);
|
||||
break;
|
||||
default:
|
||||
RDR_to_PC_SlotStatus(SLOTERROR_CMD_NOT_SUPPORTED);
|
||||
break;
|
||||
}
|
||||
|
||||
CCID_Send_Reply(pdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transfer_Data_Request
|
||||
* Prepare the request response to be sent to the host
|
||||
* @param uint8_t* dataPointer: Pointer to the data buffer to send
|
||||
* @param uint16_t dataLen : number of bytes to send
|
||||
* @retval None
|
||||
*/
|
||||
void Transfer_Data_Request(void)
|
||||
{
|
||||
/********** Update Global Variables ***************/
|
||||
G_io_ccid.Ccid_BulkState = CCID_STATE_SEND_RESP;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief CCID_Response_SendData
|
||||
* Send the data on bulk-in EP
|
||||
* @param pdev: device instance
|
||||
* @param uint8_t* buf: pointer to data buffer
|
||||
* @param uint16_t len: Data Length
|
||||
* @retval None
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
G_io_seproxyhal_spi_buffer[0] = SEPROXYHAL_TAG_USB_EP_PREPARE;
|
||||
G_io_seproxyhal_spi_buffer[1] = (3+len)>>8;
|
||||
G_io_seproxyhal_spi_buffer[2] = (3+len);
|
||||
G_io_seproxyhal_spi_buffer[3] = CCID_BULK_IN_EP;
|
||||
G_io_seproxyhal_spi_buffer[4] = SEPROXYHAL_TAG_USB_EP_PREPARE_DIR_IN;
|
||||
G_io_seproxyhal_spi_buffer[5] = len;
|
||||
io_seproxyhal_spi_send(G_io_seproxyhal_spi_buffer, 6);
|
||||
io_seproxyhal_spi_send(buf, len);
|
||||
}
|
||||
|
||||
#ifdef HAVE_CCID_INTERRUPT
|
||||
/**
|
||||
* @brief CCID_IntMessage
|
||||
* Send the Interrupt-IN data to the host
|
||||
* @param pdev: device instance
|
||||
* @retval None
|
||||
*/
|
||||
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 */
|
||||
|
||||
G_io_seproxyhal_spi_buffer[0] = SEPROXYHAL_TAG_USB_EP_PREPARE;
|
||||
G_io_seproxyhal_spi_buffer[1] = (3+2)>>8;
|
||||
G_io_seproxyhal_spi_buffer[2] = (3+2);
|
||||
G_io_seproxyhal_spi_buffer[3] = CCID_INTR_IN_EP;
|
||||
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(G_io_ccid.UsbIntMessageBuffer, 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief CCID_IsIntrTransferComplete
|
||||
* Provides the status of previous Interrupt transfer status
|
||||
* @param None
|
||||
* @retval uint8_t PrevXferComplete_IntrIn: Value of the previous transfer status
|
||||
*/
|
||||
uint8_t CCID_IsIntrTransferComplete (void)
|
||||
{
|
||||
return G_io_ccid.PrevXferComplete_IntrIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief CCID_IsIntrTransferComplete
|
||||
* Set the value of the Interrupt transfer status
|
||||
* @param uint8_t xfer_Status: Value of the Interrupt transfer status to set
|
||||
* @retval None
|
||||
*/
|
||||
void CCID_SetIntrTransferStatus (uint8_t xfer_Status)
|
||||
{
|
||||
G_io_ccid.PrevXferComplete_IntrIn = xfer_Status;
|
||||
}
|
||||
#endif // HAVE_CCID_INTERRUPT
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
uint8_t SC_Detect(void) {
|
||||
return G_io_ccid.ccid_card_inserted;
|
||||
}
|
||||
|
||||
void SC_InitParams (void) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
uint8_t SC_SetParams (Protocol0_DataStructure_t* pt0) {
|
||||
UNUSED(pt0);
|
||||
return SLOT_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
off = 15;
|
||||
//ret_len = dwLength - 15;
|
||||
ret_len = 5;
|
||||
break;
|
||||
case 1: // modify pin
|
||||
switch(pbuf[11])
|
||||
{
|
||||
case 3:
|
||||
off = 20;
|
||||
break;
|
||||
case 2:
|
||||
case 1:
|
||||
off = 19;
|
||||
break;
|
||||
// 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;
|
||||
}
|
||||
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 // 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
|
||||
|
||||
return SLOT_NO_ERROR;
|
||||
}
|
||||
|
||||
void io_usb_ccid_reply(unsigned char* buffer, unsigned short length) {
|
||||
// avoid memory overflow
|
||||
if (length > IO_CCID_DATA_BUFFER_SIZE) {
|
||||
THROW(EXCEPTION_IO_OVERFLOW);
|
||||
}
|
||||
// copy the responde apdu
|
||||
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) {
|
||||
G_io_ccid.ccid_card_inserted = inserted;
|
||||
CCID_UpdSlotChange(1);
|
||||
#ifdef HAVE_CCID_INTERRUPT
|
||||
CCID_IntMessage(&USBD_Device);
|
||||
#endif // HAVE_CCID_INTERRUPT
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // HAVE_USB_CLASS_CCID
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
@ -44,8 +44,11 @@
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
#pragma message "Override SDK source file :" __FILE__
|
||||
|
||||
#include "os.h"
|
||||
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
|
||||
#include "usbd_hid.h"
|
Loading…
Reference in New Issue
Block a user