53 Commits
v1.6 ... v1.10

Author SHA1 Message Date
Pol Henarejos
73c1bf786d Upgrade to v1.10
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-06 00:03:18 +01:00
Pol Henarejos
02a5695b61 Added missing file.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-05 00:00:47 +01:00
Pol Henarejos
9310e1af55 Use correct branch name.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-04 23:58:18 +01:00
Pol Henarejos
8399cd47db Wrong branch name.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-04 23:57:37 +01:00
Pol Henarejos
dddb9f2824 Lets try manual trigger.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-04 23:56:56 +01:00
Pol Henarejos
c97dd77404 Update test.yml
Only master branch is used.
2023-02-04 23:49:55 +01:00
Pol Henarejos
94930e5f7d Some fixes in HSM SDK.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-04 23:48:04 +01:00
Pol Henarejos
e697e30c6c Add virtual smart card emulation for CD/CI.
On each push, the software is built in a container, run as a virtual smartcard and test it.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-04 23:47:49 +01:00
Pol Henarejos
1288d25999 Adding workflow for autobuild.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-04 23:46:20 +01:00
Pol Henarejos
51742153d0 Adding checks for platform.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-04 23:45:31 +01:00
Pol Henarejos
1863971a1b It can be safely checked.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-04 23:45:07 +01:00
Pol Henarejos
3c7df3aa42 Fix checking length in ECDH.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-04 23:44:55 +01:00
Pol Henarejos
e5871d5791 Fix returning algo attributes for authentication key.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-04 21:07:10 +01:00
Pol Henarejos
7ccbb0103f Fix computing length of algorithm attributes.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-04 21:06:33 +01:00
Pol Henarejos
8e03ce28a3 Fix returning signature in some cases.
In some particular cases where signature has a 0 prepended, mpi is written without that which caused variable length signatures. Now it returns the signature whose length is always the same.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-04 20:28:33 +01:00
Pol Henarejos
b300ed87f3 Fix returning ecdsa response for keys > 512 bits.
In that case, TLV shall contain 81 length.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-04 20:27:20 +01:00
Pol Henarejos
7b17cc7b49 Public point is now computed when private key is imported.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-04 19:44:44 +01:00
Pol Henarejos
abf190f767 Tuned returning public key information for ECDSA.
Despite it was not a bug, it seems some ASN.1 readers do not recognize 0x81/0x82 tags when len < 128.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-04 19:44:00 +01:00
Pol Henarejos
fda29e0e61 Adding verifies.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-04 01:44:44 +01:00
Pol Henarejos
d9ed002af2 Signature counter is only increased on PSO:SIGN and not for authentication.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-04 01:31:34 +01:00
Pol Henarejos
e1407636b8 Fix DEK loading when resetting code is used.
If no pw3, on cmd_reset_retry pw1 is changed without providing the original, since it is done via rc. Thus, there is no way to recover DEK. To solve, another ciphered field is added for loading DEK via rc. In case rc is changed (PUT DATA), DEK is reciphered with new rc.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-04 01:23:03 +01:00
Pol Henarejos
d117442825 Adding emulation support.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-03 23:28:12 +01:00
Pol Henarejos
365acbd68b Fix setting RC.
When setting RC it was not hashed and then it was not recognized.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-03 20:21:14 +01:00
Pol Henarejos
f6facc1154 When a private key is imported, a public key is generated and stored.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-03 20:08:26 +01:00
Pol Henarejos
e914d5f576 Identify before running this test.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-03 19:17:52 +01:00
Pol Henarejos
5e257729a3 Some fixes.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-03 19:17:42 +01:00
Pol Henarejos
89ed242fcd Fix historical bytes and ext capabilities test.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-03 17:44:06 +01:00
Pol Henarejos
4a629fe53f Fix returning size of some DO.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-03 17:27:02 +01:00
Pol Henarejos
22689b3784 Fix returning DO.
If the TLV container contains a single DO, the header is removed.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-03 17:25:18 +01:00
Pol Henarejos
4f1cd1f2f8 Fix returning sig counter.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-03 16:55:32 +01:00
Pol Henarejos
c9ef78b3c9 Fix returning app data.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-03 16:52:55 +01:00
Pol Henarejos
6c81fe4b1c Fix test sex.
By default, sex is \x30.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-03 16:49:30 +01:00
Pol Henarejos
3fe15c815c Fix returning chunked data.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-03 16:47:46 +01:00
Pol Henarejos
cf53fdd903 Adapt select_applet to new call.
Also fixes sex test.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-03 16:32:28 +01:00
Pol Henarejos
29b4aec24e Adapting Cmakefile to new HSM SDK.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-03 16:32:03 +01:00
Pol Henarejos
0c63c457e7 Upgrading to newer Pico HSM SDK.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-03 16:31:51 +01:00
Pol Henarejos
2c24c348b5 Adding tests for OpenPGP.
All tests are taken from gnuk. Reader is migrated from pyusb to pyscard.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-02 23:08:49 +01:00
Pol Henarejos
32868dfc31 Fix signature counter storage.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-01-09 17:33:43 +01:00
Pol Henarejos
303116ffea Fix P1P2 on termination check.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-01-09 17:10:12 +01:00
Pol Henarejos
23824afc1f Fix importing data with TLV length > 0x7f.
Should fix #3.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-29 20:21:47 +01:00
Pol Henarejos
2c5b67597e Moving pointer of HSM SDK.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-28 18:56:16 +01:00
Pol Henarejos
bcefdb3c84 Upgrading patcher to HSM SDK 3.4
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-25 14:26:06 +01:00
Pol Henarejos
685ee2bbd5 Moving pointer to Pico HSM SDK 3.4
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-25 14:25:50 +01:00
Pol Henarejos
6ae2a91e55 More fixes to build tool.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-23 11:09:38 +01:00
Pol Henarejos
2373f21994 Fix build tool.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-23 11:04:12 +01:00
Pol Henarejos
25bddb7230 Upgrading to version 1.8.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-23 11:00:44 +01:00
Pol Henarejos
418fa9c143 Fix patch tool.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-23 10:59:08 +01:00
Pol Henarejos
ce9ef47bb2 Using pico_hsm_sdk cmake library.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-08-31 14:18:37 +02:00
Pol Henarejos
603963123b Upgrade pico-hsm-sdk to v3.0
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-08-31 14:12:55 +02:00
Pol Henarejos
79ce35e944 Fix endianness of vid/pid patcher.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-08-31 14:12:26 +02:00
Pol Henarejos
5ddfa6382b Upgrading to new pico-hsm-sdk module.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-08-30 17:48:44 +02:00
Pol Henarejos
9a99baafca Renaming submodule.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-08-30 17:48:02 +02:00
Pol Henarejos
819fb99646 Update README.md
Added clarifications for AES.
2022-06-06 14:51:04 +02:00
150 changed files with 6948 additions and 259 deletions

72
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,72 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
schedule:
- cron: '23 5 * * 4'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp', 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
# - name: Autobuild
# uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
- run: |
echo "Run, Build Application using script"
./workflows/autobuild.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

37
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "Emulation and test"
on:
workflow_dispatch:
push:
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
schedule:
- cron: '23 5 * * 4'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository and submodules
uses: actions/checkout@v3
with:
submodules: recursive
- name: Build in container
run: ./tests/build-in-docker.sh
- name: Start emulation and test
run: ./tests/run-test-in-docker.sh

6
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "pico-ccid"]
path = pico-ccid
url = https://github.com/polhenarejos/pico-ccid.git
[submodule "pico-hsm-sdk"]
path = pico-hsm-sdk
url = ../pico-hsm-sdk

View File

