Initial commit

Functional application in beta stage
This commit is contained in:
Cédric Mesnil 2017-03-21 16:32:26 +01:00
parent 20ba34417c
commit bfb950e21b
27 changed files with 5439 additions and 0 deletions

178
Makefile Normal file
View 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
View 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

Binary file not shown.

BIN
icon.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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>&copy; 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
View 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