Initial commit
Functional application in beta stage
This commit is contained in:
parent
20ba34417c
commit
bfb950e21b
178
Makefile
Normal file
178
Makefile
Normal file
@ -0,0 +1,178 @@
|
||||
# Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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.
|
||||
#
|
||||
|
||||
#extract TARGET_ID from the SDK to allow for makefile choices
|
||||
APPNAME = "GNUPG3"
|
||||
APPVERSION = "0.9"
|
||||
TARGET_ID = 0x31100002
|
||||
$(info TARGET_ID=$(TARGET_ID))
|
||||
|
||||
APP_LOAD_PARAMS=--appFlags 0
|
||||
LOADFLAGS = --params --appVersion $(APPVERSION)
|
||||
|
||||
ICONNAME=icon.gif
|
||||
|
||||
|
||||
################
|
||||
# Default rule #
|
||||
################
|
||||
|
||||
all: default
|
||||
|
||||
# consider every intermediate target as final to avoid deleting intermediate files
|
||||
.SECONDARY:
|
||||
|
||||
# disable builtin rules that overload the build process (and the debug log !!)
|
||||
.SUFFIXES:
|
||||
MAKEFLAGS += -r
|
||||
|
||||
SHELL = /bin/bash
|
||||
#.ONESHELL:
|
||||
|
||||
|
||||
############
|
||||
# Platform #
|
||||
############
|
||||
PROG := token
|
||||
|
||||
CONFIG_PRODUCTIONS := bin/$(PROG)
|
||||
|
||||
SOURCE_PATH := src $(BOLOS_SDK)/src $(dir $(shell find $(BOLOS_SDK)/lib_stusb* | grep "\.c$$"))
|
||||
SOURCE_FILES := $(foreach path, $(SOURCE_PATH),$(shell find $(path) | grep "\.c$$") )
|
||||
INCLUDES_PATH := $(dir $(shell find $(BOLOS_SDK)/lib_stusb* | grep "\.h$$")) include src $(BOLOS_SDK)/include $(BOLOS_SDK)/include/arm
|
||||
|
||||
### platform definitions
|
||||
DEFINES := ST31 gcc __IO=volatile
|
||||
|
||||
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 += PRINTF=screen_printf
|
||||
DEFINES += PRINTF\(...\)=
|
||||
|
||||
DEFINES += $(GPG_CONFIG)
|
||||
|
||||
##############
|
||||
# Compiler #
|
||||
##############
|
||||
GCCPATH := $(BOLOS_ENV)/gcc-arm-none-eabi-5_3-2016q1/bin/
|
||||
CLANGPATH := $(BOLOS_ENV)/clang-arm-fropi/bin
|
||||
CC := $(CLANGPATH)/clang
|
||||
|
||||
CFLAGS :=
|
||||
CFLAGS += -gdwarf-2 -gstrict-dwarf
|
||||
#CFLAGS += -O0
|
||||
CFLAGS += -O0 -g3
|
||||
#CFLAGS += -O3 -Os
|
||||
CFLAGS += -mcpu=cortex-m0 -mthumb
|
||||
CFLAGS += -fno-common -mtune=cortex-m0 -mlittle-endian
|
||||
CFLAGS += -std=gnu99 -Werror=int-to-pointer-cast -Wall -Wextra -Wno-unused-variable #-save-temps
|
||||
CFLAGS += -fdata-sections -ffunction-sections -funsigned-char -fshort-enums
|
||||
CFLAGS += -mno-unaligned-access
|
||||
CFLAGS += -Wno-unused-parameter -Wno-duplicate-decl-specifier
|
||||
|
||||
CFLAGS += -fropi --target=armv6m-none-eabi
|
||||
#CFLAGS += -finline-limit-0 -funsigned-bitfields
|
||||
|
||||
AS := $(GCCPATH)/arm-none-eabi-gcc
|
||||
AFLAGS += -ggdb2 -O3 -Os -mcpu=cortex-m0 -fno-common -mtune=cortex-m0
|
||||
|
||||
# NOT SUPPORTED BY STM3L152 CFLAGS += -fpack-struct
|
||||
#-pg --coverage
|
||||
LD := $(GCCPATH)/arm-none-eabi-gcc
|
||||
LDFLAGS :=
|
||||
LDFLAGS += -gdwarf-2 -gstrict-dwarf
|
||||
LDFLAGS += -O0 -g3
|
||||
#LDFLAGS += -O3 -Os
|
||||
#LDFLAGS += -O0
|
||||
LDFLAGS += -Wall
|
||||
LDFLAGS += -mcpu=cortex-m0 -mthumb
|
||||
LDFLAGS += -fno-common -ffunction-sections -fdata-sections -fwhole-program -nostartfiles
|
||||
LDFLAGS += -mno-unaligned-access
|
||||
#LDFLAGS += -nodefaultlibs
|
||||
#LDFLAGS += -nostdlib -nostdinc
|
||||
LDFLAGS += -T$(BOLOS_SDK)/script.ld -Wl,--gc-sections -Wl,-Map,debug/$(PROG).map,--cref
|
||||
LDLIBS += -Wl,--library-path -Wl,$(GCCPATH)/../lib/armv6-m/
|
||||
#LDLIBS += -Wl,--start-group
|
||||
LDLIBS += -lm -lgcc -lc
|
||||
#LDLIBS += -Wl,--end-group
|
||||
# -mno-unaligned-access
|
||||
#-pg --coverage
|
||||
|
||||
### computed variables
|
||||
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))))))
|
||||
|
||||
ifeq ($(filter clean,$(MAKECMDGOALS)),)
|
||||
-include $(DEPEND_FILES)
|
||||
endif
|
||||
|
||||
clean:
|
||||
rm -fr obj bin debug dep
|
||||
|
||||
prepare:
|
||||
@mkdir -p bin obj debug dep
|
||||
|
||||
.SECONDEXPANSION:
|
||||
|
||||
# default is not to display make commands
|
||||
log = $(if $(strip $(VERBOSE)),$1,@$1)
|
||||
|
||||
default: prepare bin/$(PROG)
|
||||
|
||||
load: default
|
||||
python -m ledgerblue.loadApp --targetId $(TARGET_ID) --fileName bin/$(PROG).hex --appName $(APPNAME) --icon `python $(BOLOS_SDK)/icon.py $(ICONNAME) hexbitmaponly` $(LOADFLAGS) $(APP_LOAD_PARAMS)
|
||||
|
||||
delete:
|
||||
python -m ledgerblue.deleteApp --targetId $(TARGET_ID) --appName $(APPNAME)
|
||||
|
||||
bin/$(PROG): $(OBJECT_FILES) $(BOLOS_SDK)/script.ld
|
||||
@echo "[LINK] $@"
|
||||
$(call log,$(call link_cmdline,$(OBJECT_FILES) $(LDLIBS),$@))
|
||||
$(call log,$(GCCPATH)/arm-none-eabi-objcopy -O ihex -S bin/$(PROG) bin/$(PROG).hex)
|
||||
$(call log,mv bin/$(PROG) bin/$(PROG).elf)
|
||||
$(call log,cp bin/$(PROG).elf obj)
|
||||
$(call log,$(GCCPATH)/arm-none-eabi-objdump -S -d bin/$(PROG).elf > debug/$(PROG).asm)
|
||||
|
||||
dep/%.d: %.c Makefile
|
||||
@echo "[DEP] $@"
|
||||
@mkdir -p dep
|
||||
$(call log,$(call dep_cmdline,$(INCLUDES_PATH), $(DEFINES),$<,$@))
|
||||
|
||||
obj/%.o: %.c dep/%.d
|
||||
@echo "[CC] $@"
|
||||
$(call log,$(call cc_cmdline,$(INCLUDES_PATH), $(DEFINES),$<,$@))
|
||||
|
||||
obj/%.o: %.s
|
||||
@echo "[CC] $@"
|
||||
$(call log,$(call as_cmdline,$(INCLUDES_PATH), $(DEFINES),$<,$@))
|
||||
|
||||
|
||||
### BEGIN GCC COMPILER RULES
|
||||
|
||||
# link_cmdline(objects,dest) Macro that is used to format arguments for the linker
|
||||
link_cmdline = $(LD) $(LDFLAGS) -o $(2) $(1)
|
||||
|
||||
# dep_cmdline(include,defines,src($<),dest($@)) Macro that is used to format arguments for the dependency creator
|
||||
dep_cmdline = $(CC) -M $(CFLAGS) $(addprefix -D,$(2)) $(addprefix -I,$(1)) $(3) | sed 's/\($*\)\.o[ :]*/obj\/\1.o: /g' | sed -e 's/[:\t ][^ ]\+\.c//g' > dep/$(basename $(notdir $(4))).d 2>/dev/null
|
||||
|
||||
# cc_cmdline(include,defines,src,dest) Macro that is used to format arguments for the compiler
|
||||
cc_cmdline = $(CC) -c $(CFLAGS) $(addprefix -D,$(2)) $(addprefix -I,$(1)) -o $(4) $(3)
|
||||
|
||||
as_cmdline = $(AS) -c $(AFLAGS) $(addprefix -D,$(2)) $(addprefix -I,$(1)) -o $(4) $(3)
|
||||
|
||||
### END GCC COMPILER RULES
|
260
doc/gpgcard3.0-addon.rst
Normal file
260
doc/gpgcard3.0-addon.rst
Normal file
@ -0,0 +1,260 @@
|
||||
License
|
||||
=======
|
||||
|
||||
Author: Cedric Mesnil <cslashm@gmail.com>
|
||||
|
||||
License:
|
||||
|
||||
|
||||
| Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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
|
||||
============
|
||||
|
||||
OpenPGP Card Application v3.0 add-ons summary
|
||||
---------------------------------------------
|
||||
|
||||
Key management:
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
OpenPGP Application manage four keys for cryptographic operation (PSO) plus two
|
||||
for secure channel.
|
||||
|
||||
The first four keys are defined as follow:
|
||||
- One asymmetric signature private key (RSA or EC), named 'sig';
|
||||
- One asymmetric decryption private key (RSA or EC), named 'dec'
|
||||
- One asymmetric authentication private key (RSA or EC), named 'aut'
|
||||
- One symmetric decryption private key (AES), named 'sym0'
|
||||
|
||||
The 3 first asymmetric keys can be either randomly generated on-card or
|
||||
explicitly put from outside.
|
||||
|
||||
The fourth is put from outside.
|
||||
|
||||
It's never possible to retrieve private key from the card.
|
||||
|
||||
This add-on specification propose a solution to derive those keys from the
|
||||
master seed managed by the Ledger Token.
|
||||
This allow owner to restore a broken token without the needs to keep track of keys
|
||||
outside the card.
|
||||
|
||||
Moreover this add-on specification propose to manage multiple set of the
|
||||
four previously described keys.
|
||||
|
||||
Random number generation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
OpenPGP Application provides, as optional feature, to generate random bytes.
|
||||
|
||||
This add-on specification propose new type of random generation:
|
||||
- random prime number generation
|
||||
- seeded random number
|
||||
- seeded prime number generation
|
||||
|
||||
|
||||
GPG-ledger
|
||||
==========
|
||||
|
||||
Definitions
|
||||
-----------
|
||||
|
||||
- The application is named GPG-ledger
|
||||
- A keys set is named 'keys slot'
|
||||
|
||||
How
|
||||
---
|
||||
|
||||
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
|
||||
|
||||
**Step1**:
|
||||
|
||||
For a given keys slot n, starting from 1, a seed is first derived with the following path
|
||||
|
||||
Sn = BIP32_derive (/0x80475047/n)
|
||||
|
||||
**Step2**:
|
||||
|
||||
Then specific seeds are derived with the SHA3-XOF function for each of the four key :
|
||||
|
||||
Sk[i] = SHA3-XOF(Sn \| <key_name> \| int16(i), length)
|
||||
|
||||
Sn is the dedicated slot seed from step 1.
|
||||
key_name is one of 'sig ','dec ', 'aut ', 'sym0', each four characters.
|
||||
i is the index, starting from 1, of the desired seed (see below)
|
||||
|
||||
|
||||
**Step 3**:
|
||||
|
||||
*RSA key are generated as follow* :
|
||||
|
||||
Generate two seed Sp, Sq in step2 with :
|
||||
- i € {1,2}
|
||||
- length equals to half key size
|
||||
|
||||
Generate two prime numbers p, q :
|
||||
- p = next_prime(Sp)
|
||||
- q = next_prime(Sq)
|
||||
|
||||
Generate RSA key pair as usual.
|
||||
- choose e
|
||||
- n = p*q
|
||||
- d = inv(e) mod (p-1)(q-1)
|
||||
|
||||
*ECC key genration* :
|
||||
|
||||
Generate one seed Sd in step2 with :
|
||||
- i = 1
|
||||
- length equals to curve size
|
||||
|
||||
Generate ECC key pair :
|
||||
- d = Sd
|
||||
- W = d.G
|
||||
|
||||
|
||||
*AES key generation* :
|
||||
|
||||
Generate one seed Sd in step2 with :
|
||||
- i = 1
|
||||
- length equals to 16
|
||||
|
||||
Generate AES key :
|
||||
- k = Sk
|
||||
|
||||
Deterministic random number
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The deterministic random number generation relies on the BIP32 scheme.
|
||||
The master install path of GPG-ledger is set to /0x80/'G'/'P'/'G', aka /0x80/0x47/0x50/0x47
|
||||
|
||||
**Random prime number generation** :
|
||||
|
||||
For a given length *L*:
|
||||
|
||||
- generate random number r of *L* bytes.
|
||||
- generate rp = next_prime(r)
|
||||
- return rp
|
||||
|
||||
**Seeded random number** :
|
||||
|
||||
For a given length *L* and seed *S*:
|
||||
|
||||
- generate Sr = BIP32_derive (/0x80/'G'/'P'/'G'/0)
|
||||
- generate r = SHA3-XOF(Sr \| 'rnd' \| S, L)
|
||||
- return r
|
||||
|
||||
**Seeded prime number generation** :
|
||||
|
||||
For a given length *L* and seed *S*:
|
||||
|
||||
- generate r as for "Seeded random number"
|
||||
- generate rp = next_prime(r)
|
||||
- return rp
|
||||
|
||||
|
||||
APDU Modification
|
||||
-----------------
|
||||
|
||||
Key Slot management
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Key slots are managed by data object 01F1 and 01F2 witch are
|
||||
manageable by PUT/GET DATA command as for others DO and organized as follow.
|
||||
|
||||
On application reset the *01F2* content is set to *Default Slot* value
|
||||
of *01F1*.
|
||||
|
||||
*01F1*
|
||||
|
||||
+------+--------------------------------------------------+--------+
|
||||
|bytes | description | R/W |
|
||||
+======+==================================================+========+
|
||||
| 1 | Number of slot | R |
|
||||
+------+--------------------------------------------------+--------+
|
||||
| 2 | Default slot | R/W |
|
||||
+------+--------------------------------------------------+--------+
|
||||
| 3 | Allowed slot selection method: | R/W |
|
||||
| | 0: selection not allowed (locked to default) | |
|
||||
| | 1: selection by APDU | |
|
||||
| | 2: selection by screen | |
|
||||
| | 3: selection by APDU and screen | |
|
||||
+------+--------------------------------------------------+--------+
|
||||
|
||||
|
||||
*01F2*
|
||||
|
||||
+------+--------------------------------------------------+--------+
|
||||
|bytes | Description | R/W |
|
||||
+======+==================================================+========+
|
||||
| 1 | Current slot | R/W |
|
||||
+------+--------------------------------------------------+--------+
|
||||
|
||||
*01F0*
|
||||
|
||||
+------+--------------------------------------------------+--------+
|
||||
|bytes | Description | R/W |
|
||||
+======+==================================================+========+
|
||||
| 1-3 | 01F1 content | R |
|
||||
+------+--------------------------------------------------+--------+
|
||||
| 4 | 01F2 content | R |
|
||||
+------+--------------------------------------------------+--------+
|
||||
|
||||
|
||||
*Access Conditions*
|
||||
|
||||
+------+--------------+-------------+
|
||||
| DO | Read | Write |
|
||||
+======+=============+=============+
|
||||
| 01F0 | Always | Never |
|
||||
+------+-------------+-------------+
|
||||
| 01F1 | Always | Verify PW3 |
|
||||
+------+-------------+-------------+
|
||||
| 01F2 | Always | Verify PW1 |
|
||||
+------+-------------+-------------+
|
||||
|
||||
|
||||
|
||||
Deterministic key derivation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
P2 parameter of GENERATE ASYMMETRIC KEY PAIR is set to (hex value):
|
||||
- 00 for true random key generation
|
||||
- 01 for seeded random key
|
||||
|
||||
|
||||
Deterministic random number
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
P1 parameter of GET CHALLENGE is set to (hex value):
|
||||
- 00 for true random
|
||||
- 81 for prime true random
|
||||
- 82 for seeded random
|
||||
- 83 for prime seeded random
|
||||
|
||||
When P1 is set to 82 or 83, Data field contains the seed
|
||||
|
||||
|
||||
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.
|
||||
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.
|
BIN
doc/openpgp-card-3.0.pdf
Normal file
BIN
doc/openpgp-card-3.0.pdf
Normal file
Binary file not shown.
15
pytools/gpgcard/__init__.py
Normal file
15
pytools/gpgcard/__init__.py
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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.
|
||||
#
|
||||
|
25
pytools/gpgcard/backup.py
Normal file
25
pytools/gpgcard/backup.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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.
|
||||
#
|
||||
|
||||
from gpgcard import GPGCard
|
||||
|
||||
gpgcard = GPGCard()
|
||||
gpgcard.connect("pcsc:Ledger")
|
||||
gpgcard.get_all()
|
||||
|
||||
|
||||
gpgcard.verify_pin(0x81, "123456")
|
||||
gpgcard.verify_pin(0x83, "12345678")
|
||||
gpgcard.backup("backup_card.pickle")
|
734
pytools/gpgcard/gpgcard.py
Normal file
734
pytools/gpgcard/gpgcard.py
Normal file
@ -0,0 +1,734 @@
|
||||
# Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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.
|
||||
#
|
||||
|
||||
|
||||
try:
|
||||
from ledgerblue.comm import getDongle
|
||||
from ledgerblue.commException import CommException
|
||||
except :
|
||||
pass
|
||||
import binascii
|
||||
|
||||
from smartcard.System import readers
|
||||
import sys
|
||||
import datetime
|
||||
import pickle
|
||||
|
||||
# decode level 0 of tlv|tlv|tlv
|
||||
# return dico {t: v, t:v, ...}
|
||||
#tlv: hexstring
|
||||
|
||||
def decode_tlv(tlv) :
|
||||
tags = {}
|
||||
while len(tlv) :
|
||||
o = 0
|
||||
l = 0
|
||||
if (tlv[0] & 0x1F) == 0x1F:
|
||||
t = (tlv[0]<<8)|tlv[1]
|
||||
o = 2
|
||||
else:
|
||||
t = tlv[0]
|
||||
o = 1
|
||||
l = tlv[o]
|
||||
if l & 0x80 :
|
||||
if (l&0x7f) == 1:
|
||||
l = tlv[o+1]
|
||||
o += 2
|
||||
if (l&0x7f) == 2:
|
||||
l = (tlv[o+1]<<8)|tlv[o+2]
|
||||
o += 3
|
||||
else:
|
||||
o += 1
|
||||
v = tlv[o:o+l]
|
||||
tags[t] = v
|
||||
tlv = tlv[o+l:]
|
||||
return tags
|
||||
|
||||
class GPGCard() :
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
#token info
|
||||
self.AID = b''
|
||||
self.aid = b''
|
||||
self.ext_length = b''
|
||||
self.ext_capabilities = b''
|
||||
self.histo_bytes = b''
|
||||
self.PW_status = b''
|
||||
|
||||
#user info
|
||||
self.login = b''
|
||||
self.url = b''
|
||||
self.name = b''
|
||||
self.sex = b''
|
||||
self.lang = b''
|
||||
|
||||
#keys info
|
||||
self.cardholder_cert = b''
|
||||
self.sig_attribute = b''
|
||||
self.dec_attribute = b''
|
||||
self.aut_attribute = b''
|
||||
self.sig_fingerprints = b''
|
||||
self.dec_fingerprints = b''
|
||||
self.aut_fingerprints = b''
|
||||
self.sig_CA_fingerprints = b''
|
||||
self.dec_CA_fingerprints = b''
|
||||
self.aut_CA_fingerprints = b''
|
||||
self.sig_date = b''
|
||||
self.dec_date = b''
|
||||
self.aut_date = b''
|
||||
self.cert_aut = b''
|
||||
self.cert_dec = b''
|
||||
self.cert_sig = b''
|
||||
self.UIF_SIG = b''
|
||||
self.UIF_DEC = b''
|
||||
self.UIF_AUT = b''
|
||||
self.digital_counter = b''
|
||||
|
||||
#private info
|
||||
self.private_01 = b''
|
||||
self.private_02 = b''
|
||||
self.private_03 = b''
|
||||
|
||||
|
||||
|
||||
def connect(self, device):
|
||||
if device.startswith("ledger:"):
|
||||
self.token = getDongle(True)
|
||||
self.exchange = self._exchange_ledger
|
||||
elif device.startswith("pcsc:"):
|
||||
allreaders = readers()
|
||||
for r in allreaders:
|
||||
rname = str(r)
|
||||
print('try: %s : %s'%(rname,device[5:]))
|
||||
if rname.startswith(device[5:]):
|
||||
r.createConnection()
|
||||
self.token = r
|
||||
self.connection = r.createConnection()
|
||||
self.connection.connect()
|
||||
self.exchange = self._exchange_pcsc
|
||||
else:
|
||||
print("No")
|
||||
if not self.token:
|
||||
print("No token")
|
||||
|
||||
|
||||
### APDU interface ###
|
||||
def _exchange_ledger(self,cmd,sw=0x9000):
|
||||
resp = b''
|
||||
cond = True
|
||||
while cond:
|
||||
try:
|
||||
resp = resp + self.token.exchange(cmd,300)
|
||||
sw = 0x9000
|
||||
cond = False
|
||||
except CommException as e:
|
||||
if (e.data) :
|
||||
resp = resp + e.data
|
||||
sw = e.sw
|
||||
if (sw&0xFF00) == 0x6100 :
|
||||
cmd = binascii.unhexlify("00C00000%.02x"%(sw&0xFF))
|
||||
else:
|
||||
cond = False
|
||||
return resp,sw
|
||||
|
||||
def _exchange_pcsc(self,apdu,sw=0x9000):
|
||||
print("xch S cmd : %s"%(binascii.hexlify(apdu)))
|
||||
apdu = [x for x in apdu]
|
||||
resp, sw1, sw2 = self.connection.transmit(apdu)
|
||||
while sw1==0x61:
|
||||
apdu = binascii.unhexlify(b"00c00000%.02x"%sw2)
|
||||
apdu = [x for x in apdu]
|
||||
resp2, sw1, sw2 = self.connection.transmit(apdu)
|
||||
resp = resp + resp2
|
||||
resp = bytes(resp)
|
||||
sw = (sw1<<8)|sw2
|
||||
print("xch S resp: %s %.04x"%(binascii.hexlify(resp),sw))
|
||||
return resp,sw
|
||||
|
||||
def select(self):
|
||||
apdu = binascii.unhexlify(b"00A4040006D27600012401")
|
||||
return self.exchange(apdu)
|
||||
|
||||
def activate(self):
|
||||
apdu = binascii.unhexlify(b"00440000")
|
||||
return self.exchange(apdu)
|
||||
|
||||
def terminate(self):
|
||||
apdu = binascii.unhexlify(b"00E60000")
|
||||
return self.exchange(apdu)
|
||||
|
||||
def get_data(self,tag):
|
||||
apdu = binascii.unhexlify(b"00CA%.04x00"%tag)
|
||||
return self.exchange(apdu)
|
||||
|
||||
def put_data(self,tag,value):
|
||||
apdu = binascii.unhexlify(b"00DA%.04x%.02x"%(tag,len(value)))+value
|
||||
return self.exchange(apdu)
|
||||
|
||||
def verify(self,id,value):
|
||||
apdu = binascii.unhexlify(b"002000%.02x%.02x"%(id,len(value)))+value
|
||||
return self.exchange(apdu)
|
||||
|
||||
def change_reference_data(self,id,value,new_value):
|
||||
lc = len(value)+len(new_value)
|
||||
apdu = binascii.unhexlify(b"002400%.02x%.02x"%(id,lc))+value+new_value
|
||||
return self.exchange(apdu)
|
||||
|
||||
def reset_retry_counter(self,RC,new_value):
|
||||
if len(RC)==0:
|
||||
p1 = 2
|
||||
else:
|
||||
p1 = 0
|
||||
lc = len(RC)+len(new_value)
|
||||
apdu = binascii.unhexlify(b"002C%02x81%.02x"%(p1,lc))+RC+new_value
|
||||
return self.exchange(apdu)
|
||||
|
||||
def generate_asym_key_pair(self, mode, key):
|
||||
apdu = binascii.unhexlify(b"0047%02x0002%.04x"%(mode,key))
|
||||
return self.exchange(apdu)
|
||||
|
||||
### API interfaces ###
|
||||
def get_all(self):
|
||||
self.reset()
|
||||
|
||||
self.AID,sw = self.get_data(0x4f)
|
||||
self.login ,sw = self.get_data(0x5e)
|
||||
self.url,sw = self.get_data(0x5f50)
|
||||
self.histo_bytes,sw = self.get_data(0x5f52)
|
||||
|
||||
cardholder,sw = self.get_data(0x65)
|
||||
tags = decode_tlv(cardholder)
|
||||
if 0x5b in tags:
|
||||
self.name = tags[0x5b]
|
||||
if 0x5f35 in tags:
|
||||
self.sex = tags[0x5f35]
|
||||
if 0x5f35 in tags:
|
||||
self.lang = tags[0x5f2d]
|
||||
|
||||
application_data,sw = self.get_data(0x6E)
|
||||
tags = decode_tlv(application_data)
|
||||
if 0x7f66 in tags:
|
||||
self.ext_length = tags[0x7f66]
|
||||
if 0x73 in tags:
|
||||
dicretionary_data = tags[0x73]
|
||||
tags = decode_tlv(dicretionary_data)
|
||||
if 0xc0 in tags:
|
||||
self.ext_capabilities = tags[0xC0]
|
||||
|
||||
if 0xc4 in tags:
|
||||
self.PW_status = tags[0xC4]
|
||||
|
||||
if 0xC1 in tags:
|
||||
self.sig_attribute = tags[0xC1]
|
||||
if 0xC2 in tags:
|
||||
self.dec_attribute = tags[0xC2]
|
||||
if 0xC3 in tags:
|
||||
self.aut_attribute = tags[0xC3]
|
||||
if 0xC5 in tags:
|
||||
fingerprints = tags[0xC5]
|
||||
self.sig_fingerprints = fingerprints[0:20]
|
||||
self.dec_fingerprints = fingerprints[20:40]
|
||||
self.aut_fingerprints = fingerprints[40:60]
|
||||
if 0xC6 in tags:
|
||||
fingerprints = tags[0xC6]
|
||||
self.sig_CA_fingerprints = fingerprints[0:20]
|
||||
self.dec_CA_fingerprints = fingerprints[20:40]
|
||||
self.aut_CA_fingerprints = fingerprints[40:60]
|
||||
if 0xcd in tags:
|
||||
dates = tags[0xCD]
|
||||
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)
|
||||
self.UIF_DEC,sw = self.get_data(0xD7)
|
||||
self.UIF_AUT,sw = self.get_data(0xD8)
|
||||
|
||||
sec_template,sw = self.get_data(0x7A)
|
||||
tags = decode_tlv(sec_template)
|
||||
if 0x93 in tags:
|
||||
self.digital_counter = tags[0x93]
|
||||
|
||||
self.private_01,sw = self.get_data(0x0101)
|
||||
self.private_02,sw = self.get_data(0x0102)
|
||||
self.private_03,sw = self.get_data(0x0103)
|
||||
self.private_04,sw = self.get_data(0x0104)
|
||||
|
||||
def set_all(self):
|
||||
self.put_data(0x4f, self.AID[10:14])
|
||||
self.put_data(0x0101, self.private_01)
|
||||
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)
|
||||
|
||||
self.put_data(0xc4, self.PW_status)
|
||||
|
||||
self.put_data(0xc7, self.sig_fingerprints)
|
||||
self.put_data(0xc8, self.dec_fingerprints)
|
||||
self.put_data(0xc9, self.aut_fingerprints)
|
||||
self.put_data(0xca, self.sig_CA_fingerprints)
|
||||
self.put_data(0xcb, self.dec_CA_fingerprints)
|
||||
self.put_data(0xcc, self.aut_CA_fingerprints)
|
||||
self.put_data(0xce, self.sig_date)
|
||||
self.put_data(0xcf, self.dec_date)
|
||||
self.put_data(0xd0, self.aut_date)
|
||||
#self.put_data(0x7f21, self.cardholder_cert)
|
||||
|
||||
self.put_data(0xd6, self.UIF_SIG)
|
||||
self.put_data(0xd7, self.UIF_DEC)
|
||||
self.put_data(0xd8, self.UIF_AUT)
|
||||
|
||||
def backup(self, file_name):
|
||||
f = open(file_name,mode='w+b')
|
||||
self.get_all();
|
||||
pickle.dump(
|
||||
(self.AID,
|
||||
self.private_01, self.private_02, self.private_03, self.private_04,
|
||||
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.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')
|
||||
(self.AID,
|
||||
self.private_01, self.private_02, self.private_03, self.private_04,
|
||||
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.cardholder_cert,
|
||||
self.UIF_SIG, self.UIF_DEC, self.UIF_AUT) = pickle.load(f)
|
||||
self.set_all()
|
||||
if seed_key :
|
||||
apdu = binascii.unhexlify(b"0047800102B600")
|
||||
self.exchange(apdu)
|
||||
apdu = binascii.unhexlify(b"0047800102B800")
|
||||
self.exchange(apdu)
|
||||
apdu = binascii.unhexlify(b"0047800102A400")
|
||||
self.exchange(apdu)
|
||||
|
||||
|
||||
|
||||
|
||||
def decode_AID(self):
|
||||
return {
|
||||
'AID': ('AID' , "%x"%int.from_bytes(self.AID,'big')),
|
||||
'RID': ('RID' , "%x"%int.from_bytes(self.AID[0:5],'big')),
|
||||
'APP': ('application' , "%.02x"%self.AID[5]),
|
||||
'VER': ('version' , "%.02x.%.02x"%(self.AID[6], self.AID[7])),
|
||||
'MAN': ('manufacturer' , "%x"%int.from_bytes(self.AID[8:10],'big')),
|
||||
'SER': ('serial' , "%x"%int.from_bytes(self.AID[10:14],'big'))
|
||||
}
|
||||
|
||||
def decode_histo(self):
|
||||
return {
|
||||
'HIST': ('historical bytes', binascii.hexlify(self.histo_bytes))
|
||||
}
|
||||
|
||||
def decode_extlength(self):
|
||||
if self.ext_length:
|
||||
return {
|
||||
'CMD': ('Max command length' , "%d" %((self.ext_length[2]<<8)|self.ext_length[3])),
|
||||
'RESP':( 'Max response length' , "%d" %((self.ext_length[6]<<8)|self.ext_length[7]))
|
||||
}
|
||||
else:
|
||||
return {
|
||||
'CMD': ('Max command length' , "unspecified"),
|
||||
'RESP':( 'Max response length' ,"unspecified"),
|
||||
}
|
||||
|
||||
def decode_capabilities(self):
|
||||
d = {}
|
||||
b1 = self.ext_capabilities[0]
|
||||
if b1&0x80 :
|
||||
if self.ext_capabilities[1] == 1:
|
||||
d['SM'] = ('Secure Messaging', "yes: 128 bits")
|
||||
elif self.ext_capabilities[1] == 2:
|
||||
d['SM'] = ('Secure Messaging', "yes: 256 bits")
|
||||
else:
|
||||
d['SM'] = ('Secure Messaging', "yes: ?? bits")
|
||||
else:
|
||||
d['SM'] = ('Secure Messaging', "no")
|
||||
|
||||
if b1&0x40 :
|
||||
d['CHAL'] = ('GET CHALLENGE', "yes")
|
||||
else:
|
||||
d['CHAL'] = ('GET CHALLENGE', "no")
|
||||
|
||||
if b1&0x20 :
|
||||
d['KEY'] = ('Key import', "yes")
|
||||
else:
|
||||
d['KEY'] = ('Key import', "no")
|
||||
|
||||
if b1&0x10 :
|
||||
d['PWS'] = ('PW status changeable', "yes")
|
||||
else:
|
||||
d['PWS'] = ('PW status changeable', "no")
|
||||
|
||||
if b1&0x08 :
|
||||
d['PDO'] = ('Private DOs', "yes")
|
||||
else:
|
||||
d['PDO'] = ('Private DOs', "no")
|
||||
|
||||
if b1&0x04 :
|
||||
d['ATTR'] = ('Algo attributes changeable', "yes")
|
||||
else:
|
||||
dd['ATTR'] = ('Algo attributes changeable', "no")
|
||||
|
||||
if b1&0x02 :
|
||||
d['PSO'] = ('PSO:DEC support AES', "yes")
|
||||
else:
|
||||
d['PSO'] = ('PSO:DEC support AES', "no")
|
||||
|
||||
|
||||
d['CHAL_MAX'] = ('Max GET_CHALLENGE length',
|
||||
"%d"% ((self.ext_capabilities[2]<<8)|self.ext_capabilities[3]))
|
||||
d['CERT_MAX'] = ('Max Cert length',
|
||||
"%d"% ((self.ext_capabilities[4]<<8)|self.ext_capabilities[5]))
|
||||
d['PDO_MAX'] = ('Max special DO length',
|
||||
"%d"% ((self.ext_capabilities[6]<<8)|self.ext_capabilities[7]))
|
||||
if self.ext_capabilities[8] :
|
||||
d['PIN2'] = ('PIN 2 format supported', "yes")
|
||||
else:
|
||||
d['PIN2'] = ('PIN 2 format supported',"no")
|
||||
|
||||
return d
|
||||
|
||||
def decode_pws(self):
|
||||
d = {}
|
||||
if self.PW_status[0]==0:
|
||||
d['ONCE'] = ('PW1 valid for several CDS', 'yes')
|
||||
elif self.PW_status[0]==1:
|
||||
d['ONCE'] = ('PW1 valid for several CDS', 'no')
|
||||
else:
|
||||
d['ONCE'] = ('PW1 valid for several CDS', 'unknown (%d)'%self.PW_status[0])
|
||||
|
||||
if self.PW_status[1] & 0x80:
|
||||
fmt = "Format-2"
|
||||
else:
|
||||
fmt = "UTF-8"
|
||||
pwlen = self.PW_status[1] & 0x7f
|
||||
d['PW1'] = ("PW1 format", "%s : %d bytes"%(fmt,pwlen))
|
||||
|
||||
if self.PW_status[2] & 0x80:
|
||||
fmt = "Format-2"
|
||||
else:
|
||||
fmt = "UTF-8"
|
||||
pwlen = self.PW_status[2] & 0x7f
|
||||
d['RC'] = ("RC format", "%s : %d bytes"%(fmt,pwlen))
|
||||
|
||||
if self.PW_status[3] & 0x80:
|
||||
fmt = "Format-2"
|
||||
else:
|
||||
fmt = "UTF-8"
|
||||
pwlen = self.PW_status[3] & 0x7f
|
||||
d['PW3'] = ("PW3 format", "%s : %d bytes"%(fmt,pwlen))
|
||||
|
||||
d['CNT1'] = ('PW1 counter', "%x"%self.PW_status[4])
|
||||
d['CNTRC'] =('RC counter', "%x"%self.PW_status[5])
|
||||
d['CNT3'] = ('PW3 counter', "%x"%self.PW_status[6])
|
||||
|
||||
return d
|
||||
|
||||
#USER Info
|
||||
# internals are always store as byres, get/set automatically convert from/to
|
||||
def set_name(self,name):
|
||||
""" Args:
|
||||
name (str) : utf8 string
|
||||
"""
|
||||
self.name = name.encode('utf-8')
|
||||
self.put_data( 0x5b, self.name)
|
||||
|
||||
def get_name(self):
|
||||
return self.name.decode('utf-8')
|
||||
|
||||
def set_login(self,login):
|
||||
""" Args:
|
||||
login (str) : utf8 string
|
||||
"""
|
||||
self.login = login.encode('utf-8')
|
||||
self.put_data( 0x5e, self.login)
|
||||
|
||||
def get_login(self):
|
||||
return self.login.decode('utf-8')
|
||||
|
||||
def set_url(self,url):
|
||||
""" Args:
|
||||
url (str) : utf8 string
|
||||
"""
|
||||
self.url = url.encode('utf-8')
|
||||
self.put_data(0x5f50, self.url)
|
||||
|
||||
def get_url(self):
|
||||
return self.url.decode('utf-8')
|
||||
|
||||
def set_sex(self,sex):
|
||||
""" Args:
|
||||
sex (str) : ascii string ('9', '1', '2')
|
||||
"""
|
||||
self.sex = sex.encode('utf-8')
|
||||
self.put_data(0x5f35, self.sex)
|
||||
|
||||
def get_sex(self):
|
||||
return self.sex.decode('utf-8')
|
||||
|
||||
def set_lang(self,lang):
|
||||
""" Args:
|
||||
lang (str) : utf8 string
|
||||
"""
|
||||
self.lang = lang.encode('utf-8')
|
||||
self.put_data(0x5f2d, self.lang)
|
||||
|
||||
def get_lang(self):
|
||||
return self.lang.decode('utf-8')
|
||||
|
||||
|
||||
#PINs
|
||||
def verify_pin(self,id,value):
|
||||
""" Args:
|
||||
id (int) : 0x81, 0x82, ox83
|
||||
value (str) : ascii string
|
||||
"""
|
||||
value = value.encode('ascii')
|
||||
resp,sw = self.verify(id,value)
|
||||
return sw == 0x9000
|
||||
|
||||
def change_pin(self, id, value,new_value):
|
||||
""" Args:
|
||||
id (int) : 0x81, ox83
|
||||
value (str) : ascii string
|
||||
"""
|
||||
value = value.encode('ascii')
|
||||
new_value = new_value.encode('ascii')
|
||||
resp,sw = self.change_reference_data(id,value,new_value)
|
||||
return sw == 0x9000
|
||||
|
||||
def change_RC(self,new_value):
|
||||
""" Args:
|
||||
id (int) : 0x81, ox83
|
||||
value (str) : ascii string
|
||||
"""
|
||||
new_value = new_value.encode('ascii')
|
||||
resp,sw = self.put_data(0xd3,new_value)
|
||||
return sw == 0x9000
|
||||
|
||||
def reset_PW1(self,RC,new_value):
|
||||
""" Args:
|
||||
id (int) : 0x81, ox83
|
||||
value (str) : ascii string
|
||||
"""
|
||||
new_value = new_value.encode('ascii')
|
||||
RC = RC.encode('ascii')
|
||||
resp,sw = self.reset_retry_counter(RC,new_value)
|
||||
return sw == 0x9000
|
||||
|
||||
#keys
|
||||
def get_key_uif(self,key):
|
||||
"""
|
||||
Returns: (int) 0,1,2,256(not supported)
|
||||
"""
|
||||
uif = None
|
||||
if key=='sig':
|
||||
uif = self.UIF_SIG
|
||||
if key=='aut':
|
||||
uif = self.UIF_DEC
|
||||
if key=='dec':
|
||||
uif = self.UIF_AUT
|
||||
|
||||
if uif:
|
||||
uif = int.from_bytes(uif,'big')
|
||||
else:
|
||||
uif = 256
|
||||
return uif
|
||||
|
||||
def get_key_fingerprints(self, key):
|
||||
"""
|
||||
Returns: (str) fingerprints hex string
|
||||
"""
|
||||
fprints = None
|
||||
if key=='sig':
|
||||
fprints = self.sig_fingerprints
|
||||
if key=='aut':
|
||||
fprints = self.aut_fingerprints
|
||||
if key=='dec':
|
||||
fprints = self.dec_fingerprints
|
||||
if fprints:
|
||||
fprints = binascii.hexlify(fprints)
|
||||
else:
|
||||
fprint = '-'
|
||||
return fprints.decode('ascii')
|
||||
|
||||
def get_key_CA_fingerprints(self, key):
|
||||
"""
|
||||
Returns: (str) CA fingerprints hex string
|
||||
"""
|
||||
fprints = None
|
||||
if key=='sig':
|
||||
fprints = self.sig_CA_fingerprints
|
||||
if key=='aut':
|
||||
fprints = self.aut_CA_fingerprints
|
||||
if key=='dec':
|
||||
fprints = self.dec_CA_fingerprints
|
||||
if fprints:
|
||||
fprints = binascii.hexlify(fprints)
|
||||
else:
|
||||
fprint = b'-'
|
||||
return fprints.decode('ascii')
|
||||
|
||||
def get_key_date(self, key):
|
||||
"""
|
||||
Returns: (str) date
|
||||
"""
|
||||
fdate = None
|
||||
if key=='sig':
|
||||
fdate = self.sig_date
|
||||
if key=='aut':
|
||||
fdate = self.aut_date
|
||||
if key=='dec':
|
||||
fdate = self.dec_date
|
||||
if fdate:
|
||||
fdate = datetime.datetime.fromtimestamp(int.from_bytes(fdate,'big')).isoformat(' ')
|
||||
else:
|
||||
fprint = b'-'.decode('ascii')
|
||||
return fdate
|
||||
|
||||
|
||||
def get_key_attribute(self, key):
|
||||
"""
|
||||
for RSA:
|
||||
{'id': (int) 0x01,
|
||||
'nsize': (int)
|
||||
'esize' (int)
|
||||
'format': (int)
|
||||
}
|
||||
|
||||
for ECC:
|
||||
|
||||
{'id': (int) 0x18|0x19,
|
||||
'OID': (bytes)
|
||||
}
|
||||
|
||||
Args:
|
||||
key: (str) 'sig' | 'aut', 'dec'
|
||||
"""
|
||||
|
||||
attributes = None
|
||||
if key=='sig':
|
||||
attributes = self.sig_attribute
|
||||
if key=='dec':
|
||||
attributes = self.dec_attribute
|
||||
if key=='aut':
|
||||
attributes = self.aut_attribute
|
||||
if not attributes:
|
||||
return None
|
||||
if len(attributes) == 0:
|
||||
return None
|
||||
|
||||
if attributes[0] == 0x01:
|
||||
return {
|
||||
'id': 1,
|
||||
'nsize': (attributes[1]<<8) | attributes[2],
|
||||
'esize': (attributes[3]<<8) | attributes[4],
|
||||
'format': attributes[5]
|
||||
}
|
||||
if attributes[0] == 18 or attributes[0] == 19 :
|
||||
return {
|
||||
'id': attributes[0] ,
|
||||
'oid': attributes[1:]
|
||||
}
|
||||
print ("NONE: %s"%binascii.hexlify(attributes))
|
||||
return None
|
||||
|
||||
def set_template(self, template):
|
||||
"""
|
||||
See get_template
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def asymmetric_key(self, op, key) :
|
||||
"""
|
||||
Args:
|
||||
op: (int) 0x80 generate, 0x81 read pub, 0x82 read pub&priv
|
||||
key: (str) 'sig' | 'aut', 'dec'
|
||||
|
||||
Returns:
|
||||
for RSA:
|
||||
{'id': (int) 0x01,
|
||||
'n': (bytes)
|
||||
'e' (bytes)
|
||||
'd': (bytes)
|
||||
}
|
||||
|
||||
for ECC:
|
||||
|
||||
{'id': (int) 0x18|0x19,
|
||||
'OID': (bytes)
|
||||
}
|
||||
|
||||
"""
|
||||
attributes = None
|
||||
if key=='sig':
|
||||
attributes = self.sig_attribute
|
||||
key = 0xb600
|
||||
if key=='dec':
|
||||
attributes = self.dec_attribute
|
||||
key = 0xb800
|
||||
if key=='aut':
|
||||
attributes = self.aut_attribute
|
||||
key = 0xa400
|
||||
if not attributes:
|
||||
return None
|
||||
if len(attributes) == 0:
|
||||
return None
|
||||
resp,sw = self.generate_asym_key_pair(op,key)
|
||||
if sw != 0x9000:
|
||||
return None
|
||||
resp,sw = self.generate_asym_key_pair(0x82,key)
|
||||
tags = decode_tlv(resp)
|
||||
tags = decode_tlv(tags[0x7f49])
|
||||
if attributes[0] == 0x01:
|
||||
return {
|
||||
'id': 1,
|
||||
'n': tags[0x81],
|
||||
'e': tags[0x82],
|
||||
'd': tags[0x98],
|
||||
}
|
||||
|
||||
def dump():
|
||||
pass
|
25
pytools/gpgcard/restore.py
Normal file
25
pytools/gpgcard/restore.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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.
|
||||
#
|
||||
|
||||
from gpgcard import GPGCard
|
||||
|
||||
gpgcard = GPGCard()
|
||||
gpgcard.connect("pcsc:Ledger")
|
||||
gpgcard.get_all()
|
||||
|
||||
|
||||
gpgcard.verify_pin(0x81, "123456")
|
||||
gpgcard.verify_pin(0x83, "12345678")
|
||||
gpgcard.restore("backup_card.pickle", True)
|
44
src/glyphs.c
Normal file
44
src/glyphs.c
Normal file
@ -0,0 +1,44 @@
|
||||
#include "glyphs.h"
|
||||
unsigned int const C_badge_back_colors[]
|
||||
= {
|
||||
0x00000000,
|
||||
0x00ffffff,
|
||||
};
|
||||
|
||||
unsigned char const C_badge_back_bitmap[] = {
|
||||
0xe0, 0x01, 0xfe, 0xc1, 0xfd, 0x38, 0x7f, 0x06, 0xdf, 0x81, 0xff, 0xc4, 0x7f, 0xf3, 0xff, 0xbc,
|
||||
0x1f, 0xe7, 0xe7, 0xf1, 0x3f, 0xf8, 0x07, 0x78, 0x00, };
|
||||
|
||||
#ifdef OS_IO_SEPROXYHAL
|
||||
#include "os_io_seproxyhal.h"
|
||||
const bagl_icon_details_t C_badge_back = { GLYPH_badge_back_WIDTH, GLYPH_badge_back_HEIGHT, 1, C_badge_back_colors, C_badge_back_bitmap };
|
||||
#endif // OS_IO_SEPROXYHAL
|
||||
#include "glyphs.h"
|
||||
unsigned int const C_fish_left_colors[]
|
||||
= {
|
||||
0x00000000,
|
||||
0x00ffffff,
|
||||
};
|
||||
|
||||
unsigned char const C_fish_left_bitmap[] = {
|
||||
0x00, 0x00, 0x18, 0x84, 0x9f, 0xb1, 0x7f, 0xfe, 0x1f, 0x7f, 0x06, 0x07, 0x01, 0x00, };
|
||||
|
||||
#ifdef OS_IO_SEPROXYHAL
|
||||
#include "os_io_seproxyhal.h"
|
||||
const bagl_icon_details_t C_fish_left = { GLYPH_fish_left_WIDTH, GLYPH_fish_left_HEIGHT, 1, C_fish_left_colors, C_fish_left_bitmap };
|
||||
#endif // OS_IO_SEPROXYHAL
|
||||
#include "glyphs.h"
|
||||
unsigned int const C_icon_dashboard_colors[]
|
||||
= {
|
||||
0x00000000,
|
||||
0x00ffffff,
|
||||
};
|
||||
|
||||
unsigned char const C_icon_dashboard_bitmap[] = {
|
||||
0xe0, 0x01, 0xfe, 0xc1, 0xff, 0x38, 0x70, 0x06, 0xd8, 0x79, 0x7e, 0x9e, 0x9f, 0xe7, 0xe7, 0xb9,
|
||||
0x01, 0xe6, 0xc0, 0xf1, 0x3f, 0xf8, 0x07, 0x78, 0x00, };
|
||||
|
||||
#ifdef OS_IO_SEPROXYHAL
|
||||
#include "os_io_seproxyhal.h"
|
||||
const bagl_icon_details_t C_icon_dashboard = { GLYPH_icon_dashboard_WIDTH, GLYPH_icon_dashboard_HEIGHT, 1, C_icon_dashboard_colors, C_icon_dashboard_bitmap };
|
||||
#endif // OS_IO_SEPROXYHAL
|
45
src/glyphs.h
Normal file
45
src/glyphs.h
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef GLYPH_badge_back_BPP
|
||||
#define GLYPH_badge_back_WIDTH 14
|
||||
#define GLYPH_badge_back_HEIGHT 14
|
||||
#define GLYPH_badge_back_BPP 1
|
||||
extern
|
||||
unsigned int const C_badge_back_colors[]
|
||||
;
|
||||
extern
|
||||
unsigned char const C_badge_back_bitmap[];
|
||||
#ifdef OS_IO_SEPROXYHAL
|
||||
#include "os_io_seproxyhal.h"
|
||||
extern
|
||||
const bagl_icon_details_t C_badge_back;
|
||||
#endif // GLYPH_badge_back_BPP
|
||||
#endif // OS_IO_SEPROXYHAL
|
||||
#ifndef GLYPH_fish_left_BPP
|
||||
#define GLYPH_fish_left_WIDTH 14
|
||||
#define GLYPH_fish_left_HEIGHT 8
|
||||
#define GLYPH_fish_left_BPP 1
|
||||
extern
|
||||
unsigned int const C_fish_left_colors[]
|
||||
;
|
||||
extern
|
||||
unsigned char const C_fish_left_bitmap[];
|
||||
#ifdef OS_IO_SEPROXYHAL
|
||||
#include "os_io_seproxyhal.h"
|
||||
extern
|
||||
const bagl_icon_details_t C_fish_left;
|
||||
#endif // GLYPH_fish_left_BPP
|
||||
#endif // OS_IO_SEPROXYHAL
|
||||
#ifndef GLYPH_icon_dashboard_BPP
|
||||
#define GLYPH_icon_dashboard_WIDTH 14
|
||||
#define GLYPH_icon_dashboard_HEIGHT 14
|
||||
#define GLYPH_icon_dashboard_BPP 1
|
||||
extern
|
||||
unsigned int const C_icon_dashboard_colors[]
|
||||
;
|
||||
extern
|
||||
unsigned char const C_icon_dashboard_bitmap[];
|
||||
#ifdef OS_IO_SEPROXYHAL
|
||||
#include "os_io_seproxyhal.h"
|
||||
extern
|
||||
const bagl_icon_details_t C_icon_dashboard;
|
||||
#endif // GLYPH_icon_dashboard_BPP
|
||||
#endif // OS_IO_SEPROXYHAL
|
92
src/gpg_api.h
Normal file
92
src/gpg_api.h
Normal file
@ -0,0 +1,92 @@
|
||||
/* Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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.
|
||||
*/
|
||||
|
||||
#ifndef GPG_API_H
|
||||
#define GPG_API_H
|
||||
|
||||
|
||||
void gpg_init(void);
|
||||
void gpg_init_ux(void);
|
||||
int gpg_install(unsigned char app_state) ;
|
||||
int gpg_dispatch(void);
|
||||
|
||||
int gpg_apdu_select_data(unsigned int ref, int reccord) ;
|
||||
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_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_reset_retry_counter(void) ;
|
||||
|
||||
|
||||
int gpg_oid2curve(unsigned char* oid, unsigned int len);
|
||||
int gpg_is_pin_verified(int id);
|
||||
int gpg_is_pin_blocked(int id);
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* --- IO ---- */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
void gpg_io_discard(int clear) ;
|
||||
void gpg_io_clear(void);
|
||||
void gpg_io_set_offset(unsigned int offset) ;
|
||||
void gpg_io_mark(void) ;
|
||||
void gpg_io_rewind(void) ;
|
||||
void gpg_io_hole(unsigned int sz) ;
|
||||
void gpg_io_inserted(unsigned int len);
|
||||
void gpg_io_insert(unsigned char const * buffer, unsigned int len) ;
|
||||
void gpg_io_insert_u32(unsigned int v32) ;
|
||||
void gpg_io_insert_u24(unsigned int v24) ;
|
||||
void gpg_io_insert_u16(unsigned int v16) ;
|
||||
void gpg_io_insert_u8(unsigned int v8) ;
|
||||
void gpg_io_insert_t(unsigned int T) ;
|
||||
void gpg_io_insert_tl(unsigned int T, unsigned int L) ;
|
||||
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_u16(void) ;
|
||||
unsigned int gpg_io_fetch_u8(void) ;
|
||||
int gpg_io_fetch_t(unsigned int *T) ;
|
||||
int gpg_io_fetch_l(unsigned int *L) ;
|
||||
int gpg_io_fetch_tl(unsigned int *T, unsigned int *L) ;
|
||||
int gpg_io_fetch_nv(unsigned char* buffer, int len) ;
|
||||
int gpg_io_fetch(unsigned char* buffer, int len) ;
|
||||
|
||||
int gpg_io_do(void) ;
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* --- DEBUG ---- */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
#ifdef GPG_DEBUG
|
||||
|
||||
#include "gpg_debug.h"
|
||||
|
||||
#else
|
||||
|
||||
#define gpg_nvm_write nvm_write
|
||||
#define gpg_io_exchange io_exchange
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
39
src/gpg_challenge.c
Normal file
39
src/gpg_challenge.c
Normal file
@ -0,0 +1,39 @@
|
||||
/* Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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"
|
||||
|
||||
int gpg_apdu_get_challenge() {
|
||||
|
||||
if (G_gpg_vstate.io_le > GPG_EXT_CHALLENGE_LENTH) {
|
||||
THROW(SW_WRONG_LENGTH);
|
||||
}
|
||||
gpg_io_discard(1);
|
||||
cx_rng(G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, G_gpg_vstate.io_le);
|
||||
cx_rng(G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, G_gpg_vstate.io_le);
|
||||
cx_rng(G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, G_gpg_vstate.io_le);
|
||||
cx_rng(G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, G_gpg_vstate.io_le);
|
||||
cx_rng(G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, G_gpg_vstate.io_le);
|
||||
|
||||
if (G_gpg_vstate.io_p1 & 0x80) {
|
||||
cx_math_next_prime(G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset,G_gpg_vstate.io_le);
|
||||
}
|
||||
gpg_io_inserted(G_gpg_vstate.io_le);
|
||||
return SW_OK;
|
||||
}
|
650
src/gpg_data.c
Normal file
650
src/gpg_data.c
Normal file
@ -0,0 +1,650 @@
|
||||
/* Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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"
|
||||
|
||||
int gpg_apdu_select_data(unsigned int ref, int reccord) {
|
||||
G_gpg_vstate.DO_current = ref;
|
||||
G_gpg_vstate.DO_reccord = reccord;
|
||||
G_gpg_vstate.DO_offset = 0;
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
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;
|
||||
G_gpg_vstate.DO_offset = 0;
|
||||
}
|
||||
sw = SW_OK;
|
||||
|
||||
gpg_io_discard(1);
|
||||
switch (ref) {
|
||||
/* ----------------- Optional DO for private use ----------------- */
|
||||
case 0x0101:
|
||||
gpg_io_insert(N_gpg_pstate->private_DO1.value, N_gpg_pstate->private_DO1.length);
|
||||
break;
|
||||
case 0x0102:
|
||||
gpg_io_insert(N_gpg_pstate->private_DO2.value, N_gpg_pstate->private_DO2.length);
|
||||
break;
|
||||
case 0x0103:
|
||||
gpg_io_insert(N_gpg_pstate->private_DO3.value, N_gpg_pstate->private_DO3.length);
|
||||
break;
|
||||
case 0x0104:
|
||||
gpg_io_insert(N_gpg_pstate->private_DO4.value, N_gpg_pstate->private_DO4.length);
|
||||
break;
|
||||
|
||||
/* ----------------- Config key slot ----------------- */
|
||||
case 0x01F0:
|
||||
gpg_io_insert(N_gpg_pstate->config_slot, 3);
|
||||
gpg_io_insert_u8(G_gpg_vstate.slot);
|
||||
break;
|
||||
case 0x01F1:
|
||||
gpg_io_insert(N_gpg_pstate->config_slot, 3);
|
||||
break;
|
||||
case 0x01F2:
|
||||
gpg_io_insert_u8(G_gpg_vstate.slot);
|
||||
break;
|
||||
|
||||
/* ----------------- Application ----------------- */
|
||||
/* Full Application identifier */
|
||||
case 0x004F:
|
||||
gpg_io_insert(N_gpg_pstate->AID, 16);
|
||||
break;
|
||||
/* Historical bytes, */
|
||||
case 0x5F52:
|
||||
gpg_io_insert(N_gpg_pstate->histo, 15);
|
||||
break;
|
||||
/* Extended length information */
|
||||
case 0x7F66:
|
||||
gpg_io_insert(C_ext_length, sizeof(C_ext_length));
|
||||
break;
|
||||
|
||||
/* ----------------- User -----------------*/
|
||||
/* Login data */
|
||||
case 0x005E:
|
||||
gpg_io_insert(N_gpg_pstate->login.value, N_gpg_pstate->login.length);
|
||||
break;
|
||||
/* Uniform resource locator */
|
||||
case 0x5F50:
|
||||
gpg_io_insert(N_gpg_pstate->url.value, N_gpg_pstate->url.length);
|
||||
break;
|
||||
/* Name, Language, Sex */
|
||||
case 0x65:
|
||||
gpg_io_insert_tlv(0x5B, N_gpg_pstate->name.length, N_gpg_pstate->name.value);
|
||||
gpg_io_insert_tlv(0x5F2D, N_gpg_pstate->lang.length, N_gpg_pstate->lang.value);
|
||||
gpg_io_insert_tlv(0x5F35, 1, N_gpg_pstate->sex);
|
||||
break;
|
||||
|
||||
/* ----------------- aid, histo, ext_length, ... ----------------- */
|
||||
case 0x6E:
|
||||
gpg_io_insert_tlv(0x4F, 16, N_gpg_pstate->AID);
|
||||
gpg_io_insert_tlv(0x5F52, 15, N_gpg_pstate->histo);
|
||||
gpg_io_insert_tlv(0x7F66, sizeof(C_ext_length), C_ext_length);
|
||||
|
||||
gpg_io_mark();
|
||||
|
||||
gpg_io_insert_tlv(0xC0,
|
||||
sizeof(C_ext_capabilities),
|
||||
C_ext_capabilities);
|
||||
gpg_io_insert_tlv(0xC1, G_gpg_vstate.kslot->sig.attributes.length, G_gpg_vstate.kslot->sig.attributes.value);
|
||||
gpg_io_insert_tlv(0xC2, G_gpg_vstate.kslot->dec.attributes.length, G_gpg_vstate.kslot->dec.attributes.value);
|
||||
gpg_io_insert_tlv(0xC3, G_gpg_vstate.kslot->aut.attributes.length, G_gpg_vstate.kslot->aut.attributes.value);
|
||||
gpg_io_insert_tl(0xC4, 7);
|
||||
gpg_io_insert(N_gpg_pstate->PW_status,4);
|
||||
gpg_io_insert_u8(N_gpg_pstate->PW1.counter);
|
||||
gpg_io_insert_u8(N_gpg_pstate->RC.counter);
|
||||
gpg_io_insert_u8(N_gpg_pstate->PW3.counter);
|
||||
gpg_io_insert_tl(0xC5, 60);
|
||||
gpg_io_insert(G_gpg_vstate.kslot->sig.fingerprints, 20);
|
||||
gpg_io_insert(G_gpg_vstate.kslot->dec.fingerprints, 20);
|
||||
gpg_io_insert(G_gpg_vstate.kslot->aut.fingerprints, 20);
|
||||
gpg_io_insert_tl(0xC6, 60);
|
||||
gpg_io_insert(G_gpg_vstate.kslot->sig.CA_fingerprints, 20);
|
||||
gpg_io_insert(G_gpg_vstate.kslot->dec.CA_fingerprints, 20);
|
||||
gpg_io_insert(G_gpg_vstate.kslot->aut.CA_fingerprints, 20);
|
||||
gpg_io_insert_tl(0xCD, 12);
|
||||
gpg_io_insert(G_gpg_vstate.kslot->sig.date, 4);
|
||||
gpg_io_insert(G_gpg_vstate.kslot->dec.date, 4);
|
||||
gpg_io_insert(G_gpg_vstate.kslot->aut.date, 4);
|
||||
gpg_io_set_offset(IO_OFFSET_MARK);
|
||||
gpg_io_insert_tl(0x73, G_gpg_vstate.io_length- G_gpg_vstate.io_offset);
|
||||
gpg_io_set_offset(IO_OFFSET_END);
|
||||
break;
|
||||
|
||||
/* ----------------- User Interaction Flag (UIF) for PSO:CDS ----------------- */
|
||||
case 0x00D6:
|
||||
gpg_io_insert(G_gpg_vstate.kslot->sig.UIF, 2);
|
||||
break;
|
||||
case 0x00D7:
|
||||
gpg_io_insert(G_gpg_vstate.kslot->dec.UIF, 2);
|
||||
break;
|
||||
case 0x00D8:
|
||||
gpg_io_insert(G_gpg_vstate.kslot->aut.UIF, 2);
|
||||
break;
|
||||
|
||||
/* ----------------- Security support template ----------------- */
|
||||
case 0x7A:
|
||||
gpg_io_insert_tl(0x93,3);
|
||||
gpg_io_insert_u24(G_gpg_vstate.kslot->sig_count);
|
||||
break;
|
||||
|
||||
/* ----------------- Cardholder certificate ----------------- */
|
||||
case 0x7F21:
|
||||
switch (G_gpg_vstate.DO_reccord) {
|
||||
case 0:
|
||||
gpg_io_insert(G_gpg_vstate.kslot->aut.CA.value,G_gpg_vstate.kslot->aut.CA.length);
|
||||
break;
|
||||
case 1:
|
||||
gpg_io_insert(G_gpg_vstate.kslot->dec.CA.value,G_gpg_vstate.kslot->dec.CA.length);
|
||||
break;
|
||||
case 2:
|
||||
gpg_io_insert(G_gpg_vstate.kslot->sig.CA.value,G_gpg_vstate.kslot->sig.CA.length);
|
||||
break;
|
||||
default :
|
||||
sw = SW_RECORD_NOT_FOUND;
|
||||
}
|
||||
break;
|
||||
|
||||
/* ----------------- PW Status Bytes ----------------- */
|
||||
case 0x00C4:
|
||||
gpg_io_insert(N_gpg_pstate->PW_status,4);
|
||||
gpg_io_insert_u8(N_gpg_pstate->PW1.counter);
|
||||
gpg_io_insert_u8(N_gpg_pstate->RC.counter);
|
||||
gpg_io_insert_u8(N_gpg_pstate->PW3.counter);
|
||||
break;
|
||||
|
||||
/* WAT */
|
||||
default:
|
||||
sw = SW_REFERENCED_DATA_NOT_FOUND;
|
||||
break;
|
||||
}
|
||||
|
||||
return sw;
|
||||
}
|
||||
|
||||
int gpg_apdu_get_next_data(unsigned int ref) {
|
||||
int sw;
|
||||
|
||||
if ((ref != 0x7F21) || (G_gpg_vstate.DO_current != 0x7F21)) {
|
||||
return SW_CONDITIONS_NOT_SATISFIED;
|
||||
}
|
||||
sw = gpg_apdu_get_data(ref);
|
||||
if (sw == SW_OK) {
|
||||
G_gpg_vstate.DO_reccord++;
|
||||
}
|
||||
return sw;
|
||||
}
|
||||
|
||||
int gpg_apdu_put_data(unsigned int ref) {
|
||||
unsigned int t,l,sw;
|
||||
unsigned int *ptr_l;
|
||||
unsigned char *ptr_v;
|
||||
|
||||
G_gpg_vstate.DO_current = ref;
|
||||
sw = SW_OK;
|
||||
|
||||
switch (ref) {
|
||||
/* ----------------- Optional DO for private use ----------------- */
|
||||
case 0x0101:
|
||||
ptr_l = &N_gpg_pstate->private_DO1.length;
|
||||
ptr_v = N_gpg_pstate->private_DO1.value;
|
||||
goto WRITE_PRIVATE_DO;
|
||||
case 0x0102:
|
||||
ptr_l = &N_gpg_pstate->private_DO2.length;
|
||||
ptr_v = N_gpg_pstate->private_DO2.value;
|
||||
goto WRITE_PRIVATE_DO;
|
||||
case 0x0103:
|
||||
ptr_l = &N_gpg_pstate->private_DO3.length;
|
||||
ptr_v = N_gpg_pstate->private_DO3.value;
|
||||
goto WRITE_PRIVATE_DO;
|
||||
case 0x0104:
|
||||
ptr_l = &N_gpg_pstate->private_DO4.length;
|
||||
ptr_v = N_gpg_pstate->private_DO4.value;
|
||||
goto WRITE_PRIVATE_DO;
|
||||
WRITE_PRIVATE_DO:
|
||||
if (G_gpg_vstate.io_length > GPG_EXT_PRIVATE_DO_LENGTH) {
|
||||
THROW(SW_WRONG_LENGTH);
|
||||
}
|
||||
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));
|
||||
sw = SW_OK;
|
||||
break;
|
||||
/* ----------------- Config key slot ----------------- */
|
||||
case 0x01F1:
|
||||
if (G_gpg_vstate.io_length != 3) {
|
||||
THROW(SW_WRONG_LENGTH);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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 (G_gpg_vstate.io_length != 1) {
|
||||
THROW(SW_WRONG_LENGTH);
|
||||
}
|
||||
if (G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset] >= GPG_KEYS_SLOTS) {
|
||||
THROW(SW_WRONG_DATA);
|
||||
}
|
||||
G_gpg_vstate.slot = G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset];
|
||||
|
||||
sw = SW_OK;
|
||||
break;
|
||||
|
||||
/* ----------------- Serial -----------------*/
|
||||
case 0x4f:
|
||||
if (G_gpg_vstate.io_length != 4) {
|
||||
THROW(SW_WRONG_LENGTH);
|
||||
}
|
||||
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 -----------------*/
|
||||
case 0x3FFF: {
|
||||
void *pkey,*vkey;
|
||||
unsigned int len_e,len_p,len_q;
|
||||
unsigned int endof,ksz,reset_cnt;
|
||||
gpg_key_t *keygpg;
|
||||
unsigned int dh;
|
||||
//fecth 4D
|
||||
gpg_io_fetch_tl(&t,&l);
|
||||
if (t!=0x4D) {
|
||||
THROW(SW_REFERENCED_DATA_NOT_FOUND);
|
||||
}
|
||||
//fecth B8/B6/A4
|
||||
gpg_io_fetch_tl(&t,&l);
|
||||
dh = 0;
|
||||
reset_cnt = 0;
|
||||
switch(t) {
|
||||
case 0xB6:
|
||||
keygpg = &G_gpg_vstate.kslot->sig;
|
||||
reset_cnt = 0x11111111;
|
||||
break;
|
||||
case 0xA4:
|
||||
keygpg = &G_gpg_vstate.kslot->aut;
|
||||
break;
|
||||
case 0xB8:
|
||||
keygpg = &G_gpg_vstate.kslot->dec;
|
||||
dh = 0x11;
|
||||
break;
|
||||
default:
|
||||
THROW(SW_REFERENCED_DATA_NOT_FOUND);
|
||||
}
|
||||
//fecth 7f78
|
||||
gpg_io_fetch_tl(&t,&l);
|
||||
if (t!=0x7f48) {
|
||||
THROW(SW_REFERENCED_DATA_NOT_FOUND);
|
||||
}
|
||||
len_e = 0; len_p = 0; len_q = 0;
|
||||
endof = G_gpg_vstate.io_offset+l;
|
||||
while (G_gpg_vstate.io_offset<endof) {
|
||||
gpg_io_fetch_tl(&t,&l);
|
||||
switch (t) {
|
||||
case 0x91:
|
||||
len_e = l;
|
||||
break;
|
||||
case 0x92:
|
||||
len_p = l;
|
||||
break;
|
||||
case 0x93:
|
||||
len_q = l;
|
||||
break;
|
||||
break;
|
||||
case 0x94:
|
||||
case 0x95:
|
||||
case 0x96:
|
||||
case 0x97:
|
||||
break;
|
||||
default:
|
||||
THROW(SW_REFERENCED_DATA_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
//fecth 5f78
|
||||
gpg_io_fetch_tl(&t,&l);
|
||||
if (t!=0x5f48) {
|
||||
THROW(SW_REFERENCED_DATA_NOT_FOUND);
|
||||
}
|
||||
|
||||
// --- RSA KEY ---
|
||||
if (keygpg->attributes.value[0] == 0x01) {
|
||||
unsigned int e;
|
||||
unsigned char *p,*q,*pq;
|
||||
cx_rsa_public_key_t *rsa_pub;
|
||||
cx_rsa_private_key_t *rsa_priv, *pkey;
|
||||
unsigned int pkey_size;
|
||||
//check length
|
||||
ksz = (keygpg->attributes.value[1]<<8)|keygpg->attributes.value[2];
|
||||
ksz = ksz >> 3;
|
||||
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;
|
||||
}
|
||||
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();
|
||||
case 3:
|
||||
e = (e<<8) | gpg_io_fetch_u8();
|
||||
case 2:
|
||||
e = (e<<8) | gpg_io_fetch_u8();
|
||||
case 1:
|
||||
e = (e<<8) | gpg_io_fetch_u8();
|
||||
}
|
||||
|
||||
//move p,q over pub key, this only work because adr<rsa_pub> < adr<p>
|
||||
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_memset(pq+ksz, 0, ksz-len_q);
|
||||
//regenerate RSA private key
|
||||
cx_rsa_generate_pair(ksz<<1, rsa_pub, rsa_priv, e, pq);
|
||||
|
||||
//write keys
|
||||
nvm_write(&keygpg->pub_key.rsa, rsa_pub->e, 4);
|
||||
nvm_write(pkey, rsa_priv, pkey_size);
|
||||
if (reset_cnt) {
|
||||
reset_cnt = 0;
|
||||
nvm_write(&G_gpg_vstate.kslot->sig_count,&reset_cnt,sizeof(unsigned int));
|
||||
}
|
||||
}
|
||||
// --- ECC KEY ---
|
||||
else if ((keygpg->attributes.value[0] == 19) ||
|
||||
(keygpg->attributes.value[0] == 18) ||
|
||||
(keygpg->attributes.value[0] == 22) ) {
|
||||
unsigned int curve;
|
||||
|
||||
ksz = 0;
|
||||
curve = gpg_oid2curve(&keygpg->attributes.value[1], keygpg->attributes.length-1);
|
||||
if (curve == 0) {
|
||||
THROW(SW_WRONG_DATA);
|
||||
}
|
||||
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));
|
||||
if (reset_cnt) {
|
||||
reset_cnt = 0;
|
||||
nvm_write(&G_gpg_vstate.kslot->sig_count,&reset_cnt,sizeof(unsigned int));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// --- UNSUPPORTED KEY ---
|
||||
else {
|
||||
THROW(SW_REFERENCED_DATA_NOT_FOUND);
|
||||
}
|
||||
break;
|
||||
} //endof of 3fff
|
||||
|
||||
|
||||
/* ----------------- User -----------------*/
|
||||
/* Name */
|
||||
case 0x5B:
|
||||
if (G_gpg_vstate.io_length > sizeof(N_gpg_pstate->name.value)) {
|
||||
THROW(SW_WRONG_LENGTH);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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 ----------------- */
|
||||
case 0x7F21:
|
||||
ptr_v = NULL;
|
||||
switch ( G_gpg_vstate.DO_reccord) {
|
||||
case 0:
|
||||
ptr_l = &G_gpg_vstate.kslot->aut.CA.length;
|
||||
ptr_v = G_gpg_vstate.kslot->aut.CA.value;
|
||||
goto WRITE_CA;
|
||||
case 1:
|
||||
ptr_l = &G_gpg_vstate.kslot->sig.CA.length;
|
||||
ptr_v = G_gpg_vstate.kslot->sig.CA.value;
|
||||
goto WRITE_CA;
|
||||
case 2:
|
||||
ptr_l = &G_gpg_vstate.kslot->dec.CA.length;
|
||||
ptr_v = G_gpg_vstate.kslot->dec.CA.value;
|
||||
goto WRITE_CA;
|
||||
default:
|
||||
sw = SW_REFERENCED_DATA_NOT_FOUND;
|
||||
break;
|
||||
}
|
||||
WRITE_CA:
|
||||
if (G_gpg_vstate.io_length > GPG_EXT_CARD_HOLDER_CERT_LENTH) {
|
||||
THROW(SW_WRONG_LENGTH);
|
||||
}
|
||||
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 ----------------- */
|
||||
case 0xC1:
|
||||
ptr_l = &G_gpg_vstate.kslot->sig.attributes.length;
|
||||
ptr_v = G_gpg_vstate.kslot->sig.attributes.value;
|
||||
goto WRITE_ATTRIBUTES;
|
||||
case 0xC2:
|
||||
ptr_l = &G_gpg_vstate.kslot->dec.attributes.length;
|
||||
ptr_v = G_gpg_vstate.kslot->dec.attributes.value;
|
||||
goto WRITE_ATTRIBUTES;
|
||||
case 0xC3:
|
||||
ptr_l = &G_gpg_vstate.kslot->aut.attributes.length;
|
||||
ptr_v = G_gpg_vstate.kslot->aut.attributes.value;
|
||||
goto WRITE_ATTRIBUTES;
|
||||
WRITE_ATTRIBUTES:
|
||||
if (G_gpg_vstate.io_length > 12) {
|
||||
THROW(SW_WRONG_LENGTH);
|
||||
}
|
||||
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 ----------------- */
|
||||
case 0xC4:
|
||||
gpg_io_fetch_nv(N_gpg_pstate->PW_status, 1);
|
||||
break;
|
||||
|
||||
/* ----------------- Fingerprints ----------------- */
|
||||
case 0xC7:
|
||||
ptr_v = G_gpg_vstate.kslot->sig.fingerprints;
|
||||
goto WRITE_FINGERPRINTS;
|
||||
case 0xC8:
|
||||
ptr_v = G_gpg_vstate.kslot->dec.fingerprints;
|
||||
goto WRITE_FINGERPRINTS;
|
||||
case 0xC9:
|
||||
ptr_v =G_gpg_vstate.kslot->aut.fingerprints;
|
||||
goto WRITE_FINGERPRINTS;
|
||||
case 0xCA:
|
||||
ptr_v = G_gpg_vstate.kslot->sig.CA_fingerprints;
|
||||
goto WRITE_FINGERPRINTS;
|
||||
case 0xCB:
|
||||
ptr_v = G_gpg_vstate.kslot->dec.CA_fingerprints;
|
||||
goto WRITE_FINGERPRINTS;
|
||||
case 0xCC:
|
||||
ptr_v = G_gpg_vstate.kslot->aut.CA_fingerprints;
|
||||
goto WRITE_FINGERPRINTS;
|
||||
WRITE_FINGERPRINTS:
|
||||
if (G_gpg_vstate.io_length != 20) {
|
||||
THROW(SW_WRONG_LENGTH);
|
||||
}
|
||||
gpg_nvm_write(ptr_v, G_gpg_vstate.work.io_buffer, 20);
|
||||
sw = SW_OK;
|
||||
break;
|
||||
|
||||
/* ----------------- Generation date/time ----------------- */
|
||||
case 0xCE:
|
||||
ptr_v = G_gpg_vstate.kslot->sig.date;
|
||||
goto WRITE_DATE;
|
||||
case 0xCF:
|
||||
ptr_v = G_gpg_vstate.kslot->dec.date;
|
||||
goto WRITE_DATE;
|
||||
case 0xD0:
|
||||
ptr_v = G_gpg_vstate.kslot->aut.date;
|
||||
goto WRITE_DATE;
|
||||
WRITE_DATE:
|
||||
if (G_gpg_vstate.io_length != 4) {
|
||||
THROW(SW_WRONG_LENGTH);
|
||||
}
|
||||
gpg_nvm_write(ptr_v, G_gpg_vstate.work.io_buffer, 4);
|
||||
sw = SW_OK;
|
||||
break;
|
||||
|
||||
/* ----------------- AES key ----------------- */
|
||||
{
|
||||
void *pkey;
|
||||
cx_aes_key_t aes_key;
|
||||
case 0xD1:
|
||||
pkey = &N_gpg_pstate->SM_enc;
|
||||
goto init_aes_key;
|
||||
case 0xD2:
|
||||
pkey = &N_gpg_pstate->SM_mac;
|
||||
goto init_aes_key;
|
||||
case 0xD5:
|
||||
pkey = &G_gpg_vstate.kslot->AES_dec;
|
||||
goto init_aes_key;
|
||||
init_aes_key:
|
||||
cx_aes_init_key(G_gpg_vstate.work.io_buffer, G_gpg_vstate.io_length, &aes_key);
|
||||
gpg_nvm_write(pkey, &aes_key, sizeof(cx_aes_key_t));
|
||||
break;
|
||||
|
||||
|
||||
/* AES key: one shot */
|
||||
case 0xF4:
|
||||
cx_aes_init_key(G_gpg_vstate.work.io_buffer, G_gpg_vstate.io_length, &aes_key);
|
||||
gpg_nvm_write(&N_gpg_pstate->SM_enc, &aes_key, sizeof(cx_aes_key_t));
|
||||
cx_aes_init_key(G_gpg_vstate.work.io_buffer+16, G_gpg_vstate.io_length, &aes_key);
|
||||
gpg_nvm_write(&N_gpg_pstate->SM_mac, &aes_key, sizeof(cx_aes_key_t));
|
||||
break;
|
||||
}
|
||||
|
||||
/* ----------------- RC ----------------- */
|
||||
case 0xD3:
|
||||
sw = gpg_apdu_change_ref_data(ID_RC);
|
||||
break;
|
||||
|
||||
/* ----------------- UIF ----------------- */
|
||||
case 0xD6:
|
||||
ptr_v = G_gpg_vstate.kslot->sig.UIF;
|
||||
goto WRITE_UIF;
|
||||
case 0xD7:
|
||||
ptr_v = G_gpg_vstate.kslot->dec.UIF;
|
||||
goto WRITE_UIF;
|
||||
case 0xD8:
|
||||
ptr_v = G_gpg_vstate.kslot->aut.UIF;
|
||||
goto WRITE_UIF;
|
||||
WRITE_UIF:
|
||||
if (G_gpg_vstate.io_length != 2) {
|
||||
THROW(SW_WRONG_LENGTH);
|
||||
}
|
||||
gpg_nvm_write(ptr_v, G_gpg_vstate.work.io_buffer, 2);
|
||||
sw = SW_OK;
|
||||
break;
|
||||
|
||||
/* ----------------- WAT ----------------- */
|
||||
default:
|
||||
sw = SW_REFERENCED_DATA_NOT_FOUND;
|
||||
break;
|
||||
}
|
||||
gpg_io_discard(1);
|
||||
return sw;
|
||||
|
||||
|
||||
}
|
365
src/gpg_dispatch.c
Normal file
365
src/gpg_dispatch.c
Normal file
@ -0,0 +1,365 @@
|
||||
/* Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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"
|
||||
|
||||
|
||||
int gpg_is_verified(id) {
|
||||
return G_gpg_vstate.verified_pin[id] ;
|
||||
}
|
||||
|
||||
void gpg_check_access_ins() {
|
||||
switch (G_gpg_vstate.io_ins) {
|
||||
case INS_SELECT:
|
||||
return;
|
||||
case INS_GET_DATA:
|
||||
case INS_GET_NEXT_DATA:
|
||||
return;
|
||||
|
||||
case INS_VERIFY:
|
||||
return;
|
||||
|
||||
case INS_CHANGE_REFERENCE_DATA:
|
||||
if (gpg_is_verified(ID_PW1) || gpg_is_verified(ID_RC)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case INS_RESET_RETRY_COUNTER:
|
||||
if (gpg_is_verified(ID_PW3) || gpg_is_verified(ID_RC)) {
|
||||
return;
|
||||
}
|
||||
|
||||
case INS_PUT_DATA:
|
||||
case INS_PUT_DATA_ODD:
|
||||
return;
|
||||
|
||||
case INS_GEN_ASYM_KEYPAIR:
|
||||
if (G_gpg_vstate.io_p1 == 0x81) {
|
||||
return;
|
||||
}
|
||||
if (gpg_is_verified(ID_PW3)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case INS_PSO:
|
||||
if (gpg_is_verified(ID_PW1) || gpg_is_verified(ID_PW2)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case INS_INTERNAL_AUTHENTICATE:
|
||||
if (gpg_is_verified(ID_PW2)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case INS_GET_CHALLENGE:
|
||||
return;
|
||||
|
||||
case INS_TERMINATE_DF:
|
||||
if (gpg_is_pin_verified(ID_PW3) || gpg_is_pin_blocked(ID_PW3)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case INS_ACTIVATE_FILE:
|
||||
return;
|
||||
}
|
||||
THROW(SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
|
||||
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 0x006E:
|
||||
case 0x0065:
|
||||
case 0x0073:
|
||||
case 0x007A:
|
||||
case 0x004F:
|
||||
case 0x005E:
|
||||
case 0x005B:
|
||||
case 0x5F2D:
|
||||
case 0x5F35:
|
||||
case 0x5F50:
|
||||
case 0x5F52:
|
||||
case 0x7F21:
|
||||
case 0x0093:
|
||||
case 0x00C0:
|
||||
case 0x00C1:
|
||||
case 0x00C2:
|
||||
case 0x00C3:
|
||||
case 0x00C4:
|
||||
case 0x00C5:
|
||||
case 0x00C7:
|
||||
case 0x00C8:
|
||||
case 0x00C9:
|
||||
case 0x00C6:
|
||||
case 0x00CA:
|
||||
case 0x00CD:
|
||||
case 0x00CC:
|
||||
case 0x00CE:
|
||||
case 0x00CF:
|
||||
case 0x00D0:
|
||||
case 0x7F74:
|
||||
case 0x7F66:
|
||||
case 0x00D6:
|
||||
case 0x00D7:
|
||||
case 0x00D8:
|
||||
return;
|
||||
|
||||
//PW1
|
||||
case 0x0103:
|
||||
if (gpg_is_verified(ID_PW1)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
//PW3
|
||||
case 0x0104:
|
||||
if (gpg_is_verified(ID_PW3)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
THROW(SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
|
||||
void gpg_check_access_write_DO() {
|
||||
unsigned int ref;
|
||||
ref = (G_gpg_vstate.io_p1 << 8) | G_gpg_vstate.io_p2 ;
|
||||
|
||||
|
||||
switch(ref) {
|
||||
//PW1
|
||||
case 0x0101:
|
||||
case 0x0103:
|
||||
if (gpg_is_verified(ID_PW1)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
//PW3
|
||||
case 0x3FFF: //only used for putkey under PW3 control
|
||||
case 0x4f:
|
||||
case 0x0102:
|
||||
case 0x0104:
|
||||
case 0x005E:
|
||||
case 0x005B:
|
||||
case 0x5F2D:
|
||||
case 0x5F35:
|
||||
case 0x5F50:
|
||||
case 0x5F48:
|
||||
case 0x7F21:
|
||||
case 0x00C1:
|
||||
case 0x00C2:
|
||||
case 0x00C3:
|
||||
case 0x00C4:
|
||||
case 0x00C5:
|
||||
case 0x00C7:
|
||||
case 0x00C8:
|
||||
case 0x00C9:
|
||||
case 0x00C6:
|
||||
case 0x00CA:
|
||||
case 0x00CD:
|
||||
case 0x00CC:
|
||||
case 0x00CE:
|
||||
case 0x00CF:
|
||||
case 0x00D0:
|
||||
case 0x00D1:
|
||||
case 0x00D2:
|
||||
case 0x00D3:
|
||||
case 0x00D5:
|
||||
case 0x00F4:
|
||||
case 0x00D6:
|
||||
case 0x00D7:
|
||||
case 0x00D8:
|
||||
if (gpg_is_verified(ID_PW3)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
THROW(SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* assume command is fully received */
|
||||
int gpg_dispatch() {
|
||||
unsigned int tag,t,l;
|
||||
int sw;
|
||||
|
||||
|
||||
|
||||
tag = (G_gpg_vstate.io_p1 << 8) | G_gpg_vstate.io_p2 ;
|
||||
|
||||
switch (G_gpg_vstate.io_ins) {
|
||||
/* --- SELECT --- */
|
||||
case INS_SELECT:
|
||||
sw = gpg_apdu_select();
|
||||
return sw;
|
||||
break;
|
||||
|
||||
|
||||
/* --- ACTIVATE/TERMINATE FILE --- */
|
||||
case INS_ACTIVATE_FILE:
|
||||
gpg_io_discard(0);
|
||||
if (N_gpg_pstate->histo[7] == STATE_TERMINATE) {
|
||||
gpg_install(STATE_ACTIVATE);
|
||||
}
|
||||
return(SW_OK);
|
||||
break;
|
||||
|
||||
case INS_TERMINATE_DF:
|
||||
gpg_io_discard(0);
|
||||
if (G_gpg_vstate.verified_pin[ID_PW3] || (N_gpg_pstate->PW3.counter == 0)) {
|
||||
gpg_install(STATE_TERMINATE);
|
||||
return(SW_OK);
|
||||
break;
|
||||
}
|
||||
THROW(SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Other commands allowed if not terminated */
|
||||
if (N_gpg_pstate->histo[7] != 0x07) {
|
||||
THROW(SW_STATE_TERMINATED);
|
||||
}
|
||||
|
||||
/* Process */
|
||||
gpg_check_access_ins();
|
||||
|
||||
switch (G_gpg_vstate.io_ins) {
|
||||
|
||||
|
||||
#ifdef GPG_DEBUG_APDU
|
||||
case 0x42:
|
||||
sw = debug_apdu();
|
||||
break;
|
||||
#endif
|
||||
/* --- CHALLENGE --- */
|
||||
case INS_GET_CHALLENGE:
|
||||
|
||||
sw = gpg_apdu_get_challenge();
|
||||
break;
|
||||
|
||||
/* --- DATA --- */
|
||||
|
||||
case INS_SELECT_DATA:
|
||||
if ((G_gpg_vstate.io_p1 > 2) || (G_gpg_vstate.io_p2 != 0x04)) {
|
||||
THROW(SW_WRONG_P1P2);
|
||||
}
|
||||
gpg_io_fetch_tl(&t,&l);
|
||||
if (t != 0x60) {
|
||||
//TODO add l check
|
||||
THROW(SW_DATA_INVALID);
|
||||
}
|
||||
gpg_io_fetch_tl(&t,&l);
|
||||
if (t != 0x5C) {
|
||||
//TODO add l check
|
||||
THROW(SW_WRONG_DATA);
|
||||
}
|
||||
if (l == 1) {
|
||||
tag = gpg_io_fetch_u8();
|
||||
} else if (l == 2) {
|
||||
tag = gpg_io_fetch_u16();
|
||||
} else {
|
||||
THROW(SW_WRONG_DATA);
|
||||
}
|
||||
sw = gpg_apdu_select_data(tag, G_gpg_vstate.io_p1);
|
||||
break;
|
||||
|
||||
case INS_GET_DATA:
|
||||
gpg_check_access_read_DO();
|
||||
|
||||
sw = gpg_apdu_get_data(tag);
|
||||
break;
|
||||
|
||||
case INS_GET_NEXT_DATA:
|
||||
gpg_check_access_read_DO();
|
||||
sw = gpg_apdu_get_next_data(tag);
|
||||
break;
|
||||
|
||||
|
||||
case INS_PUT_DATA_ODD:
|
||||
case INS_PUT_DATA:
|
||||
gpg_check_access_write_DO();
|
||||
sw = gpg_apdu_put_data(tag);
|
||||
break;
|
||||
|
||||
/* --- PIN -- */
|
||||
case INS_VERIFY:
|
||||
if ((G_gpg_vstate.io_p2 == 0x81) ||
|
||||
(G_gpg_vstate.io_p2 == 0x82) ||
|
||||
(G_gpg_vstate.io_p2 == 0x83)
|
||||
) {
|
||||
sw = gpg_apdu_verify(G_gpg_vstate.io_p2&0x0F);
|
||||
break;
|
||||
}
|
||||
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);
|
||||
break;
|
||||
}
|
||||
THROW(SW_INCORRECT_P1P2);
|
||||
|
||||
case INS_RESET_RETRY_COUNTER:
|
||||
if ((G_gpg_vstate.io_p2 == 0x81) &&
|
||||
( (G_gpg_vstate.io_p1 == 0) ||
|
||||
(G_gpg_vstate.io_p1 == 2) )
|
||||
) {
|
||||
sw = gpg_apdu_reset_retry_counter();
|
||||
break;
|
||||
}
|
||||
THROW(SW_INCORRECT_P1P2);
|
||||
|
||||
/* --- Key Management --- */
|
||||
case INS_GEN_ASYM_KEYPAIR:
|
||||
sw = gpg_apdu_gen();
|
||||
break;
|
||||
|
||||
/* --- PSO --- */
|
||||
case INS_PSO:
|
||||
sw = gpg_apdu_pso(tag);
|
||||
break;
|
||||
case INS_INTERNAL_AUTHENTICATE:
|
||||
sw = gpg_apdu_internal_authenticate();
|
||||
break;
|
||||
|
||||
default:
|
||||
THROW( SW_INS_NOT_SUPPORTED);
|
||||
break;
|
||||
}
|
||||
|
||||
return sw;
|
||||
}
|
279
src/gpg_gen.c
Normal file
279
src/gpg_gen.c
Normal file
@ -0,0 +1,279 @@
|
||||
/* Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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"
|
||||
|
||||
/* @in slot slot num [0 ; GPG_KEYS_SLOTS[
|
||||
* @out seed 32 bytes master seed for given slot
|
||||
*/
|
||||
static void gpg_pso_derive_slot_seed(int slot, unsigned char *seed) {
|
||||
unsigned int path[2];
|
||||
unsigned char chain[32];
|
||||
|
||||
os_memset(chain, 0, 32);
|
||||
path[0] = 0x80475047;
|
||||
path[1] = slot+1;
|
||||
os_perso_derive_node_bip32(CX_CURVE_SECP256K1, path, 2 , seed, chain);
|
||||
}
|
||||
|
||||
/* @in Sn master seed slot number
|
||||
* @in key_name key name: 'sig ', 'auth ', 'dec '
|
||||
* @in idx sub-seed index
|
||||
* @out Ski generated sub_seed
|
||||
* @in Ski_len sub-seed length
|
||||
*/
|
||||
static void gpg_pso_derive_key_seed(unsigned char *Sn, unsigned char* key_name, unsigned int idx,
|
||||
unsigned char *Ski, unsigned int Ski_len,
|
||||
cx_sha3_t *xof) {
|
||||
|
||||
unsigned char idx16[2];
|
||||
idx16[0] = idx >>8;
|
||||
idx16[1] = idx;
|
||||
|
||||
cx_sha3_xof_init(xof, 256, Ski_len);
|
||||
cx_hash((cx_hash_t *)xof, 0, Sn, 32, NULL);
|
||||
cx_hash((cx_hash_t *)xof, 0, (unsigned char *)key_name, 4, NULL);
|
||||
cx_hash((cx_hash_t *)xof, CX_LAST, idx16 , 2, Ski);
|
||||
}
|
||||
|
||||
|
||||
/* assume command is fully received */
|
||||
int gpg_apdu_gen() {
|
||||
unsigned int t,l,ksz,reset_cnt;
|
||||
gpg_key_t *keygpg;
|
||||
unsigned char seed[32];
|
||||
unsigned char* name;
|
||||
|
||||
switch ((G_gpg_vstate.io_p1<<8)|G_gpg_vstate.io_p2) {
|
||||
case 0x8000:
|
||||
case 0x8001:
|
||||
case 0x8100:
|
||||
break;
|
||||
default:
|
||||
THROW(SW_INCORRECT_P1P2);
|
||||
}
|
||||
|
||||
if (G_gpg_vstate.io_lc != 2){
|
||||
THROW(SW_WRONG_LENGTH);
|
||||
}
|
||||
|
||||
gpg_io_fetch_tl(&t,&l);
|
||||
gpg_io_discard(1);
|
||||
reset_cnt = 0;
|
||||
switch(t) {
|
||||
case 0xB6:
|
||||
keygpg = &G_gpg_vstate.kslot->sig;
|
||||
name = (unsigned char*)PIC("sig ");
|
||||
reset_cnt = 0;
|
||||
break;
|
||||
case 0xA4:
|
||||
keygpg = &G_gpg_vstate.kslot->aut;
|
||||
name = (unsigned char*)PIC("aut ");
|
||||
break;
|
||||
case 0xB8:
|
||||
keygpg = &G_gpg_vstate.kslot->dec;
|
||||
name = (unsigned char*)PIC("dec ");
|
||||
break;
|
||||
default:
|
||||
THROW(SW_WRONG_DATA);
|
||||
}
|
||||
|
||||
switch ((G_gpg_vstate.io_p1<<8)|G_gpg_vstate.io_p2) {
|
||||
// -- generate keypair ---
|
||||
case 0x8000:
|
||||
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;
|
||||
unsigned int pkey_size;
|
||||
|
||||
ksz = (keygpg->attributes.value[1]<<8)|keygpg->attributes.value[2];
|
||||
ksz = ksz >> 3;
|
||||
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;
|
||||
}
|
||||
pq = NULL;
|
||||
if ((G_gpg_vstate.io_p2 == 0x01) || (G_gpg_vstate.seed_mode)){
|
||||
pq = &rsa_pub->n[0];
|
||||
unsigned int size;
|
||||
size = ksz>>1;
|
||||
gpg_pso_derive_slot_seed(G_gpg_vstate.slot, seed);
|
||||
gpg_pso_derive_key_seed(seed, name, 1, pq, size, (cx_sha3_t*)((unsigned int)&G_gpg_vstate.work.io_buffer[1024] & (~3)));
|
||||
gpg_pso_derive_key_seed(seed, name, 2, pq+size, size, (cx_sha3_t*)((unsigned int)&G_gpg_vstate.work.io_buffer[1024] & (~3)));
|
||||
*pq |= 0x80;
|
||||
*(pq+size) |= 0x80;
|
||||
cx_math_next_prime(pq,size);
|
||||
cx_math_next_prime(pq+size,size);
|
||||
}
|
||||
|
||||
|
||||
cx_rsa_generate_pair(ksz, rsa_pub, rsa_priv, GPG_RSA_DEFAULT_PUB, pq);
|
||||
|
||||
nvm_write(pkey, rsa_priv, pkey_size);
|
||||
nvm_write(&keygpg->pub_key.rsa[0], rsa_pub->e, 4);
|
||||
if (reset_cnt) {
|
||||
reset_cnt = 0;
|
||||
nvm_write(&G_gpg_vstate.kslot->sig_count,&reset_cnt,sizeof(unsigned int));
|
||||
}
|
||||
gpg_io_clear();
|
||||
|
||||
goto send_rsa_pub;
|
||||
#undef GPG_RSA_DEFAULT_PUB
|
||||
}
|
||||
// ECC
|
||||
if ((keygpg->attributes.value[0] == 18) ||
|
||||
(keygpg->attributes.value[0] == 19) ||
|
||||
(keygpg->attributes.value[0] == 22) ){
|
||||
unsigned int curve,keepprivate;
|
||||
unsigned char *d;
|
||||
keepprivate = 0;
|
||||
curve = gpg_oid2curve(keygpg->attributes.value+1, keygpg->attributes.length-1);
|
||||
if ((G_gpg_vstate.io_p2 == 0x01) | (G_gpg_vstate.seed_mode)) {
|
||||
gpg_pso_derive_slot_seed(G_gpg_vstate.slot, seed);
|
||||
gpg_pso_derive_key_seed(seed, name, 1, seed, 32, (cx_sha3_t*)&G_gpg_vstate.work.io_buffer[1024]);
|
||||
cx_ecfp_init_private_key(curve,seed, 32, &G_gpg_vstate.work.ecfp256.private);
|
||||
keepprivate = 1;
|
||||
}
|
||||
|
||||
cx_ecfp_generate_pair(curve,
|
||||
&G_gpg_vstate.work.ecfp256.public,
|
||||
&G_gpg_vstate.work.ecfp256.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));
|
||||
if (reset_cnt) {
|
||||
reset_cnt = 0;
|
||||
nvm_write(&G_gpg_vstate.kslot->sig_count,&reset_cnt,sizeof(unsigned int));
|
||||
}
|
||||
gpg_io_clear();
|
||||
goto send_ecc_pub;
|
||||
}
|
||||
break;
|
||||
|
||||
// --- read pubkey ---
|
||||
case 0x8100:
|
||||
if (keygpg->attributes.value[0] == 0x01) {
|
||||
/// read RSA
|
||||
send_rsa_pub:
|
||||
gpg_io_discard(1);
|
||||
//check length
|
||||
ksz = (keygpg->attributes.value[1]<<8)|keygpg->attributes.value[2];
|
||||
ksz = ksz>>3;
|
||||
gpg_io_mark();
|
||||
switch(ksz) {
|
||||
case 1024/8:
|
||||
if (keygpg->key.rsa1024.size == 0) {
|
||||
THROW (SW_REFERENCED_DATA_NOT_FOUND);
|
||||
}
|
||||
gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->key.rsa1024.n);
|
||||
break;
|
||||
case 2048/8:
|
||||
if (keygpg->key.rsa2048.size == 0) {
|
||||
THROW (SW_REFERENCED_DATA_NOT_FOUND);
|
||||
}
|
||||
gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->key.rsa2048.n);
|
||||
break;
|
||||
case 3072/8:
|
||||
if (keygpg->key.rsa3072.size == 0) {
|
||||
THROW (SW_REFERENCED_DATA_NOT_FOUND);
|
||||
}
|
||||
gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->key.rsa3072.n);
|
||||
break;
|
||||
case 4096/8:
|
||||
if (keygpg->key.rsa4096.size == 0) {
|
||||
THROW (SW_REFERENCED_DATA_NOT_FOUND);
|
||||
}
|
||||
gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->key.rsa4096.n);
|
||||
break;
|
||||
}
|
||||
gpg_io_insert_tlv(0x82, 4, keygpg->pub_key.rsa);
|
||||
|
||||
l = G_gpg_vstate.io_length;
|
||||
gpg_io_set_offset(IO_OFFSET_MARK);
|
||||
gpg_io_insert_tl(0x7f49,l);
|
||||
gpg_io_set_offset(IO_OFFSET_END);
|
||||
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
if ((keygpg->attributes.value[0] == 18) ||
|
||||
(keygpg->attributes.value[0] == 19) ||
|
||||
(keygpg->attributes.value[0] == 22) ){
|
||||
unsigned int curve;
|
||||
/// read ECC
|
||||
send_ecc_pub:
|
||||
|
||||
if (keygpg->pub_key.ecfp256.W_len == 0) {
|
||||
THROW (SW_REFERENCED_DATA_NOT_FOUND);
|
||||
}
|
||||
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);
|
||||
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;
|
||||
len = keygpg->pub_key.ecfp256.W_len-1;
|
||||
for (i = 0; i <=len; i++) {
|
||||
G_gpg_vstate.work.io_buffer[128+i] = keygpg->pub_key.ecfp256.W[len-i];
|
||||
}
|
||||
gpg_io_insert_tlv(0x86, 32, G_gpg_vstate.work.io_buffer+128);
|
||||
} else {
|
||||
gpg_io_insert_tlv(0x86, keygpg->pub_key.ecfp256.W_len, (unsigned char*)&keygpg->pub_key.ecfp256.W );
|
||||
}
|
||||
l = G_gpg_vstate.io_length;
|
||||
gpg_io_set_offset(IO_OFFSET_MARK);
|
||||
gpg_io_insert_tl(0x7f49,l);
|
||||
gpg_io_set_offset(IO_OFFSET_END);
|
||||
return SW_OK;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
THROW(SW_WRONG_DATA);
|
||||
}
|
307
src/gpg_init.c
Normal file
307
src/gpg_init.c
Normal file
@ -0,0 +1,307 @@
|
||||
/* Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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"
|
||||
|
||||
#define SHORT(x) ((x)>>8)&0xFF, (x)&0xFF
|
||||
/* ----------------------*/
|
||||
/* -- A Kind of Magic -- */
|
||||
/* ----------------------*/
|
||||
|
||||
const unsigned char C_MAGIC[8] = {
|
||||
'G','P','G','C','A','R','D','3'
|
||||
};
|
||||
/* ----------------------*/
|
||||
/* --ECC OID -- */
|
||||
/* ----------------------*/
|
||||
|
||||
//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
|
||||
};
|
||||
//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
|
||||
};
|
||||
//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,
|
||||
};
|
||||
|
||||
//Ed25519/curve25519: 1.3.6.1.4.1.11591.15.1
|
||||
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) {
|
||||
|
||||
if ( (len == sizeof(C_OID_SECP256R1)) && (os_memcmp(oid, C_OID_SECP256R1, len)==0) ) {
|
||||
return CX_CURVE_256R1;
|
||||
}
|
||||
|
||||
if ( (len == sizeof(C_OID_Ed25519)) && (os_memcmp(oid, C_OID_Ed25519, len)==0) ) {
|
||||
return CX_CURVE_Ed25519;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* -------------------------------*/
|
||||
/* -- Non Mutable Capabilities -- */
|
||||
/* -------------------------------*/
|
||||
|
||||
const unsigned char C_ext_capabilities[10] = {
|
||||
//-SM, +getchallenge, +keyimport, +PWchangeable, +privateDO, +algAttrChangeable, +AES, -RFU
|
||||
0x7E,
|
||||
// No SM,
|
||||
0x00,
|
||||
//max challenge
|
||||
SHORT(GPG_EXT_CHALLENGE_LENTH),
|
||||
//max cert holder length
|
||||
SHORT(GPG_EXT_CARD_HOLDER_CERT_LENTH),
|
||||
//maximum do length
|
||||
SHORT(GPG_EXT_PRIVATE_DO_LENGTH),
|
||||
//PIN block formart 2 not supported
|
||||
0x00,
|
||||
//RFU
|
||||
0x00
|
||||
|
||||
};
|
||||
|
||||
const unsigned char C_ext_length[8] = {
|
||||
0x02, 0x02, SHORT(GPG_APDU_LENGTH),
|
||||
0x02, 0x02, SHORT(GPG_APDU_LENGTH)
|
||||
};
|
||||
|
||||
|
||||
/* ---------------------*/
|
||||
/* -- default values -- */
|
||||
/* ---------------------*/
|
||||
|
||||
const unsigned char C_default_AID[] = {
|
||||
0xD2, 0x76, 0x00, 0x01, 0x24, 0x01,
|
||||
//version
|
||||
0x03, 0x00,
|
||||
//manufacturer
|
||||
0x2C, 0x97,
|
||||
//serial
|
||||
0x42, 0x42, 0x42, 0x42,
|
||||
//RFU
|
||||
0x00,0x00
|
||||
};
|
||||
|
||||
const unsigned char C_default_Histo[] = {
|
||||
0x00,
|
||||
0x31,
|
||||
0xC5, // select method: by DF/partialDF; IO-file:readbinary; RFU???
|
||||
0x73,
|
||||
0xC0, // select method: by DF/partialDF ,
|
||||
0x01, // data coding style: ontime/byte
|
||||
0x80, // chaining
|
||||
0x7F, // zero state
|
||||
0x90, 0x00
|
||||
};
|
||||
|
||||
const unsigned char C_default_AlgoAttrRSA[] = {
|
||||
// RSA
|
||||
0x01,
|
||||
// Modulus default length 2048
|
||||
0x08, 0x00,
|
||||
// PubExp length 32
|
||||
0x00, 0x20,
|
||||
// std: e,p,q with modulus (n)
|
||||
0x01
|
||||
};
|
||||
|
||||
#if 1
|
||||
const unsigned char C_default_AlgoAttrECC_sig[] = {
|
||||
// ecdsa
|
||||
0x13,
|
||||
// NIST-P256
|
||||
0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x07
|
||||
};
|
||||
const unsigned char C_default_AlgoAttrECC_dec[] = {
|
||||
// ecdh
|
||||
0x12,
|
||||
// NIST-P256
|
||||
0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x07
|
||||
};
|
||||
|
||||
#else
|
||||
const unsigned char C_default_AlgoAttrECC_sig[] = {
|
||||
// eddsa
|
||||
22,
|
||||
// ed25519
|
||||
0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01,
|
||||
};
|
||||
const unsigned char C_default_AlgoAttrECC_dec[] = {
|
||||
// ecdh
|
||||
18,
|
||||
//cv25519
|
||||
0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01,
|
||||
};
|
||||
#endif
|
||||
|
||||
// sha256 0x3132343536
|
||||
const unsigned char C_sha256_PW1[] = {
|
||||
0x8d, 0x96, 0x9e, 0xef, 0x6e, 0xca, 0xd3, 0xc2,
|
||||
0x9a, 0x3a, 0x62, 0x92, 0x80, 0xe6, 0x86, 0xcf,
|
||||
0x0c, 0x3f, 0x5d, 0x5a, 0x86, 0xaf, 0xf3, 0xca,
|
||||
0x12, 0x02, 0x0c, 0x92, 0x3a, 0xdc, 0x6c, 0x92,
|
||||
};
|
||||
// sha256 0x31323435363738
|
||||
const unsigned char C_sha256_PW2[] = {
|
||||
0xef, 0x79, 0x7c, 0x81, 0x18, 0xf0, 0x2d, 0xfb,
|
||||
0x64, 0x96, 0x07, 0xdd, 0x5d, 0x3f, 0x8c, 0x76,
|
||||
0x23, 0x04, 0x8c, 0x9c, 0x06, 0x3d, 0x53, 0x2c,
|
||||
0xc9, 0x5c, 0x5e, 0xd7, 0xa8, 0x98, 0xa6, 0x4f,
|
||||
};
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* --- boot init --- */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
|
||||
void gpg_init() {
|
||||
os_memset(&G_gpg_vstate, 0, sizeof(gpg_v_state_t));
|
||||
//first init ?
|
||||
if (os_memcmp(N_gpg_pstate->magic, (void*)C_MAGIC, sizeof(C_MAGIC)) != 0) {
|
||||
gpg_install(STATE_ACTIVATE);
|
||||
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];
|
||||
//ux conf
|
||||
gpg_init_ux();
|
||||
}
|
||||
|
||||
void gpg_init_ux() {
|
||||
snprintf(G_gpg_vstate.menu_cur_slot, 16, ">>SLOT: %d<<", G_gpg_vstate.slot+1);
|
||||
snprintf(G_gpg_vstate.menu_seed_mode, 16, ">>%s<<", G_gpg_vstate.seed_mode?"ON":"OFF");
|
||||
snprintf(G_gpg_vstate.menu_template_key, 16, "choose key...");
|
||||
snprintf(G_gpg_vstate.menu_template_type, 16, "choose type...");
|
||||
G_gpg_vstate.ux_type = 0;
|
||||
G_gpg_vstate.ux_key = 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* --- Install/ReInstall GPGapp --- */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
int gpg_install(unsigned char app_state) {
|
||||
gpg_pin_t pin;
|
||||
unsigned int l;
|
||||
unsigned char config[4];
|
||||
|
||||
//full reset data
|
||||
gpg_nvm_write(N_gpg_pstate, NULL, sizeof(gpg_nv_state_t));
|
||||
|
||||
//historical bytes
|
||||
os_memmove(G_gpg_vstate.work.io_buffer, C_default_Histo, sizeof(C_default_Histo));
|
||||
G_gpg_vstate.work.io_buffer[7] = app_state;
|
||||
gpg_nvm_write(N_gpg_pstate->histo, G_gpg_vstate.work.io_buffer, sizeof(C_default_Histo));
|
||||
|
||||
//AID (TODO: set serial)
|
||||
os_memmove(G_gpg_vstate.work.io_buffer, C_default_AID, sizeof(C_default_AID));
|
||||
cx_rng(G_gpg_vstate.work.io_buffer+10, 4);
|
||||
G_gpg_vstate.work.io_buffer[13] &= 0x07;
|
||||
gpg_nvm_write(N_gpg_pstate->AID, &G_gpg_vstate.work.io_buffer, sizeof(C_default_AID));
|
||||
|
||||
if (app_state == STATE_ACTIVATE) {
|
||||
|
||||
//default sex: none
|
||||
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
|
||||
os_memmove(pin.value, C_sha256_PW1, sizeof(C_sha256_PW1));
|
||||
pin.length = 6;
|
||||
pin.counter = 3;
|
||||
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;
|
||||
gpg_nvm_write(&N_gpg_pstate->PW3, &pin, sizeof(gpg_pin_t));
|
||||
|
||||
//PWs status
|
||||
G_gpg_vstate.work.io_buffer[0] = 1;
|
||||
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->PW_status, G_gpg_vstate.work.io_buffer, 4);
|
||||
|
||||
//config slot
|
||||
config[0] = GPG_KEYS_SLOTS;
|
||||
config[1] = 0;
|
||||
config[2] = 3; // 3: selection by APDU and screen
|
||||
|
||||
//default key template: RSA 2048)
|
||||
|
||||
for (int s = 0; s< GPG_KEYS_SLOTS; s++) {
|
||||
#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));
|
||||
gpg_nvm_write(&N_gpg_pstate->keys[s].aut.attributes.value, (void*)C_default_AlgoAttrRSA, l);
|
||||
gpg_nvm_write(&N_gpg_pstate->keys[s].aut.attributes.length, &l, sizeof(unsigned int));
|
||||
gpg_nvm_write(&N_gpg_pstate->keys[s].dec.attributes.value, (void*)C_default_AlgoAttrRSA, l);
|
||||
gpg_nvm_write(&N_gpg_pstate->keys[s].dec.attributes.length, &l, sizeof(unsigned int));
|
||||
#else
|
||||
l = sizeof(C_default_AlgoAttrECC_sig);
|
||||
gpg_nvm_write(&N_gpg_pstate->keys[s].sig.attributes.value, (void*)C_default_AlgoAttrECC_sig, l);
|
||||
gpg_nvm_write(&N_gpg_pstate->keys[s].sig.attributes.length, &l, sizeof(unsigned int));
|
||||
gpg_nvm_write(&N_gpg_pstate->keys[s].aut.attributes.value, (void*)C_default_AlgoAttrECC_sig, l);
|
||||
gpg_nvm_write(&N_gpg_pstate->keys[s].aut.attributes.length, &l, sizeof(unsigned int));
|
||||
l = sizeof(C_default_AlgoAttrECC_dec);
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
321
src/gpg_io.c
Normal file
321
src/gpg_io.c
Normal file
@ -0,0 +1,321 @@
|
||||
/* Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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"
|
||||
|
||||
|
||||
/*
|
||||
* io_buff: contains current message part
|
||||
* io_off: offset in current message part
|
||||
* io_length: length of current message part
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* MISC */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
void gpg_io_set_offset(unsigned int offset) {
|
||||
if (offset == IO_OFFSET_END) {
|
||||
G_gpg_vstate.io_offset = G_gpg_vstate.io_length;
|
||||
}
|
||||
else if (offset == IO_OFFSET_MARK) {
|
||||
G_gpg_vstate.io_offset = G_gpg_vstate.io_mark;
|
||||
}
|
||||
else if (offset < G_gpg_vstate.io_length) {
|
||||
G_gpg_vstate.io_offset = G_gpg_vstate.io_length;
|
||||
}
|
||||
else {
|
||||
THROW(ERROR_IO_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gpg_io_mark() {
|
||||
G_gpg_vstate.io_mark = G_gpg_vstate.io_offset;
|
||||
}
|
||||
|
||||
|
||||
void gpg_io_inserted(unsigned int len) {
|
||||
G_gpg_vstate.io_offset += len;
|
||||
G_gpg_vstate.io_length += len;
|
||||
}
|
||||
|
||||
void gpg_io_discard(int clear) {
|
||||
G_gpg_vstate.io_length = 0;
|
||||
G_gpg_vstate.io_offset = 0;
|
||||
G_gpg_vstate.io_mark = 0;
|
||||
if (clear) {
|
||||
gpg_io_clear();
|
||||
}
|
||||
}
|
||||
|
||||
void gpg_io_clear() {
|
||||
os_memset(G_gpg_vstate.work.io_buffer, 0 , GPG_IO_BUFFER_LENGTH);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* INSERT data to be sent */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
void gpg_io_hole(unsigned int sz) {
|
||||
if ((G_gpg_vstate.io_length + sz) > GPG_IO_BUFFER_LENGTH) {
|
||||
THROW(ERROR_IO_FULL);
|
||||
}
|
||||
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,
|
||||
G_gpg_vstate.io_length-G_gpg_vstate.io_offset);
|
||||
G_gpg_vstate.io_length += sz;
|
||||
}
|
||||
|
||||
void gpg_io_insert(unsigned char const *buff, unsigned int len) {
|
||||
gpg_io_hole(len);
|
||||
os_memmove(G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, buff, len);
|
||||
G_gpg_vstate.io_offset += len;
|
||||
}
|
||||
|
||||
void gpg_io_insert_u32(unsigned int v32) {
|
||||
gpg_io_hole(4);
|
||||
G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+0] = v32>>24;
|
||||
G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+1] = v32>>16;
|
||||
G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+2] = v32>>8;
|
||||
G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+3] = v32>>0;
|
||||
G_gpg_vstate.io_offset += 4;
|
||||
|
||||
}
|
||||
|
||||
void gpg_io_insert_u24(unsigned int v24) {
|
||||
gpg_io_hole(3);
|
||||
G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+0] = v24>>16;
|
||||
G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+1] = v24>>8;
|
||||
G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+2] = v24>>0;
|
||||
G_gpg_vstate.io_offset += 3;
|
||||
}
|
||||
void gpg_io_insert_u16(unsigned int v16) {
|
||||
gpg_io_hole(2);
|
||||
G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+0] = v16>>8;
|
||||
G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+1] = v16>>0;
|
||||
G_gpg_vstate.io_offset += 2;
|
||||
}
|
||||
void gpg_io_insert_u8(unsigned int v8) {
|
||||
gpg_io_hole(1);
|
||||
G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+0] = v8;
|
||||
G_gpg_vstate.io_offset += 1;
|
||||
}
|
||||
|
||||
void gpg_io_insert_t(unsigned int T) {
|
||||
if (T &0xFF00) {
|
||||
gpg_io_insert_u16(T);
|
||||
} else {
|
||||
gpg_io_insert_u8(T);
|
||||
}
|
||||
}
|
||||
|
||||
void gpg_io_insert_tl(unsigned int T, unsigned int L) {
|
||||
gpg_io_insert_t(T);
|
||||
if (L < 128) {
|
||||
gpg_io_insert_u8(L);
|
||||
} else if (L < 256) {
|
||||
gpg_io_insert_u16(0x8100|L);
|
||||
} else {
|
||||
gpg_io_insert_u8(0x82);
|
||||
gpg_io_insert_u16(L);
|
||||
}
|
||||
}
|
||||
|
||||
void gpg_io_insert_tlv(unsigned int T, unsigned int L, unsigned char const *V) {
|
||||
gpg_io_insert_tl(T,L);
|
||||
gpg_io_insert(V, L);
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* 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;
|
||||
v32 = ( (G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+0] << 24) |
|
||||
(G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+1] << 16) |
|
||||
(G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+2] << 8) |
|
||||
(G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+3] << 0) );
|
||||
G_gpg_vstate.io_offset += 4;
|
||||
return v32;
|
||||
}
|
||||
|
||||
unsigned int gpg_io_fetch_u16() {
|
||||
unsigned int v16;
|
||||
v16 = ( (G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+0] << 8) |
|
||||
(G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+1] << 0) );
|
||||
G_gpg_vstate.io_offset += 2;
|
||||
return v16;
|
||||
}
|
||||
|
||||
unsigned int gpg_io_fetch_u8() {
|
||||
unsigned int v8;
|
||||
v8 = G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset] ;
|
||||
G_gpg_vstate.io_offset += 1;
|
||||
return v8;
|
||||
}
|
||||
|
||||
int gpg_io_fetch_t(unsigned int *T) {
|
||||
*T = G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset];
|
||||
G_gpg_vstate.io_offset++;
|
||||
if ((*T & 0x1F) == 0x1F) {
|
||||
*T = (*T << 8) | G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset];
|
||||
G_gpg_vstate.io_offset++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpg_io_fetch_l(unsigned int *L) {
|
||||
*L = G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset];
|
||||
|
||||
if ((*L & 0x80) != 0) {
|
||||
*L &= 0x7F;
|
||||
if (*L == 1) {
|
||||
*L = G_gpg_vstate.work.io_buffer[G_gpg_vstate.io_offset+1];
|
||||
G_gpg_vstate.io_offset += 2;
|
||||
} else if (*L == 2) {
|
||||
*L = (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] ;
|
||||
G_gpg_vstate.io_offset += 3 ;
|
||||
} else {
|
||||
*L = -1;
|
||||
}
|
||||
} else {
|
||||
G_gpg_vstate.io_offset += 1 ;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpg_io_fetch_tl(unsigned int *T, unsigned int *L) {
|
||||
gpg_io_fetch_t(T);
|
||||
gpg_io_fetch_l(L);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpg_io_fetch_nv(unsigned char* buffer, int len) {
|
||||
gpg_nvm_write(buffer, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, len);
|
||||
G_gpg_vstate.io_offset += len;
|
||||
return len;
|
||||
}
|
||||
int gpg_io_fetch(unsigned char* buffer, int len) {
|
||||
if (buffer) {
|
||||
os_memmove(buffer, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, len);
|
||||
}
|
||||
G_gpg_vstate.io_offset += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* REAL IO */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
|
||||
int gpg_io_do() {
|
||||
|
||||
#define MAX_OUT GPG_APDU_LENGTH
|
||||
|
||||
|
||||
//if pending input chaining
|
||||
if (G_gpg_vstate.io_cla & 0x01) {
|
||||
goto in_chaining;
|
||||
}
|
||||
|
||||
// --- full out chaining ---
|
||||
G_gpg_vstate.io_offset = 0;
|
||||
while(G_gpg_vstate.io_length > MAX_OUT) {
|
||||
unsigned int tx,xx;
|
||||
//send chunk
|
||||
tx = MAX_OUT-2;
|
||||
os_memmove(G_io_apdu_buffer, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, tx);
|
||||
G_gpg_vstate.io_length -= tx;
|
||||
G_gpg_vstate.io_offset += tx;
|
||||
G_io_apdu_buffer[tx] = 0x61;
|
||||
if (G_gpg_vstate.io_length > MAX_OUT-2) {
|
||||
xx = MAX_OUT-2;
|
||||
} else {
|
||||
xx = G_gpg_vstate.io_length-2;
|
||||
}
|
||||
G_io_apdu_buffer[tx+1] = xx;
|
||||
gpg_io_exchange(CHANNEL_APDU | G_gpg_vstate.io_flags, tx+2);
|
||||
//check get response
|
||||
if ((G_io_apdu_buffer[0] != 0x00) ||
|
||||
(G_io_apdu_buffer[1] != 0xc0) ||
|
||||
(G_io_apdu_buffer[2] != 0x00) ||
|
||||
(G_io_apdu_buffer[3] != 0x00) ) {
|
||||
THROW(SW_COMMAND_NOT_ALLOWED);
|
||||
}
|
||||
}
|
||||
os_memmove(G_io_apdu_buffer, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, G_gpg_vstate.io_length);
|
||||
gpg_io_exchange(CHANNEL_APDU | G_gpg_vstate.io_flags, G_gpg_vstate.io_length);
|
||||
|
||||
//--- full in chaining ---
|
||||
G_gpg_vstate.io_offset = 0;
|
||||
G_gpg_vstate.io_length = 0;
|
||||
G_gpg_vstate.io_cla = G_io_apdu_buffer[0];
|
||||
G_gpg_vstate.io_ins = G_io_apdu_buffer[1];
|
||||
G_gpg_vstate.io_p1 = G_io_apdu_buffer[2];
|
||||
G_gpg_vstate.io_p2 = G_io_apdu_buffer[3];
|
||||
G_gpg_vstate.io_lc = 0;
|
||||
G_gpg_vstate.io_le = 0;
|
||||
|
||||
switch (G_gpg_vstate.io_ins) {
|
||||
case INS_GET_DATA:
|
||||
case INS_GET_RESPONSE:
|
||||
case INS_GET_CHALLENGE:
|
||||
case INS_TERMINATE_DF:
|
||||
case INS_ACTIVATE_FILE:
|
||||
G_gpg_vstate.io_le = G_io_apdu_buffer[4];
|
||||
break;
|
||||
default:
|
||||
G_gpg_vstate.io_lc = G_io_apdu_buffer[4];
|
||||
os_memmove(G_gpg_vstate.work.io_buffer, G_io_apdu_buffer+5, G_gpg_vstate.io_lc);
|
||||
G_gpg_vstate.io_length = G_gpg_vstate.io_lc;
|
||||
break;
|
||||
}
|
||||
|
||||
while(G_gpg_vstate.io_cla & 0x10) {
|
||||
|
||||
G_io_apdu_buffer[0] = 0x90;
|
||||
G_io_apdu_buffer[1] = 0x00;
|
||||
gpg_io_exchange(CHANNEL_APDU | G_gpg_vstate.io_flags, 2);
|
||||
in_chaining:
|
||||
if (((G_io_apdu_buffer[0] & 0xEF) != (G_gpg_vstate.io_cla& 0xEF)) ||
|
||||
(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);
|
||||
}
|
||||
G_gpg_vstate.io_cla = G_io_apdu_buffer[0];
|
||||
G_gpg_vstate.io_lc = G_io_apdu_buffer[4];
|
||||
if ((G_gpg_vstate.io_length + G_gpg_vstate.io_lc) > GPG_IO_BUFFER_LENGTH) {
|
||||
return 1;
|
||||
}
|
||||
os_memmove(G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_length, G_io_apdu_buffer+5, G_gpg_vstate.io_lc);
|
||||
G_gpg_vstate.io_length += G_gpg_vstate.io_lc;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
615
src/gpg_main.c
Normal file
615
src/gpg_main.c
Normal file
@ -0,0 +1,615 @@
|
||||
/* Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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.
|
||||
*/
|
||||
|
||||
#ifndef GPG_DEBUG_MAIN
|
||||
|
||||
#include "os.h"
|
||||
#include "cx.h"
|
||||
#include "gpg_types.h"
|
||||
#include "gpg_api.h"
|
||||
#include "gpg_vars.h"
|
||||
|
||||
|
||||
#include "os_io_seproxyhal.h"
|
||||
#include "string.h"
|
||||
#include "glyphs.h"
|
||||
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* --- UI layout --- */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* screeen size:
|
||||
blue; 320x480
|
||||
nanoS: 128x32
|
||||
*/
|
||||
#if 0
|
||||
static const bagl_element_t const ui_idle_blue[] = {
|
||||
{{BAGL_RECTANGLE, 0x00, 0, 60, 320, 420, 0, 0,
|
||||
BAGL_FILL, 0xf9f9f9, 0xf9f9f9,
|
||||
0, 0},
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL},
|
||||
|
||||
{{BAGL_RECTANGLE, 0x00, 0, 0, 320, 60, 0, 0,
|
||||
BAGL_FILL, 0x1d2028, 0x1d2028,
|
||||
0, 0},
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL},
|
||||
|
||||
{{BAGL_LABEL, 0x00, 20, 0, 320, 60, 0, 0,
|
||||
BAGL_FILL, 0xFFFFFF, 0x1d2028,
|
||||
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_MIDDLE, 0},
|
||||
"GPG Card",
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL},
|
||||
|
||||
{{BAGL_BUTTON | BAGL_FLAG_TOUCHABLE, 0x00, 190, 215, 120, 40, 0, 6,
|
||||
BAGL_FILL, 0x41ccb4, 0xF9F9F9,
|
||||
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_CENTER | BAGL_FONT_ALIGNMENT_MIDDLE,
|
||||
0},
|
||||
"Exit",
|
||||
0,
|
||||
0x37ae99,
|
||||
0xF9F9F9,
|
||||
gpg_io_seproxyhal_touch_exit,
|
||||
NULL,
|
||||
NULL},
|
||||
|
||||
#ifdef GPG_DEBUG
|
||||
{{BAGL_BUTTON | BAGL_FLAG_TOUCHABLE, 0x00, 20, 215, 120, 40, 0, 6,
|
||||
BAGL_FILL, 0x41ccb4, 0xF9F9F9,
|
||||
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_CENTER | BAGL_FONT_ALIGNMENT_MIDDLE,
|
||||
0},
|
||||
"Init",
|
||||
0,
|
||||
0x37ae99,
|
||||
0xF9F9F9,
|
||||
gpg_io_seproxyhal_touch_debug,
|
||||
NULL,
|
||||
NULL}
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
|
||||
const bagl_element_t ui_idle_nanos[] = {
|
||||
// type
|
||||
{{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0,
|
||||
BAGL_FILL, 0x000000, 0xFFFFFF,
|
||||
0,
|
||||
0},
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL},
|
||||
|
||||
{{BAGL_LABELINE, 0x00, 0, 12, 128, 32, 0, 0,
|
||||
0 , 0xFFFFFF, 0x000000,
|
||||
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER,
|
||||
0 },
|
||||
"GPGCard",
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL },
|
||||
//{{BAGL_LABELINE , 0x02, 0, 26, 128, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Waiting for requests...", 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 },
|
||||
};
|
||||
|
||||
unsigned int gpg_io_seproxyhal_touch_exit(const bagl_element_t *e) {
|
||||
// Go back to the dashboard
|
||||
os_sched_exit(0);
|
||||
return 0; // do not redraw the widget
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
unsigned int ui_idle_blue_button(unsigned int button_mask,
|
||||
unsigned int button_mask_counter) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
const ux_menu_entry_t ui_idle_mainmenu[];
|
||||
|
||||
/* ----- Helpers UX ----- */
|
||||
void ui_info(const ux_menu_entry_t *back, const char* msg1, const char* msg2) {
|
||||
ux_menu_entry_t ui_invalid[2] = {
|
||||
{back, NULL, 0, NULL, msg1, msg2, 0, 0},
|
||||
UX_MENU_END
|
||||
};
|
||||
UX_MENU_DISPLAY(0, ui_invalid, NULL);
|
||||
};
|
||||
|
||||
/* ----- SLOT UX ----- */
|
||||
void ui_idle_sub_slot_action(unsigned int value) {
|
||||
unsigned char s;
|
||||
|
||||
if (value == 0) {
|
||||
s = G_gpg_vstate.slot;
|
||||
gpg_nvm_write(&N_gpg_pstate->config_slot[1], &s,1);
|
||||
}
|
||||
else {
|
||||
s = (unsigned char)(value-1);
|
||||
G_gpg_vstate.slot = s;
|
||||
G_gpg_vstate.kslot = &N_gpg_pstate->keys[G_gpg_vstate.slot];
|
||||
|
||||
}
|
||||
// redisplay first entry of the idle menu
|
||||
gpg_init_ux();
|
||||
UX_MENU_DISPLAY(0, ui_idle_mainmenu, NULL);
|
||||
}
|
||||
|
||||
const ux_menu_entry_t ui_idle_sub_slot[] = {
|
||||
#if GPG_KEYS_SLOTS != 3
|
||||
#error menu definition not correct for current value of GPG_KEYS_SLOTS
|
||||
#endif
|
||||
{NULL, ui_idle_sub_slot_action, 1, NULL, "Select slot 1", NULL, 0, 0},
|
||||
{NULL, ui_idle_sub_slot_action, 2, NULL, "Select slot 2", NULL, 0, 0},
|
||||
{NULL, ui_idle_sub_slot_action, 3, NULL, "Select slot 3", NULL, 0, 0},
|
||||
{NULL, ui_idle_sub_slot_action, 0, NULL, "Set as default", NULL, 0, 0},
|
||||
{ui_idle_mainmenu, NULL, 1, &C_badge_back, "Back", NULL, 61, 40},
|
||||
UX_MENU_END
|
||||
};
|
||||
|
||||
/* ----- template UX ----- */
|
||||
#define LABEL_SIG "Signature"
|
||||
#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"
|
||||
|
||||
const ux_menu_entry_t ui_idle_sub_template[];
|
||||
const ux_menu_entry_t ui_idle_sub_tmpl_key[];
|
||||
const ux_menu_entry_t ui_idle_sub_tmpl_type[];
|
||||
|
||||
/*
|
||||
const bagl_element_t* ui_idle_sub_tmpl_key_prepocessor(const ux_menu_entry_t* entry, bagl_element_t* element) {
|
||||
if (entry == &ui_idle_sub_tmpl_key[2]) {
|
||||
if(element->component.userid==0x20) {
|
||||
element->component.stroke = 10; // 1 second stop in each way
|
||||
element->component.icon_id = 26; // roundtrip speed in pixel/s
|
||||
UX_CALLBACK_SET_INTERVAL(MAX(3000, 2*(1000+bagl_label_roundtrip_duration_ms(element, 7))));
|
||||
}
|
||||
}
|
||||
return element;
|
||||
}
|
||||
*/
|
||||
|
||||
void ui_idle_sub_tmpl_key_action(unsigned int value) {
|
||||
|
||||
switch(value) {
|
||||
case 1:
|
||||
snprintf(G_gpg_vstate.menu_template_key, 16, "%s", LABEL_SIG);
|
||||
G_gpg_vstate.ux_key = 1;
|
||||
break;
|
||||
case 2:
|
||||
snprintf(G_gpg_vstate.menu_template_key, 16, "%s", LABEL_AUT);
|
||||
G_gpg_vstate.ux_key = 2;
|
||||
break;
|
||||
case 3:
|
||||
snprintf(G_gpg_vstate.menu_template_key, 16," %s", LABEL_DEC);
|
||||
G_gpg_vstate.ux_key = 3;
|
||||
break;
|
||||
}
|
||||
UX_MENU_DISPLAY(0, ui_idle_sub_template, NULL);
|
||||
}
|
||||
const ux_menu_entry_t ui_idle_sub_tmpl_key[] = {
|
||||
{NULL, ui_idle_sub_tmpl_key_action, 1, NULL, LABEL_SIG, NULL, 0, 0},
|
||||
{NULL, ui_idle_sub_tmpl_key_action, 2, NULL, LABEL_DEC, NULL, 0, 0},
|
||||
{NULL, ui_idle_sub_tmpl_key_action, 3, NULL, LABEL_AUT, NULL, 0, 0},
|
||||
{ui_idle_sub_template, NULL, 0, &C_badge_back, "Back", NULL, 61, 40},
|
||||
UX_MENU_END
|
||||
};
|
||||
|
||||
void ui_idle_sub_tmpl_type_action(unsigned int value) {
|
||||
switch(value) {
|
||||
case 2048:
|
||||
snprintf(G_gpg_vstate.menu_template_type, 16," %s", LABEL_RSA2048);
|
||||
break;
|
||||
case 3072:
|
||||
snprintf(G_gpg_vstate.menu_template_type, 16," %s", LABEL_RSA3072);
|
||||
break;
|
||||
case 4096:
|
||||
snprintf(G_gpg_vstate.menu_template_type, 16," %s", LABEL_RSA4096);
|
||||
break;
|
||||
case CX_CURVE_SECP256R1:
|
||||
snprintf(G_gpg_vstate.menu_template_type, 16," %s", LABEL_NISTP256);
|
||||
break;
|
||||
case CX_CURVE_BrainPoolP256R1:
|
||||
snprintf(G_gpg_vstate.menu_template_type, 16," %s", LABEL_BPOOLR1);
|
||||
break;
|
||||
case CX_CURVE_Ed25519:
|
||||
snprintf(G_gpg_vstate.menu_template_type, 16," %s", LABEL_Ed25519);
|
||||
break;
|
||||
}
|
||||
G_gpg_vstate.ux_type = value;
|
||||
UX_MENU_DISPLAY(1, ui_idle_sub_template, NULL);
|
||||
}
|
||||
const ux_menu_entry_t ui_idle_sub_tmpl_type[] = {
|
||||
{NULL, ui_idle_sub_tmpl_type_action, 2048, NULL, LABEL_RSA2048, NULL, 0, 0},
|
||||
{NULL, ui_idle_sub_tmpl_type_action, 3072, NULL, LABEL_RSA3072, NULL, 0, 0},
|
||||
{NULL, ui_idle_sub_tmpl_type_action, 4096, NULL, LABEL_RSA4096, NULL, 0, 0},
|
||||
{NULL, ui_idle_sub_tmpl_type_action, CX_CURVE_SECP256R1, NULL, LABEL_NISTP256, NULL, 0, 0},
|
||||
{NULL, ui_idle_sub_tmpl_type_action, CX_CURVE_BrainPoolP256R1, NULL, LABEL_BPOOLR1, NULL, 0, 0},
|
||||
{NULL, ui_idle_sub_tmpl_type_action, CX_CURVE_Ed25519, NULL, LABEL_Ed25519, NULL, 0, 0},
|
||||
{ui_idle_sub_template, NULL, 1, &C_badge_back, "Back", NULL, 61, 40},
|
||||
UX_MENU_END
|
||||
};
|
||||
|
||||
|
||||
void ui_idle_sub_tmpl_set_action(unsigned int value) {
|
||||
LV(attributes,GPG_KEY_ATTRIBUTES_LENGTH);
|
||||
gpg_key_t* dest;
|
||||
char* err;
|
||||
|
||||
err = NULL;
|
||||
if (!G_gpg_vstate.ux_key) {
|
||||
err = "key";
|
||||
goto ERROR;
|
||||
}
|
||||
if (!G_gpg_vstate.ux_type){
|
||||
err = "type ";
|
||||
goto ERROR;
|
||||
}
|
||||
|
||||
os_memset(&attributes, 0, sizeof(attributes));
|
||||
switch (G_gpg_vstate.ux_type) {
|
||||
case 2048:
|
||||
case 3072:
|
||||
case 4096:
|
||||
attributes.value[0] = 0x01;
|
||||
attributes.value[1] = (G_gpg_vstate.ux_type>>8) &0xFF;
|
||||
attributes.value[2] = G_gpg_vstate.ux_type&0xFF;
|
||||
attributes.value[3] = 0x00;
|
||||
attributes.value[4] = 0x20;
|
||||
attributes.value[5] = 0x01;
|
||||
attributes.length = 6;
|
||||
break;
|
||||
|
||||
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_BrainPoolP256R1:
|
||||
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);
|
||||
break;
|
||||
|
||||
case CX_CURVE_Ed25519:
|
||||
if (G_gpg_vstate.ux_key == 2) {
|
||||
attributes.value[0] = 18; //ecdh
|
||||
os_memmove(attributes.value+1, C_OID_cv25519, sizeof(C_OID_cv25519));
|
||||
attributes.length = 1+sizeof(C_OID_cv25519);
|
||||
} else {
|
||||
attributes.value[0] = 22; //eddsa
|
||||
os_memmove(attributes.value+1, C_OID_Ed25519, sizeof(C_OID_Ed25519));
|
||||
attributes.length = 1+sizeof(C_OID_Ed25519);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
err = "not supported";
|
||||
goto ERROR;
|
||||
}
|
||||
|
||||
dest = NULL;
|
||||
switch(G_gpg_vstate.ux_key) {
|
||||
case 1:
|
||||
dest = &G_gpg_vstate.kslot->sig;
|
||||
break;
|
||||
case 2:
|
||||
dest = &G_gpg_vstate.kslot->dec;
|
||||
break;
|
||||
case 3:
|
||||
dest = &G_gpg_vstate.kslot->aut;
|
||||
break;
|
||||
default:
|
||||
err = "key not found";
|
||||
goto ERROR;
|
||||
}
|
||||
|
||||
gpg_nvm_write(dest, NULL, sizeof(gpg_key_t));
|
||||
gpg_nvm_write(&dest->attributes, &attributes, sizeof(attributes));
|
||||
ui_info(ui_idle_sub_template, "Template set!", NULL);
|
||||
return;
|
||||
|
||||
ERROR:
|
||||
ui_info(ui_idle_sub_template, "Invalid selection:", err);
|
||||
|
||||
}
|
||||
/*
|
||||
void ui_idle_sub_tmpl_key_display(unsigned int value) {
|
||||
UX_MENU_DISPLAY(0,ui_idle_sub_tmpl_key,ui_idle_sub_tmpl_key_prepocessor);
|
||||
}
|
||||
*/
|
||||
const ux_menu_entry_t ui_idle_sub_template[] = {
|
||||
{ui_idle_sub_tmpl_key, NULL, 0, NULL, G_gpg_vstate.menu_template_key, NULL, 0, 0},
|
||||
{ui_idle_sub_tmpl_type, NULL, 0, NULL, G_gpg_vstate.menu_template_type, NULL, 0, 0},
|
||||
{NULL, ui_idle_sub_tmpl_set_action, 0, NULL, "Set template", NULL, 0, 0},
|
||||
{ui_idle_mainmenu, NULL, 2, &C_badge_back, "Back", NULL, 61, 40},
|
||||
UX_MENU_END
|
||||
};
|
||||
|
||||
|
||||
/* ----- SEED UX ----- */
|
||||
const ux_menu_entry_t ui_idle_sub_seed[];
|
||||
void ui_idle_sub_seed_action(unsigned int value) {
|
||||
G_gpg_vstate.seed_mode = value;
|
||||
gpg_init_ux();
|
||||
UX_MENU_DISPLAY(0, ui_idle_sub_seed, NULL);
|
||||
}
|
||||
const ux_menu_entry_t ui_idle_sub_seed[] = {
|
||||
#if GPG_KEYS_SLOTS != 3
|
||||
#error menu definition not correct for current value of GPG_KEYS_SLOTS
|
||||
#endif
|
||||
{NULL, NULL, 0, NULL, G_gpg_vstate.menu_seed_mode, NULL, 0, 0},
|
||||
{NULL, ui_idle_sub_seed_action, 1, NULL, "Set on", NULL, 0, 0},
|
||||
{NULL, ui_idle_sub_seed_action, 0, NULL, "Set off", NULL, 0, 0},
|
||||
{ui_idle_mainmenu, NULL, 2, &C_badge_back, "Back", NULL, 61, 40},
|
||||
UX_MENU_END
|
||||
};
|
||||
|
||||
/* ----- RESET UX ----- */
|
||||
void ui_idle_sub_reset_action(unsigned int value) {
|
||||
unsigned char magic[4];
|
||||
magic[0] = 0; magic[1] = 0; magic[2] = 0; magic[3] = 0;
|
||||
gpg_nvm_write(N_gpg_pstate->magic, magic, 4);
|
||||
gpg_init();
|
||||
UX_MENU_DISPLAY(0, ui_idle_mainmenu, NULL);
|
||||
}
|
||||
const ux_menu_entry_t ui_idle_sub_reset[] = {
|
||||
#if GPG_KEYS_SLOTS != 3
|
||||
#error menu definition not correct for current value of GPG_KEYS_SLOTS
|
||||
#endif
|
||||
{NULL, NULL, 0, NULL, "Really Reset ?", NULL, 0, 0},
|
||||
{NULL, ui_idle_sub_reset_action, 0, NULL, "Yes!", NULL, 0, 0},
|
||||
{ui_idle_mainmenu, NULL, 0, &C_badge_back, "Oh No!", NULL, 61, 40},
|
||||
UX_MENU_END
|
||||
};
|
||||
|
||||
/* ----- SETTINGS UX ----- */
|
||||
const ux_menu_entry_t ui_idle_settings[] = {
|
||||
{NULL, NULL, 0, NULL, G_gpg_vstate.menu_cur_slot, NULL, 0, 0},
|
||||
{ui_idle_sub_template, NULL, 0, NULL, "Key template", NULL, 0, 0},
|
||||
{ui_idle_sub_seed, NULL, 0, NULL, "Seed mode", NULL, 0, 0},
|
||||
{ui_idle_sub_reset, NULL, 0, NULL, "Reset", NULL, 0, 0},
|
||||
{ui_idle_mainmenu, NULL, 2, &C_badge_back, "Back", NULL, 61, 40},
|
||||
UX_MENU_END
|
||||
};
|
||||
|
||||
/* ----- MAIN UX ----- */
|
||||
const ux_menu_entry_t ui_idle_mainmenu[] = {
|
||||
{NULL, NULL, 0, NULL, G_gpg_vstate.menu_cur_slot, NULL, 0, 0},
|
||||
{ui_idle_sub_slot, NULL, 0, NULL, "Select slot", NULL, 0, 0},
|
||||
{ui_idle_settings, NULL, 0, NULL, "Settings", NULL, 0, 0},
|
||||
{NULL, os_sched_exit, 0, &C_icon_dashboard, "Quit app" , NULL, 50, 29},
|
||||
UX_MENU_END
|
||||
};
|
||||
|
||||
|
||||
void io_seproxyhal_display(const bagl_element_t *element) {
|
||||
io_seproxyhal_display_default((bagl_element_t *)element);
|
||||
}
|
||||
void ui_idle_init(void) {
|
||||
UX_MENU_DISPLAY(0, ui_idle_mainmenu, NULL);
|
||||
// setup the first screen changing
|
||||
UX_CALLBACK_SET_INTERVAL(1000);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* --- Application Entry --- */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
void gpg_main(void) {
|
||||
|
||||
for (;;) {
|
||||
volatile unsigned short sw = 0;
|
||||
BEGIN_TRY {
|
||||
TRY {
|
||||
gpg_io_do();
|
||||
sw = gpg_dispatch();
|
||||
}
|
||||
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 {
|
||||
gpg_io_insert_u16(sw);
|
||||
}
|
||||
}
|
||||
END_TRY;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
unsigned char io_event(unsigned char channel) {
|
||||
// nothing done with the event, throw an error on the transport layer if
|
||||
// needed
|
||||
// can't have more than one tag in the reply, not supported yet.
|
||||
switch (G_io_seproxyhal_spi_buffer[0]) {
|
||||
case SEPROXYHAL_TAG_FINGER_EVENT:
|
||||
UX_FINGER_EVENT(G_io_seproxyhal_spi_buffer);
|
||||
break;
|
||||
// power off if long push, else pass to the application callback if any
|
||||
case SEPROXYHAL_TAG_BUTTON_PUSH_EVENT: // for Nano S
|
||||
UX_BUTTON_PUSH_EVENT(G_io_seproxyhal_spi_buffer);
|
||||
break;
|
||||
|
||||
|
||||
// other events are propagated to the UX just in case
|
||||
default:
|
||||
UX_DEFAULT_EVENT();
|
||||
break;
|
||||
|
||||
case SEPROXYHAL_TAG_DISPLAY_PROCESSED_EVENT:
|
||||
UX_DISPLAYED_EVENT({});
|
||||
break;
|
||||
case SEPROXYHAL_TAG_TICKER_EVENT:
|
||||
UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer,
|
||||
{
|
||||
// only allow display when not locked of overlayed by an OS UX.
|
||||
if (UX_ALLOWED ) {
|
||||
UX_REDISPLAY();
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
// close the event if not done previously (by a display or whatever)
|
||||
if (!io_seproxyhal_spi_is_status_sent()) {
|
||||
io_seproxyhal_general_status();
|
||||
}
|
||||
// command has been processed, DO NOT reset the current APDU transport
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) {
|
||||
switch (channel & ~(IO_FLAGS)) {
|
||||
case CHANNEL_KEYBOARD:
|
||||
break;
|
||||
|
||||
// multiplexed io exchange over a SPI channel and TLV encapsulated protocol
|
||||
case CHANNEL_SPI:
|
||||
if (tx_len) {
|
||||
io_seproxyhal_spi_send(G_io_apdu_buffer, tx_len);
|
||||
|
||||
if (channel & IO_RESET_AFTER_REPLIED) {
|
||||
reset();
|
||||
}
|
||||
return 0; // nothing received from the master so far (it's a tx
|
||||
// transaction)
|
||||
} else {
|
||||
return io_seproxyhal_spi_recv(G_io_apdu_buffer,
|
||||
sizeof(G_io_apdu_buffer), 0);
|
||||
}
|
||||
|
||||
default:
|
||||
THROW(INVALID_PARAMETER);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void app_exit(void) {
|
||||
BEGIN_TRY_L(exit) {
|
||||
TRY_L(exit) {
|
||||
os_sched_exit(-1);
|
||||
}
|
||||
FINALLY_L(exit) {
|
||||
}
|
||||
}
|
||||
END_TRY_L(exit);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
|
||||
__attribute__((section(".boot"))) int main(void) {
|
||||
// exit critical section
|
||||
__asm volatile("cpsie i");
|
||||
|
||||
|
||||
// ensure exception will work as planned
|
||||
os_boot();
|
||||
UX_INIT();
|
||||
|
||||
BEGIN_TRY {
|
||||
TRY {
|
||||
|
||||
//start communication with MCU
|
||||
io_seproxyhal_init();
|
||||
|
||||
USB_CCID_power(1);
|
||||
|
||||
//set up
|
||||
gpg_init();
|
||||
|
||||
//set up initial screen
|
||||
ui_idle_init();
|
||||
|
||||
|
||||
|
||||
//start the application
|
||||
//the first exchange will:
|
||||
// - display the initial screen
|
||||
// - send the ATR
|
||||
// - receive the first command
|
||||
gpg_main();
|
||||
}
|
||||
CATCH_OTHER(e) {
|
||||
}
|
||||
FINALLY {
|
||||
}
|
||||
}
|
||||
END_TRY;
|
||||
|
||||
app_exit();
|
||||
}
|
||||
|
||||
#endif
|
22
src/gpg_nvram.c
Normal file
22
src/gpg_nvram.c
Normal file
@ -0,0 +1,22 @@
|
||||
/* Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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"
|
||||
|
||||
gpg_nv_state_t N_state_pic;
|
223
src/gpg_pin.c
Normal file
223
src/gpg_pin.c
Normal file
@ -0,0 +1,223 @@
|
||||
/* Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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 gpg_pin_t *gpg_get_pin(int id) {
|
||||
switch (id) {
|
||||
case ID_PW1 :
|
||||
case ID_PW2 :
|
||||
return &N_gpg_pstate->PW1;
|
||||
case ID_PW3:
|
||||
return &N_gpg_pstate->PW3;
|
||||
case ID_RC:
|
||||
return &N_gpg_pstate->RC;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void gpg_set_verified(int id, int verified) {
|
||||
G_gpg_vstate.verified_pin[id] = verified;
|
||||
}
|
||||
|
||||
static int gpg_check_pin(gpg_pin_t *pin, unsigned char *pin_val, int pin_len) {
|
||||
cx_sha256_t sha256;
|
||||
unsigned int counter;
|
||||
|
||||
if (pin->counter == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
if (os_memcmp(sha256.acc, pin->value, 32)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
counter = 3;
|
||||
gpg_nvm_write(&(pin->counter), &counter, sizeof(int));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void gpg_set_pin(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);
|
||||
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_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);
|
||||
return pin->counter == 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* @return: 1 verified
|
||||
* 0 not verified
|
||||
* -1 blocked
|
||||
*/
|
||||
int gpg_apdu_verify(int id) {
|
||||
gpg_pin_t *pin;
|
||||
|
||||
gpg_set_verified(id,0);
|
||||
pin = gpg_get_pin(id);
|
||||
if (gpg_check_pin(pin,
|
||||
G_gpg_vstate.work.io_buffer+ G_gpg_vstate.io_offset,
|
||||
G_gpg_vstate.io_lc)) {
|
||||
gpg_set_verified(id,1);
|
||||
gpg_io_discard(1);
|
||||
return SW_OK;
|
||||
}
|
||||
THROW(SW_SECURITY_STATUS_NOT_SATISFIED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpg_apdu_change_ref_data(int id) {
|
||||
gpg_pin_t *pin;
|
||||
int len, newlen;
|
||||
|
||||
gpg_set_verified(id,0);
|
||||
pin = gpg_get_pin(id);
|
||||
if (pin == NULL) {
|
||||
THROW(SW_WRONG_DATA);
|
||||
}
|
||||
|
||||
// --- RC pin ---
|
||||
if (id == ID_RC) {
|
||||
newlen = G_gpg_vstate.io_lc;
|
||||
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);
|
||||
}
|
||||
gpg_io_discard(1);
|
||||
return SW_OK;
|
||||
}
|
||||
|
||||
// --- PW1/PW3 pin ---
|
||||
|
||||
//avoid any-overflow whitout giving info
|
||||
if (pin->length > G_gpg_vstate.io_lc) {
|
||||
len = G_gpg_vstate.io_lc;
|
||||
} else {
|
||||
len = pin->length;
|
||||
}
|
||||
|
||||
if (gpg_check_pin(pin,
|
||||
G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset,
|
||||
len)) {
|
||||
|
||||
newlen = G_gpg_vstate.io_lc-len;
|
||||
if ( (newlen > GPG_MAX_PW_LENGTH) ||
|
||||
((id == ID_PW1) && (newlen < 6)) ||
|
||||
((id == ID_PW3) && (newlen < 8)) ) {
|
||||
THROW(SW_WRONG_DATA);
|
||||
}
|
||||
gpg_set_pin(pin,
|
||||
G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset+len,
|
||||
newlen);
|
||||
gpg_io_discard(1);
|
||||
return SW_OK;
|
||||
}
|
||||
THROW(SW_SECURITY_STATUS_NOT_SATISFIED);
|
||||
return 0.;
|
||||
}
|
||||
|
||||
int gpg_apdu_reset_retry_counter() {
|
||||
gpg_pin_t *pin_pw1;
|
||||
gpg_pin_t *pin_rc;
|
||||
int rc_len, pw1_len;
|
||||
|
||||
pin_pw1 = gpg_get_pin(ID_PW1);
|
||||
|
||||
if (G_gpg_vstate.io_p1 == 2) {
|
||||
if (!G_gpg_vstate.verified_pin[ID_PW3]) {
|
||||
THROW(SW_SECURITY_STATUS_NOT_SATISFIED);
|
||||
}
|
||||
rc_len = 0;
|
||||
pw1_len = G_gpg_vstate.io_lc;
|
||||
} else {
|
||||
pin_rc = gpg_get_pin(ID_RC);
|
||||
//avoid any-overflow whitout giving info
|
||||
if (pin_rc->length > G_gpg_vstate.io_lc) {
|
||||
rc_len = G_gpg_vstate.io_lc;
|
||||
} else {
|
||||
rc_len = pin_rc->length;
|
||||
}
|
||||
pw1_len = G_gpg_vstate.io_lc-rc_len;
|
||||
if (!gpg_check_pin(pin_rc,
|
||||
G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset,
|
||||
rc_len)) {
|
||||
THROW(SW_SECURITY_STATUS_NOT_SATISFIED);
|
||||
}
|
||||
}
|
||||
|
||||
if ((pw1_len > GPG_MAX_PW_LENGTH) ||(pw1_len < 6)) {
|
||||
THROW(SW_WRONG_DATA);
|
||||
}
|
||||
gpg_set_pin(pin_pw1,
|
||||
G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset+rc_len,
|
||||
pw1_len);
|
||||
gpg_io_discard(1);
|
||||
return SW_OK;
|
||||
|
||||
}
|
274
src/gpg_pso.c
Normal file
274
src/gpg_pso.c
Normal file
@ -0,0 +1,274 @@
|
||||
/* Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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"
|
||||
|
||||
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
|
||||
};
|
||||
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 int gpg_sign(gpg_key_t *sigkey) {
|
||||
// --- RSA
|
||||
if (sigkey->attributes.value[0] == 1) {
|
||||
cx_rsa_private_key_t *key;
|
||||
unsigned int ksz,l;
|
||||
ksz = (sigkey->attributes.value[1]<<8) | sigkey->attributes.value[2];
|
||||
ksz = ksz>>3;
|
||||
switch(ksz) {
|
||||
case 1024/8:
|
||||
key = (cx_rsa_private_key_t *)&sigkey->key.rsa1024;
|
||||
break;
|
||||
case 2048/8:
|
||||
key = (cx_rsa_private_key_t *)&sigkey->key.rsa2048;
|
||||
break;
|
||||
case 3072/8:
|
||||
key = (cx_rsa_private_key_t *)&sigkey->key.rsa3072;
|
||||
break;
|
||||
case 4096/8:
|
||||
key = (cx_rsa_private_key_t *)&sigkey->key.rsa4096;
|
||||
break;
|
||||
}
|
||||
if (key->size != ksz) {
|
||||
THROW(SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
|
||||
//sign
|
||||
|
||||
l = ksz - G_gpg_vstate.io_length;
|
||||
os_memmove(G_gpg_vstate.work.io_buffer+l,
|
||||
G_gpg_vstate.work.io_buffer,
|
||||
G_gpg_vstate.io_length);
|
||||
os_memset(G_gpg_vstate.work.io_buffer, 0xFF, l);
|
||||
G_gpg_vstate.work.io_buffer[0] = 0;
|
||||
G_gpg_vstate.work.io_buffer[1] = 1;
|
||||
G_gpg_vstate.work.io_buffer[l-1] = 0;
|
||||
ksz = cx_rsa_decrypt(key,
|
||||
CX_PAD_NONE,
|
||||
CX_NONE,
|
||||
G_gpg_vstate.work.io_buffer, ksz,
|
||||
G_gpg_vstate.work.io_buffer, ksz);
|
||||
//send
|
||||
gpg_io_discard(0);
|
||||
gpg_io_inserted(ksz);
|
||||
return SW_OK;
|
||||
}
|
||||
// --- ECDSA/EdDSA
|
||||
if ((sigkey->attributes.value[0] == 19) ||
|
||||
(sigkey->attributes.value[0] == 22)) {
|
||||
cx_ecfp_private_key_t *key;
|
||||
unsigned int sz,i,j,rs_len;
|
||||
unsigned char *rs;
|
||||
|
||||
key = &sigkey->key.ecfp256;
|
||||
if (key->d_len != 32) {
|
||||
THROW(SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
//sign
|
||||
if (sigkey->attributes.value[0] == 19) {
|
||||
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);
|
||||
//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];
|
||||
|
||||
for (i = 0; i<2; i++) {
|
||||
if (*rs == 0) {
|
||||
rs++;
|
||||
rs_len--;
|
||||
}
|
||||
gpg_io_insert_u8(0);
|
||||
gpg_io_insert(rs,rs_len);
|
||||
rs = rs+rs_len;
|
||||
rs_len = rs[1];
|
||||
rs += 2;
|
||||
}
|
||||
} else{
|
||||
sz = cx_eddsa_sign(key, NULL,
|
||||
CX_NONE,
|
||||
CX_SHA512,
|
||||
G_gpg_vstate.work.io_buffer, G_gpg_vstate.io_length,
|
||||
G_gpg_vstate.work.io_buffer+128);
|
||||
gpg_io_discard(0);
|
||||
gpg_io_insert(G_gpg_vstate.work.io_buffer+128, sz);
|
||||
}
|
||||
|
||||
//send
|
||||
return SW_OK;
|
||||
}
|
||||
// --- PSO:CDS NOT SUPPORTED
|
||||
THROW(SW_REFERENCED_DATA_NOT_FOUND);
|
||||
|
||||
}
|
||||
|
||||
|
||||
int gpg_apdu_pso(unsigned int pso) {
|
||||
unsigned int t,l,ksz;
|
||||
unsigned int hid,oidlen;
|
||||
switch(pso) {
|
||||
// --- PSO:CDS ---
|
||||
case 0x9e9a: {
|
||||
return gpg_sign(&G_gpg_vstate.kslot->sig);
|
||||
}
|
||||
// --- PSO:DEC ---
|
||||
case 0x8086: {
|
||||
unsigned int msg_len;
|
||||
unsigned int pad_byte;
|
||||
unsigned int sz;
|
||||
pad_byte = gpg_io_fetch_u8();
|
||||
|
||||
switch(pad_byte) {
|
||||
// --- RSA
|
||||
case 0x00: {
|
||||
cx_rsa_private_key_t *key;
|
||||
if (G_gpg_vstate.kslot->dec.attributes.value[0] != 0x01) {
|
||||
THROW(SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
ksz = (G_gpg_vstate.kslot->dec.attributes.value[1]<<8) | G_gpg_vstate.kslot->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;
|
||||
break;
|
||||
case 2048/8:
|
||||
key = (cx_rsa_private_key_t *)&G_gpg_vstate.kslot->dec.key.rsa2048;
|
||||
break;
|
||||
case 3072/8:
|
||||
key = (cx_rsa_private_key_t *)&G_gpg_vstate.kslot->dec.key.rsa3072;
|
||||
break;
|
||||
case 4096/8:
|
||||
key = (cx_rsa_private_key_t *)&G_gpg_vstate.kslot->dec.key.rsa4096;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((key == NULL) || (key->size != ksz)) {
|
||||
THROW(SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
msg_len = G_gpg_vstate.io_length - G_gpg_vstate.io_offset;
|
||||
sz = cx_rsa_decrypt(key,
|
||||
CX_PAD_PKCS1_1o5,
|
||||
CX_NONE,
|
||||
G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, msg_len,
|
||||
G_gpg_vstate.work.io_buffer,ksz);
|
||||
//send
|
||||
gpg_io_discard(0);
|
||||
gpg_io_inserted(sz);
|
||||
return SW_OK;
|
||||
}
|
||||
// --- 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);
|
||||
}
|
||||
msg_len = G_gpg_vstate.io_length - G_gpg_vstate.io_offset;
|
||||
sz = cx_aes(key,
|
||||
CX_DECRYPT|CX_LAST,
|
||||
G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, msg_len,
|
||||
G_gpg_vstate.work.io_buffer);
|
||||
//send
|
||||
gpg_io_discard(0);
|
||||
gpg_io_inserted(sz);
|
||||
return SW_OK;
|
||||
}
|
||||
// --- 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) {
|
||||
THROW(SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
gpg_io_fetch_l(&l);
|
||||
gpg_io_fetch_tl(&t, &l);
|
||||
if (t != 0x7f49) {
|
||||
THROW(SW_WRONG_DATA);
|
||||
}
|
||||
gpg_io_fetch_tl(&t, &l);
|
||||
if (t != 0x86) {
|
||||
THROW(SW_WRONG_DATA);
|
||||
}
|
||||
|
||||
curve = gpg_oid2curve(G_gpg_vstate.kslot->dec.attributes.value+1, G_gpg_vstate.kslot->dec.attributes.length-1);
|
||||
if (curve == CX_CURVE_Curve25519) {
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i <=31; i++) {
|
||||
G_gpg_vstate.work.io_buffer[512+i] = (G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset)[31-i];
|
||||
}
|
||||
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);
|
||||
for (i = 0; i <=31; i++) {
|
||||
G_gpg_vstate.work.io_buffer[128+i] = G_gpg_vstate.work.io_buffer[287-i];
|
||||
}
|
||||
sz = 32;
|
||||
} 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);
|
||||
}
|
||||
//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 ---
|
||||
default:
|
||||
THROW(SW_REFERENCED_DATA_NOT_FOUND);
|
||||
|
||||
}
|
||||
THROW(SW_WRONG_DATA);
|
||||
}}
|
||||
|
||||
|
||||
int gpg_apdu_internal_authenticate() {
|
||||
gpg_key_t *sigkey;
|
||||
sigkey = &G_gpg_vstate.kslot->aut;
|
||||
|
||||
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) {
|
||||
THROW(SW_WRONG_LENGTH);
|
||||
}
|
||||
}
|
||||
return gpg_sign(&G_gpg_vstate.kslot->aut);
|
||||
}
|
34
src/gpg_ram.c
Normal file
34
src/gpg_ram.c
Normal file
@ -0,0 +1,34 @@
|
||||
/* Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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"
|
||||
|
||||
#include "os_io_seproxyhal.h"
|
||||
|
||||
#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;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
gpg_v_state_t G_gpg_vstate;
|
42
src/gpg_select.c
Normal file
42
src/gpg_select.c
Normal file
@ -0,0 +1,42 @@
|
||||
/* Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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"
|
||||
|
||||
int gpg_apdu_select() {
|
||||
int sw;
|
||||
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[ID_PW1] = 0;
|
||||
G_gpg_vstate.verified_pin[ID_PW2] = 0;
|
||||
G_gpg_vstate.verified_pin[ID_PW3] = 0;
|
||||
G_gpg_vstate.verified_pin[ID_RC] = 0;
|
||||
}
|
||||
gpg_io_discard(0);
|
||||
sw = SW_OK;
|
||||
} else {
|
||||
THROW(SW_FILE_NOT_FOUND);
|
||||
}
|
||||
return sw;
|
||||
}
|
331
src/gpg_types.h
Normal file
331
src/gpg_types.h
Normal file
@ -0,0 +1,331 @@
|
||||
/* Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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.
|
||||
*/
|
||||
|
||||
#ifndef GPG_TYPES_H
|
||||
#define GPG_TYPES_H
|
||||
|
||||
/* cannot send more that F0 bytes in CCID, why? do not know for now
|
||||
* So set up length to F0 minus 2 bytes for SW
|
||||
*/
|
||||
#define GPG_APDU_LENGTH 0xFE
|
||||
|
||||
|
||||
/* big private DO */
|
||||
#define GPG_EXT_PRIVATE_DO_LENGTH 1024
|
||||
/* will be fixed..1024 is not enougth */
|
||||
#define GPG_EXT_CARD_HOLDER_CERT_LENTH 4096
|
||||
/* random choice */
|
||||
#define GPG_EXT_CHALLENGE_LENTH 254
|
||||
/* accpet long PW, but less than one sha256 block */
|
||||
#define GPG_MAX_PW_LENGTH 48
|
||||
|
||||
#define GPG_KEYS_SLOTS 3
|
||||
|
||||
#define GPG_KEY_ATTRIBUTES_LENGTH 12
|
||||
|
||||
|
||||
struct gpg_pin_s {
|
||||
//initial pin length, 0 means not set
|
||||
unsigned int length;
|
||||
unsigned int counter;
|
||||
//only store sha256 of PIN/RC
|
||||
unsigned char value[32];
|
||||
};
|
||||
typedef struct gpg_pin_s gpg_pin_t;
|
||||
|
||||
|
||||
|
||||
#define LV(name,maxlen) \
|
||||
struct { \
|
||||
unsigned int length; \
|
||||
unsigned char value[maxlen]; \
|
||||
} name
|
||||
|
||||
typedef struct gpg_lv_s {
|
||||
unsigned int length;
|
||||
unsigned char value[];
|
||||
} gpg_lv_t;
|
||||
|
||||
typedef struct gpg_key_s {
|
||||
/* C1 C2 C3 */
|
||||
LV(attributes,GPG_KEY_ATTRIBUTES_LENGTH);
|
||||
/* key value */
|
||||
union {
|
||||
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;
|
||||
union {
|
||||
unsigned char rsa[4];
|
||||
cx_ecfp_public_key_t ecfp256;
|
||||
} pub_key;
|
||||
/* C7 C8 C9 , C5 = C7|C8|C9*/
|
||||
unsigned char fingerprints[20];
|
||||
/* 7F21 */
|
||||
LV(CA,GPG_EXT_CARD_HOLDER_CERT_LENTH);
|
||||
/* C7 C8 C9, C6 = C7|C8|C9*/
|
||||
unsigned char CA_fingerprints[20];
|
||||
/* CE CF D0, CD = CE|CF|D0 */
|
||||
unsigned char date[4];
|
||||
/* D6/D7/D8- */
|
||||
unsigned char UIF[2];
|
||||
|
||||
|
||||
|
||||
} gpg_key_t;
|
||||
|
||||
|
||||
typedef struct gpg_key_slot_s{
|
||||
/* */
|
||||
gpg_key_t sig;
|
||||
gpg_key_t aut;
|
||||
gpg_key_t dec;
|
||||
/* -- Security support template -- */
|
||||
/* 93 */
|
||||
unsigned int sig_count;
|
||||
/* D5 */
|
||||
cx_aes_key_t AES_dec;
|
||||
|
||||
} gpg_key_slot_t;
|
||||
|
||||
|
||||
struct gpg_nv_state_s {
|
||||
/* magic */
|
||||
unsigned char magic[8];
|
||||
|
||||
/* 01F1 (01F2 is volatile)*/
|
||||
unsigned char config_slot[3];
|
||||
|
||||
/* 0101 0102 0103 0104 */
|
||||
LV(private_DO1, GPG_EXT_PRIVATE_DO_LENGTH);
|
||||
LV(private_DO2, GPG_EXT_PRIVATE_DO_LENGTH);
|
||||
LV(private_DO3, GPG_EXT_PRIVATE_DO_LENGTH);
|
||||
LV(private_DO4, GPG_EXT_PRIVATE_DO_LENGTH);
|
||||
|
||||
/* 5E */
|
||||
LV(login, GPG_EXT_PRIVATE_DO_LENGTH);
|
||||
/* 5F50 */
|
||||
LV(url, GPG_EXT_PRIVATE_DO_LENGTH);
|
||||
|
||||
/* -- Cardholder Related Data -- */
|
||||
/* 5B */
|
||||
LV(name,39);
|
||||
/* 5F2D */
|
||||
LV(lang, 8);
|
||||
/* 5F35 */
|
||||
unsigned char sex[1];
|
||||
|
||||
/* -- Application Related Data -- */
|
||||
/* 4F */
|
||||
unsigned char AID[16];
|
||||
/* 5F52 */
|
||||
unsigned char histo[15];
|
||||
/* 7f66 */
|
||||
//unsigned char ext_length_info[8];
|
||||
/* C0 */
|
||||
//unsigned char ext_capabilities[10];
|
||||
|
||||
/* C4 */
|
||||
unsigned char PW_status[4];
|
||||
|
||||
/* PINs */
|
||||
gpg_pin_t PW1;
|
||||
gpg_pin_t PW3;
|
||||
gpg_pin_t RC;
|
||||
|
||||
/* gpg keys */
|
||||
gpg_key_slot_t keys[GPG_KEYS_SLOTS];
|
||||
|
||||
/* --- SM --- */
|
||||
/* D1 */
|
||||
cx_aes_key_t SM_enc;
|
||||
/* D2 */
|
||||
cx_aes_key_t SM_mac;
|
||||
} ;
|
||||
|
||||
typedef struct gpg_nv_state_s gpg_nv_state_t;
|
||||
|
||||
#define GPG_IO_BUFFER_LENGTH (1512)
|
||||
|
||||
struct gpg_v_state_s {
|
||||
/* app state */
|
||||
unsigned char selected;
|
||||
unsigned char slot; /* DO 01F2 */
|
||||
gpg_key_slot_t *kslot;
|
||||
unsigned char seed_mode;
|
||||
|
||||
/* io state*/
|
||||
unsigned char io_cla;
|
||||
unsigned char io_ins;
|
||||
unsigned char io_p1;
|
||||
unsigned char io_p2;
|
||||
unsigned char io_lc;
|
||||
unsigned char io_le;
|
||||
unsigned short io_length;
|
||||
unsigned short io_offset;
|
||||
unsigned short io_mark;
|
||||
unsigned short io_flags;
|
||||
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;
|
||||
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;
|
||||
} work;
|
||||
|
||||
/* data state */
|
||||
unsigned short DO_current;
|
||||
unsigned short DO_reccord;
|
||||
unsigned short DO_offset;
|
||||
|
||||
/* PINs state */
|
||||
unsigned char verified_pin[5];
|
||||
|
||||
|
||||
/* ux menus */
|
||||
char menu_cur_slot[16];
|
||||
char menu_seed_mode[16];
|
||||
char menu_template_key[16];
|
||||
char menu_template_type[16];
|
||||
unsigned int ux_key;
|
||||
unsigned int ux_type;
|
||||
|
||||
|
||||
#ifdef GPG_DEBUG
|
||||
unsigned char print;
|
||||
#endif
|
||||
} ;
|
||||
typedef struct gpg_v_state_s gpg_v_state_t;
|
||||
|
||||
/* --- Errors --- */
|
||||
|
||||
#define ERROR(x) ((x)<<16)
|
||||
|
||||
#define ERROR_IO_OFFSET ERROR(1)
|
||||
#define ERROR_IO_FULL ERROR(2)
|
||||
|
||||
/* --- IDentifiers --- */
|
||||
|
||||
#define ID_PW1 1
|
||||
#define ID_PW2 2
|
||||
#define ID_PW3 3
|
||||
#define ID_RC 4
|
||||
|
||||
#define ID_AUTH 1
|
||||
#define ID_DEC 2
|
||||
#define ID_SIG 3
|
||||
|
||||
|
||||
#define STATE_ACTIVATE 0x07
|
||||
#define STATE_TERMINATE 0x03
|
||||
|
||||
#define IO_OFFSET_END (unsigned int)-1
|
||||
#define IO_OFFSET_MARK (unsigned int)-2
|
||||
|
||||
|
||||
/* --- INS --- */
|
||||
#define INS_SELECT 0xa4
|
||||
#define INS_TERMINATE_DF 0xe6
|
||||
#define INS_ACTIVATE_FILE 0x44
|
||||
|
||||
#define INS_SELECT_DATA 0xa5
|
||||
#define INS_GET_DATA 0xca
|
||||
#define INS_GET_NEXT_DATA 0xcc
|
||||
#define INS_PUT_DATA 0xda
|
||||
#define INS_PUT_DATA_ODD 0xdb
|
||||
|
||||
#define INS_VERIFY 0x20
|
||||
#define INS_CHANGE_REFERENCE_DATA 0x24
|
||||
#define INS_RESET_RETRY_COUNTER 0x2c
|
||||
|
||||
#define INS_GEN_ASYM_KEYPAIR 0x47
|
||||
#define INS_PSO 0x2a
|
||||
//#define INS_COMPUTEDIGSIG 0x2a
|
||||
//#define INS_DECIPHER 0x2a
|
||||
#define INS_INTERNAL_AUTHENTICATE 0x88
|
||||
|
||||
#define INS_GET_CHALLENGE 0x84
|
||||
#define INS_GET_RESPONSE 0xc0
|
||||
|
||||
|
||||
/* --- IO constants --- */
|
||||
#define OFFSET_CLA 0
|
||||
#define OFFSET_INS 1
|
||||
#define OFFSET_P1 2
|
||||
#define OFFSET_P2 3
|
||||
#define OFFSET_P3 4
|
||||
#define OFFSET_CDATA 5
|
||||
#define OFFSET_EXT_CDATA 7
|
||||
|
||||
|
||||
#define SW_OK 0x9000
|
||||
#define SW_ALGORITHM_UNSUPPORTED 0x9484
|
||||
|
||||
#define SW_BYTES_REMAINING_00 0x6100
|
||||
|
||||
#define SW_WARNING_STATE_UNCHANGED 0x6200
|
||||
#define SW_STATE_TERMINATED 0x6285
|
||||
|
||||
#define SW_MORE_DATA_AVAILABLE 0x6310
|
||||
|
||||
#define SW_WRONG_LENGTH 0x6700
|
||||
|
||||
#define SW_LOGICAL_CHANNEL_NOT_SUPPORTED 0x6881
|
||||
#define SW_SECURE_MESSAGING_NOT_SUPPORTED 0x6882
|
||||
#define SW_LAST_COMMAND_EXPECTED 0x6883
|
||||
#define SW_COMMAND_CHAINING_NOT_SUPPORTED 0x6884
|
||||
|
||||
#define SW_SECURITY_STATUS_NOT_SATISFIED 0x6982
|
||||
#define SW_FILE_INVALID 0x6983
|
||||
#define SW_DATA_INVALID 0x6984
|
||||
#define SW_CONDITIONS_NOT_SATISFIED 0x6985
|
||||
#define SW_COMMAND_NOT_ALLOWED 0x6986
|
||||
#define SW_APPLET_SELECT_FAILED 0x6999
|
||||
|
||||
#define SW_WRONG_DATA 0x6a80
|
||||
#define SW_FUNC_NOT_SUPPORTED 0x6a81
|
||||
#define SW_FILE_NOT_FOUND 0x6a82
|
||||
#define SW_RECORD_NOT_FOUND 0x6a83
|
||||
#define SW_FILE_FULL 0x6a84
|
||||
#define SW_INCORRECT_P1P2 0x6a86
|
||||
#define SW_REFERENCED_DATA_NOT_FOUND 0x6a88
|
||||
|
||||
#define SW_WRONG_P1P2 0x6b00
|
||||
#define SW_CORRECT_LENGTH_00 0x6c00
|
||||
#define SW_INS_NOT_SUPPORTED 0x6d00
|
||||
#define SW_CLA_NOT_SUPPORTED 0x6e00
|
||||
|
||||
#define SW_UNKNOWN 0x6f00
|
||||
|
||||
|
||||
#endif
|
43
src/gpg_vars.h
Normal file
43
src/gpg_vars.h
Normal file
@ -0,0 +1,43 @@
|
||||
/* Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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.
|
||||
*/
|
||||
|
||||
#ifndef GPG_VARS_H
|
||||
#define GPG_VARS_H
|
||||
|
||||
#include "os.h"
|
||||
#include "cx.h"
|
||||
#include "gpg_types.h"
|
||||
#include "gpg_api.h"
|
||||
|
||||
extern const unsigned char C_ext_capabilities[10];
|
||||
extern const unsigned char C_ext_length[8];
|
||||
extern const unsigned char C_OID_SECP256K1[5];
|
||||
extern const unsigned char C_OID_SECP256R1[8];
|
||||
extern const unsigned char C_OID_BRAINPOOL256R1[9];
|
||||
extern const unsigned char C_OID_BRAINPOOL256T1[9];
|
||||
extern const unsigned char C_OID_Ed25519[9];
|
||||
extern const unsigned char C_OID_cv25519[10];
|
||||
|
||||
extern gpg_v_state_t G_gpg_vstate;
|
||||
extern gpg_nv_state_t N_state_pic;
|
||||
#define N_gpg_pstate ((WIDE gpg_nv_state_t *)PIC(&N_state_pic))
|
||||
|
||||
|
||||
#ifdef GPG_DEBUG_MAIN
|
||||
extern int apdu_n;
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
454
src/usbd_ccid_impl.c
Normal file
454
src/usbd_ccid_impl.c
Normal file
@ -0,0 +1,454 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @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
|
||||
*
|
||||
* <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.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/* 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 */
|
||||
static const uint8_t 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. */
|
||||
0x01, /* 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*/
|
||||
|
||||
|
||||
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 (USBD_CfgDesc);
|
||||
return (uint8_t*)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 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
|
22
src/usbd_ccid_impl.h
Normal file
22
src/usbd_ccid_impl.h
Normal file
@ -0,0 +1,22 @@
|
||||
#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
|
||||
|
||||
|
||||
#endif // USBD_CCID_IMPL_H
|
Loading…
Reference in New Issue
Block a user