@@ -1,99 +1,79 @@
#
#
# This file is part of the Pico OpenPGP distribution (https://github.com/polhenarejos/pico-openpgp).
# Copyright (c) 2022 Pol Henarejos.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
cmake_minimum_required(VERSION 3.13)
if(ENABLE_EMULATION)
else()
include(pico_sdk_import.cmake)
endif()
project(pico_openpgp C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
if(ENABLE_EMULATION)
else()
pico_sdk_init()
endif()
add_executable(pico_openpgp)
if (NOT DEFINED USB_VID)
set(USB_VID 0xFEFF)
endif()
add_definitions(-DUSB_VID=${USB_VID})
if (NOT DEFINED USB_PID)
set(USB_PID 0xFCFD)
endif()
add_definitions(-DUSB_PID=${USB_PID})
configure_file(${CMAKE_CURRENT_LIST_DIR}/pico-ccid/config/mbedtls_config.h ${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/include/mbedtls COPYONLY)
target_sources(pico_openpgp PUBLIC
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/src/usb/usb.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/src/usb/usb_descriptors.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/src/ccid/ccid2040.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/src/ccid/asn1.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/src/fs/file.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/src/fs/flash.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/src/fs/low_flash.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/src/rng/random.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/src/rng/neug.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/src/ccid/crypto_utils.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/src/ccid/eac.c
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/src/openpgp/openpgp.c
${CMAKE_CURRENT_LIST_DIR}/src/openpgp/files.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/aes.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/asn1parse.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/bignum.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/cmac.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/cipher.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/cipher_wrap.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/constant_time.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/ecdsa.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/ecdh.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/ecp.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/ecp_curves.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/md.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/md5.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/oid.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/platform_util.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/ripemd160.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/rsa.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/rsa_alt_helpers.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/sha1.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/sha256.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library/sha512.c
)
target_include_directories(pico_openpgp PUBLIC
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/src/fs
set(INCLUDES ${INCLUDES}
${CMAKE_CURRENT_LIST_DIR}/src/openpgp
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/src/ccid
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/src/rng
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/src/usb
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/include
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/mbedtls/library
)
set(USB_ITF_CCID 1)
include(pico-hsm-sdk/pico_hsm_sdk_import.cmake)
target_sources(pico_openpgp PUBLIC ${SOURCES})
target_include_directories(pico_openpgp PUBLIC ${INCLUDES})
target_compile_options(pico_openpgp PUBLIC
-Wall
-Werror
)
if(ENABLE_EMULATION)
target_compile_options(pico_openpgp PUBLIC
-fdata-sections
-ffunction-sections
)
if(APPLE)
target_link_options(pico_openpgp PUBLIC
-Wl,-dead_strip
)
else()
target_link_options(pico_openpgp PUBLIC
-Wl,--gc-sections
)
endif (APPLE)
else()
pico_add_extra_outputs(pico_openpgp)
#target_compile_definitions(pico_openpgp PRIVATE MBEDTLS_ECDSA_DETERMINISTIC=1)
target_link_libraries(pico_openpgp PRIVATE pico_stdlib tinyusb_device tinyusb_board pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id hardware_rtc)
target_link_libraries(pico_openpgp PRIVATE pico_hsm_sdk pico_stdlib tinyusb_device tinyusb_board pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id hardware_rtc)
endif()

View File

@@ -26,9 +26,19 @@ Pico OpenPGP has implemented the following features:
- Key Derivation Function (KDF) for PIN.
- Manage Security Environment (MSE).
- DEK for internal safe storage.
- AES key generation.
- AES ciphering and deciphering.
- Cardholder certificates support.
All these features are compliant with the specification. Therefore, if you detect some behaviour that is not expected or it does not follow the rules of specs, please open an issue.
## AES support
There is no known software that supports AES with OpenPGP. Nevertheless, it can be used with customized PKCS11 modules or interfacing with raw APDU packets.
During asymmetric key generation for DEC key, Pico OpenPGP also generates a 32 bits symmetric key for AES operations.
OpenPGP card 3.4 specifications describe the procedure to perform ciphering (encryption and decryption) with AES via PSO:ENCIPHER and PSO:DECIPHER. Both commands are supported by Pico OpenPGP.
### About Gnuk
This project was inspired by [Gnuk](https://wiki.debian.org/GNUK "Gnuk"), a same project but focused on STM32 processor family. Despite the initial idea was to port Gnuk to the Raspberry Pico family, the underlaying architecture is widely different (although boh run on ARM). For instance, the Pico has two ARM cores, with an appropiate SDK able to leverage them. Also, Pico has an internal flash storage, which is farly larger compared to STM32 ROM storage. Finally, the Pico has a complete USB interface based on TinyUSB, which difficults to port Gnuk. These are only few examples of the difficulties of porting Gnuk to the Raspberry Pico.
@@ -68,7 +78,7 @@ After make ends, the binary file pico_openpgp.uf2 will be generated. Put your pi
### Keypair generation
Generating EC keys is almost instant. RSA keypair generation takes some time, specially for 3072 and 4096 bits.
### Keypair generation
Generating EC keys is almost instant. RSA keypair generation takes some time, specially for `3072` and `4096` bits.
Generating EC keys is almost instant. RSA keypair generation takes some time, specially for `3072` and `4096` bits.
| RSA key length (bits) | Average time (seconds) |
| :---: | :---: |
@@ -109,7 +119,7 @@ While processing, the Pico OpenPGP is busy and cannot receive additional command
## Driver
Pico OpenPGP uses the `openpgp` driver provided by [OpenSC](https://github.com/OpenSC/OpenSC/ "OpenSC"). This driver utilizes the standardized PKCS#11 interface to communicate with the user and it can be used with many engines that accept PKCS#11 interface, such as OpenSSL, P11 library or pkcs11-tool.
Pico OpenPGP uses the `openpgp` driver provided by [OpenSC](https://github.com/OpenSC/OpenSC/ "OpenSC"). This driver utilizes the standardized PKCS#11 interface to communicate with the user and it can be used with many engines that accept PKCS#11 interface, such as OpenSSL, P11 library or pkcs11-tool.
It also accepts the use of GnuPG programs (`gpg` and `gpg2`) to manipulate the card. For instance, it can be used with the `gpg --edit-card --expert` interface to change the cryptographic keys, generate new keypairs or simply set the cardholder name.
@@ -122,6 +132,6 @@ OpenSC relies on PCSC driver, which reads a list (`Info.plist`) that contains a
## Credits
Pico OpenPGP uses the following libraries or portion of code:
- mbedTLS for cryptographic operations.
- MbedTLS for cryptographic operations.
- TinyUSB for low level USB procedures.

View File

@@ -1 +1 @@
Version=1.4
Version=1.10

View File

@@ -1,21 +1,53 @@
#!/bin/bash
VERSION_MAJOR="1"
VERSION_MINOR="6"
VERSION_MINOR="10"
rm -rf release/*
cd build_release
for board in adafruit_feather_rp2040 adafruit_itsybitsy_rp2040 adafruit_qtpy_rp2040 adafruit_trinkey_qt2040 arduino_nano_rp2040_connect melopero_shake_rp2040 pimoroni_interstate75 pimoroni_keybow2040 pimoroni_pga2040 pimoroni_picolipo_4mb pimoroni_picolipo_16mb pimoroni_picosystem pimoroni_plasma2040 pimoroni_tiny2040 pybstick26_rp2040 sparkfun_micromod sparkfun_promicro sparkfun_thingplus vgaboard waveshare_rp2040_lcd_0.96 waveshare_rp2040_plus_4mb waveshare_rp2040_plus_16mb waveshare_rp2040_zero
for board in adafruit_feather_rp2040 \
adafruit_itsybitsy_rp2040 \
adafruit_kb2040 \
adafruit_macropad_rp2040 \
adafruit_qtpy_rp2040 \
adafruit_trinkey_qt2040 \
arduino_nano_rp2040_connect \
datanoisetv_rp2040_dsp \
eetree_gamekit_rp2040 \
garatronic_pybstick26_rp2040 \
melopero_shake_rp2040 \
pico \
pico_w \
pimoroni_badger2040 \
pimoroni_interstate75 \
pimoroni_keybow2040 \
pimoroni_motor2040 \
pimoroni_pga2040 \
pimoroni_picolipo_4mb \
pimoroni_picolipo_16mb \
pimoroni_picosystem \
pimoroni_plasma2040 \
pimoroni_servo2040 \
pimoroni_tiny2040 \
pimoroni_tiny2040_2mb \
seeed_xiao_rp2040 \
solderparty_rp2040_stamp \
solderparty_rp2040_stamp_carrier \
solderparty_rp2040_stamp_round_carrier \
sparkfun_micromod \
sparkfun_promicro \
sparkfun_thingplus \
vgaboard \
waveshare_rp2040_lcd_0.96 \
waveshare_rp2040_plus_4mb \
waveshare_rp2040_plus_16mb \
waveshare_rp2040_zero \
wiznet_w5100s_evb_pico
do
rm -rf *
PICO_SDK_PATH=~/Devel/pico/pico-sdk cmake .. -DPICO_BOARD=$board
PICO_SDK_PATH=../../pico-sdk cmake .. -DPICO_BOARD=$board
make -kj20
mv pico_openpgp.uf2 ../release/pico_openpgp_$board-$VERSION_MAJOR.$VERSION_MINOR.uf2
done
rm -rf *
PICO_SDK_PATH=~/Devel/pico/pico-sdk cmake ..
make -kj20
mv pico_openpgp.uf2 ../release/pico_openpgp_pico_generic-$VERSION_MAJOR.$VERSION_MINOR.uf2
done

View File

@@ -1,24 +1,24 @@
#!/bin/bash
#
#
# This file is part of the Pico OpenPGP distribution (https://github.com/polhenarejos/pico-openpgp).
# Copyright (c) 2022 Pol Henarejos.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
VERSION_MAJOR="2" #Version of Pico CCID Core
VERSION_MINOR="0"
VERSION_MAJOR="3" #Version of Pico CCID Core
VERSION_MINOR="4"
echo "----------------------------"
echo "VID/PID patcher for Pico OpenPGP"

Submodule pico-ccid deleted from fe53f9a729

1
pico-hsm-sdk Submodule

Submodule pico-hsm-sdk added at 31e66007d3

View File

@@ -1,17 +1,17 @@
/*
/*
* This file is part of the Pico OpenPGP distribution (https://github.com/polhenarejos/pico-openpgp).
* Copyright (c) 2022 Pol Henarejos.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -96,7 +96,7 @@ file_t file_entries[] = {
/* 10 */ { .fid = EF_CH_CERT, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *)parse_ch_cert, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
/* 11 */ { .fid = EF_EXLEN_INFO, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = exlen_info, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
/* 12 */ { .fid = EF_GFM, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = feature_mngmnt, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
/* 13 */ { .fid = EF_SIG_COUNT, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
/* 13 */ { .fid = EF_SIG_COUNT, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
/* 14 */ { .fid = EF_EXT_CAP, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = extended_capabilities, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
/* 15 */ { .fid = EF_ALGO_SIG, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *)parse_algoinfo, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 16 */ { .fid = EF_ALGO_DEC, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *)parse_algoinfo, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
@@ -122,7 +122,6 @@ file_t file_entries[] = {
/* 36 */ { .fid = EF_ALGO_INFO, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *)parse_algoinfo, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
/* 37 */ { .fid = EF_APP_DATA, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *)parse_app_data, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
/* 38 */ { .fid = EF_DISCRETE_DO, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *)parse_discrete_do, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
/* 39 */ { .fid = EF_PW1, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
/* 40 */ { .fid = EF_RC, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
/* 41 */ { .fid = EF_PW3, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
@@ -141,7 +140,7 @@ file_t file_entries[] = {
/* 54 */ { .fid = EF_CH_1, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_NONE },
/* 55 */ { .fid = EF_CH_2, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_NONE },
/* 56 */ { .fid = EF_CH_3, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_NONE },
/* 57 */ { .fid = 0x0000, .parent = 0, .name = openpgp_aid, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
/* 58 */ { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL, .ef_structure = 0, .acl = ACL_NONE } //end
};

View File

@@ -1,17 +1,17 @@
/*
/*
* This file is part of the Pico OpenPGP distribution (https://github.com/polhenarejos/pico-openpgp).
* Copyright (c) 2022 Pol Henarejos.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -27,27 +27,31 @@
#include "mbedtls/ecdh.h"
#include "mbedtls/asn1.h"
#include "asn1.h"
#include "usb.h"
#include "ccid/ccid.h"
bool has_pw1 = false;
bool has_pw2 = false;
bool has_pw3 = false;
bool has_rc = false;
uint8_t session_pw1[32];
uint8_t session_rc[32];
uint8_t session_pw3[32];
static uint8_t dek[IV_SIZE+32];
static uint16_t algo_dec = EF_ALGO_PRIV2, algo_aut = EF_ALGO_PRIV3, pk_dec = EF_PK_DEC, pk_aut = EF_PK_AUT;
uint8_t openpgp_aid[] = {
6,
6,
0xD2,0x76,0x00,0x01,0x24,0x01,
};
uint8_t openpgp_aid_full[] = {
16,00,
16,00,
0xD2,0x76,0x00,0x01,0x24,0x01,
OPGP_VERSION_MAJOR,OPGP_VERSION_MINOR,0xff,0xfe,0xff,0xff,0xff,0xff,0x00,0x00
};
char atr_openpgp[] = {
char atr_openpgp[] = {
21,
0x3b,0xda,0x18,0xff,0x81,0xb1,0xfe,0x75,0x1f,0x03,0x00,0x31,0xf5,0x73,0xc0,0x01,0x60,0x00,0x90,0x00,0x1c
};
@@ -57,16 +61,18 @@ int openpgp_process_apdu();
extern uint32_t board_button_read(void);
static bool wait_button(uint16_t fid) {
file_t *ef = search_by_fid(fid, NULL, SPECIFY_ANY);
static bool wait_button_pressed(uint16_t fid) {
uint32_t val = EV_PRESS_BUTTON;
if (ef && ef->data && file_read_uint8(ef->data+2) > 0) {
queue_try_add(&card_to_ccid_q, &val);
#ifndef ENABLE_EMULATION
file_t *ef = search_by_fid(fid, NULL, SPECIFY_ANY);
if (ef && ef->data && file_get_data(ef)[0] > 0) {
queue_try_add(&card_to_usb_q, &val);
do {
queue_remove_blocking(&ccid_to_card_q, &val);
queue_remove_blocking(&usb_to_card_q, &val);
}
while (val != EV_BUTTON_PRESSED && val != EV_BUTTON_TIMEOUT);
}
#endif
return val == EV_BUTTON_TIMEOUT;
}
@@ -130,7 +136,7 @@ static int cmd_select() {
}
if (card_terminated) {
return set_res_sw (0x62, 0x85);
}
}
}
else if (p1 == 0x08) { //Select from the MF - Path without the MF identifier
if (!(pe = search_by_path(apdu.data, apdu.nc, MF))) {
@@ -158,7 +164,11 @@ void scan_files() {
file_t *ef;
if ((ef = search_by_fid(EF_FULL_AID, NULL, SPECIFY_ANY))) {
ef->data = openpgp_aid_full;
#ifndef ENABLE_EMULATION
pico_get_unique_board_id_string((char *)ef->data+12, 4);
#else
memset((char *)ef->data + 12, 0, 4);
#endif
}
if ((ef = search_by_fid(EF_PW1, NULL, SPECIFY_ANY))) {
if (!ef->data) {
@@ -173,7 +183,7 @@ void scan_files() {
if ((ef = search_by_fid(EF_RC, NULL, SPECIFY_ANY))) {
if (!ef->data) {
printf("RC is empty. Initializing with default password\r\n");
const uint8_t def[8] = { 0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38 };
uint8_t dhash[33];
dhash[0] = sizeof(def);
@@ -184,7 +194,7 @@ void scan_files() {
if ((ef = search_by_fid(EF_PW3, NULL, SPECIFY_ANY))) {
if (!ef->data) {
printf("PW3 is empty. Initializing with default password\r\n");
const uint8_t def[8] = { 0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38 };
uint8_t dhash[33];
dhash[0] = sizeof(def);
@@ -211,18 +221,19 @@ void scan_files() {
printf("DEK is empty\r\n");
const uint8_t def1[6] = { 0x31,0x32,0x33,0x34,0x35,0x36 };
const uint8_t def3[8] = { 0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38 };
uint8_t def[IV_SIZE+32+32];
uint8_t def[IV_SIZE+32+32+32];
const uint8_t *dek = random_bytes_get(IV_SIZE+32);
memcpy(def, dek, IV_SIZE+32);
memcpy(def+IV_SIZE+32, dek+IV_SIZE, 32);
hash_multi(def1, sizeof(def1), session_pw1);
aes_encrypt_cfb_256(session_pw1, def, def+IV_SIZE, 32);
memset(session_pw1, 0, sizeof(session_pw1));
hash_multi(def3, sizeof(def3), session_pw3);
aes_encrypt_cfb_256(session_pw3, def, def+IV_SIZE+32, 32);
memset(session_pw1, 0, sizeof(session_pw1));
aes_encrypt_cfb_256(session_pw3, def, def+IV_SIZE+32+32, 32);
memset(session_pw3, 0, sizeof(session_pw3));
flash_write_data_to_file(ef, def, sizeof(def));
}
}
@@ -254,6 +265,14 @@ void scan_files() {
flash_write_data_to_file(ef, def, sizeof(def));
}
}
if ((ef = search_by_fid(EF_SEX, NULL, SPECIFY_ANY))) {
if (!ef->data) {
printf("Sex is empty. Initializing to default\r\n");
const uint8_t def[] = { 0x30 };
flash_write_data_to_file(ef, def, sizeof(def));
}
}
low_flash_available();
}
@@ -265,12 +284,12 @@ int load_dek() {
return CCID_ERR_FILE_NOT_FOUND;
int r = CCID_OK;
if (has_pw1 || has_pw2) {
memcpy(dek, file_read(tf->data+sizeof(uint16_t)), IV_SIZE+32);
memcpy(dek, file_get_data(tf), IV_SIZE+32);
r = aes_decrypt_cfb_256(session_pw1, dek, dek+IV_SIZE, 32);
}
else if (has_pw3) {
memcpy(dek, file_read(tf->data+sizeof(uint16_t)), IV_SIZE);
memcpy(dek+IV_SIZE, file_read(tf->data+sizeof(uint16_t)+IV_SIZE+32), 32);
memcpy(dek, file_get_data(tf), IV_SIZE);
memcpy(dek+IV_SIZE, file_get_data(tf)+IV_SIZE+32+32, 32);
r = aes_decrypt_cfb_256(session_pw3, dek, dek+IV_SIZE, 32);
}
if (r != 0)
@@ -323,14 +342,18 @@ int openpgp_unload() {
extern char __StackLimit;
int heapLeft() {
#ifndef ENABLE_EMULATION
char *p = malloc(256); // try to avoid undue fragmentation
int left = &__StackLimit - p;
free(p);
#else
int left = 1024*1024;
#endif
return left;
}
app_t *openpgp_select_aid(app_t *a) {
if (!memcmp(apdu.data, openpgp_aid+1, openpgp_aid[0])) {
app_t *openpgp_select_aid(app_t *a, const uint8_t *aid, uint8_t aid_len) {
if (!memcmp(aid, openpgp_aid+1, MIN(aid_len,openpgp_aid[0]))) {
a->aid = openpgp_aid;
a->process_apdu = openpgp_process_apdu;
a->unload = openpgp_unload;
@@ -344,12 +367,13 @@ app_t *openpgp_select_aid(app_t *a) {
res_APDU[res_APDU_size++] = ((heap_left >> 8) & 0xff);
res_APDU[res_APDU_size++] = ((heap_left >> 0) & 0xff);
res_APDU[1] += 8;
apdu.ne = res_APDU_size;
return a;
}
return NULL;
}
void __attribute__ ((constructor)) openpgp_ctor() {
void __attribute__ ((constructor)) openpgp_ctor() {
ccid_atr = (uint8_t *)atr_openpgp;
register_app(openpgp_select_aid);
}
@@ -365,7 +389,7 @@ int parse_do(uint16_t *fids, int mode) {
}
else {
if (ef->data)
data_len = file_read_uint16(ef->data);
data_len = file_get_size(ef);
else
data_len = 0;
if (mode == 1) {
@@ -380,7 +404,7 @@ int parse_do(uint16_t *fids, int mode) {
res_APDU_size += format_tlv_len(data_len, res_APDU+res_APDU_size);
}
if (ef->data)
memcpy(res_APDU+res_APDU_size, file_read(ef->data+2), data_len);
memcpy(res_APDU+res_APDU_size, file_get_data(ef), data_len);
res_APDU_size += data_len;
}
}
@@ -394,8 +418,8 @@ int parse_trium(uint16_t fid, uint8_t num, size_t size) {
for (uint8_t i = 0; i < num; i++) {
file_t *ef;
if ((ef = search_by_fid(fid+i, NULL, SPECIFY_EF)) && ef->data) {
uint16_t data_len = file_read_uint16(ef->data);
memcpy(res_APDU+res_APDU_size, file_read(ef->data+2), data_len);
uint16_t data_len = file_get_size(ef);
memcpy(res_APDU+res_APDU_size, file_get_data(ef), data_len);
res_APDU_size += data_len;
}
else {
@@ -409,7 +433,7 @@ int parse_trium(uint16_t fid, uint8_t num, size_t size) {
int parse_ch_data(const file_t *f, int mode) {
uint16_t fids[] = {
3,
EF_CH_NAME, EF_LANG_PREF, EF_SEX,
EF_CH_NAME, EF_LANG_PREF, EF_SEX,
};
res_APDU[res_APDU_size++] = EF_CH_DATA & 0xff;
res_APDU[res_APDU_size++] = 0x82;
@@ -426,12 +450,12 @@ int inc_sig_count() {
file_t *pw_status;
if (!(pw_status = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF)) || !pw_status->data)
return SW_REFERENCE_NOT_FOUND();
if (file_read_uint8(pw_status->data+2) == 1)
if (file_get_data(pw_status)[0] == 1)
has_pw1 = false;
file_t *ef = search_by_fid(EF_SIG_COUNT, NULL, SPECIFY_ANY);
if (!ef || !ef->data)
return CCID_ERR_FILE_NOT_FOUND;
uint8_t *p = file_read(ef->data+2);
uint8_t *p = file_get_data(ef);
uint32_t counter = (p[0] << 16) | (p[1] << 8) | p[2];
counter++;
uint8_t q[3] = { (counter>>16) & 0xff, (counter>>8) & 0xff, counter&0xff };
@@ -461,7 +485,7 @@ int parse_sec_tpl(const file_t *f, int mode) {
if (ef && ef->data) {
res_APDU[res_APDU_size++] = EF_SIG_COUNT & 0xff;
res_APDU[res_APDU_size++] = 3;
memcpy(res_APDU+res_APDU_size, file_read(ef->data+2), 3);
memcpy(res_APDU+res_APDU_size, file_get_data(ef), 3);
res_APDU_size += 3;
}
return 5+2;
@@ -474,19 +498,19 @@ int parse_ch_cert(const file_t *f, int mode) {
int parse_fp(const file_t *f, int mode) {
res_APDU[res_APDU_size++] = EF_FP & 0xff;
res_APDU[res_APDU_size++] = 60;
return parse_trium(EF_FP_SIG, 3, 20);
return parse_trium(EF_FP_SIG, 3, 20) + 2;
}
int parse_cafp(const file_t *f, int mode) {
res_APDU[res_APDU_size++] = EF_CA_FP & 0xff;
res_APDU[res_APDU_size++] = 60;
return parse_trium(EF_FP_CA1, 3, 20);
return parse_trium(EF_FP_CA1, 3, 20) + 2;
}
int parse_ts(const file_t *f, int mode) {
res_APDU[res_APDU_size++] = EF_TS_ALL & 0xff;
res_APDU[res_APDU_size++] = 12;
return parse_trium(EF_TS_SIG, 3, 4);
return parse_trium(EF_TS_SIG, 3, 4) + 2;
}
int parse_keyinfo(const file_t *f, int mode) {
@@ -501,14 +525,14 @@ int parse_keyinfo(const file_t *f, int mode) {
res_APDU[res_APDU_size++] = 0x01;
else
res_APDU[res_APDU_size++] = 0x00;
ef = search_by_fid(EF_PK_DEC, NULL, SPECIFY_ANY);
res_APDU[res_APDU_size++] = 0x01;
if (ef && ef->data)
res_APDU[res_APDU_size++] = 0x01;
else
res_APDU[res_APDU_size++] = 0x00;
ef = search_by_fid(EF_PK_AUT, NULL, SPECIFY_ANY);
res_APDU[res_APDU_size++] = 0x02;
if (ef && ef->data)
@@ -527,7 +551,7 @@ int parse_pw_status(const file_t *f, int mode) {
}
ef = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_ANY);
if (ef && ef->data) {
memcpy(res_APDU+res_APDU_size, file_read(ef->data+2), 7);
memcpy(res_APDU+res_APDU_size, file_get_data(ef), 7);
res_APDU_size += 7;
}
return res_APDU_size-init_len;
@@ -597,7 +621,7 @@ static const uint8_t algorithm_attr_rsa4k[] = {
static const uint8_t algorithm_attr_p256k1[] = {
6,
ALGO_ECDSA,
0x2b, 0x81, 0x04, 0x00, 0x0a
0x2b, 0x81, 0x04, 0x00, 0x0a
};
static const uint8_t algorithm_attr_p256r1[] = {
@@ -650,11 +674,12 @@ int parse_algo(const uint8_t *algo, uint16_t tag) {
}
int parse_algoinfo(const file_t *f, int mode) {
uint8_t datalen = 0;
int datalen = 0;
if (f->fid == EF_ALGO_INFO) {
res_APDU[res_APDU_size++] = EF_ALGO_INFO & 0xff;
uint8_t *lp = res_APDU+res_APDU_size;
res_APDU_size++;
res_APDU[res_APDU_size++] = 0x82;
uint8_t *lp = res_APDU + res_APDU_size;
res_APDU_size += 2;
datalen += parse_algo(algorithm_attr_rsa1k, EF_ALGO_SIG);
datalen += parse_algo(algorithm_attr_rsa2k, EF_ALGO_SIG);
datalen += parse_algo(algorithm_attr_rsa3k, EF_ALGO_SIG);
@@ -666,7 +691,7 @@ int parse_algoinfo(const file_t *f, int mode) {
datalen += parse_algo(algorithm_attr_bp256r1, EF_ALGO_SIG);
datalen += parse_algo(algorithm_attr_bp384r1, EF_ALGO_SIG);
datalen += parse_algo(algorithm_attr_bp512r1, EF_ALGO_SIG);
datalen += parse_algo(algorithm_attr_rsa1k, EF_ALGO_DEC);
datalen += parse_algo(algorithm_attr_rsa2k, EF_ALGO_DEC);
datalen += parse_algo(algorithm_attr_rsa3k, EF_ALGO_DEC);
@@ -680,7 +705,7 @@ int parse_algoinfo(const file_t *f, int mode) {
datalen += parse_algo(algorithm_attr_bp512r1, EF_ALGO_DEC);
datalen += parse_algo(algorithm_attr_cv25519, EF_ALGO_DEC);
datalen += parse_algo(algorithm_attr_x448, EF_ALGO_DEC);
datalen += parse_algo(algorithm_attr_rsa1k, EF_ALGO_AUT);
datalen += parse_algo(algorithm_attr_rsa2k, EF_ALGO_AUT);
datalen += parse_algo(algorithm_attr_rsa3k, EF_ALGO_AUT);
@@ -689,11 +714,13 @@ int parse_algoinfo(const file_t *f, int mode) {
datalen += parse_algo(algorithm_attr_p256r1, EF_ALGO_AUT);
datalen += parse_algo(algorithm_attr_p384r1, EF_ALGO_AUT);
datalen += parse_algo(algorithm_attr_p521r1, EF_ALGO_AUT);
datalen += parse_algo(algorithm_attr_bp256r1, EF_ALGO_SIG);
datalen += parse_algo(algorithm_attr_bp384r1, EF_ALGO_SIG);
datalen += parse_algo(algorithm_attr_bp512r1, EF_ALGO_SIG);
*lp = res_APDU+res_APDU_size-lp-1;
datalen = *lp;
datalen += parse_algo(algorithm_attr_bp256r1, EF_ALGO_AUT);
datalen += parse_algo(algorithm_attr_bp384r1, EF_ALGO_AUT);
datalen += parse_algo(algorithm_attr_bp512r1, EF_ALGO_AUT);
uint16_t lpdif = res_APDU+res_APDU_size-lp-2;
*lp++ = lpdif >> 8;
*lp++ = lpdif & 0xff;
datalen = lpdif+4;
}
else if (f->fid == EF_ALGO_SIG || f->fid == EF_ALGO_DEC || f->fid == EF_ALGO_AUT) {
uint16_t fid = 0x1000 | f->fid;
@@ -701,13 +728,13 @@ int parse_algoinfo(const file_t *f, int mode) {
if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF)) || !ef->data)
datalen += parse_algo(algorithm_attr_rsa2k, f->fid);
else {
uint16_t len = file_read_uint16(ef->data);
uint16_t len = file_get_size(ef);
if (res_APDU_size > 0) {
res_APDU[res_APDU_size++] = f->fid & 0xff;
res_APDU[res_APDU_size++] = len & 0xff;
datalen += 2;
}
memcpy(res_APDU+res_APDU_size, file_read(ef->data+2), len);
memcpy(res_APDU+res_APDU_size, file_get_data(ef), len);
res_APDU_size += len;
datalen += len;
}
@@ -765,40 +792,55 @@ static int cmd_get_data() {
if (ef->data) {
uint16_t fids[] = {1,fid};
uint16_t data_len = parse_do(fids, 1);
if (apdu.ne > data_len)
apdu.ne = data_len;
uint8_t *p = NULL;
uint16_t tg = 0;
size_t tg_len = 0;
if (walk_tlv(res_APDU, data_len, &p, &tg, &tg_len, NULL)) {
uint8_t dec = 2;
if ((tg & 0x1f) == 0x1f)
dec++;
if ((res_APDU[dec-1] & 0xF0) == 0x80)
dec += (res_APDU[dec - 1] & 0x0F);
if (tg_len + dec == data_len) {
memmove(res_APDU, res_APDU + dec, data_len - dec);
data_len -= dec;
res_APDU_size -= dec;
}
}
//if (apdu.ne > data_len)
// apdu.ne = data_len;
}
return SW_OK();
}
int pin_reset_retries(const file_t *pin, bool force) {
if (!pin)
return CCID_ERR_NULL_PARAM;
return CCID_ERR_NULL_PARAM;
file_t *pw_status = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF);
if (!pw_status)
return CCID_ERR_FILE_NOT_FOUND;
uint8_t p[7];
memcpy(p, file_read(pw_status->data+2), 7);
memcpy(p, file_get_data(pw_status), 7);
uint8_t retries = p[3+(pin->fid&0x3)];
if (retries == 0 && force == false) //blocked
return CCID_ERR_BLOCKED;
p[3+(pin->fid&0x3)] = 3;
int r = flash_write_data_to_file(pw_status, p, file_read_uint16(pw_status->data));
int r = flash_write_data_to_file(pw_status, p, file_get_size(pw_status));
low_flash_available();
return r;
}
int pin_wrong_retry(const file_t *pin) {
if (!pin)
return CCID_ERR_NULL_PARAM;
return CCID_ERR_NULL_PARAM;
file_t *pw_status = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF);
if (!pw_status)
return CCID_ERR_FILE_NOT_FOUND;
uint8_t p[7];
memcpy(p, file_read(pw_status->data+2), 7);
memcpy(p, file_get_data(pw_status), 7);
if (p[3+(pin->fid&0x3)] > 0) {
p[3+(pin->fid&0x3)] -= 1;
int r = flash_write_data_to_file(pw_status, p, file_read_uint16(pw_status->data));
int r = flash_write_data_to_file(pw_status, p, file_get_size(pw_status));
if (r != CCID_OK)
return r;
low_flash_available();
@@ -820,9 +862,9 @@ int check_pin(const file_t *pin, const uint8_t *data, size_t len) {
uint8_t dhash[32];
double_hash_pin(data, len, dhash);
if (sizeof(dhash) != file_read_uint16(pin->data)-1) //1 byte for pin len
if (sizeof(dhash) != file_get_size(pin)-1) //1 byte for pin len
return SW_CONDITIONS_NOT_SATISFIED();
if (memcmp(file_read(pin->data+3), dhash, sizeof(dhash)) != 0) {
if (memcmp(file_get_data(pin)+1, dhash, sizeof(dhash)) != 0) {
int retries;
if ((retries = pin_wrong_retry(pin)) < CCID_OK)
return SW_PIN_BLOCKED();
@@ -852,7 +894,7 @@ int check_pin(const file_t *pin, const uint8_t *data, size_t len) {
static int cmd_verify() {
uint8_t p1 = P1(apdu);
uint8_t p2 = P2(apdu);
if (p1 == 0xFF) {
if (apdu.nc != 0)
return SW_WRONG_DATA();
@@ -874,12 +916,12 @@ static int cmd_verify() {
return SW_REFERENCE_NOT_FOUND();
if (!(pw_status = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF)))
return SW_REFERENCE_NOT_FOUND();
if (file_read_uint8(pw->data+2) == 0) //not initialized
if (file_get_data(pw)[0] == 0) //not initialized
return SW_REFERENCE_NOT_FOUND();
if (apdu.nc > 0) {
return check_pin(pw, apdu.data, apdu.nc);
}
uint8_t retries = file_read_uint8(pw_status->data+2+3+(fid&0x3));
uint8_t retries = *(file_get_data(pw_status)+3+(fid&0x3));
if (retries == 0)
return SW_PIN_BLOCKED();
if ((p2 == 0x81 && has_pw1) || (p2 == 0x82 && has_pw2) || (p2 == 0x83 && has_pw3))
@@ -907,7 +949,28 @@ static int cmd_put_data() {
ef = currentEF;
}
if (apdu.nc > 0 && (ef->type & FILE_DATA_FLASH)) {
int r = flash_write_data_to_file(ef, apdu.data, apdu.nc);
int r = 0;
if (fid == EF_RC) {
has_rc = false;
if ((r = load_dek()) != CCID_OK)
return SW_EXEC_ERROR();
uint8_t dhash[33];
dhash[0] = apdu.nc;
double_hash_pin(apdu.data, apdu.nc, dhash+1);
r = flash_write_data_to_file(ef, dhash, sizeof(dhash));
file_t *tf = search_by_fid(EF_DEK, NULL, SPECIFY_EF);
if (!tf)
return SW_REFERENCE_NOT_FOUND();
uint8_t def[IV_SIZE+32+32+32];
memcpy(def, file_get_data(tf), file_get_size(tf));
hash_multi(apdu.data, apdu.nc, session_rc);
memcpy(def + IV_SIZE + 32, dek + IV_SIZE, 32);
aes_encrypt_cfb_256(session_rc, def, def + IV_SIZE + 32, 32);
r = flash_write_data_to_file(tf, def, sizeof(def));
}
else
r = flash_write_data_to_file(ef, apdu.data, apdu.nc);
if (r != CCID_OK)
return SW_MEMORY_FAILURE();
low_flash_available();
@@ -922,14 +985,34 @@ static int cmd_change_pin() {
file_t *pw;
if (!(pw = search_by_fid(fid, NULL, SPECIFY_EF)))
return SW_REFERENCE_NOT_FOUND();
uint8_t pin_len = file_read_uint8(pw->data+2);
uint16_t r = check_pin(pw, apdu.data, pin_len);
uint8_t pin_len = file_get_data(pw)[0];
uint16_t r = 0;
if ((r = load_dek()) != CCID_OK)
return SW_EXEC_ERROR();
r = check_pin(pw, apdu.data, pin_len);
if (r != 0x9000)
return r;
uint8_t dhash[33];
dhash[0] = apdu.nc-pin_len;
double_hash_pin(apdu.data+pin_len, apdu.nc-pin_len, dhash+1);
flash_write_data_to_file(pw, dhash, sizeof(dhash));
file_t *tf = search_by_fid(EF_DEK, NULL, SPECIFY_EF);
if (!tf)
return SW_REFERENCE_NOT_FOUND();
uint8_t def[IV_SIZE+32+32+32];
memcpy(def, file_get_data(tf), file_get_size(tf));
if (P2(apdu) == 0x81) {
hash_multi(apdu.data+pin_len, apdu.nc-pin_len, session_pw1);
memcpy(def + IV_SIZE, dek + IV_SIZE, 32);
aes_encrypt_cfb_256(session_pw1, def, def + IV_SIZE, 32);
}
else if (P2(apdu) == 0x83) {
hash_multi(apdu.data+pin_len, apdu.nc-pin_len, session_pw3);
memcpy(def + IV_SIZE + 32 + 32, dek + IV_SIZE, 32);
aes_encrypt_cfb_256(session_pw3, def, def+IV_SIZE+32+32, 32);
}
flash_write_data_to_file(tf, def, sizeof(def));
low_flash_available();
return SW_OK();
}
@@ -940,25 +1023,41 @@ static int cmd_reset_retry() {
if (P1(apdu) == 0x0 || P1(apdu) == 0x2) {
int newpin_len = 0;
file_t *pw = NULL;
has_pw1 = false;
if (!(pw = search_by_fid(EF_PW1, NULL, SPECIFY_EF)))
return SW_REFERENCE_NOT_FOUND();
if (P1(apdu) == 0x0) {
file_t *rc;
if (!(rc = search_by_fid(EF_RC, NULL, SPECIFY_EF)))
return SW_REFERENCE_NOT_FOUND();
uint8_t pin_len = file_read_uint8(rc->data+2);
uint8_t pin_len = file_get_data(rc)[0];
if (apdu.nc <= pin_len)
return SW_WRONG_LENGTH();
uint16_t r = check_pin(rc, apdu.data, pin_len);
if (r != 0x9000)
return r;
newpin_len = apdu.nc-pin_len;
has_rc = true;
hash_multi(apdu.data, pin_len, session_rc);
}
else if (P1(apdu) == 0x2) {
else if (P1(apdu) == 0x2) {
if (!has_pw3)
return SW_CONDITIONS_NOT_SATISFIED();
newpin_len = apdu.nc;
}
int r = 0;
if ((r = load_dek()) != CCID_OK)
return SW_EXEC_ERROR();
file_t *tf = search_by_fid(EF_DEK, NULL, SPECIFY_EF);
if (!tf)
return SW_REFERENCE_NOT_FOUND();
uint8_t def[IV_SIZE+32+32+32];
memcpy(def, file_get_data(tf), file_get_size(tf));
hash_multi(apdu.data+(apdu.nc-newpin_len), newpin_len, session_pw1);
memcpy(def + IV_SIZE, dek + IV_SIZE, 32);
aes_encrypt_cfb_256(session_pw1, def, def + IV_SIZE, 32);
r = flash_write_data_to_file(tf, def, sizeof(def));
uint8_t dhash[33];
dhash[0] = newpin_len;
double_hash_pin(apdu.data+(apdu.nc-newpin_len), newpin_len, dhash+1);
@@ -973,8 +1072,8 @@ static int cmd_reset_retry() {
int store_keys(void *key_ctx, int type, uint16_t key_id) {
int r, key_size = 0;
uint8_t kdata[4096/8]; //worst
uint8_t kdata[4096/8]; //worst
//if (!has_pw3)
// return CCID_NO_LOGIN;
//file_t *pw3 = search_by_fid(EF_PW3, NULL, SPECIFY_EF);
@@ -1020,9 +1119,9 @@ int store_keys(void *key_ctx, int type, uint16_t key_id) {
}
int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey) {
int key_size = file_read_uint16(fkey->data);
int key_size = file_get_size(fkey);
uint8_t kdata[4096/8];
memcpy(kdata, file_read(fkey->data+2), key_size);
memcpy(kdata, file_get_data(fkey), key_size);
if (dek_decrypt(kdata, key_size) != 0) {
return CCID_EXEC_ERROR;
}
@@ -1054,9 +1153,9 @@ int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey) {
}
int load_private_key_ecdsa(mbedtls_ecdsa_context *ctx, file_t *fkey) {
int key_size = file_read_uint16(fkey->data);
int key_size = file_get_size(fkey);
uint8_t kdata[67]; //Worst case, 521 bit + 1byte
memcpy(kdata, file_read(fkey->data+2), key_size);
memcpy(kdata, file_get_data(fkey), key_size);
if (dek_decrypt(kdata, key_size) != 0) {
return CCID_EXEC_ERROR;
}
@@ -1070,8 +1169,8 @@ int load_private_key_ecdsa(mbedtls_ecdsa_context *ctx, file_t *fkey) {
}
int load_aes_key(uint8_t *aes_key, file_t *fkey) {
int key_size = file_read_uint16(fkey->data);
memcpy(aes_key, file_read(fkey->data+2), key_size);
int key_size = file_get_size(fkey);
memcpy(aes_key, file_get_data(fkey), key_size);
if (dek_decrypt(aes_key, key_size) != 0) {
return CCID_EXEC_ERROR;
}
@@ -1114,16 +1213,20 @@ void make_rsa_response(mbedtls_rsa_context *rsa) {
}
void make_ecdsa_response(mbedtls_ecdsa_context *ecdsa) {
size_t plen = mbedtls_mpi_size(&ecdsa->grp.P);
memcpy(res_APDU, "\x7f\x49\x81\x00", 4);
res_APDU_size = 4;
uint8_t pt[MBEDTLS_ECP_MAX_PT_LEN];
size_t plen = 0;
mbedtls_ecp_point_write_binary(&ecdsa->grp, &ecdsa->Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &plen, pt, sizeof(pt));
res_APDU[res_APDU_size++] = 0x7f;
res_APDU[res_APDU_size++] = 0x49;
if (plen >= 128)
res_APDU[res_APDU_size++] = 0x81;
res_APDU[res_APDU_size++] = plen + (plen >= 128 ? 3 : 2);
res_APDU[res_APDU_size++] = 0x86;
res_APDU[res_APDU_size++] = 0x81;
res_APDU[res_APDU_size++] = 2*plen+1;
res_APDU[res_APDU_size++] = 0x04;
mbedtls_mpi_write_binary(&ecdsa->Q.X, res_APDU+res_APDU_size, plen); res_APDU_size += plen;
mbedtls_mpi_write_binary(&ecdsa->Q.Y, res_APDU+res_APDU_size, plen); res_APDU_size += plen;
res_APDU[3] = res_APDU_size-4;
if (plen >= 128)
res_APDU[res_APDU_size++] = 0x81;
res_APDU[res_APDU_size++] = plen;
memcpy(res_APDU + res_APDU_size, pt, plen);
res_APDU_size += plen;
}
static int cmd_keypair_gen() {
@@ -1133,7 +1236,7 @@ static int cmd_keypair_gen() {
return SW_WRONG_LENGTH();
if (!has_pw3 && P1(apdu) == 0x80)
return SW_SECURITY_STATUS_NOT_SATISFIED();
uint16_t fid = 0x0;
int r = CCID_OK;
if (apdu.data[0] == 0xB6)
@@ -1144,15 +1247,15 @@ static int cmd_keypair_gen() {
fid = EF_PK_AUT;
else
return SW_WRONG_DATA();
file_t *algo_ef = search_by_fid(fid-0x0010, NULL, SPECIFY_EF);
if (!algo_ef)
return SW_REFERENCE_NOT_FOUND();
const uint8_t *algo = algorithm_attr_rsa2k+1;
uint16_t algo_len = algorithm_attr_rsa2k[0];
if (algo_ef && algo_ef->data) {
algo = file_read(algo_ef->data+2);
algo_len = file_read_uint16(algo_ef->data);
algo = file_get_data(algo_ef);
algo_len = file_get_size(algo_ef);
}
if (P1(apdu) == 0x80) { //generate
if (algo[0] == ALGO_RSA) {
@@ -1211,7 +1314,7 @@ static int cmd_keypair_gen() {
memcpy(aes_key, random_bytes_get(key_size), key_size);
r = store_keys(aes_key, ALGO_AES_256, EF_AES_KEY);
/* if storing the key fails, we silently continue */
//if (r != CCID_OK)
//if (r != CCID_OK)
// return SW_EXEC_ERROR();
}
low_flash_available();
@@ -1221,8 +1324,8 @@ static int cmd_keypair_gen() {
file_t *ef = search_by_fid(fid+3, NULL, SPECIFY_EF);
if (!ef || !ef->data)
return SW_REFERENCE_NOT_FOUND();
res_APDU_size = file_read_uint16(ef->data);
memcpy(res_APDU, file_read(ef->data+2), res_APDU_size);
res_APDU_size = file_get_size(ef);
memcpy(res_APDU, file_get_data(ef), res_APDU_size);
return SW_OK();
}
return SW_INCORRECT_P1P2();
@@ -1238,15 +1341,15 @@ int rsa_sign(mbedtls_rsa_context *ctx, const uint8_t *data, size_t data_len, uin
r = mbedtls_asn1_get_alg_null(&d, end, &mdb);
if (r == 0) {
if (mbedtls_asn1_get_tag(&d, end, &hash_len, MBEDTLS_ASN1_OCTET_STRING) == 0) {
if (memcmp(mdb.p, "\x2B\x0E\x03\x02\x1A", 5) == 0)
if (memcmp(mdb.p, "\x2B\x0E\x03\x02\x1A", 5) == 0)
md = MBEDTLS_MD_SHA1;
else if (memcmp(mdb.p, "\x60\x86\x48\x01\x65\x03\x04\x02\x04", 9) == 0)
else if (memcmp(mdb.p, "\x60\x86\x48\x01\x65\x03\x04\x02\x04", 9) == 0)
md = MBEDTLS_MD_SHA224;
else if (memcmp(mdb.p, "\x60\x86\x48\x01\x65\x03\x04\x02\x01", 9) == 0)
else if (memcmp(mdb.p, "\x60\x86\x48\x01\x65\x03\x04\x02\x01", 9) == 0)
md = MBEDTLS_MD_SHA256;
else if (memcmp(mdb.p, "\x60\x86\x48\x01\x65\x03\x04\x02\x02", 9) == 0)
else if (memcmp(mdb.p, "\x60\x86\x48\x01\x65\x03\x04\x02\x02", 9) == 0)
md = MBEDTLS_MD_SHA384;
else if (memcmp(mdb.p, "\x60\x86\x48\x01\x65\x03\x04\x02\x03", 9) == 0)
else if (memcmp(mdb.p, "\x60\x86\x48\x01\x65\x03\x04\x02\x03", 9) == 0)
md = MBEDTLS_MD_SHA512;
hsh = d;
}
@@ -1278,7 +1381,6 @@ int rsa_sign(mbedtls_rsa_context *ctx, const uint8_t *data, size_t data_len, uin
free(signature);
}
*out_len = key_size;
inc_sig_count();
return r;
}
@@ -1288,14 +1390,13 @@ int ecdsa_sign(mbedtls_ecdsa_context *ctx, const uint8_t *data, size_t data_len,
mbedtls_mpi_init(&si);
int r = mbedtls_ecdsa_sign(&ctx->grp, &ri, &si, &ctx->d, data, data_len, random_gen, NULL);
if (r == 0) {
mbedtls_mpi_write_binary(&ri, out, mbedtls_mpi_size(&ri));
*out_len = mbedtls_mpi_size(&ri);
mbedtls_mpi_write_binary(&si, out+*out_len, mbedtls_mpi_size(&si));
*out_len += mbedtls_mpi_size(&si);
size_t plen = (ctx->grp.nbits + 7) / 8;
mbedtls_mpi_write_binary(&ri, out, plen);
mbedtls_mpi_write_binary(&si, out + plen, plen);
*out_len = 2*plen;
}
mbedtls_mpi_free(&ri);
mbedtls_mpi_free(&si);
inc_sig_count();
return r;
}
@@ -1321,7 +1422,7 @@ static int cmd_pso() {
return SW_REFERENCE_NOT_FOUND();
const uint8_t *algo = algorithm_attr_rsa2k+1;
if (algo_ef && algo_ef->data) {
algo = file_read(algo_ef->data+2);
algo = file_get_data(algo_ef);
}
if (apdu.data[0] == 0x2) { //AES PSO?
if (((apdu.nc - 1)%16 == 0 && P1(apdu) == 0x80 && P2(apdu) == 0x86) || (apdu.nc%16 == 0 && P1(apdu) == 0x86 && P2(apdu) == 0x80)) {
@@ -1332,10 +1433,10 @@ static int cmd_pso() {
file_t *ef = search_by_fid(pk_fid, NULL, SPECIFY_EF);
if (!ef)
return SW_REFERENCE_NOT_FOUND();
if (wait_button(pk_fid == EF_PK_SIG ? EF_UIF_SIG : EF_UIF_DEC) == true)
if (wait_button_pressed(pk_fid == EF_PK_SIG ? EF_UIF_SIG : EF_UIF_DEC) == true)
return SW_SECURE_MESSAGE_EXEC_ERROR();
int r = CCID_OK;
int key_size = file_read_uint16(ef->data);
int key_size = file_get_size(ef);
if (is_aes) {
uint8_t aes_key[32];
r = load_aes_key(aes_key, ef);
@@ -1378,6 +1479,7 @@ static int cmd_pso() {
return SW_EXEC_ERROR();
res_APDU_size = olen;
//apdu.ne = key_size;
inc_sig_count();
}
else if (P1(apdu) == 0x80 && P2(apdu) == 0x86) {
if (apdu.nc < key_size) //needs padding
@@ -1406,6 +1508,7 @@ static int cmd_pso() {
if (r != 0)
return SW_EXEC_ERROR();
res_APDU_size = olen;
inc_sig_count();
}
else if (P1(apdu) == 0x80 && P2(apdu) == 0x86) {
mbedtls_ecdh_context ctx;
@@ -1418,9 +1521,9 @@ static int cmd_pso() {
return SW_WRONG_DATA();
if (mbedtls_asn1_get_tag(&data, end, &len, 0x49) != 0 || mbedtls_asn1_get_tag(&data, end, &len, 0x86) != 0)
return SW_WRONG_DATA();
if (len != 2*key_size-1)
return SW_WRONG_LENGTH();
memcpy(kdata, file_read(ef->data+2), key_size);
//if (len != 2*key_size-1)
// return SW_WRONG_LENGTH();
memcpy(kdata, file_get_data(ef), key_size);
if (dek_decrypt(kdata, key_size) != 0) {
return SW_EXEC_ERROR();
}
@@ -1455,12 +1558,12 @@ static int cmd_pso() {
}
static int cmd_terminate_df() {
if (P1(apdu) != 0x0 && P2(apdu) != 0x0)
if (P1(apdu) != 0x0 || P2(apdu) != 0x0)
return SW_INCORRECT_P1P2();
file_t *retries;
if (!(retries = search_by_fid(EF_PW_PRIV, NULL, SPECIFY_EF)))
return SW_REFERENCE_NOT_FOUND();
if (!has_pw3 && file_read_uint8(retries->data+2+6) > 0)
if (!has_pw3 && *(file_get_data(retries)+6) > 0)
return SW_SECURITY_STATUS_NOT_SATISFIED();
if (apdu.nc != 0)
return SW_WRONG_LENGTH();
@@ -1492,12 +1595,12 @@ static int cmd_internal_aut() {
return SW_REFERENCE_NOT_FOUND();
const uint8_t *algo = algorithm_attr_rsa2k+1;
if (algo_ef && algo_ef->data) {
algo = file_read(algo_ef->data+2);
algo = file_get_data(algo_ef);
}
file_t *ef = search_by_fid(pk_aut, NULL, SPECIFY_EF);
if (!ef)
return SW_REFERENCE_NOT_FOUND();
if (wait_button(EF_UIF_AUT) == true)
if (wait_button_pressed(EF_UIF_AUT) == true)
return SW_SECURE_MESSAGE_EXEC_ERROR();
int r = CCID_OK;
if (algo[0] == ALGO_RSA) {
@@ -1561,6 +1664,18 @@ static int cmd_mse() {
return SW_OK();
}
size_t tag_len(uint8_t **data) {
size_t len = *(*data)++;
if (len == 0x82) {
len = *(*data)++ << 8;
len |= *(*data)++;
}
else if (len == 0x81) {
len = *(*data)++;
}
return len;
}
static int cmd_import_data() {
file_t *ef = NULL;
uint16_t fid = 0x0;
@@ -1568,53 +1683,58 @@ static int cmd_import_data() {
return SW_WRONG_P1P2();
if (apdu.nc < 5)
return SW_WRONG_LENGTH();
if (apdu.data[0] != 0x4D || (apdu.data[2] != 0xB6 && apdu.data[2] != 0xB8 && apdu.data[2] != 0xA4))
uint8_t *start = apdu.data;
if (*start++ != 0x4D)
return SW_WRONG_DATA();
if (apdu.data[2] == 0xB6)
size_t tgl = tag_len(&start);
if (*start != 0xB6 && *start != 0xB8 && *start != 0xA4)
return SW_WRONG_DATA();
if (*start == 0xB6)
fid = EF_PK_SIG;
else if (apdu.data[2] != 0xB8)
else if (*start == 0xB8)
fid = EF_PK_DEC;
else if (apdu.data[2] != 0xA4)
else if (*start == 0xA4)
fid = EF_PK_AUT;
else
return SW_WRONG_DATA();
start++;
if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF)))
return SW_REFERENCE_NOT_FOUND();
if (!authenticate_action(ef, ACL_OP_UPDATE_ERASE)) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
uint8_t *start = apdu.data + 4 + apdu.data[3];
start += (*start + 1);
if (*start++ != 0x7F || *start++ != 0x48)
return SW_WRONG_DATA();
uint8_t tag_len = *start++, *end = start+tag_len, len[9] = {0}, *p[9] = {NULL};
tgl = tag_len(&start);
uint8_t *end = start + tgl, len[9] = {0}, *p[9] = {0};
while (start < end) {
uint8_t tag = *start++;
if ((tag >= 0x91 && tag <= 0x97) || tag == 0x99) {
len[tag-0x91] = *start++;
len[tag-0x91] = tag_len(&start);
}
else
return SW_WRONG_DATA();
}
if (*start++ != 0x5F || *start++ != 0x48)
return SW_WRONG_DATA();
tag_len = *start++;
end = start+tag_len;
for (int t = 0; start < end; t++) {
tgl = tag_len(&start);
end = start+tgl;
for (int t = 0; start < end && t < 9; t++) {
if (len[t] > 0) {
p[t] = start;
start += len[t];
}
}
file_t *algo_ef = search_by_fid(fid-0x0010, NULL, SPECIFY_EF);
if (!algo_ef)
return SW_REFERENCE_NOT_FOUND();
const uint8_t *algo = algorithm_attr_rsa2k+1;
uint16_t algo_len = algorithm_attr_rsa2k[0];
if (algo_ef && algo_ef->data) {
algo = file_read(algo_ef->data+2);
algo_len = file_read_uint16(algo_ef->data);
algo = file_get_data(algo_ef);
algo_len = file_get_size(algo_ef);
}
int r = 0;
if (algo[0] == ALGO_RSA) {
@@ -1653,6 +1773,7 @@ static int cmd_import_data() {
return SW_EXEC_ERROR();
}
r = store_keys(&rsa, ALGO_RSA, fid);
make_rsa_response(&rsa);
mbedtls_rsa_free(&rsa);
if (r != CCID_OK)
return SW_EXEC_ERROR();
@@ -1661,7 +1782,7 @@ static int cmd_import_data() {
mbedtls_ecdsa_context ecdsa;
if (p[1] == NULL || len[1] == 0)
return SW_WRONG_DATA();
mbedtls_ecp_group_id gid = get_ec_group_id_from_attr(algo+1, algo_len-1);
mbedtls_ecp_group_id gid = get_ec_group_id_from_attr(algo+1, algo_len-1);
if (gid == MBEDTLS_ECP_DP_NONE)
return SW_FUNC_NOT_SUPPORTED();
mbedtls_ecdsa_init(&ecdsa);
@@ -1670,13 +1791,26 @@ static int cmd_import_data() {
mbedtls_ecdsa_free(&ecdsa);
return SW_EXEC_ERROR();
}
r = mbedtls_ecp_mul(&ecdsa.grp, &ecdsa.Q, &ecdsa.d, &ecdsa.grp.G, random_gen, NULL);
if (r != 0) {
mbedtls_ecdsa_free(&ecdsa);
return SW_EXEC_ERROR();
}
r = store_keys(&ecdsa, ALGO_ECDSA, fid);
make_ecdsa_response(&ecdsa);
mbedtls_ecdsa_free(&ecdsa);
if (r != CCID_OK)
return SW_EXEC_ERROR();
}
if (fid == EF_PK_SIG)
reset_sig_count();
file_t *pbef = search_by_fid(fid+3, NULL, SPECIFY_EF);
if (!pbef)
return SW_REFERENCE_NOT_FOUND();
r = flash_write_data_to_file(pbef, res_APDU, res_APDU_size);
if (r != CCID_OK)
return SW_EXEC_ERROR();
res_APDU_size = 0; //make_*_response sets a response. we need to overwrite
return SW_OK();
}
@@ -1736,12 +1870,6 @@ static int cmd_get_next_data() {
return cmd_get_data();
}
typedef struct cmd
{
uint8_t ins;
int (*cmd_handler)();
} cmd_t;
#define INS_VERIFY 0x20
#define INS_MSE 0x22
#define INS_CHANGE_PIN 0x24

View File

@@ -1,17 +1,17 @@
/*
/*
* This file is part of the Pico OpenPGP distribution (https://github.com/polhenarejos/pico-openpgp).
* Copyright (c) 2022 Pol Henarejos.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -19,9 +19,12 @@
#define __OPENPGP_H_
#include "stdlib.h"
#ifndef ENABLE_EMULATION
#include <pico/stdlib.h>
#endif
#include "ccid2040.h"
#include "hsm.h"
#include "apdu.h"
extern bool has_pw1;
extern bool has_pw3;

View File

@@ -1,17 +1,17 @@
/*
/*
* This file is part of the Pico OpenPGP distribution (https://github.com/polhenarejos/pico-openpgp).
* Copyright (c) 2022 Pol Henarejos.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -24,7 +24,7 @@
#define OPGP_VERSION_MINOR (OPGP_VERSION & 0xff)
#define PIPGP_VERSION 0x0106
#define PIPGP_VERSION 0x010A
#define PIPGP_VERSION_MAJOR ((PIPGP_VERSION >> 8) & 0xff)
#define PIPGP_VERSION_MINOR (PIPGP_VERSION & 0xff)

View File

@@ -0,0 +1 @@
from card_test_check_card import *

View File

@@ -0,0 +1 @@
from card_test_empty_card import *

View File

@@ -0,0 +1 @@
from card_test_set_attr import *

View File

@@ -0,0 +1,2 @@
from skip_if_kdfreq import *
from card_test_personalize_card_1 import *

View File

@@ -0,0 +1,2 @@
from skip_if_kdfreq import *
from card_test_personalize_card_2 import *

View File

@@ -0,0 +1,2 @@
from skip_if_kdfreq import *
from card_test_public_key_operations import *

View File

@@ -0,0 +1,2 @@
from skip_if_kdfreq import *
from card_test_ds_counter2 import *

View File

@@ -0,0 +1,24 @@
"""
test_005_keygen.py - test key generation
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from skip_if_kdfreq import *
from card_test_keygen import *

View File

@@ -0,0 +1,24 @@
"""
test_005_keygen.py - test key generation
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from skip_if_kdfreq import *
from card_test_public_key_operations_kg import *

View File

@@ -0,0 +1,24 @@
"""
test_005_keygen.py - test key generation
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from skip_if_kdfreq import *
from card_test_ds_counter1 import *

View File

@@ -0,0 +1,2 @@
from skip_if_kdfreq import *
from card_test_personalize_reset import *

View File

@@ -0,0 +1,2 @@
from skip_if_kdfreq import *
from card_test_remove_keys import *

View File

@@ -0,0 +1,2 @@
from skip_if_kdfreq import *
from card_test_reset_pw3 import *

View File

@@ -0,0 +1,25 @@
"""
test_005_adminless_kdfnone.py - test admin-less mode
Copyright (C) 2016, 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from skip_if_kdfreq import *
from skip_gnuk_only_tests import *
from card_test_personalize_admin_less_1 import *

View File

@@ -0,0 +1,25 @@
"""
test_005_adminless_kdfnone.py - test admin-less mode
Copyright (C) 2016, 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from skip_if_kdfreq import *
from skip_gnuk_only_tests import *
from card_test_public_key_operations_alt import *

View File

@@ -0,0 +1,25 @@
"""
test_005_adminless_kdfnone.py - test admin-less mode
Copyright (C) 2016, 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from skip_if_kdfreq import *
from skip_gnuk_only_tests import *
from card_test_ds_counter1 import *

View File

@@ -0,0 +1,25 @@
"""
test_005_adminless_kdfnone.py - test admin-less mode
Copyright (C) 2016, 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from skip_if_kdfreq import *
from skip_gnuk_only_tests import *
from card_test_personalize_admin_less_2 import *

View File

@@ -0,0 +1,25 @@
"""
test_005_adminless_kdfnone.py - test admin-less mode
Copyright (C) 2016, 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from skip_if_kdfreq import *
from skip_gnuk_only_tests import *
from card_test_personalize_reset import *

View File

@@ -0,0 +1,25 @@
"""
test_005_adminless_kdfnone.py - test admin-less mode
Copyright (C) 2016, 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from skip_if_kdfreq import *
from skip_gnuk_only_tests import *
from card_test_remove_keys import *

View File

@@ -0,0 +1,25 @@
"""
test_005_adminless_kdfnone.py - test admin-less mode
Copyright (C) 2016, 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from skip_if_kdfreq import *
from skip_gnuk_only_tests import *
from card_test_reset_pw3 import *

View File

@@ -0,0 +1,23 @@
"""
test_011_adminfull_kdffull.py - test KDF data object
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from card_test_kdf_full import *

View File

@@ -0,0 +1,23 @@
"""
test_011_adminfull_kdffull.py - test KDF data object
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from card_test_personalize_card_1 import *

View File

@@ -0,0 +1 @@
from card_test_ki_pko_dsc_rsa2k import *

View File

@@ -0,0 +1 @@
from card_test_ki_pko_dsc_nistp256r1 import *

View File

@@ -0,0 +1 @@
from card_test_ki_pko_dsc_brainpoolp256r1 import *

View File

@@ -0,0 +1 @@
from card_test_ki_pko_dsc_secp256k1 import *

View File

@@ -0,0 +1 @@
from card_test_ki_pko_dsc_nistp384r1 import *

View File

@@ -0,0 +1 @@
from card_test_ki_pko_dsc_brainpoolp384r1 import *

View File

@@ -0,0 +1 @@
from card_test_ki_pko_dsc_nistp521r1 import *

View File

@@ -0,0 +1 @@
from card_test_ki_pko_dsc_brainpoolp512r1 import *

View File

@@ -0,0 +1 @@
from card_test_ki_pko_dsc_curve25519 import *

View File

@@ -0,0 +1 @@
from card_test_kg_pko_dsc_rsa2k import *

View File

@@ -0,0 +1 @@
from card_test_ki_pko_dsc_nistp256r1 import *

View File

@@ -0,0 +1 @@
from card_test_kg_pko_dsc_brainpoolp256r1 import *

View File

@@ -0,0 +1 @@
from card_test_kg_pko_dsc_secp256k1 import *

View File

@@ -0,0 +1 @@
from card_test_kg_pko_dsc_nistp384r1 import *

View File

@@ -0,0 +1 @@
from card_test_kg_pko_dsc_brainpoolp384r1 import *

View File

@@ -0,0 +1 @@
from card_test_kg_pko_dsc_nistp521r1 import *

View File

@@ -0,0 +1 @@
from card_test_kg_pko_dsc_brainpoolp512r1 import *

View File

@@ -0,0 +1 @@
from card_test_ki_pko_dsc_curve25519 import *

View File

@@ -0,0 +1,15 @@
import pytest
from card_const import *
from constants_for_test import *
def test_setup_pw1_4(card):
r = card.change_passwd(1, FACTORY_PASSPHRASE_PW1, PW1_TEST4)
assert r
def test_verify_pw1_4(card):
v = card.verify(1, PW1_TEST4)
assert v
def test_verify_pw1_4_2(card):
v = card.verify(2, PW1_TEST4)
assert v

View File

@@ -0,0 +1,23 @@
"""
test_011_adminfull_kdffull.py - test KDF data object
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from card_test_personalize_reset import *

View File

@@ -0,0 +1,23 @@
"""
test_011_adminfull_kdffull.py - test KDF data object
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from card_test_remove_keys import *

View File

@@ -0,0 +1,23 @@
"""
test_011_adminfull_kdffull.py - test KDF data object
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from card_test_reset_pw3 import *

View File

@@ -0,0 +1 @@
from skip_if_no_kdf_support import *

View File

@@ -0,0 +1,24 @@
"""
test_016_adminfull_kdfsingle.py - test KDF data object
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from skip_gnuk_only_tests import *
from card_test_kdf_single import *

View File

@@ -0,0 +1,25 @@
"""
test_016_adminfull_kdfsingle.py - test KDF data object
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from skip_gnuk_only_tests import *
from card_test_personalize_card_1 import *
from card_test_personalize_card_2 import *

View File

@@ -0,0 +1,24 @@
"""
test_016_adminfull_kdfsingle.py - test KDF data object
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from skip_gnuk_only_tests import *
from card_test_public_key_operations import *

View File

@@ -0,0 +1,24 @@
"""
test_016_adminfull_kdfsingle.py - test KDF data object
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from skip_gnuk_only_tests import *
from card_test_ds_counter2 import *

View File

@@ -0,0 +1,24 @@
"""
test_016_adminfull_kdfsingle.py - test KDF data object
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from skip_gnuk_only_tests import *
from card_test_personalize_reset import *

View File

@@ -0,0 +1,24 @@
"""
test_016_adminfull_kdfsingle.py - test KDF data object
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from skip_gnuk_only_tests import *
from card_test_remove_keys import *

View File

@@ -0,0 +1,24 @@
"""
test_016_adminfull_kdfsingle.py - test KDF data object
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from skip_gnuk_only_tests import *
from card_test_reset_pw3 import *

View File

@@ -0,0 +1,2 @@
from skip_gnuk_only_tests import *
from card_test_personalize_admin_less_1 import *

View File

@@ -0,0 +1,2 @@
from skip_gnuk_only_tests import *
from card_test_public_key_operations_alt import *

View File

@@ -0,0 +1,2 @@
from skip_gnuk_only_tests import *
from card_test_ds_counter1 import *

View File

@@ -0,0 +1,2 @@
from skip_gnuk_only_tests import *
from card_test_personalize_admin_less_2 import *

View File

@@ -0,0 +1,2 @@
from skip_gnuk_only_tests import *
from card_test_personalize_reset import *

View File

@@ -0,0 +1,2 @@
from skip_gnuk_only_tests import *
from card_test_remove_keys import *

View File

@@ -0,0 +1,2 @@
from skip_gnuk_only_tests import *
from card_test_reset_pw3 import *

View File

@@ -0,0 +1,42 @@
"""
test_025_kdf_none.py - test KDF data object
Copyright (C) 2018 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from skip_if_no_kdf_support import *
from card_const import *
from constants_for_test import *
def test_verify_pw3(card):
v = card.verify(3, FACTORY_PASSPHRASE_PW3)
assert v
def test_kdf_put_none(card):
if card.is_yubikey:
KDF_SETUP_NONE=b"\x81\x01\x00"
else:
KDF_SETUP_NONE=b""
r = card.configure_kdf(KDF_SETUP_NONE)
assert r
def test_verify_pw3_1(card):
v = card.verify(3, FACTORY_PASSPHRASE_PW3)
assert v

View File

@@ -0,0 +1,23 @@
"""
test_091_reset_attr.py - test resetting key attributes
Copyright (C) 2021 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from card_test_reset_attr import *

View File

@@ -0,0 +1,148 @@
from time import time
from struct import pack
from hashlib import sha1, sha256
from pk_signed_mpi_with_libgcrypt import PK_libgcrypt
from card_const import KEY_ATTRIBUTES_ECDH_BRAINPOOLP256R1, KEY_ATTRIBUTES_ECDSA_BRAINPOOLP256R1
lg_bp256 = PK_libgcrypt(32, "brainpoolP256r1")
class PK_Crypto(object):
@staticmethod
def pk_from_pk_info(pk_info):
return pk_info[5:]
@staticmethod
def compute_digestinfo(msg):
return sha256(msg).digest()
@staticmethod
def enc_data(enc_info):
return b'\xa6\x46\x7f\x49\x43\x86\x41' + enc_info[1]
@staticmethod
def enc_check(enc_info, s):
point = enc_info[0]
# It's 04 || X || Y, extract X
return point[1:33] == s
def __init__(self, keyno=None, pk_info=None, data=None):
if keyno == None:
# Just for name space
return
self.keyno = keyno
self.for_encryption = (self.keyno == 1)
self.timestamp = pack('>I', int(time()))
if pk_info:
# Public part only (no private data) from card
self.q = pk_info[5:]
else:
# Private part (in big endian)
self.d = data[0]
self.q = data[1]
self.fpr = self.calc_fpr()
def calc_fpr(self):
m_len = 6 + 2 + 65
ver = b'\x04'
algo = b'\x12' if self.for_encryption else b'\x16'
m = b'\x99' + pack('>H', m_len) + ver + self.timestamp + algo \
+ pack('>H', 512+3) + self.q
return sha1(m).digest()
def build_privkey_template(self, is_yubikey):
openpgp_keyno = self.keyno + 1
if openpgp_keyno == 1:
keyspec = b'\xb6'
elif openpgp_keyno == 2:
keyspec = b'\xb8'
else:
keyspec = b'\xa4'
key_template = b'\x92\x20'
exthdr = keyspec + b'\x00' + b'\x7f\x48' + b'\x02' + key_template
suffix = b'\x5f\x48' + b'\x20'
return b'\x4d' + b'\x2a' + exthdr + suffix + self.d
def compute_signature(self, digestinfo):
return lg_bp256.call_pk_sign(self.d, digestinfo)
def verify_signature(self, digestinfo, sig):
return lg_bp256.call_pk_verify(self.q, digestinfo, sig)
def encrypt(self, plaintext):
# Do ECDH
return lg_bp256.call_pk_encrypt(self.q, plaintext)
def get_fpr(self):
return self.fpr
def get_timestamp(self):
return self.timestamp
def get_pk(self):
return self.q
key = [ None, None, None ]
# https://datatracker.ietf.org/doc/html/rfc6932#appendix-A.2
bp256_data0 = (
b'\x04\x1e\xb8\xb1\xe2\xbc\x68\x1b\xce\x8e\x39\x96\x3b\x2e\x9f\xc4'
b'\x15\xb0\x52\x83\x31\x3d\xd1\xa8\xbc\xc0\x55\xf1\x1a\xe4\x96\x99',
b'\x04'
b'\x78\x02\x84\x96\xb5\xec\xaa\xb3\xc8\xb6\xc1\x2e\x45\xdb\x1e\x02'
b'\xc9\xe4\xd2\x6b\x41\x13\xbc\x4f\x01\x5f\x60\xc5\xcc\xc0\xd2\x06'
b'\xa2\xae\x17\x62\xa3\x83\x1c\x1d\x20\xf0\x3f\x8d\x1e\x3c\x0c\x39'
b'\xaf\xe6\xf0\x9b\x4d\x44\xbb\xe8\x0c\xd1\x00\x98\x7b\x05\xf9\x2b'
)
bp256_data2 = (
b'\x06\xf5\x24\x0e\xac\xdb\x98\x37\xbc\x96\xd4\x82\x74\xc8\xaa\x83'
b'\x4b\x6c\x87\xba\x9c\xc3\xee\xdd\x81\xf9\x9a\x16\xb8\xd8\x04\xd3',
b'\x04'
b'\x8e\x07\xe2\x19\xba\x58\x89\x16\xc5\xb0\x6a\xa3\x0a\x2f\x46\x4c'
b'\x2f\x2a\xcf\xc1\x61\x0a\x3b\xe2\xfb\x24\x0b\x63\x53\x41\xf0\xdb'
b'\x14\x8e\xa1\xd7\xd1\xe7\xe5\x4b\x95\x55\xb6\xc9\xac\x90\x62\x9c'
b'\x18\xb6\x3b\xee\x5d\x7a\xa6\x94\x9e\xbb\xf4\x7b\x24\xfd\xe4\x0d'
)
# https://tools.ietf.org/html/rfc7027#appendix-A.1
bp256_data1 = (
b'\x81\xdb\x1e\xe1\x00\x15\x0f\xf2\xea\x33\x8d\x70\x82\x71\xbe\x38'
b'\x30\x0c\xb5\x42\x41\xd7\x99\x50\xf7\x7b\x06\x30\x39\x80\x4f\x1d',
b'\x04'
b'\x44\x10\x6e\x91\x3f\x92\xbc\x02\xa1\x70\x5d\x99\x53\xa8\x41\x4d'
b'\xb9\x5e\x1a\xaa\x49\xe8\x1d\x9e\x85\xf9\x29\xa8\xe3\x10\x0b\xe5'
b'\x8a\xb4\x84\x6f\x11\xca\xcc\xb7\x3c\xe4\x9c\xbd\xd1\x20\xf5\xa9'
b'\x00\xa6\x9f\xd3\x2c\x27\x22\x23\xf7\x89\xef\x10\xeb\x08\x9b\xdc'
)
key[0] = PK_Crypto(0, data=bp256_data0)
key[1] = PK_Crypto(1, data=bp256_data1)
key[2] = PK_Crypto(2, data=bp256_data2)
PLAIN_TEXT0=b"In this test, we verify card generated result by libgcrypt."
PLAIN_TEXT1=b"Signature is non-deterministic (it uses nonce K internally)."
PLAIN_TEXT2=b"We don't use brainpoolp256r1 test vectors (it specifies K of ECDSA)."
PLAIN_TEXT3=b"NOTE: Our test is not for ECDSA implementation itself."
ENCRYPT_TEXT0 = sha256(b"encrypt me please").digest()
ENCRYPT_TEXT1 = sha256(b"encrypt me please, another").digest()
ENCRYPT_TEXT2 = sha256(b"encrypt me please, the other").digest()
test_vector = {
'sign_0' : PLAIN_TEXT0,
'sign_1' : PLAIN_TEXT1,
'auth_0' : PLAIN_TEXT2,
'auth_1' : PLAIN_TEXT3,
'decrypt_0' : ENCRYPT_TEXT0,
'decrypt_1' : ENCRYPT_TEXT1,
'encrypt_0' : ENCRYPT_TEXT2,
}
brainpoolp256r1_pk = PK_Crypto()
brainpoolp256r1_pk.test_vector = test_vector
brainpoolp256r1_pk.key_list = key
brainpoolp256r1_pk.key_attr_list = [KEY_ATTRIBUTES_ECDSA_BRAINPOOLP256R1, KEY_ATTRIBUTES_ECDH_BRAINPOOLP256R1, KEY_ATTRIBUTES_ECDSA_BRAINPOOLP256R1]
brainpoolp256r1_pk.PK_Crypto = PK_Crypto

View File

@@ -0,0 +1,157 @@
from time import time
from struct import pack
from hashlib import sha1, sha384
from pk_signed_mpi_with_libgcrypt import PK_libgcrypt
from card_const import KEY_ATTRIBUTES_ECDH_BRAINPOOLP384R1, KEY_ATTRIBUTES_ECDSA_BRAINPOOLP384R1
lg_bp384 = PK_libgcrypt(48, "brainpoolP384r1")
class PK_Crypto(object):
@staticmethod
def pk_from_pk_info(pk_info):
return pk_info[5:]
@staticmethod
def compute_digestinfo(msg):
return sha384(msg).digest()
@staticmethod
def enc_data(enc_info):
return b'\xa6\x66\x7f\x49\x63\x86\x61' + enc_info[1]
@staticmethod
def enc_check(enc_info, s):
point = enc_info[0]
# It's 04 || X || Y, extract X
return point[1:49] == s
def __init__(self, keyno=None, pk_info=None, data=None):
if keyno == None:
# Just for name space
return
self.keyno = keyno
self.for_encryption = (self.keyno == 1)
self.timestamp = pack('>I', int(time()))
if pk_info:
# Public part only (no private data) from card
self.q = pk_info[5:]
else:
# Private part (in big endian)
self.d = data[0]
self.q = data[1]
self.fpr = self.calc_fpr()
def calc_fpr(self):
m_len = 6 + 2 + 97
ver = b'\x04'
algo = b'\x12' if self.for_encryption else b'\x16'
m = b'\x99' + pack('>H', m_len) + ver + self.timestamp + algo \
+ pack('>H', 768+3) + self.q
return sha1(m).digest()
def build_privkey_template(self, is_yubikey):
openpgp_keyno = self.keyno + 1
if openpgp_keyno == 1:
keyspec = b'\xb6'
elif openpgp_keyno == 2:
keyspec = b'\xb8'
else:
keyspec = b'\xa4'
key_template = b'\x92\x30'
exthdr = keyspec + b'\x00' + b'\x7f\x48' + b'\x02' + key_template
suffix = b'\x5f\x48' + b'\x30'
return b'\x4d' + b'\x3a' + exthdr + suffix + self.d
def compute_signature(self, digestinfo):
return lg_bp384.call_pk_sign(self.d, digestinfo)
def verify_signature(self, digestinfo, sig):
return lg_bp384.call_pk_verify(self.q, digestinfo, sig)
def encrypt(self, plaintext):
# Do ECDH
return lg_bp384.call_pk_encrypt(self.q, plaintext)
def get_fpr(self):
return self.fpr
def get_timestamp(self):
return self.timestamp
def get_pk(self):
return self.q
key = [ None, None, None ]
# https://datatracker.ietf.org/doc/html/rfc6932#appendix-A.3
bp384_data0 = (
b'\x01\x4e\xc0\x75\x5b\x78\x59\x4b\xa4\x7f\xb0\xa5\x6f\x61\x73\x04'
b'\x5b\x43\x31\xe7\x4b\xa1\xa6\xf4\x73\x22\xe7\x0d\x79\xd8\x28\xd9'
b'\x7e\x09\x58\x84\xca\x72\xb7\x3f\xda\xbd\x59\x10\xdf\x0f\xa7\x6a',
b'\x04'
b'\x45\xcb\x26\xe4\x38\x4d\xaf\x6f\xb7\x76\x88\x53\x07\xb9\xa3\x8b'
b'\x7a\xd1\xb5\xc6\x92\xe0\xc3\x2f\x01\x25\x33\x27\x78\xf3\xb8\xd3'
b'\xf5\x0c\xa3\x58\x09\x9b\x30\xde\xb5\xee\x69\xa9\x5c\x05\x8b\x4e'
b'\x81\x73\xa1\xc5\x4a\xff\xa7\xe7\x81\xd0\xe1\xe1\xd1\x2c\x0d\xc2'
b'\xb7\x4f\x4d\xf5\x8e\x4a\x4e\x3a\xf7\x02\x6c\x5d\x32\xdc\x53\x0a'
b'\x2c\xd8\x9c\x85\x9b\xb4\xb4\xb7\x68\x49\x7f\x49\xab\x8c\xc8\x59'
)
bp384_data2 = (
b'\x6b\x46\x1c\xb7\x9b\xd0\xea\x51\x9a\x87\xd6\x82\x88\x15\xd8\xce'
b'\x7c\xd9\xb3\xca\xa0\xb5\xa8\x26\x2c\xbc\xd5\x50\xa0\x15\xc9\x00'
b'\x95\xb9\x76\xf3\x52\x99\x57\x50\x6e\x12\x24\xa8\x61\x71\x1d\x54',
b'\x04'
b'\x01\xbf\x92\xa9\x2e\xe4\xbe\x8d\xed\x1a\x91\x11\x25\xc2\x09\xb0'
b'\x3f\x99\xe3\x16\x1c\xfc\xc9\x86\xdc\x77\x11\x38\x3f\xc3\x0a\xf9'
b'\xce\x28\xca\x33\x86\xd5\x9e\x2c\x8d\x72\xce\x1e\x7b\x46\x66\xe8'
b'\x32\x89\xc4\xa3\xa4\xfe\xe0\x35\xe3\x9b\xdb\x88\x5d\x50\x9d\x22'
b'\x4a\x14\x2f\xf9\xfb\xcc\x5c\xfe\x5c\xcb\xb3\x02\x68\xee\x47\x48'
b'\x7e\xd8\x04\x48\x58\xd3\x1d\x84\x8f\x7a\x95\xc6\x35\xa3\x47\xac'
)
# https://tools.ietf.org/html/rfc7027#appendix-A.2
bp384_data1 = (
b'\x1e\x20\xf5\xe0\x48\xa5\x88\x6f\x1f\x15\x7c\x74\xe9\x1b\xde\x2b'
b'\x98\xc8\xb5\x2d\x58\xe5\x00\x3d\x57\x05\x3f\xc4\xb0\xbd\x65\xd6'
b'\xf1\x5e\xb5\xd1\xee\x16\x10\xdf\x87\x07\x95\x14\x36\x27\xd0\x42',
b'\x04'
b'\x68\xb6\x65\xdd\x91\xc1\x95\x80\x06\x50\xcd\xd3\x63\xc6\x25\xf4'
b'\xe7\x42\xe8\x13\x46\x67\xb7\x67\xb1\xb4\x76\x79\x35\x88\xf8\x85'
b'\xab\x69\x8c\x85\x2d\x4a\x6e\x77\xa2\x52\xd6\x38\x0f\xca\xf0\x68'
b'\x55\xbc\x91\xa3\x9c\x9e\xc0\x1d\xee\x36\x01\x7b\x7d\x67\x3a\x93'
b'\x12\x36\xd2\xf1\xf5\xc8\x39\x42\xd0\x49\xe3\xfa\x20\x60\x74\x93'
b'\xe0\xd0\x38\xff\x2f\xd3\x0c\x2a\xb6\x7d\x15\xc8\x5f\x7f\xaa\x59'
)
key[0] = PK_Crypto(0, data=bp384_data0)
key[1] = PK_Crypto(1, data=bp384_data1)
key[2] = PK_Crypto(2, data=bp384_data2)
PLAIN_TEXT0=b"In this test, we verify card generated result by libgcrypt."
PLAIN_TEXT1=b"Signature is non-deterministic (it uses nonce K internally)."
PLAIN_TEXT2=b"We don't use brainpoolp384r1 test vectors (it specifies K of ECDSA)."
PLAIN_TEXT3=b"NOTE: Our test is not for ECDSA implementation itself."
ENCRYPT_TEXT0 = sha384(b"encrypt me please").digest()
ENCRYPT_TEXT1 = sha384(b"encrypt me please, another").digest()
ENCRYPT_TEXT2 = sha384(b"encrypt me please, the other").digest()
test_vector = {
'sign_0' : PLAIN_TEXT0,
'sign_1' : PLAIN_TEXT1,
'auth_0' : PLAIN_TEXT2,
'auth_1' : PLAIN_TEXT3,
'decrypt_0' : ENCRYPT_TEXT0,
'decrypt_1' : ENCRYPT_TEXT1,
'encrypt_0' : ENCRYPT_TEXT2,
}
brainpoolp384r1_pk = PK_Crypto()
brainpoolp384r1_pk.test_vector = test_vector
brainpoolp384r1_pk.key_list = key
brainpoolp384r1_pk.key_attr_list = [KEY_ATTRIBUTES_ECDSA_BRAINPOOLP384R1, KEY_ATTRIBUTES_ECDH_BRAINPOOLP384R1, KEY_ATTRIBUTES_ECDSA_BRAINPOOLP384R1]
brainpoolp384r1_pk.PK_Crypto = PK_Crypto

View File

@@ -0,0 +1,166 @@
from time import time
from struct import pack
from hashlib import sha1, sha512
from pk_signed_mpi_with_libgcrypt import PK_libgcrypt
from card_const import KEY_ATTRIBUTES_ECDH_BRAINPOOLP512R1, KEY_ATTRIBUTES_ECDSA_BRAINPOOLP512R1
lg_bp512 = PK_libgcrypt(64, "brainpoolP512r1")
class PK_Crypto(object):
@staticmethod
def pk_from_pk_info(pk_info):
return pk_info[7:]
@staticmethod
def compute_digestinfo(msg):
return sha512(msg).digest()
@staticmethod
def enc_data(enc_info):
return b'\xa6\x81\x88\x7f\x49\x81\x84\x86\x81\x81' + enc_info[1]
@staticmethod
def enc_check(enc_info, s):
point = enc_info[0]
# It's 04 || X || Y, extract X
return point[1:65] == s
def __init__(self, keyno=None, pk_info=None, data=None):
if keyno == None:
# Just for name space
return
self.keyno = keyno
self.for_encryption = (self.keyno == 1)
self.timestamp = pack('>I', int(time()))
if pk_info:
# Public part only (no private data) from card
self.q = pk_info[7:]
else:
# Private part (in big endian)
self.d = data[0]
self.q = data[1]
self.fpr = self.calc_fpr()
def calc_fpr(self):
m_len = 6 + 2 + 129
ver = b'\x04'
algo = b'\x12' if self.for_encryption else b'\x16'
m = b'\x99' + pack('>H', m_len) + ver + self.timestamp + algo \
+ pack('>H', 1024+3) + self.q
return sha1(m).digest()
def build_privkey_template(self, is_yubikey):
openpgp_keyno = self.keyno + 1
if openpgp_keyno == 1:
keyspec = b'\xb6'
elif openpgp_keyno == 2:
keyspec = b'\xb8'
else:
keyspec = b'\xa4'
key_template = b'\x92\x40'
exthdr = keyspec + b'\x00' + b'\x7f\x48' + b'\x02' + key_template
suffix = b'\x5f\x48' + b'\x40'
return b'\x4d' + b'\x4a' + exthdr + suffix + self.d
def compute_signature(self, digestinfo):
return lg_bp512.call_pk_sign(self.d, digestinfo)
def verify_signature(self, digestinfo, sig):
return lg_bp512.call_pk_verify(self.q, digestinfo, sig)
def encrypt(self, plaintext):
# Do ECDH
return lg_bp512.call_pk_encrypt(self.q, plaintext)
def get_fpr(self):
return self.fpr
def get_timestamp(self):
return self.timestamp
def get_pk(self):
return self.q
key = [ None, None, None ]
# https://datatracker.ietf.org/doc/html/rfc6932#appendix-A.4
bp512_data0 = (
b'\x63\x6b\x6b\xe0\x48\x2a\x6c\x1c\x41\xaa\x7a\xe7\xb2\x45\xe9\x83'
b'\x39\x2d\xb9\x4c\xec\xea\x26\x60\xa3\x79\xcf\xe1\x59\x55\x9e\x35'
b'\x75\x81\x82\x53\x91\x17\x5f\xc1\x95\xd2\x8b\xac\x0c\xf0\x3a\x78'
b'\x41\xa3\x83\xb9\x5c\x26\x2b\x98\x37\x82\x87\x4c\xce\x6f\xe3\x33',
b'\x04'
b'\x05\x62\xe6\x8b\x9a\xf7\xcb\xfd\x55\x65\xc6\xb1\x68\x83\xb7\x77'
b'\xff\x11\xc1\x99\x16\x1e\xcc\x42\x7a\x39\xd1\x7e\xc2\x16\x64\x99'
b'\x38\x95\x71\xd6\xa9\x94\x97\x7c\x56\xad\x82\x52\x65\x8b\xa8\xa1'
b'\xb7\x2a\xe4\x2f\x4f\xb7\x53\x21\x51\xaf\xc3\xef\x09\x71\xcc\xda'
b'\xa7\xca\x2d\x81\x91\xe2\x17\x76\xa8\x98\x60\xaf\xbc\x1f\x58\x2f'
b'\xaa\x30\x8d\x55\x1c\x1d\xc6\x13\x3a\xf9\xf9\xc3\xca\xd5\x99\x98'
b'\xd7\x00\x79\x54\x81\x40\xb9\x0b\x1f\x31\x1a\xfb\x37\x8a\xa8\x1f'
b'\x51\xb2\x75\xb2\xbe\x6b\x7d\xee\x97\x8e\xfc\x73\x43\xea\x64\x2e'
)
bp512_data2 = (
b'\x0a\xf4\xe7\xf6\xd5\x2e\xdd\x52\x90\x7b\xb8\xdb\xab\x39\x92\xa0'
b'\xbb\x69\x6e\xc1\x0d\xf1\x18\x92\xff\x20\x5b\x66\xd3\x81\xec\xe7'
b'\x23\x14\xe6\xa6\xea\x07\x9c\xea\x06\x96\x1d\xba\x5a\xe6\x42\x2e'
b'\xf2\xe9\xee\x80\x3a\x1f\x23\x6f\xb9\x6a\x17\x99\xb8\x6e\x5c\x8b',
b'\x04'
b'\x5a\x79\x54\xe3\x26\x63\xdf\xf1\x1a\xe2\x47\x12\xd8\x74\x19\xf2'
b'\x6b\x70\x8a\xc2\xb9\x28\x77\xd6\xbf\xee\x2b\xfc\x43\x71\x4d\x89'
b'\xbb\xdb\x6d\x24\xd8\x07\xbb\xd3\xae\xb7\xf0\xc3\x25\xf8\x62\xe8'
b'\xba\xde\x4f\x74\x63\x6b\x97\xea\xac\xe7\x39\xe1\x17\x20\xd3\x23'
b'\x96\xd1\x46\x21\xa9\x28\x3a\x1b\xed\x84\xde\x8d\xd6\x48\x36\xb2'
b'\xc0\x75\x8b\x11\x44\x11\x79\xdc\x0c\x54\xc0\xd4\x9a\x47\xc0\x38'
b'\x07\xd1\x71\xdd\x54\x4b\x72\xca\xae\xf7\xb7\xce\x01\xc7\x75\x3e'
b'\x2c\xad\x1a\x86\x1e\xca\x55\xa7\x19\x54\xee\x1b\xa3\x5e\x04\xbe'
)
# https://tools.ietf.org/html/rfc7027#appendix-A.3
bp512_data1 = (
b'\x16\x30\x2f\xf0\xdb\xbb\x5a\x8d\x73\x3d\xab\x71\x41\xc1\xb4\x5a'
b'\xcb\xc8\x71\x59\x39\x67\x7f\x6a\x56\x85\x0a\x38\xbd\x87\xbd\x59'
b'\xb0\x9e\x80\x27\x96\x09\xff\x33\x3e\xb9\xd4\xc0\x61\x23\x1f\xb2'
b'\x6f\x92\xee\xb0\x49\x82\xa5\xf1\xd1\x76\x4c\xad\x57\x66\x54\x22',
b'\x04'
b'\x0a\x42\x05\x17\xe4\x06\xaa\xc0\xac\xdc\xe9\x0f\xcd\x71\x48\x77'
b'\x18\xd3\xb9\x53\xef\xd7\xfb\xec\x5f\x7f\x27\xe2\x8c\x61\x49\x99'
b'\x93\x97\xe9\x1e\x02\x9e\x06\x45\x7d\xb2\xd3\xe6\x40\x66\x8b\x39'
b'\x2c\x2a\x7e\x73\x7a\x7f\x0b\xf0\x44\x36\xd1\x16\x40\xfd\x09\xfd'
b'\x72\xe6\x88\x2e\x8d\xb2\x8a\xad\x36\x23\x7c\xd2\x5d\x58\x0d\xb2'
b'\x37\x83\x96\x1c\x8d\xc5\x2d\xfa\x2e\xc1\x38\xad\x47\x2a\x0f\xce'
b'\xf3\x88\x7c\xf6\x2b\x62\x3b\x2a\x87\xde\x5c\x58\x83\x01\xea\x3e'
b'\x5f\xc2\x69\xb3\x73\xb6\x07\x24\xf5\xe8\x2a\x6a\xd1\x47\xfd\xe7'
)
key[0] = PK_Crypto(0, data=bp512_data0)
key[1] = PK_Crypto(1, data=bp512_data1)
key[2] = PK_Crypto(2, data=bp512_data2)
PLAIN_TEXT0=b"In this test, we verify card generated result by libgcrypt."
PLAIN_TEXT1=b"Signature is non-deterministic (it uses nonce K internally)."
PLAIN_TEXT2=b"We don't use brainpoolp512r1 test vectors (it specifies K of ECDSA)."
PLAIN_TEXT3=b"NOTE: Our test is not for ECDSA implementation itself."
ENCRYPT_TEXT0 = sha512(b"encrypt me please").digest()
ENCRYPT_TEXT1 = sha512(b"encrypt me please, another").digest()
ENCRYPT_TEXT2 = sha512(b"encrypt me please, the other").digest()
test_vector = {
'sign_0' : PLAIN_TEXT0,
'sign_1' : PLAIN_TEXT1,
'auth_0' : PLAIN_TEXT2,
'auth_1' : PLAIN_TEXT3,
'decrypt_0' : ENCRYPT_TEXT0,
'decrypt_1' : ENCRYPT_TEXT1,
'encrypt_0' : ENCRYPT_TEXT2,
}
brainpoolp512r1_pk = PK_Crypto()
brainpoolp512r1_pk.test_vector = test_vector
brainpoolp512r1_pk.key_list = key
brainpoolp512r1_pk.key_attr_list = [KEY_ATTRIBUTES_ECDSA_BRAINPOOLP512R1, KEY_ATTRIBUTES_ECDH_BRAINPOOLP512R1, KEY_ATTRIBUTES_ECDSA_BRAINPOOLP512R1]
brainpoolp512r1_pk.PK_Crypto = PK_Crypto

7
tests/build-in-docker.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/bash -eu
source tests/docker_env.sh
#run_in_docker rm -rf CMakeFiles
run_in_docker mkdir -p build_in_docker
run_in_docker -w "$PWD/build_in_docker" cmake -DENABLE_EMULATION=1 ..
run_in_docker -w "$PWD/build_in_docker" make -j ${NUM_PROC}

46
tests/card_const.py Normal file
View File

@@ -0,0 +1,46 @@
FACTORY_PASSPHRASE_PW1=b"123456"
FACTORY_PASSPHRASE_PW3=b"12345678"
KEY_ATTRIBUTES_RSA4K=b"\x01\x10\x00\x00\x20\x00"
KEY_ATTRIBUTES_RSA2K=b"\x01\x08\x00\x00\x20\x00"
KEY_ATTRIBUTES_RSA2K_ALT=b"\x01\x08\x00\x00\x11\x00"
KEY_ATTRIBUTES_ECDH_NISTP256R1=b"\x12\x2a\x86\x48\xce\x3d\x03\x01\x07"
KEY_ATTRIBUTES_ECDH_NISTP384R1=b"\x12\x2b\x81\x04\x00\x22"
KEY_ATTRIBUTES_ECDH_NISTP521R1=b"\x12\x2b\x81\x04\x00\x23"
KEY_ATTRIBUTES_ECDH_BRAINPOOLP256R1=b"\x12\x2b\x24\x03\x03\x02\x08\x01\x01\x07"
KEY_ATTRIBUTES_ECDH_BRAINPOOLP384R1=b"\x12\x2b\x24\x03\x03\x02\x08\x01\x01\x0b"
KEY_ATTRIBUTES_ECDH_BRAINPOOLP512R1=b"\x12\x2b\x24\x03\x03\x02\x08\x01\x01\x0d"
KEY_ATTRIBUTES_ECDSA_NISTP256R1=b"\x13\x2a\x86\x48\xce\x3d\x03\x01\x07"
KEY_ATTRIBUTES_ECDSA_NISTP384R1=b"\x13\x2b\x81\x04\x00\x22"
KEY_ATTRIBUTES_ECDSA_NISTP521R1=b"\x13\x2b\x81\x04\x00\x23"
KEY_ATTRIBUTES_ECDSA_BRAINPOOLP256R1=b"\x13\x2b\x24\x03\x03\x02\x08\x01\x01\x07"
KEY_ATTRIBUTES_ECDSA_BRAINPOOLP384R1=b"\x13\x2b\x24\x03\x03\x02\x08\x01\x01\x0b"
KEY_ATTRIBUTES_ECDSA_BRAINPOOLP512R1=b"\x13\x2b\x24\x03\x03\x02\x08\x01\x01\x0d"
KEY_ATTRIBUTES_CV25519=b"\x12\x2b\x06\x01\x04\x01\x97\x55\x01\x05\x01"
KEY_ATTRIBUTES_ED25519=b"\x16\x2b\x06\x01\x04\x01\xda\x47\x0f\x01"
KEY_ATTRIBUTES_ECDH_SECP256K1=b"\x12\x2b\x81\x04\x00\x0a"
KEY_ATTRIBUTES_ECDSA_SECP256K1=b"\x13\x2b\x81\x04\x00\x0a"
def default_key(card, is_for_encr):
if card.is_gnuk:
if is_for_encr:
return KEY_ATTRIBUTES_CV25519
else:
return KEY_ATTRIBUTES_ED25519
else:
# if is_for_encr:
# return KEY_ATTRIBUTES_ECDH_BRAINPOOLP512R1
# else:
# return KEY_ATTRIBUTES_ECDSA_BRAINPOOLP512R1
return KEY_ATTRIBUTES_RSA2K
def alt_key(card, is_for_encr):
if card.is_gnuk or card.is_yubikey:
if is_for_encr:
return KEY_ATTRIBUTES_ECDH_SECP256K1
else:
return KEY_ATTRIBUTES_ECDSA_SECP256K1
else:
if is_for_encr:
return KEY_ATTRIBUTES_ECDH_BRAINPOOLP256R1
else:
return KEY_ATTRIBUTES_ECDSA_BRAINPOOLP256R1

67
tests/card_reader.py Normal file
View File

@@ -0,0 +1,67 @@
"""
card_reader.py - a library for smartcard reader
Copyright (C) 2016, 2017, 2019 Free Software Initiative of Japan
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from struct import pack
from binascii import hexlify
import sys
try:
from smartcard.CardType import AnyCardType
from smartcard.CardRequest import CardRequest
from smartcard.Exceptions import CardRequestTimeoutException, CardConnectionException
except ModuleNotFoundError:
print('ERROR: smarctard module not found! Install pyscard package.\nTry with `pip install pyscard`')
sys.exit(-1)
class CardReader(object):
def __init__(self):
"""
__init__() -> None
Initialize the reader
device: usb.core.Device object.
"""
cardtype = AnyCardType()
try:
# request card insertion
cardrequest = CardRequest(timeout=10, cardType=cardtype)
self.__card = cardrequest.waitforcard()
# connect to the card and perform a few transmits
self.__card.connection.connect()
except CardRequestTimeoutException:
raise Exception('time-out: no card inserted during last 10s')
def reset_device(self):
self.__card.connection.reconnect()
def send_cmd(self, cmd):
response, sw1, sw2 = self.__card.connection.transmit(list(bytearray(cmd)))
return bytes(response + [sw1,sw2])
def ccid_power_off(self):
self.__card.connection.disconnect()
def get_ccid_device():
return CardReader()

View File

@@ -0,0 +1,41 @@
"""
card_test_set_attr.py - test setting key attributes
Copyright (C) 2021 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from card_const import *
import pytest
class Test_Set_ATTRS(object):
#def test_verify_pw3(self, card):
# v = card.verify(3, FACTORY_PASSPHRASE_PW3)
# assert v
def test_keyattr_set_1(self, card, pk):
r = card.cmd_put_data(0x00, 0xc1, pk.key_attr_list[0])
assert r
def test_keyattr_set_2(self, card, pk):
r = card.cmd_put_data(0x00, 0xc2, pk.key_attr_list[1])
assert r
def test_keyattr_set_3(self, card, pk):
r = card.cmd_put_data(0x00, 0xc3, pk.key_attr_list[2])
assert r

View File

@@ -0,0 +1,184 @@
"""
card_test_personalize_card.py - test personalizing card
Copyright (C) 2016, 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from struct import pack
from re import match, DOTALL
from util import *
from card_const import *
from constants_for_test import *
import pytest
class Test_Card_Personalize_Card_2(object):
def test_import_key_1(self, card, pk):
t = pk.key_list[0].build_privkey_template(card.is_yubikey)
r = card.cmd_put_data_odd(0x3f, 0xff, t)
assert r
def test_import_key_2(self, card, pk):
t = pk.key_list[1].build_privkey_template(card.is_yubikey)
r = card.cmd_put_data_odd(0x3f, 0xff, t)
assert r
def test_import_key_3(self, card, pk):
t = pk.key_list[2].build_privkey_template(card.is_yubikey)
r = card.cmd_put_data_odd(0x3f, 0xff, t)
assert r
def test_fingerprint_1_put(self, card, pk):
fpr1 = pk.key_list[0].get_fpr()
r = card.cmd_put_data(0x00, 0xc7, fpr1)
assert r
def test_fingerprint_2_put(self, card, pk):
fpr2 = pk.key_list[1].get_fpr()
r = card.cmd_put_data(0x00, 0xc8, fpr2)
assert r
def test_fingerprint_3_put(self, card, pk):
fpr3 = pk.key_list[2].get_fpr()
r = card.cmd_put_data(0x00, 0xc9, fpr3)
assert r
def test_timestamp_1_put(self, card, pk):
timestamp1 = pk.key_list[0].get_timestamp()
r = card.cmd_put_data(0x00, 0xce, timestamp1)
assert r
def test_timestamp_2_put(self, card, pk):
timestamp2 = pk.key_list[1].get_timestamp()
r = card.cmd_put_data(0x00, 0xcf, timestamp2)
assert r
def test_timestamp_3_put(self, card, pk):
timestamp3 = pk.key_list[2].get_timestamp()
r = card.cmd_put_data(0x00, 0xd0, timestamp3)
assert r
def test_ds_counter_0(self, card):
c = get_data_object(card, 0x7a)
assert c == b'\x93\x03\x00\x00\x00'
def test_pw1_status(self, card):
s = get_data_object(card, 0xc4)
assert match(b'\x01...\x03[\x00\x03]\x03', s, DOTALL)
def test_app_data(self, card):
if card.is_yubikey:
pytest.skip("Yubikey raises 6e82 error for composed data object 6E")
else:
app_data = get_data_object(card, 0x6e)
hist_len = app_data[20]
# FIXME: parse and check DO of C0, C1, C2, C3, C4, and C6
assert app_data[0:8] == b"\x4f\x10\xd2\x76\x00\x01\x24\x01" and \
app_data[18:18+2] == b"\x5f\x52"
def test_public_key_1(self, card, pk):
pk_info = card.cmd_get_public_key(1)
assert pk.key_list[0].get_pk() == pk.pk_from_pk_info(pk_info)
def test_public_key_2(self, card, pk):
pk_info = card.cmd_get_public_key(2)
assert pk.key_list[1].get_pk() == pk.pk_from_pk_info(pk_info)
def test_public_key_3(self, card, pk):
pk_info = card.cmd_get_public_key(3)
assert pk.key_list[2].get_pk() == pk.pk_from_pk_info(pk_info)
def test_setup_pw1_0(self, card):
r = card.change_passwd(1, FACTORY_PASSPHRASE_PW1, PW1_TEST0)
assert r
def test_verify_pw1_0(self, card):
v = card.verify(1, PW1_TEST0)
assert v
def test_verify_pw1_0_2(self, card):
v = card.verify(2, PW1_TEST0)
assert v
def test_setup_pw1_1(self, card):
r = card.change_passwd(1, PW1_TEST0, PW1_TEST1)
assert r
def test_verify_pw1_1(self, card):
v = card.verify(1, PW1_TEST1)
assert v
def test_verify_pw1_1_2(self, card):
v = card.verify(2, PW1_TEST1)
assert v
def test_setup_reset_code(self, card):
r = card.setup_reset_code(RESETCODE_TEST)
assert r
def test_reset_code(self, card):
r = card.reset_passwd_by_resetcode(RESETCODE_TEST, PW1_TEST2)
assert r
def test_verify_pw1_2(self, card):
v = card.verify(1, PW1_TEST2)
assert v
def test_verify_pw1_2_2(self, card):
v = card.verify(2, PW1_TEST2)
assert v
def test_setup_pw3_1(self, card):
r = card.change_passwd(3, PW3_TEST0, PW3_TEST1)
assert r
def test_verify_pw3_1(self, card):
v = card.verify(3, PW3_TEST1)
assert v
def test_reset_userpass_admin(self, card):
r = card.reset_passwd_by_admin(PW1_TEST3)
assert r
def test_verify_pw1_3(self, card):
v = card.verify(1, PW1_TEST3)
assert v
def test_verify_pw1_3_2(self, card):
v = card.verify(2, PW1_TEST3)
assert v
def test_setup_pw1_4(self, card):
r = card.change_passwd(1, PW1_TEST3, FACTORY_PASSPHRASE_PW1)
assert r
def test_verify_pw1_4(self, card):
v = card.verify(1, FACTORY_PASSPHRASE_PW1)
assert v
def test_verify_pw1_4_2(self, card):
v = card.verify(2, FACTORY_PASSPHRASE_PW1)
assert v
def test_setup_pw3_2(self, card):
r = card.change_passwd(3, PW3_TEST1, PW3_TEST0)
assert r
def test_verify_pw3_2(self, card):
v = card.verify(3, PW3_TEST0)
assert v

View File

@@ -0,0 +1,99 @@
"""
card_test_keygen.py - test key generation
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from binascii import hexlify
from card_const import *
from constants_for_test import *
import pytest
class Test_Card_Keygen(object):
def test_000_setup_pw1_0(self, card):
if card.is_gnuk:
pytest.skip("Gnuk doesn't support passphrase with no key")
else:
r = card.change_passwd(1, FACTORY_PASSPHRASE_PW1, PW1_TEST4)
assert r
def test_verify_pw1_0_1(self, card):
if card.is_gnuk:
pytest.skip("Gnuk doesn't support passphrase with no key")
else:
v = card.verify(1, PW1_TEST4)
assert v
def test_verify_pw1_0_2(self, card):
if card.is_gnuk:
pytest.skip("Gnuk doesn't support passphrase with no key")
else:
v = card.verify(2, PW1_TEST4)
assert v
def test_keygen_1(self, card, pk):
pk_info = card.cmd_genkey(1)
k = pk.PK_Crypto(keyno=0, pk_info=pk_info)
r = card.cmd_put_data(0x00, 0xc7, k.get_fpr())
if r:
r = card.cmd_put_data(0x00, 0xce, k.get_timestamp())
assert r
def test_keygen_2(self, card, pk):
pk_info = card.cmd_genkey(2)
k = pk.PK_Crypto(keyno=1, pk_info=pk_info)
r = card.cmd_put_data(0x00, 0xc8, k.get_fpr())
if r:
r = card.cmd_put_data(0x00, 0xcf, k.get_timestamp())
assert r
def test_keygen_3(self, card, pk):
pk_info = card.cmd_genkey(3)
k = pk.PK_Crypto(keyno=2, pk_info=pk_info)
r = card.cmd_put_data(0x00, 0xc9, k.get_fpr())
if r:
r = card.cmd_put_data(0x00, 0xd0, k.get_timestamp())
assert r
def test_setup_pw1_0(self, card):
if card.is_gnuk:
r = card.change_passwd(1, FACTORY_PASSPHRASE_PW1, PW1_TEST4)
assert r
else:
pytest.skip("Gnuk resets passphrase on keygen, so, change passwd")
def test_verify_pw1(self, card):
v = card.verify(1, PW1_TEST4)
assert v
def test_verify_pw1_2(self, card):
v = card.verify(2, PW1_TEST4)
assert v
def test_setup_pw1_reset(self, card):
r = card.change_passwd(1, PW1_TEST4, FACTORY_PASSPHRASE_PW1)
assert r
def test_verify_pw1_reset_1(self, card):
v = card.verify(1, FACTORY_PASSPHRASE_PW1)
assert v
def test_verify_pw1_reset_2(self, card):
v = card.verify(2, FACTORY_PASSPHRASE_PW1)
assert v

62
tests/card_test_2_pkop.py Normal file
View File

@@ -0,0 +1,62 @@
"""
card_test_public_key_operations.py - test the sign/dec/auth
Copyright (C) 2021 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from struct import pack
from re import match, DOTALL
from util import *
from card_const import *
from constants_for_test import *
class Test_Card_PK_OPs(object):
def test_sign_0(self, card, pk):
digestinfo = pk.compute_digestinfo(pk.test_vector['sign_0'])
sig = card.cmd_pso(0x9e, 0x9a, digestinfo)
r = pk.key_list[0].verify_signature(digestinfo, sig)
assert r
def test_sign_1(self, card, pk):
digestinfo = pk.compute_digestinfo(pk.test_vector['sign_1'])
sig = card.cmd_pso(0x9e, 0x9a, digestinfo)
r = pk.key_list[0].verify_signature(digestinfo, sig)
assert r
def test_decrypt_0(self, card, pk):
encrypted_data = pk.key_list[1].encrypt(pk.test_vector['decrypt_0'])
r = card.cmd_pso(0x80, 0x86, pk.enc_data(encrypted_data))
assert pk.enc_check(encrypted_data, r)
def test_decrypt_1(self, card, pk):
encrypted_data = pk.key_list[1].encrypt(pk.test_vector['decrypt_1'])
r = card.cmd_pso(0x80, 0x86, pk.enc_data(encrypted_data))
assert pk.enc_check(encrypted_data, r)
def test_auth_0(self, card, pk):
digestinfo = pk.compute_digestinfo(pk.test_vector['auth_0'])
sig = card.cmd_internal_authenticate(digestinfo)
r = pk.key_list[2].verify_signature(digestinfo, sig)
assert r
def test_auth_1(self, card, pk):
digestinfo = pk.compute_digestinfo(pk.test_vector['auth_1'])
sig = card.cmd_internal_authenticate(digestinfo)
r = pk.key_list[2].verify_signature(digestinfo, sig)
assert r

View File

@@ -0,0 +1,51 @@
"""
card_test_public_key_operations_kg.py - test the sign/dec/auth
Copyright (C) 2021 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from struct import pack
from re import match, DOTALL
from util import *
from card_const import *
from constants_for_test import *
class Test_Card_PK_OPs_KG(object):
def test_signature_sigkey(self, card, pk):
pk_info = card.cmd_get_public_key(1)
k = pk.PK_Crypto(keyno=0, pk_info=pk_info)
digestinfo = pk.compute_digestinfo(pk.test_vector['sign_0'])
sig_bytes = card.cmd_pso(0x9e, 0x9a, digestinfo)
r = k.verify_signature(digestinfo, sig_bytes)
assert r
def test_decryption(self, card, pk):
pk_info = card.cmd_get_public_key(2)
k = pk.PK_Crypto(keyno=1, pk_info=pk_info)
encrypted_data = k.encrypt(pk.test_vector['encrypt_0'])
r = card.cmd_pso(0x80, 0x86, pk.enc_data(encrypted_data))
assert pk.enc_check(encrypted_data, r)
def test_signature_authkey(self, card, pk):
pk_info = card.cmd_get_public_key(3)
k = pk.PK_Crypto(2, pk_info=pk_info)
digestinfo = pk.compute_digestinfo(pk.test_vector['sign_0'])
sig_bytes = card.cmd_internal_authenticate(digestinfo)
r = k.verify_signature(digestinfo, sig_bytes)
assert r

View File

@@ -0,0 +1,31 @@
"""
card_test_ds_counter.py - test the result of DS counter
Copyright (C) 2021 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from struct import pack
from re import match, DOTALL
from util import *
from constants_for_test import *
class Test_Card_DS_Counter(object):
def test_ds_counter_1(self, card):
c = get_data_object(card, 0x7a)
assert c == b'\x93\x03\x00\x00\x01'

View File

@@ -0,0 +1,31 @@
"""
card_test_ds_counter.py - test the result of DS counter
Copyright (C) 2021 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from struct import pack
from re import match, DOTALL
from util import *
from constants_for_test import *
class Test_Card_DS_Counter(object):
def test_ds_counter_2(self, card):
c = get_data_object(card, 0x7a)
assert c == b'\x93\x03\x00\x00\x02'

View File

@@ -0,0 +1,80 @@
"""
card_test_check_card.py - test configuration of card
Copyright (C) 2021 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from binascii import hexlify
from util import *
from card_const import *
import pytest
def print_key_attr(keyno, p):
algo = p[0]
if algo == 0x01 and len(p) >= 6:
# RSA
nbits = (p[1] << 8) | p[2]
ebits = (p[3] << 8) | p[4]
flags = p[5]
print(keyno)
print("RSA")
print(nbits)
print(ebits)
print(flags)
elif len(p) >= 2 and (algo == 0x12 or algo == 0x13 or algo == 0x16):
# ECC
if p[-1] == 0x00 or p[-1] == 0x00:
flag = True # Pubkey required
curve = p[1:-1]
else:
flag = False # Pubkey is not required
curve = p[1:]
print(keyno)
print("ECDSA" if algo == 0x13 else "ECDH" if algo == 0x12 else "EDDSA")
print(curve)
print(flag)
else:
print("Unknown algo attr")
def parse_list_of_key_attributes(card, p, printout=False):
while True:
if len(p) < 2:
break
tag = p[0]
length = p[1]
if tag < 0xc1:
p = p[2:]
continue
if tag == 0xda:
keyno = 0x81
else:
keyno = tag - 0xc1 + 1
if len(p) - 2 < length:
break
attr = p[2:2+length]
if printout:
print_key_attr(keyno, attr)
p = p[2+length:]
if keyno >= 1 and keyno <= 3:
card.add_to_key_attrlist(keyno - 1, attr)
def test_list_of_key_attributes(card):
a = get_data_object(card, 0xfa)
parse_list_of_key_attributes(card, a, True)

View File

@@ -0,0 +1,31 @@
"""
card_test_ds_counter.py - test the result of DS counter
Copyright (C) 2021 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from struct import pack
from re import match, DOTALL
from util import *
from constants_for_test import *
class Test_Card_DS_Counter(object):
def test_ds_counter_1(self, card):
c = get_data_object(card, 0x7a)
assert c == b'\x93\x03\x00\x00\x01'

View File

@@ -0,0 +1,31 @@
"""
card_test_ds_counter.py - test the result of DS counter
Copyright (C) 2021 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from struct import pack
from re import match, DOTALL
from util import *
from constants_for_test import *
class Test_Card_DS_Counter(object):
def test_ds_counter_2(self, card):
c = get_data_object(card, 0x7a)
assert c == b'\x93\x03\x00\x00\x02'

View File

@@ -0,0 +1,272 @@
"""
test_empty_card.py - test empty card
Copyright (C) 2016, 2018 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from binascii import hexlify
from re import match, DOTALL
from struct import pack
from util import *
from card_const import *
import pytest
EMPTY_60=bytes(60)
def test_login(card):
login = get_data_object(card, 0x5e)
assert check_null(login)
"""
def test_name(card):
name = get_data_object(card, 0x5b)
assert check_null(name)
def test_lang(card):
lang = get_data_object(card, 0x5f2d)
assert check_null(lang)
def test_sex(card):
sex = get_data_object(card, 0x5f35)
assert check_null(sex)
"""
def test_name_lang_sex(card):
name = b""
lang = b""
lang_de = b"de"
sex = b"9"
sex_alt = b"0"
expected = b'\x5b' + pack('B', len(name)) + name \
+ b'\x5f\x2d' + pack('B', len(lang)) + lang \
+ b'\x5f\x35' + pack('B', len(sex)) + sex_alt
expected_de = b'\x5b' + pack('B', len(name)) + name \
+ b'\x5f\x2d' + pack('B', len(lang_de)) + lang_de \
+ b'\x5f\x35' + pack('B', len(sex)) + sex
expected_de_alt = b'\x5b' + pack('B', len(name)) + name \
+ b'\x5f\x2d' + pack('B', len(lang_de)) + lang_de \
+ b'\x5f\x35' + pack('B', len(sex_alt)) + sex_alt
name_lang_sex = get_data_object(card, 0x65)
assert name_lang_sex == b'' or name_lang_sex == expected \
or name_lang_sex == expected_de or name_lang_sex == expected_de_alt
def test_app_data(card):
if card.is_yubikey:
pytest.skip("Yubikey raises 6e82 error for composed data object 6E")
else:
app_data = get_data_object(card, 0x6e)
hist_len = app_data[20]
# FIXME: parse and check DO of C0, C1, C2, C3, C4, and C6
assert app_data[0:8] == b"\x4f\x10\xd2\x76\x00\x01\x24\x01" and \
app_data[18:18+2] == b"\x5f\x52"
def test_url(card):
url = get_data_object(card, 0x5f50)
assert check_null(url)
def test_ds_counter(card):
c = get_data_object(card, 0x7a)
assert c == b'\x93\x03\x00\x00\x00'
def test_pw1_status(card):
s = get_data_object(card, 0xc4)
assert match(b'....\x03[\x00\x03]\x03', s, DOTALL)
def test_fingerprint_all(card):
if card.is_yubikey:
pytest.skip("Yubikey returns 6B00 when no key")
else:
fprlist = get_data_object(card, 0xC5)
assert fprlist == None or fprlist == EMPTY_60
def test_fingerprint_1(card):
if card.is_yubikey:
pytest.skip("Yubikey returns 6B00 when no key")
else:
fpr = get_data_object(card, 0xC7)
assert check_null(fpr)
def test_fingerprint_2(card):
if card.is_yubikey:
pytest.skip("Yubikey returns 6B00 when no key")
else:
fpr = get_data_object(card, 0xC8)
assert check_null(fpr)
def test_fingerprint_3(card):
if card.is_yubikey:
pytest.skip("Yubikey returns 6B00 when no key")
else:
fpr = get_data_object(card, 0xC9)
assert check_null(fpr)
def test_ca_fingerprint_all(card):
if card.is_yubikey:
pytest.skip("Yubikey returns 6B00 when no key")
else:
cafprlist = get_data_object(card, 0xC6)
assert cafprlist == None or cafprlist == EMPTY_60
def test_ca_fingerprint_1(card):
if card.is_yubikey:
pytest.skip("Yubikey returns 6B00 when no key")
else:
cafp = get_data_object(card, 0xCA)
assert check_null(cafp)
def test_ca_fingerprint_2(card):
if card.is_yubikey:
pytest.skip("Yubikey returns 6B00 when no key")
else:
cafp = get_data_object(card, 0xCB)
assert check_null(cafp)
def test_ca_fingerprint_3(card):
if card.is_yubikey:
pytest.skip("Yubikey returns 6B00 when no key")
else:
cafp = get_data_object(card, 0xCC)
assert check_null(cafp)
def test_timestamp_all(card):
if card.is_yubikey:
pytest.skip("Yubikey returns 6B00 when no key")
else:
t = get_data_object(card, 0xCD)
assert t == None or t == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
def test_timestamp_1(card):
if card.is_yubikey:
pytest.skip("Yubikey returns 6B00 when no key")
else:
t = get_data_object(card, 0xCE)
assert check_null(t)
def test_timestamp_2(card):
if card.is_yubikey:
pytest.skip("Yubikey returns 6B00 when no key")
else:
t = get_data_object(card, 0xCF)
assert check_null(t)
def test_timestamp_3(card):
if card.is_yubikey:
pytest.skip("Yubikey returns 6B00 when no key")
else:
t = get_data_object(card, 0xD0)
assert check_null(t)
def test_verify_pw1_1(card):
v = card.cmd_verify(1, FACTORY_PASSPHRASE_PW1)
assert v
def test_verify_pw1_2(card):
v = card.cmd_verify(2, FACTORY_PASSPHRASE_PW1)
assert v
def test_verify_pw3(card):
v = card.cmd_verify(3, FACTORY_PASSPHRASE_PW3)
assert v
def test_historical_bytes(card):
h = get_data_object(card, 0x5f52)
if card.is_yubikey:
assert h == b'\x00\x73\x00\x00\x80\x05\x90\x00' or \
h == b'\x00\x73\x00\x00\xe0\x05\x90\x00'
else:
assert h == b'\x001\xc5s\xc0\x01@\x05\x90\x00' or \
h == b'\x00\x31\x84\x73\x80\x01\x80\x00\x90\x00' or \
h == b'\x00\x31\x84\x73\x80\x01\x80\x05\x90\x00' or \
h == b'\x00\x31\xf5\x73\xc0\x01\x60\x05\x90\x00' or \
h == b'\x00\x31\x84\x73\x80\x01\xC0\x05\x90\x00'
def test_extended_capabilities(card):
if card.is_yubikey:
pytest.skip("Yubikey returns 6B00 when no key")
else:
a = get_data_object(card, 0xc0)
assert a == None or match(b'[\x70\x74\x75\x77]\x00\x00.[\x00\x08]\x00\x00\xff[\x00\x01][\x00\x01]', a)
def test_key_attributes_1(card):
if card.is_yubikey:
a = None
else:
a = get_data_object(card, 0xc1)
assert a == None or a == b'\x01\x08\x00\x00\x20\x00' or a == b'\x16\x2b\x06\x01\x04\x01\xda\x47\x0f\x01'
if not a:
a = KEY_ATTRIBUTES_RSA2K
card.save_algo_attribute(1, a)
if card.is_yubikey:
pytest.skip("Yubikey returns 6B00 when no key")
def test_key_attributes_2(card):
if card.is_yubikey:
a = None
else:
a = get_data_object(card, 0xc2)
assert a == None or a == b'\x01\x08\x00\x00\x20\x00' or a == b'\x12\x2b\x06\x01\x04\x01\x97\x55\x01\x05\x01'
if not a:
a = KEY_ATTRIBUTES_RSA2K
card.save_algo_attribute(2, a)
if card.is_yubikey:
pytest.skip("Yubikey returns 6B00 when no key")
def test_key_attributes_3(card):
if card.is_yubikey:
a = None
else:
a = get_data_object(card, 0xc3)
assert a == None or a == b'\x01\x08\x00\x00\x20\x00' or a == b'\x16\x2b\x06\x01\x04\x01\xda\x47\x0f\x01'
if not a:
a = KEY_ATTRIBUTES_RSA2K
card.save_algo_attribute(3, a)
if card.is_yubikey:
pytest.skip("Yubikey returns 6B00 when no key")
def test_public_key_1(card):
with pytest.raises(Exception) as excinfo:
pk = card.cmd_get_public_key(1)
if card.is_yubikey:
assert excinfo.value.args[0] == "6581"
else:
assert excinfo.value.args[0] == "6a88"
def test_public_key_2(card):
with pytest.raises(Exception) as excinfo:
pk = card.cmd_get_public_key(2)
if card.is_yubikey:
assert excinfo.value.args[0] == "6581"
else:
assert excinfo.value.args[0] == "6a88"
def test_public_key_3(card):
with pytest.raises(Exception) as excinfo:
pk = card.cmd_get_public_key(3)
if card.is_yubikey:
assert excinfo.value.args[0] == "6581"
else:
assert excinfo.value.args[0] == "6a88"
def test_AID(card):
a = get_data_object(card, 0x4f)
print()
print("OpenPGP card version: %d.%d" % (a[6], a[7]))
print("Card Manufacturer: ", hexlify(a[8:10]).decode("UTF-8"))
print("Card serial: ", hexlify(a[10:14]).decode("UTF-8"))
assert match(b'\xd2\x76\x00\x01\\$\x01........\x00\x00', a, DOTALL)

View File

@@ -0,0 +1,34 @@
"""
card_test_kdf_full.py - test KDF data object
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from card_const import *
from constants_for_test import *
class Test_Card_KDF_full(object):
def test_verify_pw3(self, card):
v = card.verify(3, FACTORY_PASSPHRASE_PW3)
assert v
def test_kdf_put_full(self, card):
r = card.configure_kdf(KDF_FULL)
assert r

View File

@@ -0,0 +1,33 @@
"""
card_test_kdf_single.py - test KDF data object
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from card_const import *
from constants_for_test import *
class Test_Card_KDF_Single(object):
def test_verify_pw3(self, card):
v = card.verify(3, FACTORY_PASSPHRASE_PW3)
assert v
def test_kdf_put_single(self, card):
r = card.configure_kdf(KDF_SINGLE)
assert r

74
tests/card_test_keygen.py Normal file
View File

@@ -0,0 +1,74 @@
"""
card_test_keygen.py - test key generation
Copyright (C) 2018, 2019 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from binascii import hexlify
from pubkey_crypto import get_PK_Crypto
from card_const import *
from constants_for_test import *
import pytest
class Test_Card_Keygen(object):
def test_verify_pw3_0(self, card):
v = card.verify(3, PW3_TEST0)
assert v
def test_keygen_1(self, card):
PK_Crypto = get_PK_Crypto(card)
pk_info = card.cmd_genkey(1)
k = PK_Crypto(0, pk_info=pk_info)
r = card.cmd_put_data(0x00, 0xc7, k.get_fpr())
if r:
r = card.cmd_put_data(0x00, 0xce, k.get_timestamp())
assert r
def test_keygen_2(self, card):
PK_Crypto = get_PK_Crypto(card)
pk_info = card.cmd_genkey(2)
k = PK_Crypto(1, pk_info=pk_info)
r = card.cmd_put_data(0x00, 0xc8, k.get_fpr())
if r:
r = card.cmd_put_data(0x00, 0xcf, k.get_timestamp())
assert r
def test_keygen_3(self, card):
PK_Crypto = get_PK_Crypto(card)
pk_info = card.cmd_genkey(3)
k = PK_Crypto(2, pk_info=pk_info)
r = card.cmd_put_data(0x00, 0xc9, k.get_fpr())
if r:
r = card.cmd_put_data(0x00, 0xd0, k.get_timestamp())
assert r
def test_setup_pw1_0(self, card):
if card.is_gnuk:
r = card.change_passwd(1, FACTORY_PASSPHRASE_PW1, PW1_TEST4)
assert r
else:
pytest.skip("Gnuk resets passphrase on keygen, so, change passwd")
def test_verify_pw1(self, card):
v = card.verify(1, PW1_TEST4)
assert v
def test_verify_pw1_2(self, card):
v = card.verify(2, PW1_TEST4)
assert v

View File

@@ -0,0 +1,40 @@
"""
card_test_kg_pko_dsc.py - test personalizing card
Copyright (C) 2021 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import pytest
from card_const import KEY_ATTRIBUTES_ECDH_BRAINPOOLP256R1
from brainpoolp256r1_keys import brainpoolp256r1_pk
@pytest.fixture(scope="module",autouse=True)
def check_brainpoolp256r1(card):
if not KEY_ATTRIBUTES_ECDSA_BRAINPOOLP256R1 in card.supported_key_attrlist[0]:
pytest.skip("Test for brainpoolP256r1", allow_module_level=True)
@pytest.fixture(scope="module")
def pk(card):
print("Select brainpoolP256r1 for testing key generation")
return brainpoolp256r1_pk
from card_test_0_set_attr import *
from card_test_1_keygen import *
from card_test_2_pkop_kg import *
from card_test_3_ds_counter1 import *

View File

@@ -0,0 +1,40 @@
"""
card_test_kg_pko_dsc.py - test personalizing card
Copyright (C) 2021 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import pytest
from card_const import KEY_ATTRIBUTES_ECDH_BRAINPOOLP384R1
from brainpoolp384r1_keys import brainpoolp384r1_pk
@pytest.fixture(scope="module",autouse=True)
def check_brainpoolp384r1(card):
if not KEY_ATTRIBUTES_ECDSA_BRAINPOOLP384R1 in card.supported_key_attrlist[0]:
pytest.skip("Test for brainpoolP384r1", allow_module_level=True)
@pytest.fixture(scope="module")
def pk(card):
print("Select brainpoolP384r1 for testing key generation")
return brainpoolp384r1_pk
from card_test_0_set_attr import *
from card_test_1_keygen import *
from card_test_2_pkop_kg import *
from card_test_3_ds_counter1 import *

View File

@@ -0,0 +1,40 @@
"""
card_test_kg_pko_dsc.py - test personalizing card
Copyright (C) 2021 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import pytest
from card_const import KEY_ATTRIBUTES_ECDH_BRAINPOOLP512R1
from brainpoolp512r1_keys import brainpoolp512r1_pk
@pytest.fixture(scope="module",autouse=True)
def check_brainpoolp512r1(card):
if not KEY_ATTRIBUTES_ECDSA_BRAINPOOLP512R1 in card.supported_key_attrlist[0]:
pytest.skip("Test for brainpoolP512r1", allow_module_level=True)
@pytest.fixture(scope="module")
def pk(card):
print("Select brainpoolP512r1 for testing key generation")
return brainpoolp512r1_pk
from card_test_0_set_attr import *
from card_test_1_keygen import *
from card_test_2_pkop_kg import *
from card_test_3_ds_counter1 import *

View File

@@ -0,0 +1,40 @@
"""
card_test_kg_pko_dsc.py - test personalizing card
Copyright (C) 2021 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import pytest
from card_const import KEY_ATTRIBUTES_CV25519
from curve25519_keys import curve25519_pk
@pytest.fixture(scope="module",autouse=True)
def check_curve25519(card):
if not KEY_ATTRIBUTES_ED25519 in card.supported_key_attrlist[0]:
pytest.skip("Test for Ed25519/Cv25519", allow_module_level=True)
@pytest.fixture(scope="module")
def pk(card):
print("Select Ed25519/Cv25519 for testing key import")
return curve25519_pk
from card_test_0_set_attr import *
from card_test_1_keygen import *
from card_test_2_pkop_kg import *
from card_test_3_ds_counter1 import *

View File

@@ -0,0 +1,40 @@
"""
card_test_kg_pko_dsc.py - test keygeneration card
Copyright (C) 2021 g10 Code GmbH
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
Gnuk is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gnuk is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import pytest
from card_const import KEY_ATTRIBUTES_ECDH_NISTP256R1
from nistp256r1_keys import nistp256r1_pk
@pytest.fixture(scope="module",autouse=True)
def check_nistp256r1(card):
if not KEY_ATTRIBUTES_ECDSA_NISTP256R1 in card.supported_key_attrlist[0]:
pytest.skip("Test for NIST P-256", allow_module_level=True)
@pytest.fixture(scope="module")
def pk(card):
print("Select NIST P-256 for testing key import")
return nistp256r1_pk
from card_test_0_set_attr import *
from card_test_1_keygen import *
from card_test_2_pkop_kg import *
from card_test_3_ds_counter1 import *

Some files were not shown because too many files have changed in this diff Show More