Compare commits
42 Commits
release/0.
...
release/0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
965dace0a4 | ||
|
|
ee4f3806c2 | ||
|
|
9ad6c6461d | ||
|
|
a796e9f145 | ||
|
|
f88e7af3ce | ||
|
|
fe31219d94 | ||
|
|
6f97e8a7c4 | ||
|
|
e78206e1cd | ||
|
|
ffba0e0158 | ||
|
|
324b648de8 | ||
|
|
9ef97836c9 | ||
|
|
628c03634b | ||
|
|
315bef4639 | ||
|
|
610573256e | ||
|
|
976e123413 | ||
|
|
6f4c868336 | ||
|
|
33c3980e46 | ||
|
|
6d8580f67a | ||
|
|
506761d823 | ||
|
|
09f22b114c | ||
|
|
9f0b8ff4c3 | ||
|
|
2764bbb5a9 | ||
|
|
3202b7d45c | ||
|
|
19e677ae74 | ||
|
|
9cc6de9e65 | ||
|
|
0988474d87 | ||
|
|
d564e4a3c1 | ||
|
|
839b0156a9 | ||
|
|
92d500d4b5 | ||
|
|
1944a78443 | ||
|
|
e11d81376c | ||
|
|
3c7a5bff61 | ||
|
|
a41476ab32 | ||
|
|
1118cd030a | ||
|
|
e6e11ddcb0 | ||
|
|
5c5074c5c7 | ||
|
|
d3f092a736 | ||
|
|
70efd3a1cd | ||
|
|
fd9f46bcc7 | ||
|
|
6f203bc4ea | ||
|
|
c25d98bc58 | ||
|
|
4290a2cc10 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -11,9 +11,6 @@ src/gnuk.elf
|
||||
src/gnuk.hex
|
||||
src/gnuk.map
|
||||
src/*.inc
|
||||
regnual/sys.h
|
||||
regnual/regnual.bin
|
||||
regnual/regnual.hex
|
||||
regnual/regnual.elf
|
||||
regnual/usb_lld.c
|
||||
regnual/usb_lld.h
|
||||
|
||||
129
ChangeLog
129
ChangeLog
@@ -1,3 +1,132 @@
|
||||
2012-07-06 Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
* Version 0.21.
|
||||
* src/usb_desc.c (gnukStringSerial): Updated.
|
||||
|
||||
* boards/FST_01/board.h (VAL_GPIOACRL): Change for SPI flash.
|
||||
* tool/stlinkv2.py (stlinkv2.setup_gpio): Likewise.
|
||||
(stlinkv2.spi_flash_init, stlinkv2.spi_flash_select)
|
||||
(stlinkv2.spi_flash_sendbyte, stlinkv2.spi_flash_read_id): New.
|
||||
(main): Add SPI flash ROM id check.
|
||||
|
||||
2012-07-05 Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
* src/call-rsa.c (rsa_sign, rsa_decrypt): Don't need to setup N.
|
||||
|
||||
* polarssl-0.14.0/library/rsa.c (rsa_check_pubkey)
|
||||
(rsa_check_privkey): Ifdef-out.
|
||||
|
||||
More tests.
|
||||
* test/*: Add tests for admin-less mode.
|
||||
* test/features/990_reset_passphrase.feature: This is now for
|
||||
admin-less mode.
|
||||
* test/features/970_key_removal.feature: Ditto.
|
||||
|
||||
* src/openpgp.c (cmd_change_password): Call ac_reset_admin when
|
||||
admin-less mode.
|
||||
(cmd_reset_user_password): Likewise.
|
||||
|
||||
* src/ac.c (ac_reset_admin, ac_fini): Clear ADMIN_AUTHORIZED.
|
||||
|
||||
Bug fix.
|
||||
* src/ac.c (verify_admin): Call s2k with ADMIN_AUTHORIZED.
|
||||
|
||||
2012-07-04 Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
Bug fixes.
|
||||
* src/ac.c (verify_admin_0): Compare PW_LEN and BUF_LEN.
|
||||
|
||||
* src/openpgp-do.c (gpg_do_chks_prvkey): Set do_ptr to NULL before
|
||||
calling flash_do_write (which might cause GC).
|
||||
(gpg_do_put_data, gpg_do_write_simple): Likewise.
|
||||
|
||||
* src/openpgp.c (cmd_reset_user_password): Write to
|
||||
DO_KEYSTRING_PW1.
|
||||
|
||||
2012-07-03 Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
* test/features/040_passphrase_change.feature: New.
|
||||
* test/features/203_passphrase_change.feature: New.
|
||||
* test/features/210_compute_signature.feature: Rename (was:
|
||||
203_compute_signature.feature)
|
||||
* test/features/211_decryption.feature: Rename (was:
|
||||
204_decryption.feature)
|
||||
|
||||
2012-07-02 Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
* tool/stlinkv2.py (stlinkv2.__init__): Don't call setAltInterface.
|
||||
|
||||
2012-06-30 Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
* src/openpgp.c (s2k): New.
|
||||
(resetcode_s2k): Remove.
|
||||
(cmd_reset_user_password, cmd_change_password): Use s2k (was:
|
||||
sha256 directly or resetcode_s2k).
|
||||
* src/openpgp-do.c (proc_resetting_code, gpg_do_write_prvkey):
|
||||
Likewise.
|
||||
* src/ac.c (verify_user_0, verify_admin): Likewise.
|
||||
|
||||
2012-06-29 Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
* regnual/Makefile: Don't copy usb_lld.c.
|
||||
|
||||
2012-06-28 Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
* test/features/204_decryption.feature: New.
|
||||
* test/features/203_compute_signature.feature: New.
|
||||
* test/features/202_keygen.feature: New.
|
||||
* test/features/201_setup_passphrase.feature: New.
|
||||
* test/features/200_key_removal.feature: New.
|
||||
|
||||
* test/rsa_keys.py (verify_signature): New.
|
||||
(encrypt_with_pubkey): New.
|
||||
|
||||
* test/gnuk.py (gnuk_token): New method: increment_seq.
|
||||
(gnuk_token.icc_send_cmd): Handle timeout.
|
||||
(gnuk_token.cmd_genkey): New.
|
||||
(gnuk_token.cmd_get_public_key): New.
|
||||
|
||||
2012-06-27 Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
* test/features/101_decryption.feature: New.
|
||||
* test/features/100_compute_signature.feature: New.
|
||||
|
||||
* src/openpgp-do.c (gpg_do_chks_prvkey): Call flash_do_release before
|
||||
flash_do_write.
|
||||
(gpg_do_write_prvkey): Bug fix when GC occurs.
|
||||
|
||||
* src/openpgp.c (cmd_change_password): Support resetting to
|
||||
factory setting of PW3.
|
||||
|
||||
* src/openpgp-do.c (gpg_do_write_prvkey): Don't reset signagure
|
||||
counter here.
|
||||
(proc_key_import): But reset here.
|
||||
Call ac_reset_* when key is imported.
|
||||
|
||||
2012-06-26 Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
* test: New.
|
||||
|
||||
2012-06-25 Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
* tool/usb_strings.py: New.
|
||||
|
||||
2012-06-22 Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
* tool/stlinkv2.py (stlinkv2.blank_check): Add blank check of
|
||||
Flash ROM.
|
||||
|
||||
2012-06-21 Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
* tool/asm-thumb/blank_check.S: New.
|
||||
|
||||
2012-06-20 Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
ST-Link/V2 flash ROM writer.
|
||||
* tool/stlinkv2.py: New.
|
||||
* tool/asm-thumb/opt_bytes_write.S: New.
|
||||
* tool/asm-thumb/flash_write.S: New.
|
||||
|
||||
2012-06-19 Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
* Version 0.20.
|
||||
|
||||
21
NEWS
21
NEWS
@@ -1,5 +1,24 @@
|
||||
Gnuk NEWS - User visible changes
|
||||
|
||||
* Major changes in Gnuk 0.21
|
||||
|
||||
Released 2012-07-06, by NIIBE Yutaka
|
||||
|
||||
** Test suite
|
||||
A functinality test suite is added under test/ directory.
|
||||
|
||||
** New tool: stlinkv2.py
|
||||
This tool is SWD flash ROM writer with ST-Link/V2.
|
||||
|
||||
** New tool: usb_strings.py
|
||||
This tool is to dump USB strings, which include revision detail and config
|
||||
options.
|
||||
|
||||
** Protection improvement (even when internal data is disclosed)
|
||||
Even if PW1 and PW3 is same, content of encrypted DEK is different
|
||||
now.
|
||||
|
||||
|
||||
* Major changes in Gnuk 0.20
|
||||
|
||||
Released 2012-06-19, by NIIBE Yutaka
|
||||
@@ -23,7 +42,7 @@ Keystring is now computed by SHA-256 (it was SHA1 before).
|
||||
|
||||
** Protection improvements (even when internal data is disclosed)
|
||||
Three improvements. (1) Even if PW1 and Reset-code is same, content
|
||||
of encripted DEK is different now. (2) DEK is now encrypted and
|
||||
of encrypted DEK is different now. (2) DEK is now encrypted and
|
||||
decrypted by keystring in ECB mode (it was just a kind of xor by
|
||||
single block CFB mode). (3) Key data plus checksum are encrypted in
|
||||
CFB mode with initial vector (it will be able to switch OCB mode
|
||||
|
||||
24
README
24
README
@@ -1,7 +1,7 @@
|
||||
Gnuk - An Implementation of USB Cryptographic Token for GnuPG
|
||||
|
||||
Version 0.20
|
||||
2012-06-19
|
||||
Version 0.21
|
||||
2012-07-06
|
||||
Niibe Yutaka
|
||||
Free Software Initiative of Japan
|
||||
|
||||
@@ -87,7 +87,7 @@ A9: GnuPG's SCDaemon has problems for handling insertion/removal of
|
||||
card/reader (problems are fixed in trunk). When your newly
|
||||
inserted token is not found by GnuPG, try killing scdaemon and let
|
||||
it to be invoked again. I do:
|
||||
$ killall -9 scdaemon
|
||||
$ gpg-connect-agent "SCD KILLSCD" "SCD BYE" /bye
|
||||
and confirm scdaemon doesn't exist, then,
|
||||
$ gpg-connect-agent learn /bye
|
||||
|
||||
@@ -106,15 +106,15 @@ Ab: That's because gnome-keyring-daemon interferes GnuPG. Type:
|
||||
"GPG Password Agent" and "SSH Key Agent".
|
||||
|
||||
Qc: Do you know a good SWD debugger to connect FST-01 or something?
|
||||
Ac: STLink v2 is cheap one. See http://code.google.com/p/arm-utilities/
|
||||
for a control program.
|
||||
Ac: STLink v2 is cheap one. We have a tool/stlinkv2.py as flash ROM
|
||||
writer.
|
||||
|
||||
|
||||
Release notes
|
||||
=============
|
||||
|
||||
This is "version 1.0 release candidate" of Gnuk. In this release, key
|
||||
generation feature is added. While it is daily use, some features
|
||||
This is another "version 1.0 release candidate" of Gnuk. In this
|
||||
release, a test suite is added. While it is daily use, some features
|
||||
(including key generation and firmware upgrade) are still considered
|
||||
experimental.
|
||||
|
||||
@@ -138,7 +138,7 @@ Tested features are:
|
||||
but you can remove all keys to import again).
|
||||
* Key generation on device side
|
||||
|
||||
Original feature of Gnuk, tested (lightly):
|
||||
Original feature of Gnuk, tested lightly:
|
||||
|
||||
* Upgrading with "EXTERNAL AUTHENTICATE" by reGNUal
|
||||
|
||||
@@ -475,7 +475,7 @@ PyUSB (python-usb package in Debian).
|
||||
If scdaemon is running, please kill it, or you will get "Smartcard
|
||||
Exception" by "Sharing violation".
|
||||
|
||||
$ killall -9 scdaemon
|
||||
$ gpg-connect-agent "SCD KILLSCD" "SCD BYE" /bye
|
||||
|
||||
In case of PyUSB tool, you need to stop pcscd.
|
||||
|
||||
@@ -554,11 +554,15 @@ This entry has been added into libccid 1.4.1 already ([r5425]).
|
||||
Testing Gnuk
|
||||
------------
|
||||
|
||||
Try following to see Gnuk runs:
|
||||
Type following command to see Gnuk runs:
|
||||
|
||||
$ gpg --card-status
|
||||
|
||||
|
||||
Besides, there is a functinality test under test/ directory. See
|
||||
test/README.
|
||||
|
||||
|
||||
Personalize the Token and import keys
|
||||
-------------------------------------
|
||||
|
||||
|
||||
@@ -97,12 +97,16 @@
|
||||
* PA0 - input with pull-up (TIM2_CH1)
|
||||
* PA1 - input with pull-down (TIM2_CH2)
|
||||
* PA2 - input with pull-up (TIM2_CH3)
|
||||
* PA4 - Push pull output (SPI1_NSS)
|
||||
* PA5 - Alternate Push pull output (SPI1_SCK)
|
||||
* PA6 - Alternate Push pull output (SPI1_MISO)
|
||||
* PA7 - Alternate Push pull output (SPI1_MOSI)
|
||||
* PA11 - input with pull-up (USBDM)
|
||||
* PA12 - input with pull-up (USBDP)
|
||||
* Everything input with pull-up except:
|
||||
* PA10 - Push pull output (USB 1:ON 0:OFF)
|
||||
*/
|
||||
#define VAL_GPIOACRL 0x88888888 /* PA7...PA0 */
|
||||
#define VAL_GPIOACRL 0xBBB38888 /* PA7...PA0 */
|
||||
#define VAL_GPIOACRH 0x88888388 /* PA15...PA8 */
|
||||
#define VAL_GPIOAODR 0xFFFFFFFD
|
||||
|
||||
|
||||
@@ -131,6 +131,7 @@ cleanup:
|
||||
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Check a public RSA key
|
||||
*/
|
||||
@@ -199,6 +200,7 @@ cleanup:
|
||||
mpi_free( &G, &I, &H, &Q1, &P1, &DE, &PQ, &G2, &L1, &L2, NULL );
|
||||
return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED | ret );
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Do an RSA public key operation
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
PROJECT = regnual
|
||||
|
||||
SRCS = regnual.c usb_lld.c sys.c
|
||||
OBJS = regnual.o usb_lld.o sys.o
|
||||
LDSCRIPT= regnual.ld
|
||||
|
||||
@@ -23,7 +22,7 @@ MCFLAGS= -mcpu=$(MCU) -mfix-cortex-m3-ldrd
|
||||
DEFS = -DFREE_STANDING
|
||||
|
||||
CFLAGS = -O2 -g
|
||||
CFLAGS += $(CWARN) -I ../src -fno-common $(MCFLAGS) $(TOPT) $(DEFS)
|
||||
CFLAGS += $(CWARN) -I . -I ../src -fno-common $(MCFLAGS) $(TOPT) $(DEFS)
|
||||
|
||||
LDFLAGS = -T$(LDSCRIPT) -nostartfiles $(MCFLAGS) $(TOPT)
|
||||
|
||||
@@ -38,8 +37,8 @@ regnual.hex: regnual.elf
|
||||
$(OBJCOPY) -Obinary regnual.elf regnual.bin
|
||||
$(OBJCOPY) -Oihex regnual.elf regnual.hex
|
||||
|
||||
usb_lld.c: ../src/usb_lld.c
|
||||
cp -p ../src/usb_lld.c .
|
||||
usb_lld.o: ../src/usb_lld.c
|
||||
$(CC) $(CFLAGS) -c -o usb_lld.o ../src/usb_lld.c
|
||||
|
||||
regnual.elf: $(OBJS) $(LDSCRIPT)
|
||||
$(CC) $(LDFLAGS) -o regnual.elf $(OBJS)
|
||||
@@ -48,4 +47,3 @@ clean:
|
||||
-rm -f $(OBJS) regnual.elf regnual.hex regnual.bin
|
||||
|
||||
distclean: clean
|
||||
-rm -f usb_lld.c
|
||||
|
||||
10
src/ac.c
10
src/ac.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* ac.c -- Check access condition
|
||||
*
|
||||
* Copyright (C) 2010 Free Software Initiative of Japan
|
||||
* Copyright (C) 2010, 2012 Free Software Initiative of Japan
|
||||
* Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
*
|
||||
* This file is a part of Gnuk, a GnuPG USB Token implementation.
|
||||
@@ -87,7 +87,7 @@ verify_user_0 (uint8_t access, const uint8_t *pw, int buf_len, int pw_len_known,
|
||||
}
|
||||
|
||||
success_one_step:
|
||||
sha256 (pw, pw_len, keystring);
|
||||
s2k (BY_USER, pw, pw_len, keystring);
|
||||
if (access == AC_PSO_CDS_AUTHORIZED)
|
||||
{
|
||||
r1 = gpg_do_load_prvkey (GPG_KEY_FOR_SIGNING, BY_USER, keystring);
|
||||
@@ -202,7 +202,7 @@ verify_admin_0 (const uint8_t *pw, int buf_len, int pw_len_known)
|
||||
return 0;
|
||||
|
||||
pw_len = pw3_keystring[0];
|
||||
if ((pw_len_known >= 0 && pw_len_known != pw_len) || pw_len < buf_len)
|
||||
if ((pw_len_known >= 0 && pw_len_known != pw_len) || pw_len > buf_len)
|
||||
goto failure;
|
||||
|
||||
salt = &pw3_keystring[1];
|
||||
@@ -280,7 +280,7 @@ verify_admin (const uint8_t *pw, int pw_len)
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
sha256 (pw, pw_len, keystring_md_pw3);
|
||||
s2k (admin_authorized, pw, pw_len, keystring_md_pw3);
|
||||
auth_status |= AC_ADMIN_AUTHORIZED;
|
||||
return 1;
|
||||
}
|
||||
@@ -290,6 +290,7 @@ ac_reset_admin (void)
|
||||
{
|
||||
memset (keystring_md_pw3, 0, KEYSTRING_MD_SIZE);
|
||||
auth_status &= ~AC_ADMIN_AUTHORIZED;
|
||||
admin_authorized = 0;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -300,4 +301,5 @@ ac_fini (void)
|
||||
gpg_do_clear_prvkey (GPG_KEY_FOR_DECRYPTION);
|
||||
gpg_do_clear_prvkey (GPG_KEY_FOR_AUTHENTICATION);
|
||||
auth_status = AC_NONE_AUTHORIZED;
|
||||
admin_authorized = 0;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,9 @@ rsa_sign (const uint8_t *raw_message, uint8_t *output, int msg_len,
|
||||
mpi_lset (&rsa_ctx.E, 0x10001);
|
||||
mpi_read_binary (&rsa_ctx.P, &kd->data[0], rsa_ctx.len / 2);
|
||||
mpi_read_binary (&rsa_ctx.Q, &kd->data[KEY_CONTENT_LEN/2], rsa_ctx.len / 2);
|
||||
#if 0 /* Using CRT, we don't use N */
|
||||
mpi_mul_mpi (&rsa_ctx.N, &rsa_ctx.P, &rsa_ctx.Q);
|
||||
#endif
|
||||
mpi_sub_int (&P1, &rsa_ctx.P, 1);
|
||||
mpi_sub_int (&Q1, &rsa_ctx.Q, 1);
|
||||
mpi_mul_mpi (&H, &P1, &Q1);
|
||||
@@ -61,17 +63,6 @@ rsa_sign (const uint8_t *raw_message, uint8_t *output, int msg_len,
|
||||
mpi_free (&P1, &Q1, &H, NULL);
|
||||
|
||||
DEBUG_INFO ("RSA sign...");
|
||||
#if 0
|
||||
if ((r = rsa_check_privkey (&rsa_ctx)) == 0)
|
||||
DEBUG_INFO ("ok...");
|
||||
else
|
||||
{
|
||||
DEBUG_INFO ("failed.\r\n");
|
||||
DEBUG_SHORT (r);
|
||||
rsa_free (&rsa_ctx);
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
r = rsa_pkcs1_sign (&rsa_ctx, RSA_PRIVATE, SIG_RSA_RAW,
|
||||
msg_len, raw_message, temp);
|
||||
@@ -142,7 +133,9 @@ rsa_decrypt (const uint8_t *input, uint8_t *output, int msg_len,
|
||||
mpi_read_binary (&rsa_ctx.P, &kd->data[0], KEY_CONTENT_LEN / 2);
|
||||
mpi_read_binary (&rsa_ctx.Q, &kd->data[KEY_CONTENT_LEN/2],
|
||||
KEY_CONTENT_LEN / 2);
|
||||
#if 0 /* Using CRT, we don't use N */
|
||||
mpi_mul_mpi (&rsa_ctx.N, &rsa_ctx.P, &rsa_ctx.Q);
|
||||
#endif
|
||||
mpi_sub_int (&P1, &rsa_ctx.P, 1);
|
||||
mpi_sub_int (&Q1, &rsa_ctx.Q, 1);
|
||||
mpi_mul_mpi (&H, &P1, &Q1);
|
||||
@@ -153,18 +146,6 @@ rsa_decrypt (const uint8_t *input, uint8_t *output, int msg_len,
|
||||
mpi_free (&P1, &Q1, &H, NULL);
|
||||
|
||||
DEBUG_INFO ("RSA decrypt ...");
|
||||
#if 0
|
||||
/* This consume some memory */
|
||||
if ((r = rsa_check_privkey (&rsa_ctx)) == 0)
|
||||
DEBUG_INFO ("ok...");
|
||||
else
|
||||
{
|
||||
DEBUG_INFO ("failed.\r\n");
|
||||
DEBUG_SHORT (r);
|
||||
rsa_free (&rsa_ctx);
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
r = rsa_pkcs1_decrypt (&rsa_ctx, RSA_PRIVATE, &output_len,
|
||||
input, output, MAX_RES_APDU_DATA_SIZE);
|
||||
|
||||
@@ -194,8 +194,8 @@ struct prvkey_data {
|
||||
#define BY_RESETCODE 2
|
||||
#define BY_ADMIN 3
|
||||
|
||||
extern void resetcode_s2k (const unsigned char *input, unsigned int ilen,
|
||||
unsigned char output[32]);
|
||||
extern void s2k (int who, const unsigned char *input, unsigned int ilen,
|
||||
unsigned char output[32]);
|
||||
|
||||
|
||||
#define KEYSTRING_PASSLEN_SIZE 1
|
||||
@@ -306,7 +306,7 @@ extern uint8_t admin_authorized;
|
||||
/*
|
||||
* Representation of Boolean object:
|
||||
* 0: No record in flash memory
|
||||
* 1: 0xc?00
|
||||
* 1: 0xf000
|
||||
*/
|
||||
#define NR_BOOL_PW1_LIFETIME 0xf0
|
||||
/*
|
||||
|
||||
@@ -1,519 +0,0 @@
|
||||
/*
|
||||
* main.c - main routine of Gnuk
|
||||
*
|
||||
* Copyright (C) 2010, 2011, 2012 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
#include "sys.h"
|
||||
#include "gnuk.h"
|
||||
#include "usb_lld.h"
|
||||
#include "usb-cdc.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
struct stdout {
|
||||
Mutex m;
|
||||
CondVar start_cnd;
|
||||
CondVar finish_cnd;
|
||||
const char *str;
|
||||
int size;
|
||||
};
|
||||
|
||||
static struct stdout stdout;
|
||||
|
||||
static void
|
||||
stdout_init (void)
|
||||
{
|
||||
chMtxInit (&stdout.m);
|
||||
chCondInit (&stdout.start_cnd);
|
||||
chCondInit (&stdout.finish_cnd);
|
||||
stdout.size = 0;
|
||||
stdout.str = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
_write (const char *s, int size)
|
||||
{
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
chMtxLock (&stdout.m);
|
||||
while (stdout.str)
|
||||
chCondWait (&stdout.finish_cnd);
|
||||
stdout.str = s;
|
||||
stdout.size = size;
|
||||
chCondSignal (&stdout.start_cnd);
|
||||
chCondWait (&stdout.finish_cnd);
|
||||
chMtxUnlock ();
|
||||
}
|
||||
|
||||
Thread *stdout_thread;
|
||||
uint32_t count_in;
|
||||
uint8_t buffer_in[VIRTUAL_COM_PORT_DATA_SIZE];
|
||||
|
||||
static WORKING_AREA(waSTDOUTthread, 128);
|
||||
|
||||
static msg_t
|
||||
STDOUTthread (void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
stdout_thread = chThdSelf ();
|
||||
|
||||
again:
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (bDeviceState == CONFIGURED)
|
||||
break;
|
||||
|
||||
chThdSleepMilliseconds (100);
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
const char *p;
|
||||
int len;
|
||||
|
||||
if (bDeviceState != CONFIGURED)
|
||||
break;
|
||||
|
||||
chMtxLock (&stdout.m);
|
||||
if (stdout.str == NULL)
|
||||
chCondWait (&stdout.start_cnd);
|
||||
|
||||
p = stdout.str;
|
||||
len = stdout.size;
|
||||
while (1)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (len == 0)
|
||||
if (count_in != VIRTUAL_COM_PORT_DATA_SIZE)
|
||||
break;
|
||||
|
||||
if (len < VIRTUAL_COM_PORT_DATA_SIZE)
|
||||
{
|
||||
for (i = 0; i < len; i++)
|
||||
buffer_in[i] = p[i];
|
||||
count_in = len;
|
||||
len = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < VIRTUAL_COM_PORT_DATA_SIZE; i++)
|
||||
buffer_in[i] = p[i];
|
||||
len -= VIRTUAL_COM_PORT_DATA_SIZE;
|
||||
count_in = VIRTUAL_COM_PORT_DATA_SIZE;
|
||||
p += count_in;
|
||||
}
|
||||
|
||||
chEvtClear (EV_TX_READY);
|
||||
|
||||
usb_lld_write (ENDP3, buffer_in, count_in);
|
||||
|
||||
chEvtWaitOne (EV_TX_READY);
|
||||
}
|
||||
|
||||
stdout.str = NULL;
|
||||
stdout.size = 0;
|
||||
chCondBroadcast (&stdout.finish_cnd);
|
||||
chMtxUnlock ();
|
||||
}
|
||||
|
||||
goto again;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
EP3_IN_Callback (void)
|
||||
{
|
||||
if (stdout_thread)
|
||||
chEvtSignalI (stdout_thread, EV_TX_READY);
|
||||
}
|
||||
|
||||
void
|
||||
EP5_OUT_Callback (void)
|
||||
{
|
||||
usb_lld_rx_enable (ENDP5);
|
||||
}
|
||||
#else
|
||||
void
|
||||
_write (const char *s, int size)
|
||||
{
|
||||
(void)s;
|
||||
(void)size;
|
||||
}
|
||||
#endif
|
||||
|
||||
static WORKING_AREA(waUSBthread, 128);
|
||||
extern msg_t USBthread (void *arg);
|
||||
|
||||
/*
|
||||
* main thread does 1-bit LED display output
|
||||
*/
|
||||
#define LED_TIMEOUT_INTERVAL MS2ST(100)
|
||||
#define LED_TIMEOUT_ZERO MS2ST(50)
|
||||
#define LED_TIMEOUT_ONE MS2ST(200)
|
||||
#define LED_TIMEOUT_STOP MS2ST(500)
|
||||
|
||||
|
||||
#define ID_OFFSET 22
|
||||
static void
|
||||
device_initialize_once (void)
|
||||
{
|
||||
const uint8_t *p = &gnukStringSerial[ID_OFFSET];
|
||||
|
||||
if (p[0] == 0xff && p[1] == 0xff && p[2] == 0xff && p[3] == 0xff)
|
||||
{
|
||||
/*
|
||||
* This is the first time invocation.
|
||||
* Setup serial number by unique device ID.
|
||||
*/
|
||||
const uint8_t *u = unique_device_id ();
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
uint8_t b = u[i];
|
||||
uint8_t nibble;
|
||||
|
||||
nibble = (b >> 4);
|
||||
nibble += (nibble >= 10 ? ('A' - 10) : '0');
|
||||
flash_put_data_internal (&p[i*4], nibble);
|
||||
nibble = (b & 0x0f);
|
||||
nibble += (nibble >= 10 ? ('A' - 10) : '0');
|
||||
flash_put_data_internal (&p[i*4+2], nibble);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static volatile uint8_t fatal_code;
|
||||
|
||||
Thread *main_thread;
|
||||
|
||||
#define GNUK_INIT 0
|
||||
#define GNUK_RUNNING 1
|
||||
#define GNUK_WAIT 2
|
||||
#define GNUK_FATAL 255
|
||||
/*
|
||||
* 0 for initializing
|
||||
* 1 for normal mode
|
||||
* 2 for input waiting / under calculation
|
||||
* 255 for fatal
|
||||
*/
|
||||
static uint8_t main_mode;
|
||||
|
||||
static void display_interaction (void)
|
||||
{
|
||||
eventmask_t m;
|
||||
|
||||
set_led (1);
|
||||
while (1)
|
||||
{
|
||||
m = chEvtWaitOne (ALL_EVENTS);
|
||||
set_led (0);
|
||||
switch (m)
|
||||
{
|
||||
case LED_ONESHOT_SHORT:
|
||||
chThdSleep (MS2ST (100));
|
||||
break;
|
||||
case LED_ONESHOT_LONG:
|
||||
chThdSleep (MS2ST (400));
|
||||
break;
|
||||
case LED_TWOSHOT:
|
||||
chThdSleep (MS2ST (50));
|
||||
set_led (1);
|
||||
chThdSleep (MS2ST (50));
|
||||
set_led (0);
|
||||
chThdSleep (MS2ST (50));
|
||||
break;
|
||||
case LED_STATUS_MODE:
|
||||
main_mode = GNUK_RUNNING;
|
||||
return;
|
||||
case LED_FATAL_MODE:
|
||||
main_mode = GNUK_FATAL;
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
set_led (1);
|
||||
}
|
||||
}
|
||||
|
||||
static void display_fatal_code (void)
|
||||
{
|
||||
set_led (1);
|
||||
chThdSleep (LED_TIMEOUT_ZERO);
|
||||
set_led (0);
|
||||
chThdSleep (LED_TIMEOUT_INTERVAL);
|
||||
set_led (1);
|
||||
chThdSleep (LED_TIMEOUT_ZERO);
|
||||
set_led (0);
|
||||
chThdSleep (LED_TIMEOUT_INTERVAL);
|
||||
set_led (1);
|
||||
chThdSleep (LED_TIMEOUT_ZERO);
|
||||
set_led (0);
|
||||
chThdSleep (LED_TIMEOUT_STOP);
|
||||
set_led (1);
|
||||
if (fatal_code & 1)
|
||||
chThdSleep (LED_TIMEOUT_ONE);
|
||||
else
|
||||
chThdSleep (LED_TIMEOUT_ZERO);
|
||||
set_led (0);
|
||||
chThdSleep (LED_TIMEOUT_INTERVAL);
|
||||
set_led (1);
|
||||
if (fatal_code & 2)
|
||||
chThdSleep (LED_TIMEOUT_ONE);
|
||||
else
|
||||
chThdSleep (LED_TIMEOUT_ZERO);
|
||||
set_led (0);
|
||||
chThdSleep (LED_TIMEOUT_INTERVAL);
|
||||
set_led (1);
|
||||
chThdSleep (LED_TIMEOUT_STOP);
|
||||
set_led (0);
|
||||
chThdSleep (LED_TIMEOUT_INTERVAL);
|
||||
}
|
||||
|
||||
static void display_status_code (void)
|
||||
{
|
||||
enum icc_state icc_state;
|
||||
|
||||
if (icc_state_p == NULL)
|
||||
icc_state = ICC_STATE_START;
|
||||
else
|
||||
icc_state = *icc_state_p;
|
||||
|
||||
if (icc_state == ICC_STATE_START)
|
||||
{
|
||||
set_led (1);
|
||||
chThdSleep (LED_TIMEOUT_ONE);
|
||||
set_led (0);
|
||||
chThdSleep (LED_TIMEOUT_STOP * 3);
|
||||
}
|
||||
else
|
||||
/* GPGthread running */
|
||||
{
|
||||
set_led (1);
|
||||
if ((auth_status & AC_ADMIN_AUTHORIZED) != 0)
|
||||
chThdSleep (LED_TIMEOUT_ONE);
|
||||
else
|
||||
chThdSleep (LED_TIMEOUT_ZERO);
|
||||
set_led (0);
|
||||
chThdSleep (LED_TIMEOUT_INTERVAL);
|
||||
set_led (1);
|
||||
if ((auth_status & AC_OTHER_AUTHORIZED) != 0)
|
||||
chThdSleep (LED_TIMEOUT_ONE);
|
||||
else
|
||||
chThdSleep (LED_TIMEOUT_ZERO);
|
||||
set_led (0);
|
||||
chThdSleep (LED_TIMEOUT_INTERVAL);
|
||||
set_led (1);
|
||||
if ((auth_status & AC_PSO_CDS_AUTHORIZED) != 0)
|
||||
chThdSleep (LED_TIMEOUT_ONE);
|
||||
else
|
||||
chThdSleep (LED_TIMEOUT_ZERO);
|
||||
|
||||
if (icc_state == ICC_STATE_WAIT)
|
||||
{
|
||||
set_led (0);
|
||||
chThdSleep (LED_TIMEOUT_STOP * 2);
|
||||
}
|
||||
else if (icc_state == ICC_STATE_RECEIVE)
|
||||
{
|
||||
set_led (0);
|
||||
chThdSleep (LED_TIMEOUT_INTERVAL);
|
||||
set_led (1);
|
||||
chThdSleep (LED_TIMEOUT_ONE);
|
||||
set_led (0);
|
||||
chThdSleep (LED_TIMEOUT_STOP);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_led (0);
|
||||
chThdSleep (LED_TIMEOUT_INTERVAL);
|
||||
set_led (1);
|
||||
chThdSleep (LED_TIMEOUT_STOP);
|
||||
set_led (0);
|
||||
chThdSleep (LED_TIMEOUT_INTERVAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
led_blink (int spec)
|
||||
{
|
||||
if (spec == 0)
|
||||
chEvtSignal (main_thread, LED_ONESHOT_SHORT);
|
||||
else if (spec == 1)
|
||||
chEvtSignal (main_thread, LED_ONESHOT_LONG);
|
||||
else
|
||||
chEvtSignal (main_thread, LED_TWOSHOT);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Entry point.
|
||||
*
|
||||
* NOTE: the main function is already a thread in the system on entry.
|
||||
* See the hwinit1_common function.
|
||||
*/
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
main_thread = chThdSelf ();
|
||||
|
||||
flash_unlock ();
|
||||
device_initialize_once ();
|
||||
usb_lld_init (Config_Descriptor.Descriptor[7]);
|
||||
random_init ();
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (bDeviceState != UNCONNECTED)
|
||||
break;
|
||||
|
||||
chThdSleepMilliseconds (250);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
stdout_init ();
|
||||
|
||||
/*
|
||||
* Creates 'stdout' thread.
|
||||
*/
|
||||
chThdCreateStatic (waSTDOUTthread, sizeof(waSTDOUTthread),
|
||||
NORMALPRIO, STDOUTthread, NULL);
|
||||
#endif
|
||||
|
||||
chThdCreateStatic (waUSBthread, sizeof(waUSBthread),
|
||||
NORMALPRIO, USBthread, NULL);
|
||||
|
||||
#ifdef PINPAD_DND_SUPPORT
|
||||
msc_init ();
|
||||
#endif
|
||||
|
||||
while (1)
|
||||
{
|
||||
eventmask_t m;
|
||||
|
||||
if (icc_state_p != NULL && *icc_state_p == ICC_STATE_EXEC_REQUESTED)
|
||||
break;
|
||||
|
||||
count++;
|
||||
m = chEvtWaitOneTimeout (ALL_EVENTS, LED_TIMEOUT_INTERVAL);
|
||||
switch (m)
|
||||
{
|
||||
case LED_STATUS_MODE:
|
||||
main_mode = GNUK_RUNNING;
|
||||
break;
|
||||
case LED_FATAL_MODE:
|
||||
main_mode = GNUK_FATAL;
|
||||
break;
|
||||
case LED_WAIT_MODE:
|
||||
main_mode = GNUK_WAIT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (main_mode)
|
||||
{
|
||||
case GNUK_FATAL:
|
||||
display_fatal_code ();
|
||||
break;
|
||||
case GNUK_INIT:
|
||||
set_led (1);
|
||||
chThdSleep (LED_TIMEOUT_ZERO);
|
||||
set_led (0);
|
||||
chThdSleep (LED_TIMEOUT_STOP * 3);
|
||||
break;
|
||||
case GNUK_WAIT:
|
||||
display_interaction ();
|
||||
break;
|
||||
case GNUK_RUNNING:
|
||||
default:
|
||||
display_status_code ();
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_MORE
|
||||
if (bDeviceState == CONFIGURED && (count % 10) == 0)
|
||||
{
|
||||
DEBUG_SHORT (count / 10);
|
||||
_write ("\r\nThis is ChibiOS 2.0.8 on STM32.\r\n"
|
||||
"Testing USB driver.\n\n"
|
||||
"Hello world\r\n\r\n", 35+21+15);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
set_led (1);
|
||||
usb_lld_shutdown ();
|
||||
/* Disable SysTick */
|
||||
SysTick->CTRL = 0;
|
||||
/* Disable all interrupts */
|
||||
port_disable ();
|
||||
/* Set vector */
|
||||
SCB->VTOR = (uint32_t)&_regnual_start;
|
||||
#ifdef DFU_SUPPORT
|
||||
#define FLASH_SYS_START_ADDR 0x08000000
|
||||
#define FLASH_SYS_END_ADDR (0x08000000+0x1000)
|
||||
{
|
||||
extern uint8_t _sys;
|
||||
uint32_t addr;
|
||||
handler *new_vector = (handler *)FLASH_SYS_START_ADDR;
|
||||
void (*func) (void (*)(void)) = (void (*)(void (*)(void)))new_vector[10];
|
||||
|
||||
/* Kill DFU */
|
||||
for (addr = FLASH_SYS_START_ADDR; addr < FLASH_SYS_END_ADDR;
|
||||
addr += FLASH_PAGE_SIZE)
|
||||
flash_erase_page (addr);
|
||||
|
||||
/* copy system service routines */
|
||||
flash_write (FLASH_SYS_START_ADDR, &_sys, 0x1000);
|
||||
|
||||
/* Leave Gnuk to exec reGNUal */
|
||||
(*func) (*((void (**)(void))(&_regnual_start+4)));
|
||||
for (;;);
|
||||
}
|
||||
#else
|
||||
/* Leave Gnuk to exec reGNUal */
|
||||
flash_erase_all_and_exec (*((void (**)(void))(&_regnual_start+4)));
|
||||
#endif
|
||||
|
||||
/* Never reached */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
fatal (uint8_t code)
|
||||
{
|
||||
fatal_code = code;
|
||||
chEvtSignal (main_thread, LED_FATAL_MODE);
|
||||
_write ("fatal\r\n", 7);
|
||||
for (;;);
|
||||
}
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "sys.h"
|
||||
#include "gnuk.h"
|
||||
#include "openpgp.h"
|
||||
#include "sha256.h"
|
||||
|
||||
#include "polarssl/config.h"
|
||||
#include "polarssl/aes.h"
|
||||
@@ -543,7 +542,7 @@ proc_resetting_code (const uint8_t *data, int len)
|
||||
|
||||
newpw_len = len;
|
||||
newpw = data;
|
||||
resetcode_s2k (newpw, newpw_len, new_ks);
|
||||
s2k (BY_RESETCODE, newpw, newpw_len, new_ks);
|
||||
new_ks0[0] = newpw_len;
|
||||
r = gpg_change_keystring (admin_authorized, old_ks, BY_RESETCODE, new_ks);
|
||||
if (r <= -2)
|
||||
@@ -724,6 +723,8 @@ gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, int key_len,
|
||||
const uint8_t *ks_rc;
|
||||
struct key_data_internal kdi;
|
||||
int modulus_allocated_here = 0;
|
||||
uint8_t ks_pw1_len = 0;
|
||||
uint8_t ks_rc_len = 0;
|
||||
|
||||
DEBUG_INFO ("Key import\r\n");
|
||||
DEBUG_SHORT (key_len);
|
||||
@@ -793,27 +794,30 @@ gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, int key_len,
|
||||
memcpy (pd->checksum_encrypted, kdi.checksum, DATA_ENCRYPTION_KEY_SIZE);
|
||||
|
||||
if (kk == GPG_KEY_FOR_SIGNING)
|
||||
{
|
||||
ac_reset_pso_cds ();
|
||||
gpg_reset_digital_signature_counter ();
|
||||
}
|
||||
ac_reset_pso_cds ();
|
||||
else
|
||||
ac_reset_other ();
|
||||
|
||||
if (ks_pw1)
|
||||
encrypt_dek (ks_pw1+1, pd->dek_encrypted_1);
|
||||
{
|
||||
ks_pw1_len = ks_pw1[0];
|
||||
encrypt_dek (ks_pw1+1, pd->dek_encrypted_1);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t ks123_pw1[KEYSTRING_SIZE_PW1];
|
||||
|
||||
ks123_pw1[0] = strlen (OPENPGP_CARD_INITIAL_PW1);
|
||||
sha256 ((uint8_t *)OPENPGP_CARD_INITIAL_PW1,
|
||||
strlen (OPENPGP_CARD_INITIAL_PW1), ks123_pw1+1);
|
||||
s2k (BY_USER, (uint8_t *)OPENPGP_CARD_INITIAL_PW1,
|
||||
strlen (OPENPGP_CARD_INITIAL_PW1), ks123_pw1+1);
|
||||
encrypt_dek (ks123_pw1+1, pd->dek_encrypted_1);
|
||||
}
|
||||
|
||||
if (ks_rc)
|
||||
encrypt_dek (ks_rc+1, pd->dek_encrypted_2);
|
||||
{
|
||||
ks_rc_len = ks_rc[0];
|
||||
encrypt_dek (ks_rc+1, pd->dek_encrypted_2);
|
||||
}
|
||||
else
|
||||
memset (pd->dek_encrypted_2, 0, DATA_ENCRYPTION_KEY_SIZE);
|
||||
|
||||
@@ -833,17 +837,11 @@ gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, int key_len,
|
||||
if (++num_prv_keys == NUM_ALL_PRV_KEYS) /* All keys are registered. */
|
||||
{
|
||||
/* Remove contents of keystrings from DO, but length */
|
||||
if (ks_pw1)
|
||||
{
|
||||
uint8_t ks_pw1_len = ks_pw1[0];
|
||||
gpg_do_write_simple (NR_DO_KEYSTRING_PW1, &ks_pw1_len, 1);
|
||||
}
|
||||
if (ks_pw1_len)
|
||||
gpg_do_write_simple (NR_DO_KEYSTRING_PW1, &ks_pw1_len, 1);
|
||||
|
||||
if (ks_rc)
|
||||
{
|
||||
uint8_t ks_rc_len = ks_rc[0];
|
||||
gpg_do_write_simple (NR_DO_KEYSTRING_RC, &ks_rc_len, 1);
|
||||
}
|
||||
if (ks_rc_len)
|
||||
gpg_do_write_simple (NR_DO_KEYSTRING_RC, &ks_rc_len, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -868,7 +866,9 @@ gpg_do_chks_prvkey (enum kind_of_key kk,
|
||||
if (pd == NULL)
|
||||
return -1;
|
||||
|
||||
memcpy (pd, &(do_data)[1], sizeof (struct prvkey_data));
|
||||
memcpy (pd, &do_data[1], sizeof (struct prvkey_data));
|
||||
flash_do_release (do_data);
|
||||
|
||||
dek_p = ((uint8_t *)pd) + 4 + INITIAL_VECTOR_SIZE
|
||||
+ DATA_ENCRYPTION_KEY_SIZE * who_old;
|
||||
memcpy (dek, dek_p, DATA_ENCRYPTION_KEY_SIZE);
|
||||
@@ -877,10 +877,10 @@ gpg_do_chks_prvkey (enum kind_of_key kk,
|
||||
dek_p += DATA_ENCRYPTION_KEY_SIZE * (who_new - who_old);
|
||||
memcpy (dek_p, dek, DATA_ENCRYPTION_KEY_SIZE);
|
||||
|
||||
do_ptr[nr - NR_DO__FIRST__] = NULL;
|
||||
p = flash_do_write (nr, (const uint8_t *)pd, sizeof (struct prvkey_data));
|
||||
do_ptr[nr - NR_DO__FIRST__] = p;
|
||||
|
||||
flash_do_release (do_data);
|
||||
free (pd);
|
||||
if (p == NULL)
|
||||
return -1;
|
||||
@@ -924,11 +924,19 @@ proc_key_import (const uint8_t *data, int len)
|
||||
p += 1;
|
||||
|
||||
if (*p == 0xb6)
|
||||
kk = GPG_KEY_FOR_SIGNING;
|
||||
else if (*p == 0xb8)
|
||||
kk = GPG_KEY_FOR_DECRYPTION;
|
||||
else /* 0xa4 */
|
||||
kk = GPG_KEY_FOR_AUTHENTICATION;
|
||||
{
|
||||
kk = GPG_KEY_FOR_SIGNING;
|
||||
ac_reset_pso_cds ();
|
||||
gpg_reset_digital_signature_counter ();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*p == 0xb8)
|
||||
kk = GPG_KEY_FOR_DECRYPTION;
|
||||
else /* 0xa4 */
|
||||
kk = GPG_KEY_FOR_AUTHENTICATION;
|
||||
ac_reset_other ();
|
||||
}
|
||||
|
||||
if (len <= 22)
|
||||
{ /* Deletion of the key */
|
||||
@@ -948,6 +956,11 @@ proc_key_import (const uint8_t *data, int len)
|
||||
/* Delete PW1 and RC if any */
|
||||
gpg_do_write_simple (NR_DO_KEYSTRING_PW1, NULL, 0);
|
||||
gpg_do_write_simple (NR_DO_KEYSTRING_RC, NULL, 0);
|
||||
|
||||
ac_reset_pso_cds ();
|
||||
ac_reset_other ();
|
||||
if (admin_authorized == BY_USER)
|
||||
ac_reset_admin ();
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -1086,18 +1099,18 @@ gpg_data_scan (const uint8_t *p_start)
|
||||
}
|
||||
else
|
||||
switch (nr)
|
||||
{
|
||||
case NR_BOOL_PW1_LIFETIME:
|
||||
pw1_lifetime_p = p - 1;
|
||||
p++;
|
||||
continue;
|
||||
case NR_COUNTER_123:
|
||||
p++;
|
||||
if (second_byte <= PW_ERR_PW3)
|
||||
pw_err_counter_p[second_byte] = p;
|
||||
p += 2;
|
||||
break;
|
||||
}
|
||||
{
|
||||
case NR_BOOL_PW1_LIFETIME:
|
||||
pw1_lifetime_p = p - 1;
|
||||
p++;
|
||||
continue;
|
||||
case NR_COUNTER_123:
|
||||
p++;
|
||||
if (second_byte <= PW_ERR_PW3)
|
||||
pw_err_counter_p[second_byte] = p;
|
||||
p += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1284,8 +1297,8 @@ copy_do (const struct do_table_entry *do_p, int with_tag)
|
||||
}
|
||||
case DO_PROC_READWRITE:
|
||||
{
|
||||
int (*rw_func)(uint16_t, int, uint8_t *, int, int)
|
||||
= (int (*)(uint16_t, int, uint8_t *, int, int))do_p->obj;
|
||||
int (*rw_func)(uint16_t, int, const uint8_t *, int, int)
|
||||
= (int (*)(uint16_t, int, const uint8_t *, int, int))do_p->obj;
|
||||
|
||||
return rw_func (do_p->tag, with_tag, NULL, 0, 0);
|
||||
}
|
||||
@@ -1386,6 +1399,7 @@ gpg_do_put_data (uint16_t tag, const uint8_t *data, int len)
|
||||
GPG_MEMORY_FAILURE ();
|
||||
else
|
||||
{
|
||||
*do_data_p = NULL;
|
||||
*do_data_p = flash_do_write (nr, data, len);
|
||||
if (*do_data_p)
|
||||
GPG_SUCCESS ();
|
||||
@@ -1500,6 +1514,7 @@ gpg_do_write_simple (uint8_t nr, const uint8_t *data, int size)
|
||||
|
||||
if (data != NULL)
|
||||
{
|
||||
*do_data_p = NULL;
|
||||
*do_data_p = flash_do_write (nr, data, size);
|
||||
if (*do_data_p == NULL)
|
||||
flash_warning ("DO WRITE ERROR");
|
||||
|
||||
@@ -220,8 +220,7 @@ cmd_change_password (void)
|
||||
uint8_t p1 = P1 (apdu); /* 0: change (old+new), 1: exchange (new) */
|
||||
uint8_t p2 = P2 (apdu);
|
||||
int len;
|
||||
const uint8_t *pw;
|
||||
const uint8_t *newpw;
|
||||
uint8_t *pw, *newpw;
|
||||
int pw_len, newpw_len;
|
||||
int who = p2 - 0x80;
|
||||
int who_old;
|
||||
@@ -284,13 +283,20 @@ cmd_change_password (void)
|
||||
{
|
||||
newpw = pw + pw_len;
|
||||
newpw_len = len - pw_len;
|
||||
gpg_set_pw3 (newpw, newpw_len);
|
||||
if (newpw_len == 0 && admin_authorized == BY_ADMIN)
|
||||
{
|
||||
newpw_len = strlen (OPENPGP_CARD_INITIAL_PW3);
|
||||
memcpy (newpw, OPENPGP_CARD_INITIAL_PW3, newpw_len);
|
||||
gpg_do_write_simple (NR_DO_KEYSTRING_PW3, NULL, 0);
|
||||
}
|
||||
else
|
||||
gpg_set_pw3 (newpw, newpw_len);
|
||||
who_old = admin_authorized;
|
||||
}
|
||||
}
|
||||
|
||||
sha256 (pw, pw_len, old_ks);
|
||||
sha256 (newpw, newpw_len, new_ks);
|
||||
s2k (who_old, pw, pw_len, old_ks);
|
||||
s2k (who, newpw, newpw_len, new_ks);
|
||||
new_ks0[0] = newpw_len;
|
||||
|
||||
r = gpg_change_keystring (who_old, old_ks, who, new_ks);
|
||||
@@ -309,6 +315,8 @@ cmd_change_password (void)
|
||||
gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, KEYSTRING_SIZE_PW1);
|
||||
ac_reset_pso_cds ();
|
||||
ac_reset_other ();
|
||||
if (admin_authorized == BY_USER)
|
||||
ac_reset_admin ();
|
||||
DEBUG_INFO ("Changed DO_KEYSTRING_PW1.\r\n");
|
||||
GPG_SUCCESS ();
|
||||
}
|
||||
@@ -317,6 +325,8 @@ cmd_change_password (void)
|
||||
gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, 1);
|
||||
ac_reset_pso_cds ();
|
||||
ac_reset_other ();
|
||||
if (admin_authorized == BY_USER)
|
||||
ac_reset_admin ();
|
||||
DEBUG_INFO ("Changed length of DO_KEYSTRING_PW1.\r\n");
|
||||
GPG_SUCCESS ();
|
||||
}
|
||||
@@ -329,18 +339,24 @@ cmd_change_password (void)
|
||||
}
|
||||
|
||||
|
||||
#define USER_S2K_MAGIC "\xffUSER\r\n"
|
||||
#define RESETCODE_S2K_MAGIC "\xffRESET\r\n"
|
||||
|
||||
void
|
||||
resetcode_s2k (const unsigned char *input, unsigned int ilen,
|
||||
unsigned char output[32])
|
||||
s2k (int who, const unsigned char *input, unsigned int ilen,
|
||||
unsigned char output[32])
|
||||
{
|
||||
sha256_context ctx;
|
||||
|
||||
sha256_start (&ctx);
|
||||
sha256_update (&ctx, input, ilen);
|
||||
sha256_update (&ctx, (unsigned char *)RESETCODE_S2K_MAGIC,
|
||||
sizeof (RESETCODE_S2K_MAGIC));
|
||||
if (who == BY_USER)
|
||||
sha256_update (&ctx, (unsigned char *)USER_S2K_MAGIC,
|
||||
sizeof (USER_S2K_MAGIC));
|
||||
else if (who == BY_RESETCODE)
|
||||
sha256_update (&ctx, (unsigned char *)RESETCODE_S2K_MAGIC,
|
||||
sizeof (RESETCODE_S2K_MAGIC));
|
||||
/* Not add any for BY_ADMIN */
|
||||
sha256_finish (&ctx, output);
|
||||
}
|
||||
|
||||
@@ -385,8 +401,8 @@ cmd_reset_user_password (void)
|
||||
pw_len = ks_rc[0];
|
||||
newpw = pw + pw_len;
|
||||
newpw_len = len - pw_len;
|
||||
resetcode_s2k (pw, pw_len, old_ks);
|
||||
sha256 (newpw, newpw_len, new_ks);
|
||||
s2k (BY_RESETCODE, pw, pw_len, old_ks);
|
||||
s2k (BY_USER, newpw, newpw_len, new_ks);
|
||||
new_ks0[0] = newpw_len;
|
||||
r = gpg_change_keystring (BY_RESETCODE, old_ks, BY_USER, new_ks);
|
||||
if (r <= -2)
|
||||
@@ -410,6 +426,8 @@ cmd_reset_user_password (void)
|
||||
KEYSTRING_SIZE_PW1);
|
||||
ac_reset_pso_cds ();
|
||||
ac_reset_other ();
|
||||
if (admin_authorized == BY_USER)
|
||||
ac_reset_admin ();
|
||||
gpg_pw_reset_err_counter (PW_ERR_RC);
|
||||
gpg_pw_reset_err_counter (PW_ERR_PW1);
|
||||
GPG_SUCCESS ();
|
||||
@@ -417,8 +435,11 @@ cmd_reset_user_password (void)
|
||||
else
|
||||
{
|
||||
DEBUG_INFO ("done.\r\n");
|
||||
gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, 1);
|
||||
ac_reset_pso_cds ();
|
||||
ac_reset_other ();
|
||||
if (admin_authorized == BY_USER)
|
||||
ac_reset_admin ();
|
||||
gpg_pw_reset_err_counter (PW_ERR_RC);
|
||||
gpg_pw_reset_err_counter (PW_ERR_PW1);
|
||||
GPG_SUCCESS ();
|
||||
@@ -437,7 +458,7 @@ cmd_reset_user_password (void)
|
||||
|
||||
newpw_len = len;
|
||||
newpw = pw;
|
||||
sha256 (newpw, newpw_len, new_ks);
|
||||
s2k (BY_USER, newpw, newpw_len, new_ks);
|
||||
new_ks0[0] = newpw_len;
|
||||
r = gpg_change_keystring (admin_authorized, old_ks, BY_USER, new_ks);
|
||||
if (r <= -2)
|
||||
@@ -457,14 +478,19 @@ cmd_reset_user_password (void)
|
||||
KEYSTRING_SIZE_PW1);
|
||||
ac_reset_pso_cds ();
|
||||
ac_reset_other ();
|
||||
if (admin_authorized == BY_USER)
|
||||
ac_reset_admin ();
|
||||
gpg_pw_reset_err_counter (PW_ERR_PW1);
|
||||
GPG_SUCCESS ();
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_INFO ("done.\r\n");
|
||||
gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, 1);
|
||||
ac_reset_pso_cds ();
|
||||
ac_reset_other ();
|
||||
if (admin_authorized == BY_USER)
|
||||
ac_reset_admin ();
|
||||
gpg_pw_reset_err_counter (PW_ERR_PW1);
|
||||
GPG_SUCCESS ();
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ const uint8_t gnukStringSerial[] = {
|
||||
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
|
||||
/* FSIJ-0.19 */
|
||||
'F', 0, 'S', 0, 'I', 0, 'J', 0, '-', 0,
|
||||
'0', 0, '.', 0, '2', 0, '0', 0, /* Version number of Gnuk */
|
||||
'0', 0, '.', 0, '2', 0, '1', 0, /* Version number of Gnuk */
|
||||
'-', 0,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
|
||||
20
test/README
Normal file
20
test/README
Normal file
@@ -0,0 +1,20 @@
|
||||
This is a functionality test suite for Gnuk.
|
||||
|
||||
You need python-nose, python-freshen as well as python-usb.
|
||||
|
||||
Besides, python-crypto is needed when you use generate_keys.py to
|
||||
update contents of *.key.
|
||||
|
||||
|
||||
Type:
|
||||
|
||||
$ nosetests --with-freshen
|
||||
|
||||
or
|
||||
|
||||
$ nosetests -v --with-freshen
|
||||
|
||||
to run the test suite.
|
||||
|
||||
To skip tests for key generation, add an option "--tag ~keygen". To
|
||||
stop running tests after the first error or failure, add "--stop" option.
|
||||
79
test/features/000_empty_check.feature
Normal file
79
test/features/000_empty_check.feature
Normal file
@@ -0,0 +1,79 @@
|
||||
Feature: confirm empty token
|
||||
In order to start tests
|
||||
A token should be empty (no data, no keys)
|
||||
|
||||
Scenario: data object Login
|
||||
When requesting login data: 5e
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object Name
|
||||
When requesting name: 5b
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object Language preference
|
||||
When requesting anguage preference: 5f2d
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object Sex
|
||||
When requesting sex: 5f35
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object URL
|
||||
When requesting URL: 5f50
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object ds counter
|
||||
When requesting ds counter: 93
|
||||
Then you should get: \x00\x00\x00
|
||||
|
||||
Scenario: data object pw1 status bytes
|
||||
When requesting pw1 status bytes: c4
|
||||
Then you should get: \x00\x7f\x7f\x7f\x03\x03\x03
|
||||
|
||||
Scenario: data object finger print 0
|
||||
When requesting finger print: c5
|
||||
Then you should get: \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
|
||||
|
||||
Scenario: data object finger print 1
|
||||
When requesting finger print: c7
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object finger print 2
|
||||
When requesting finger print: c8
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object finger print 3
|
||||
When requesting finger print: c9
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object CA finger print 0
|
||||
When requesting finger print: c6
|
||||
Then you should get: \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
|
||||
|
||||
Scenario: data object CA finger print 1
|
||||
When requesting finger print: ca
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object CA finger print 2
|
||||
When requesting finger print: cb
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object CA finger print 3
|
||||
When requesting finger print: cc
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object date/time of key pair 0
|
||||
When requesting date/time of key pair: cd
|
||||
Then you should get: \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
|
||||
|
||||
Scenario: data object date/time of key pair 1
|
||||
When requesting date/time of key pair: ce
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object date/time of key pair 2
|
||||
When requesting date/time of key pair: cf
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object date/time of key pair 3
|
||||
When requesting date/time of key pair: d0
|
||||
Then you should get NULL
|
||||
15
test/features/001_empty_check_passphrase.feature
Normal file
15
test/features/001_empty_check_passphrase.feature
Normal file
@@ -0,0 +1,15 @@
|
||||
Feature: confirm empty token
|
||||
In order to start tests
|
||||
A token should be empty (no pass phrase)
|
||||
|
||||
Scenario: verify PW1 factory setting (1)
|
||||
Given cmd_verify with 1 and "123456"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 factory setting (2)
|
||||
Given cmd_verify with 2 and "123456"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 factory setting
|
||||
Given cmd_verify with 3 and "12345678"
|
||||
Then it should get success
|
||||
27
test/features/002_get_data_static.feature
Normal file
27
test/features/002_get_data_static.feature
Normal file
@@ -0,0 +1,27 @@
|
||||
Feature: command GET DATA
|
||||
In order to conform OpenPGP card 2.0 specification
|
||||
A token should support all mandatory features of the specification
|
||||
|
||||
Scenario: data object historical bytes
|
||||
When requesting historical bytes: 5f52
|
||||
Then you should get: \x00\x31\x84\x73\x80\x01\x80\x00\x90\x00
|
||||
|
||||
Scenario: data object extended capabilities
|
||||
When requesting extended capabilities: c0
|
||||
Then you should get: \x30\x00\x00\x00\x00\x00\x00\xff\x01\x00
|
||||
|
||||
Scenario: data object algorithm attributes 1
|
||||
When requesting algorithm attributes 1: c1
|
||||
Then you should get: \x01\x08\x00\x00\x20\x00
|
||||
|
||||
Scenario: data object algorithm attributes 2
|
||||
When requesting algorithm attributes 2: c2
|
||||
Then you should get: \x01\x08\x00\x00\x20\x00
|
||||
|
||||
Scenario: data object algorithm attributes 3
|
||||
When requesting algorighm attributes 3: c3
|
||||
Then you should get: \x01\x08\x00\x00\x20\x00
|
||||
|
||||
Scenario: data object AID
|
||||
When requesting AID: 4f
|
||||
Then data should match: \xd2\x76\x00\x01\x24\x01\x02\x00......\x00\x00
|
||||
63
test/features/010_setup_passphrase.feature
Normal file
63
test/features/010_setup_passphrase.feature
Normal file
@@ -0,0 +1,63 @@
|
||||
Feature: setup pass phrase
|
||||
In order to conform OpenPGP card 2.0 specification
|
||||
A token should support pass phrase: PW1, PW3 and reset code
|
||||
|
||||
Scenario: setup PW1 (admin-less mode)
|
||||
Given cmd_change_reference_data with 1 and "123456user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1)
|
||||
Given cmd_verify with 1 and "user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2)
|
||||
Given cmd_verify with 2 and "user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-less mode)
|
||||
Given cmd_verify with 3 and "user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup reset code (in admin-less mode)
|
||||
Given cmd_put_data with d3 and "example reset code 000"
|
||||
Then it should get success
|
||||
|
||||
Scenario: reset pass phrase by reset code (in admin-less mode)
|
||||
Given cmd_reset_retry_counter with 0 and "example reset code 000new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-less mode) again
|
||||
Given cmd_verify with 3 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup PW3 (admin-full mode)
|
||||
Given cmd_change_reference_data with 3 and "new user pass phraseadmin pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-full mode)
|
||||
Given cmd_verify with 3 and "admin pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup reset code (in admin-full mode)
|
||||
Given cmd_put_data with d3 and "another reset code 000"
|
||||
Then it should get success
|
||||
|
||||
Scenario: reset pass phrase by reset code (in admin-full mode)
|
||||
Given cmd_reset_retry_counter with 0 and "another reset code 000another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "another user pass phrase"
|
||||
Then it should get success
|
||||
27
test/features/020_personalization_write.feature
Normal file
27
test/features/020_personalization_write.feature
Normal file
@@ -0,0 +1,27 @@
|
||||
Feature: personalize token write
|
||||
In order to use a token
|
||||
A token should be personalized with name, sex, url, etc.
|
||||
|
||||
Scenario: data object Login
|
||||
Given cmd_put_data with 5e and "gpg_user"
|
||||
Then it should get success
|
||||
|
||||
Scenario: data object Name
|
||||
Given cmd_put_data with 5b and "GnuPG User"
|
||||
Then it should get success
|
||||
|
||||
Scenario: data object Language preference
|
||||
Given cmd_put_data with 5f2d and "ja"
|
||||
Then it should get success
|
||||
|
||||
Scenario: data object Sex
|
||||
Given cmd_put_data with 5f35 and "1"
|
||||
Then it should get success
|
||||
|
||||
Scenario: data object URL
|
||||
Given cmd_put_data with 5f50 and "http://www.fsij.org/gnuk/"
|
||||
Then it should get success
|
||||
|
||||
Scenario: data object pw1 status bytes
|
||||
Given cmd_put_data with c4 and "\x01"
|
||||
Then it should get success
|
||||
27
test/features/021_personalization_read.feature
Normal file
27
test/features/021_personalization_read.feature
Normal file
@@ -0,0 +1,27 @@
|
||||
Feature: personalize token read
|
||||
In order to use a token
|
||||
A token should be personalized with name, sex, url, etc.
|
||||
|
||||
Scenario: data object Login
|
||||
When requesting login data: 5e
|
||||
Then you should get: gpg_user
|
||||
|
||||
Scenario: data object Name
|
||||
When requesting name: 5b
|
||||
Then you should get: GnuPG User
|
||||
|
||||
Scenario: data object Language preference
|
||||
When requesting anguage preference: 5f2d
|
||||
Then you should get: ja
|
||||
|
||||
Scenario: data object Sex
|
||||
When requesting sex: 5f35
|
||||
Then you should get: 1
|
||||
|
||||
Scenario: data object URL
|
||||
When requesting URL: 5f50
|
||||
Then you should get: http://www.fsij.org/gnuk/
|
||||
|
||||
Scenario: data object pw1 status bytes
|
||||
When requesting pw1 status bytes: c4
|
||||
Then you should get: \x01\x7f\x7f\x7f\x03\x03\x03
|
||||
56
test/features/030_key_registration.feature
Normal file
56
test/features/030_key_registration.feature
Normal file
@@ -0,0 +1,56 @@
|
||||
Feature: import keys to token
|
||||
In order to use a token
|
||||
A token should have keys
|
||||
|
||||
Scenario: importing OPENPGP.1 key (sign)
|
||||
Given a RSA key pair 0
|
||||
And importing it to the token as OPENPGP.1
|
||||
Then it should get success
|
||||
|
||||
Scenario: importing OPENPGP.2 key (decrypt)
|
||||
Given a RSA key pair 1
|
||||
And importing it to the token as OPENPGP.2
|
||||
Then it should get success
|
||||
|
||||
Scenario: importing OPENPGP.3 key (authentication)
|
||||
Given a RSA key pair 2
|
||||
And importing it to the token as OPENPGP.3
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup data object Finger print sig
|
||||
Given a fingerprint of OPENPGP.1 key
|
||||
And put the data to c7
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup data object Finger print dec
|
||||
Given a fingerprint of OPENPGP.2 key
|
||||
And put the data to c8
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup data object Finger print aut
|
||||
Given a fingerprint of OPENPGP.3 key
|
||||
And put the data to c9
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup data object keygeneration data/time sig
|
||||
Given a timestamp of OPENPGP.1 key
|
||||
And put the data to ce
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup data object keygeneration data/time dec
|
||||
Given a timestamp of OPENPGP.2 key
|
||||
And put the data to cf
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup data object keygeneration data/time aut
|
||||
Given a timestamp of OPENPGP.3 key
|
||||
And put the data to d0
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "another user pass phrase"
|
||||
Then it should get success
|
||||
71
test/features/040_passphrase_change.feature
Normal file
71
test/features/040_passphrase_change.feature
Normal file
@@ -0,0 +1,71 @@
|
||||
Feature: change pass phrase
|
||||
In order to conform OpenPGP card 2.0 specification
|
||||
A token should support pass phrase: PW1, PW3 and reset code
|
||||
|
||||
Scenario: change PW1
|
||||
Given cmd_change_reference_data with 1 and "another user pass phrasePASSPHRASE SHOULD BE LONG"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "PASSPHRASE SHOULD BE LONG"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "PASSPHRASE SHOULD BE LONG"
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup reset code again (in admin-full mode)
|
||||
Given cmd_put_data with d3 and "example reset code 000"
|
||||
Then it should get success
|
||||
|
||||
Scenario: reset pass phrase by reset code (in admin-full mode)
|
||||
Given cmd_reset_retry_counter with 0 and "example reset code 000new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: change PW3 (admin-full mode)
|
||||
Given cmd_change_reference_data with 3 and "admin pass phraseanother admin pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-full mode)
|
||||
Given cmd_verify with 3 and "another admin pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: reset pass phrase by admin (in admin-full mode)
|
||||
Given cmd_reset_retry_counter with 2 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: change PW1
|
||||
Given cmd_change_reference_data with 1 and "new user pass phraseanother user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: change PW3 (admin-full mode)
|
||||
Given cmd_change_reference_data with 3 and "another admin pass phraseadmin pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-full mode)
|
||||
Given cmd_verify with 3 and "admin pass phrase"
|
||||
Then it should get success
|
||||
31
test/features/100_compute_signature.feature
Normal file
31
test/features/100_compute_signature.feature
Normal file
@@ -0,0 +1,31 @@
|
||||
Feature: compute digital signature
|
||||
In order to use a token
|
||||
A token should compute digital signature properly
|
||||
|
||||
Scenario: compute digital signature by OPENPGP.1 key (1)
|
||||
Given a message "This is a test message."
|
||||
And let a token compute digital signature
|
||||
And compute digital signature on host with RSA key pair 0
|
||||
Then results should be same
|
||||
|
||||
Scenario: compute digital signature by OPENPGP.1 key (2)
|
||||
Given a message "This is another test message.\nMultiple lines.\n"
|
||||
And let a token compute digital signature
|
||||
And compute digital signature on host with RSA key pair 0
|
||||
Then results should be same
|
||||
|
||||
Scenario: compute digital signature by OPENPGP.3 key (1)
|
||||
Given a message "This is a test message."
|
||||
And let a token authenticate
|
||||
And compute digital signature on host with RSA key pair 2
|
||||
Then results should be same
|
||||
|
||||
Scenario: compute digital signature by OPENPGP.3 key (2)
|
||||
Given a message "This is another test message.\nMultiple lines.\n"
|
||||
And let a token authenticate
|
||||
And compute digital signature on host with RSA key pair 2
|
||||
Then results should be same
|
||||
|
||||
Scenario: data object ds counter
|
||||
When requesting ds counter: 93
|
||||
Then you should get: \x00\x00\x02
|
||||
16
test/features/101_decryption.feature
Normal file
16
test/features/101_decryption.feature
Normal file
@@ -0,0 +1,16 @@
|
||||
Feature: decryption
|
||||
In order to use a token
|
||||
A token should decrypt encrypted data
|
||||
|
||||
Scenario: decrypt by OPENPGP.2 key (1)
|
||||
Given a plain text "This is a test message."
|
||||
And encrypt it on host with RSA key pair 1
|
||||
And let a token decrypt encrypted data
|
||||
Then decrypted data should be same as a plain text
|
||||
|
||||
Scenario: decrypt by OPENPGP.2 key (2)
|
||||
Given a plain text "RSA decryption is as easy as pie."
|
||||
And encrypt it on host with RSA key pair 1
|
||||
And let a token decrypt encrypted data
|
||||
Then decrypted data should be same as a plain text
|
||||
|
||||
40
test/features/200_key_removal.feature
Normal file
40
test/features/200_key_removal.feature
Normal file
@@ -0,0 +1,40 @@
|
||||
@keygen
|
||||
Feature: key removal
|
||||
In order to use a token
|
||||
A token should have keys
|
||||
|
||||
Scenario: remove OPENPGP.1 key (sign)
|
||||
When removing a key OPENPGP.1
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove OPENPGP.2 key (decrypt)
|
||||
When removing a key OPENPGP.2
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove OPENPGP.3 key (authentication)
|
||||
When removing a key OPENPGP.3
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object Finger print sig
|
||||
Given cmd_put_data with c7 and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object Finger print dec
|
||||
Given cmd_put_data with c8 and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object Finger print aut
|
||||
Given cmd_put_data with c9 and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object keygeneration data/time sig
|
||||
Given cmd_put_data with ce and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object keygeneration data/time dec
|
||||
Given cmd_put_data with cf and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object keygeneration data/time aut
|
||||
Given cmd_put_data with d0 and ""
|
||||
Then it should get success
|
||||
32
test/features/201_setup_passphrase.feature
Normal file
32
test/features/201_setup_passphrase.feature
Normal file
@@ -0,0 +1,32 @@
|
||||
@keygen
|
||||
Feature: setup pass phrase
|
||||
In order to conform OpenPGP card 2.0 specification
|
||||
A token should support pass phrase: PW1, PW3 and reset code
|
||||
|
||||
Scenario: setup PW1 (admin-full mode)
|
||||
Given cmd_change_reference_data with 1 and "123456user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1)
|
||||
Given cmd_verify with 1 and "user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2)
|
||||
Given cmd_verify with 2 and "user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup reset code (in admin-full mode)
|
||||
Given cmd_put_data with d3 and "example reset code 000"
|
||||
Then it should get success
|
||||
|
||||
Scenario: reset pass phrase by reset code (in admin-full mode)
|
||||
Given cmd_reset_retry_counter with 0 and "example reset code 000another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "another user pass phrase"
|
||||
Then it should get success
|
||||
30
test/features/202_keygen.feature
Normal file
30
test/features/202_keygen.feature
Normal file
@@ -0,0 +1,30 @@
|
||||
@keygen
|
||||
Feature: key generation
|
||||
In order to use a token
|
||||
A token should have keys
|
||||
|
||||
Scenario: generate OPENPGP.1 key (sign)
|
||||
When generating a key of OPENPGP.1
|
||||
And put the first data to c7
|
||||
And put the second data to ce
|
||||
Then it should get success
|
||||
|
||||
Scenario: generate OPENPGP.2 key (decrypt)
|
||||
When generating a key of OPENPGP.2
|
||||
And put the first data to c8
|
||||
And put the second data to cf
|
||||
Then it should get success
|
||||
|
||||
Scenario: generate OPENPGP.3 key (authentication)
|
||||
When generating a key of OPENPGP.3
|
||||
And put the first data to c9
|
||||
And put the second data to d0
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) after keygen
|
||||
Given cmd_verify with 1 and "another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) after keygen
|
||||
Given cmd_verify with 2 and "another user pass phrase"
|
||||
Then it should get success
|
||||
71
test/features/203_passphrase_change.feature
Normal file
71
test/features/203_passphrase_change.feature
Normal file
@@ -0,0 +1,71 @@
|
||||
Feature: change pass phrase
|
||||
In order to conform OpenPGP card 2.0 specification
|
||||
A token should support pass phrase: PW1, PW3 and reset code
|
||||
|
||||
Scenario: change PW1
|
||||
Given cmd_change_reference_data with 1 and "another user pass phrasePASSPHRASE SHOULD BE LONG"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "PASSPHRASE SHOULD BE LONG"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "PASSPHRASE SHOULD BE LONG"
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup reset code again (in admin-full mode)
|
||||
Given cmd_put_data with d3 and "example reset code 111"
|
||||
Then it should get success
|
||||
|
||||
Scenario: reset pass phrase by reset code (in admin-full mode)
|
||||
Given cmd_reset_retry_counter with 0 and "example reset code 111new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: change PW3 (admin-full mode)
|
||||
Given cmd_change_reference_data with 3 and "admin pass phraseanother admin pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-full mode)
|
||||
Given cmd_verify with 3 and "another admin pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: reset pass phrase by admin (in admin-full mode)
|
||||
Given cmd_reset_retry_counter with 2 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: change PW1
|
||||
Given cmd_change_reference_data with 1 and "new user pass phraseanother user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: change PW3 (admin-full mode)
|
||||
Given cmd_change_reference_data with 3 and "another admin pass phraseadmin pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-full mode)
|
||||
Given cmd_verify with 3 and "admin pass phrase"
|
||||
Then it should get success
|
||||
36
test/features/210_compute_signature.feature
Normal file
36
test/features/210_compute_signature.feature
Normal file
@@ -0,0 +1,36 @@
|
||||
@keygen
|
||||
Feature: compute digital signature
|
||||
In order to use a token
|
||||
A token should compute digital signature properly
|
||||
|
||||
Scenario: compute digital signature by OPENPGP.1 key (1)
|
||||
Given a message "This is a test message."
|
||||
And a public key from token for OPENPGP.1
|
||||
And let a token compute digital signature
|
||||
And verify signature
|
||||
Then it should get success
|
||||
|
||||
Scenario: compute digital signature by OPENPGP.1 key (2)
|
||||
Given a message "This is another test message.\nMultiple lines.\n"
|
||||
And a public key from token for OPENPGP.1
|
||||
And let a token compute digital signature
|
||||
And verify signature
|
||||
Then it should get success
|
||||
|
||||
Scenario: compute digital signature by OPENPGP.3 key (1)
|
||||
Given a message "This is a test message."
|
||||
And a public key from token for OPENPGP.3
|
||||
And let a token authenticate
|
||||
And verify signature
|
||||
Then it should get success
|
||||
|
||||
Scenario: compute digital signature by OPENPGP.3 key (2)
|
||||
Given a message "This is another test message.\nMultiple lines.\n"
|
||||
And a public key from token for OPENPGP.3
|
||||
And let a token authenticate
|
||||
And verify signature
|
||||
Then it should get success
|
||||
|
||||
Scenario: data object ds counter
|
||||
When requesting ds counter: 93
|
||||
Then you should get: \x00\x00\x02
|
||||
19
test/features/211_decryption.feature
Normal file
19
test/features/211_decryption.feature
Normal file
@@ -0,0 +1,19 @@
|
||||
@keygen
|
||||
Feature: decryption
|
||||
In order to use a token
|
||||
A token should decrypt encrypted data
|
||||
|
||||
Scenario: decrypt by OPENPGP.2 key (1)
|
||||
Given a plain text "This is a test message."
|
||||
And a public key from token for OPENPGP.2
|
||||
And encrypt it on host
|
||||
And let a token decrypt encrypted data
|
||||
Then decrypted data should be same as a plain text
|
||||
|
||||
Scenario: decrypt by OPENPGP.2 key (2)
|
||||
Given a plain text "RSA decryption is as easy as pie."
|
||||
And a public key from token for OPENPGP.2
|
||||
And encrypt it on host
|
||||
And let a token decrypt encrypted data
|
||||
Then decrypted data should be same as a plain text
|
||||
|
||||
39
test/features/370_key_removal.feature
Normal file
39
test/features/370_key_removal.feature
Normal file
@@ -0,0 +1,39 @@
|
||||
Feature: key removal
|
||||
In order to use a token
|
||||
A token should have keys
|
||||
|
||||
Scenario: remove OPENPGP.1 key (sign)
|
||||
When removing a key OPENPGP.1
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove OPENPGP.2 key (decrypt)
|
||||
When removing a key OPENPGP.2
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove OPENPGP.3 key (authentication)
|
||||
When removing a key OPENPGP.3
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object Finger print sig
|
||||
Given cmd_put_data with c7 and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object Finger print dec
|
||||
Given cmd_put_data with c8 and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object Finger print aut
|
||||
Given cmd_put_data with c9 and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object keygeneration data/time sig
|
||||
Given cmd_put_data with ce and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object keygeneration data/time dec
|
||||
Given cmd_put_data with cf and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object keygeneration data/time aut
|
||||
Given cmd_put_data with d0 and ""
|
||||
Then it should get success
|
||||
27
test/features/380_personalization_reset.feature
Normal file
27
test/features/380_personalization_reset.feature
Normal file
@@ -0,0 +1,27 @@
|
||||
Feature: removal of data objects
|
||||
In order to use a token
|
||||
A token should have personalized data
|
||||
|
||||
Scenario: remove data object Login
|
||||
Given cmd_put_data with 5e and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object Name
|
||||
Given cmd_put_data with 5b and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object Language preference
|
||||
Given cmd_put_data with 5f2d and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object Sex
|
||||
Given cmd_put_data with 5f35 and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object URL
|
||||
Given cmd_put_data with 5f50 and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object pw1 status bytes
|
||||
Given cmd_put_data with c4 and "\x00"
|
||||
Then it should get success
|
||||
7
test/features/390_reset_passphrase.feature
Normal file
7
test/features/390_reset_passphrase.feature
Normal file
@@ -0,0 +1,7 @@
|
||||
Feature: reset pass phrase
|
||||
In order to conform OpenPGP card 2.0 specification
|
||||
A token should support pass phrase: PW1, PW3 and reset code
|
||||
|
||||
Scenario: setup PW3 (admin-full mode)
|
||||
Given cmd_change_reference_data with 3 and "admin pass phrase"
|
||||
Then it should get success
|
||||
79
test/features/400_empty_check.feature
Normal file
79
test/features/400_empty_check.feature
Normal file
@@ -0,0 +1,79 @@
|
||||
Feature: confirm empty token
|
||||
In order to start tests
|
||||
A token should be empty (no data, no keys)
|
||||
|
||||
Scenario: data object Login
|
||||
When requesting login data: 5e
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object Name
|
||||
When requesting name: 5b
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object Language preference
|
||||
When requesting anguage preference: 5f2d
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object Sex
|
||||
When requesting sex: 5f35
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object URL
|
||||
When requesting URL: 5f50
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object ds counter
|
||||
When requesting ds counter: 93
|
||||
Then you should get: \x00\x00\x00
|
||||
|
||||
Scenario: data object pw1 status bytes
|
||||
When requesting pw1 status bytes: c4
|
||||
Then you should get: \x00\x7f\x7f\x7f\x03\x03\x03
|
||||
|
||||
Scenario: data object finger print 0
|
||||
When requesting finger print: c5
|
||||
Then you should get: \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
|
||||
|
||||
Scenario: data object finger print 1
|
||||
When requesting finger print: c7
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object finger print 2
|
||||
When requesting finger print: c8
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object finger print 3
|
||||
When requesting finger print: c9
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object CA finger print 0
|
||||
When requesting finger print: c6
|
||||
Then you should get: \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
|
||||
|
||||
Scenario: data object CA finger print 1
|
||||
When requesting finger print: ca
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object CA finger print 2
|
||||
When requesting finger print: cb
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object CA finger print 3
|
||||
When requesting finger print: cc
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object date/time of key pair 0
|
||||
When requesting date/time of key pair: cd
|
||||
Then you should get: \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
|
||||
|
||||
Scenario: data object date/time of key pair 1
|
||||
When requesting date/time of key pair: ce
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object date/time of key pair 2
|
||||
When requesting date/time of key pair: cf
|
||||
Then you should get NULL
|
||||
|
||||
Scenario: data object date/time of key pair 3
|
||||
When requesting date/time of key pair: d0
|
||||
Then you should get NULL
|
||||
15
test/features/401_empty_check_passphrase.feature
Normal file
15
test/features/401_empty_check_passphrase.feature
Normal file
@@ -0,0 +1,15 @@
|
||||
Feature: confirm empty token
|
||||
In order to start tests
|
||||
A token should be empty (no pass phrase)
|
||||
|
||||
Scenario: verify PW1 factory setting (1)
|
||||
Given cmd_verify with 1 and "123456"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 factory setting (2)
|
||||
Given cmd_verify with 2 and "123456"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 factory setting
|
||||
Given cmd_verify with 3 and "12345678"
|
||||
Then it should get success
|
||||
27
test/features/402_get_data_static.feature
Normal file
27
test/features/402_get_data_static.feature
Normal file
@@ -0,0 +1,27 @@
|
||||
Feature: command GET DATA
|
||||
In order to conform OpenPGP card 2.0 specification
|
||||
A token should support all mandatory features of the specification
|
||||
|
||||
Scenario: data object historical bytes
|
||||
When requesting historical bytes: 5f52
|
||||
Then you should get: \x00\x31\x84\x73\x80\x01\x80\x00\x90\x00
|
||||
|
||||
Scenario: data object extended capabilities
|
||||
When requesting extended capabilities: c0
|
||||
Then you should get: \x30\x00\x00\x00\x00\x00\x00\xff\x01\x00
|
||||
|
||||
Scenario: data object algorithm attributes 1
|
||||
When requesting algorithm attributes 1: c1
|
||||
Then you should get: \x01\x08\x00\x00\x20\x00
|
||||
|
||||
Scenario: data object algorithm attributes 2
|
||||
When requesting algorithm attributes 2: c2
|
||||
Then you should get: \x01\x08\x00\x00\x20\x00
|
||||
|
||||
Scenario: data object algorithm attributes 3
|
||||
When requesting algorighm attributes 3: c3
|
||||
Then you should get: \x01\x08\x00\x00\x20\x00
|
||||
|
||||
Scenario: data object AID
|
||||
When requesting AID: 4f
|
||||
Then data should match: \xd2\x76\x00\x01\x24\x01\x02\x00......\x00\x00
|
||||
55
test/features/410_setup_passphrase.feature
Normal file
55
test/features/410_setup_passphrase.feature
Normal file
@@ -0,0 +1,55 @@
|
||||
Feature: setup pass phrase
|
||||
In order to conform OpenPGP card 2.0 specification
|
||||
A token should support pass phrase: PW1, PW3 and reset code
|
||||
|
||||
Scenario: setup PW1 (admin-less mode)
|
||||
Given cmd_change_reference_data with 1 and "123456user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1)
|
||||
Given cmd_verify with 1 and "user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2)
|
||||
Given cmd_verify with 2 and "user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-less mode)
|
||||
Given cmd_verify with 3 and "user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup reset code (in admin-less mode)
|
||||
Given cmd_put_data with d3 and "example reset code 000"
|
||||
Then it should get success
|
||||
|
||||
Scenario: reset pass phrase by reset code (in admin-less mode)
|
||||
Given cmd_reset_retry_counter with 0 and "example reset code 000new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-less mode) again
|
||||
Given cmd_verify with 3 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: change PW1
|
||||
Given cmd_change_reference_data with 1 and "new user pass phraseanother user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-less mode) again
|
||||
Given cmd_verify with 3 and "another user pass phrase"
|
||||
Then it should get success
|
||||
27
test/features/420_personalization_write.feature
Normal file
27
test/features/420_personalization_write.feature
Normal file
@@ -0,0 +1,27 @@
|
||||
Feature: personalize token write
|
||||
In order to use a token
|
||||
A token should be personalized with name, sex, url, etc.
|
||||
|
||||
Scenario: data object Login
|
||||
Given cmd_put_data with 5e and "gpg_user"
|
||||
Then it should get success
|
||||
|
||||
Scenario: data object Name
|
||||
Given cmd_put_data with 5b and "GnuPG User"
|
||||
Then it should get success
|
||||
|
||||
Scenario: data object Language preference
|
||||
Given cmd_put_data with 5f2d and "ja"
|
||||
Then it should get success
|
||||
|
||||
Scenario: data object Sex
|
||||
Given cmd_put_data with 5f35 and "1"
|
||||
Then it should get success
|
||||
|
||||
Scenario: data object URL
|
||||
Given cmd_put_data with 5f50 and "http://www.fsij.org/gnuk/"
|
||||
Then it should get success
|
||||
|
||||
Scenario: data object pw1 status bytes
|
||||
Given cmd_put_data with c4 and "\x01"
|
||||
Then it should get success
|
||||
27
test/features/421_personalization_read.feature
Normal file
27
test/features/421_personalization_read.feature
Normal file
@@ -0,0 +1,27 @@
|
||||
Feature: personalize token read
|
||||
In order to use a token
|
||||
A token should be personalized with name, sex, url, etc.
|
||||
|
||||
Scenario: data object Login
|
||||
When requesting login data: 5e
|
||||
Then you should get: gpg_user
|
||||
|
||||
Scenario: data object Name
|
||||
When requesting name: 5b
|
||||
Then you should get: GnuPG User
|
||||
|
||||
Scenario: data object Language preference
|
||||
When requesting anguage preference: 5f2d
|
||||
Then you should get: ja
|
||||
|
||||
Scenario: data object Sex
|
||||
When requesting sex: 5f35
|
||||
Then you should get: 1
|
||||
|
||||
Scenario: data object URL
|
||||
When requesting URL: 5f50
|
||||
Then you should get: http://www.fsij.org/gnuk/
|
||||
|
||||
Scenario: data object pw1 status bytes
|
||||
When requesting pw1 status bytes: c4
|
||||
Then you should get: \x01\x7f\x7f\x7f\x03\x03\x03
|
||||
56
test/features/430_key_registration.feature
Normal file
56
test/features/430_key_registration.feature
Normal file
@@ -0,0 +1,56 @@
|
||||
Feature: import keys to token
|
||||
In order to use a token
|
||||
A token should have keys
|
||||
|
||||
Scenario: importing OPENPGP.1 key (sign)
|
||||
Given a RSA key pair 0
|
||||
And importing it to the token as OPENPGP.1
|
||||
Then it should get success
|
||||
|
||||
Scenario: importing OPENPGP.2 key (decrypt)
|
||||
Given a RSA key pair 1
|
||||
And importing it to the token as OPENPGP.2
|
||||
Then it should get success
|
||||
|
||||
Scenario: importing OPENPGP.3 key (authentication)
|
||||
Given a RSA key pair 2
|
||||
And importing it to the token as OPENPGP.3
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup data object Finger print sig
|
||||
Given a fingerprint of OPENPGP.1 key
|
||||
And put the data to c7
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup data object Finger print dec
|
||||
Given a fingerprint of OPENPGP.2 key
|
||||
And put the data to c8
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup data object Finger print aut
|
||||
Given a fingerprint of OPENPGP.3 key
|
||||
And put the data to c9
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup data object keygeneration data/time sig
|
||||
Given a timestamp of OPENPGP.1 key
|
||||
And put the data to ce
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup data object keygeneration data/time dec
|
||||
Given a timestamp of OPENPGP.2 key
|
||||
And put the data to cf
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup data object keygeneration data/time aut
|
||||
Given a timestamp of OPENPGP.3 key
|
||||
And put the data to d0
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "another user pass phrase"
|
||||
Then it should get success
|
||||
51
test/features/440_passphrase_change.feature
Normal file
51
test/features/440_passphrase_change.feature
Normal file
@@ -0,0 +1,51 @@
|
||||
Feature: change pass phrase
|
||||
In order to conform OpenPGP card 2.0 specification
|
||||
A token should support pass phrase: PW1, PW3 and reset code
|
||||
|
||||
Scenario: change PW1 (in admin-less mode)
|
||||
Given cmd_change_reference_data with 1 and "another user pass phrasePASSPHRASE SHOULD BE LONG"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "PASSPHRASE SHOULD BE LONG"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "PASSPHRASE SHOULD BE LONG"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-less mode)
|
||||
Given cmd_verify with 3 and "PASSPHRASE SHOULD BE LONG"
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup reset code again (in admin-less mode)
|
||||
Given cmd_put_data with d3 and "example reset code 000"
|
||||
Then it should get success
|
||||
|
||||
Scenario: reset pass phrase by reset code (in admin-less mode)
|
||||
Given cmd_reset_retry_counter with 0 and "example reset code 000new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: change PW1
|
||||
Given cmd_change_reference_data with 1 and "new user pass phraseanother user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-less mode)
|
||||
Given cmd_verify with 3 and "another user pass phrase"
|
||||
Then it should get success
|
||||
31
test/features/500_compute_signature.feature
Normal file
31
test/features/500_compute_signature.feature
Normal file
@@ -0,0 +1,31 @@
|
||||
Feature: compute digital signature
|
||||
In order to use a token
|
||||
A token should compute digital signature properly
|
||||
|
||||
Scenario: compute digital signature by OPENPGP.1 key (1)
|
||||
Given a message "This is a test message."
|
||||
And let a token compute digital signature
|
||||
And compute digital signature on host with RSA key pair 0
|
||||
Then results should be same
|
||||
|
||||
Scenario: compute digital signature by OPENPGP.1 key (2)
|
||||
Given a message "This is another test message.\nMultiple lines.\n"
|
||||
And let a token compute digital signature
|
||||
And compute digital signature on host with RSA key pair 0
|
||||
Then results should be same
|
||||
|
||||
Scenario: compute digital signature by OPENPGP.3 key (1)
|
||||
Given a message "This is a test message."
|
||||
And let a token authenticate
|
||||
And compute digital signature on host with RSA key pair 2
|
||||
Then results should be same
|
||||
|
||||
Scenario: compute digital signature by OPENPGP.3 key (2)
|
||||
Given a message "This is another test message.\nMultiple lines.\n"
|
||||
And let a token authenticate
|
||||
And compute digital signature on host with RSA key pair 2
|
||||
Then results should be same
|
||||
|
||||
Scenario: data object ds counter
|
||||
When requesting ds counter: 93
|
||||
Then you should get: \x00\x00\x02
|
||||
16
test/features/501_decryption.feature
Normal file
16
test/features/501_decryption.feature
Normal file
@@ -0,0 +1,16 @@
|
||||
Feature: decryption
|
||||
In order to use a token
|
||||
A token should decrypt encrypted data
|
||||
|
||||
Scenario: decrypt by OPENPGP.2 key (1)
|
||||
Given a plain text "This is a test message."
|
||||
And encrypt it on host with RSA key pair 1
|
||||
And let a token decrypt encrypted data
|
||||
Then decrypted data should be same as a plain text
|
||||
|
||||
Scenario: decrypt by OPENPGP.2 key (2)
|
||||
Given a plain text "RSA decryption is as easy as pie."
|
||||
And encrypt it on host with RSA key pair 1
|
||||
And let a token decrypt encrypted data
|
||||
Then decrypted data should be same as a plain text
|
||||
|
||||
44
test/features/600_key_removal.feature
Normal file
44
test/features/600_key_removal.feature
Normal file
@@ -0,0 +1,44 @@
|
||||
@keygen
|
||||
Feature: key removal
|
||||
In order to use a token
|
||||
A token should have keys
|
||||
|
||||
Scenario: remove OPENPGP.1 key (sign)
|
||||
When removing a key OPENPGP.1
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove OPENPGP.2 key (decrypt)
|
||||
When removing a key OPENPGP.2
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove OPENPGP.3 key (authentication)
|
||||
When removing a key OPENPGP.3
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-less mode)
|
||||
Given cmd_verify with 3 and "12345678"
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object Finger print sig
|
||||
Given cmd_put_data with c7 and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object Finger print dec
|
||||
Given cmd_put_data with c8 and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object Finger print aut
|
||||
Given cmd_put_data with c9 and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object keygeneration data/time sig
|
||||
Given cmd_put_data with ce and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object keygeneration data/time dec
|
||||
Given cmd_put_data with cf and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object keygeneration data/time aut
|
||||
Given cmd_put_data with d0 and ""
|
||||
Then it should get success
|
||||
40
test/features/601_setup_passphrase.feature
Normal file
40
test/features/601_setup_passphrase.feature
Normal file
@@ -0,0 +1,40 @@
|
||||
@keygen
|
||||
Feature: setup pass phrase
|
||||
In order to conform OpenPGP card 2.0 specification
|
||||
A token should support pass phrase: PW1, PW3 and reset code
|
||||
|
||||
Scenario: setup PW1 (admin-less mode)
|
||||
Given cmd_change_reference_data with 1 and "123456user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1)
|
||||
Given cmd_verify with 1 and "user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2)
|
||||
Given cmd_verify with 2 and "user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-less mode)
|
||||
Given cmd_verify with 3 and "user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup reset code (in admin-less mode)
|
||||
Given cmd_put_data with d3 and "example reset code 000"
|
||||
Then it should get success
|
||||
|
||||
Scenario: reset pass phrase by reset code (in admin-less mode)
|
||||
Given cmd_reset_retry_counter with 0 and "example reset code 000another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-less mode) again
|
||||
Given cmd_verify with 3 and "another user pass phrase"
|
||||
Then it should get success
|
||||
30
test/features/602_keygen.feature
Normal file
30
test/features/602_keygen.feature
Normal file
@@ -0,0 +1,30 @@
|
||||
@keygen
|
||||
Feature: key generation
|
||||
In order to use a token
|
||||
A token should have keys
|
||||
|
||||
Scenario: generate OPENPGP.1 key (sign)
|
||||
When generating a key of OPENPGP.1
|
||||
And put the first data to c7
|
||||
And put the second data to ce
|
||||
Then it should get success
|
||||
|
||||
Scenario: generate OPENPGP.2 key (decrypt)
|
||||
When generating a key of OPENPGP.2
|
||||
And put the first data to c8
|
||||
And put the second data to cf
|
||||
Then it should get success
|
||||
|
||||
Scenario: generate OPENPGP.3 key (authentication)
|
||||
When generating a key of OPENPGP.3
|
||||
And put the first data to c9
|
||||
And put the second data to d0
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) after keygen
|
||||
Given cmd_verify with 1 and "another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) after keygen
|
||||
Given cmd_verify with 2 and "another user pass phrase"
|
||||
Then it should get success
|
||||
67
test/features/603_passphrase_change.feature
Normal file
67
test/features/603_passphrase_change.feature
Normal file
@@ -0,0 +1,67 @@
|
||||
Feature: change pass phrase
|
||||
In order to conform OpenPGP card 2.0 specification
|
||||
A token should support pass phrase: PW1, PW3 and reset code
|
||||
|
||||
Scenario: change PW1
|
||||
Given cmd_change_reference_data with 1 and "another user pass phrasePASSPHRASE SHOULD BE LONG"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "PASSPHRASE SHOULD BE LONG"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "PASSPHRASE SHOULD BE LONG"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-less mode)
|
||||
Given cmd_verify with 3 and "PASSPHRASE SHOULD BE LONG"
|
||||
Then it should get success
|
||||
|
||||
Scenario: setup reset code again (in admin-less mode)
|
||||
Given cmd_put_data with d3 and "example reset code 111"
|
||||
Then it should get success
|
||||
|
||||
Scenario: reset pass phrase by reset code (in admin-less mode)
|
||||
Given cmd_reset_retry_counter with 0 and "example reset code 111new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-less mode)
|
||||
Given cmd_verify with 3 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: reset pass phrase by admin (in admin-less mode)
|
||||
Given cmd_reset_retry_counter with 2 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "new user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: change PW1
|
||||
Given cmd_change_reference_data with 1 and "new user pass phraseanother user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (1) again
|
||||
Given cmd_verify with 1 and "another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW1 (2) again
|
||||
Given cmd_verify with 2 and "another user pass phrase"
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-less mode)
|
||||
Given cmd_verify with 3 and "another user pass phrase"
|
||||
Then it should get success
|
||||
36
test/features/610_compute_signature.feature
Normal file
36
test/features/610_compute_signature.feature
Normal file
@@ -0,0 +1,36 @@
|
||||
@keygen
|
||||
Feature: compute digital signature
|
||||
In order to use a token
|
||||
A token should compute digital signature properly
|
||||
|
||||
Scenario: compute digital signature by OPENPGP.1 key (1)
|
||||
Given a message "This is a test message."
|
||||
And a public key from token for OPENPGP.1
|
||||
And let a token compute digital signature
|
||||
And verify signature
|
||||
Then it should get success
|
||||
|
||||
Scenario: compute digital signature by OPENPGP.1 key (2)
|
||||
Given a message "This is another test message.\nMultiple lines.\n"
|
||||
And a public key from token for OPENPGP.1
|
||||
And let a token compute digital signature
|
||||
And verify signature
|
||||
Then it should get success
|
||||
|
||||
Scenario: compute digital signature by OPENPGP.3 key (1)
|
||||
Given a message "This is a test message."
|
||||
And a public key from token for OPENPGP.3
|
||||
And let a token authenticate
|
||||
And verify signature
|
||||
Then it should get success
|
||||
|
||||
Scenario: compute digital signature by OPENPGP.3 key (2)
|
||||
Given a message "This is another test message.\nMultiple lines.\n"
|
||||
And a public key from token for OPENPGP.3
|
||||
And let a token authenticate
|
||||
And verify signature
|
||||
Then it should get success
|
||||
|
||||
Scenario: data object ds counter
|
||||
When requesting ds counter: 93
|
||||
Then you should get: \x00\x00\x02
|
||||
19
test/features/611_decryption.feature
Normal file
19
test/features/611_decryption.feature
Normal file
@@ -0,0 +1,19 @@
|
||||
@keygen
|
||||
Feature: decryption
|
||||
In order to use a token
|
||||
A token should decrypt encrypted data
|
||||
|
||||
Scenario: decrypt by OPENPGP.2 key (1)
|
||||
Given a plain text "This is a test message."
|
||||
And a public key from token for OPENPGP.2
|
||||
And encrypt it on host
|
||||
And let a token decrypt encrypted data
|
||||
Then decrypted data should be same as a plain text
|
||||
|
||||
Scenario: decrypt by OPENPGP.2 key (2)
|
||||
Given a plain text "RSA decryption is as easy as pie."
|
||||
And a public key from token for OPENPGP.2
|
||||
And encrypt it on host
|
||||
And let a token decrypt encrypted data
|
||||
Then decrypted data should be same as a plain text
|
||||
|
||||
43
test/features/970_key_removal.feature
Normal file
43
test/features/970_key_removal.feature
Normal file
@@ -0,0 +1,43 @@
|
||||
Feature: key removal
|
||||
In order to use a token
|
||||
A token should have keys
|
||||
|
||||
Scenario: remove OPENPGP.1 key (sign)
|
||||
When removing a key OPENPGP.1
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove OPENPGP.2 key (decrypt)
|
||||
When removing a key OPENPGP.2
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove OPENPGP.3 key (authentication)
|
||||
When removing a key OPENPGP.3
|
||||
Then it should get success
|
||||
|
||||
Scenario: verify PW3 (admin-less mode)
|
||||
Given cmd_verify with 3 and "12345678"
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object Finger print sig
|
||||
Given cmd_put_data with c7 and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object Finger print dec
|
||||
Given cmd_put_data with c8 and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object Finger print aut
|
||||
Given cmd_put_data with c9 and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object keygeneration data/time sig
|
||||
Given cmd_put_data with ce and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object keygeneration data/time dec
|
||||
Given cmd_put_data with cf and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object keygeneration data/time aut
|
||||
Given cmd_put_data with d0 and ""
|
||||
Then it should get success
|
||||
27
test/features/980_personalization_reset.feature
Normal file
27
test/features/980_personalization_reset.feature
Normal file
@@ -0,0 +1,27 @@
|
||||
Feature: removal of data objects
|
||||
In order to use a token
|
||||
A token should have personalized data
|
||||
|
||||
Scenario: remove data object Login
|
||||
Given cmd_put_data with 5e and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object Name
|
||||
Given cmd_put_data with 5b and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object Language preference
|
||||
Given cmd_put_data with 5f2d and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object Sex
|
||||
Given cmd_put_data with 5f35 and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object URL
|
||||
Given cmd_put_data with 5f50 and ""
|
||||
Then it should get success
|
||||
|
||||
Scenario: remove data object pw1 status bytes
|
||||
Given cmd_put_data with c4 and "\x00"
|
||||
Then it should get success
|
||||
7
test/features/990_reset_passphrase.feature
Normal file
7
test/features/990_reset_passphrase.feature
Normal file
@@ -0,0 +1,7 @@
|
||||
Feature: confirm factory setting pass phrase
|
||||
In order to conform OpenPGP card 2.0 specification
|
||||
A token should support pass phrase: PW1, PW3 and reset code
|
||||
|
||||
Scenario: verify PW3 (admin-less mode)
|
||||
Given cmd_verify with 3 and "12345678"
|
||||
Then it should get success
|
||||
169
test/features/steps.py
Normal file
169
test/features/steps.py
Normal file
@@ -0,0 +1,169 @@
|
||||
from freshen import *
|
||||
from freshen.checks import *
|
||||
from nose.tools import assert_regexp_matches
|
||||
from binascii import hexlify
|
||||
|
||||
import ast
|
||||
|
||||
import gnuk
|
||||
import rsa_keys
|
||||
|
||||
@Before
|
||||
def ini(sc):
|
||||
if not ftc.token:
|
||||
ftc.token = gnuk.get_gnuk_device()
|
||||
ftc.token.cmd_select_openpgp()
|
||||
|
||||
@Given("cmd_verify with (.*) and \"(.*)\"")
|
||||
def cmd_verify(who_str,pass_str):
|
||||
who = int(who_str)
|
||||
scc.result = ftc.token.cmd_verify(who, pass_str)
|
||||
|
||||
@Given("cmd_change_reference_data with (.*) and \"(.*)\"")
|
||||
def cmd_change_reference_data(who_str,pass_str):
|
||||
who = int(who_str)
|
||||
scc.result = ftc.token.cmd_change_reference_data(who, pass_str)
|
||||
|
||||
@Given("cmd_put_data with (.*) and (\".*\")")
|
||||
def cmd_put_data(tag_str,content_str_repr):
|
||||
content_str = ast.literal_eval(content_str_repr)
|
||||
tag = int(tag_str, 16)
|
||||
tagh = tag >> 8
|
||||
tagl = tag & 0xff
|
||||
scc.result = ftc.token.cmd_put_data(tagh, tagl, content_str)
|
||||
|
||||
@Given("cmd_reset_retry_counter with (.*) and \"(.*)\"")
|
||||
def cmd_reset_retry_counter(how_str, data):
|
||||
how = int(how_str)
|
||||
scc.result = ftc.token.cmd_reset_retry_counter(how, data)
|
||||
|
||||
@Given("a RSA key pair (.*)")
|
||||
def set_rsa_key(keyno_str):
|
||||
scc.keyno = int(keyno_str)
|
||||
|
||||
@Given("importing it to the token as OPENPGP.(.*)")
|
||||
def import_key(openpgp_keyno_str):
|
||||
openpgp_keyno = int(openpgp_keyno_str)
|
||||
t = rsa_keys.build_privkey_template(openpgp_keyno, scc.keyno)
|
||||
scc.result = ftc.token.cmd_put_data_odd(0x3f, 0xff, t)
|
||||
|
||||
@Given("a fingerprint of OPENPGP.(.*) key")
|
||||
def get_key_fpr(openpgp_keyno_str):
|
||||
openpgp_keyno = int(openpgp_keyno_str)
|
||||
scc.result = rsa_keys.fpr[openpgp_keyno - 1]
|
||||
|
||||
@Given("a timestamp of OPENPGP.(.*) key")
|
||||
def get_key_timestamp(openpgp_keyno_str):
|
||||
openpgp_keyno = int(openpgp_keyno_str)
|
||||
scc.result = rsa_keys.timestamp[openpgp_keyno - 1]
|
||||
|
||||
@Given("put the data to (.*)")
|
||||
def cmd_put_data_with_result(tag_str):
|
||||
tag = int(tag_str, 16)
|
||||
tagh = tag >> 8
|
||||
tagl = tag & 0xff
|
||||
scc.result = ftc.token.cmd_put_data(tagh, tagl, scc.result)
|
||||
|
||||
@Given("a message (\".*\")")
|
||||
def set_msg(content_str_repr):
|
||||
msg = ast.literal_eval(content_str_repr)
|
||||
scc.digestinfo = rsa_keys.compute_digestinfo(msg)
|
||||
|
||||
@Given("a public key from token for OPENPGP.(.*)")
|
||||
def get_public_key(openpgp_keyno_str):
|
||||
openpgp_keyno = int(openpgp_keyno_str)
|
||||
scc.pubkey_info = ftc.token.cmd_get_public_key(openpgp_keyno)
|
||||
|
||||
@Given("verify signature")
|
||||
def verify_signature():
|
||||
scc.result = rsa_keys.verify_signature(scc.pubkey_info, scc.digestinfo, scc.sig)
|
||||
|
||||
@Given("let a token compute digital signature")
|
||||
def compute_signature():
|
||||
scc.sig = int(hexlify(ftc.token.cmd_pso(0x9e, 0x9a, scc.digestinfo)),16)
|
||||
|
||||
@Given("let a token authenticate")
|
||||
def internal_authenticate():
|
||||
scc.sig = int(hexlify(ftc.token.cmd_internal_authenticate(scc.digestinfo)),16)
|
||||
|
||||
@Given("compute digital signature on host with RSA key pair (.*)")
|
||||
def compute_signature_on_host(keyno_str):
|
||||
keyno = int(keyno_str)
|
||||
scc.result = rsa_keys.compute_signature(keyno, scc.digestinfo)
|
||||
|
||||
@Given("a plain text (\".*\")")
|
||||
def set_plaintext(content_str_repr):
|
||||
scc.plaintext = ast.literal_eval(content_str_repr)
|
||||
|
||||
@Given("encrypt it on host with RSA key pair (.*)$")
|
||||
def encrypt_on_host(keyno_str):
|
||||
keyno = int(keyno_str)
|
||||
scc.ciphertext = rsa_keys.encrypt(keyno, scc.plaintext)
|
||||
|
||||
@Given("encrypt it on host$")
|
||||
def encrypt_on_host_public_key():
|
||||
scc.ciphertext = rsa_keys.encrypt_with_pubkey(scc.pubkey_info, scc.plaintext)
|
||||
|
||||
@Given("let a token decrypt encrypted data")
|
||||
def decrypt():
|
||||
scc.result = ftc.token.cmd_pso_longdata(0x80, 0x86, scc.ciphertext)
|
||||
|
||||
|
||||
@When("requesting (.+): ([0-9a-fA-F]+)")
|
||||
def get_data(name, tag_str):
|
||||
tag = int(tag_str, 16)
|
||||
tagh = tag >> 8
|
||||
tagl = tag & 0xff
|
||||
scc.result = ftc.token.cmd_get_data(tagh, tagl)
|
||||
|
||||
@When("removing a key OPENPGP.(.*)")
|
||||
def remove_key(openpgp_keyno_str):
|
||||
openpgp_keyno = int(openpgp_keyno_str)
|
||||
t = rsa_keys.build_privkey_template_for_remove(openpgp_keyno)
|
||||
scc.result = ftc.token.cmd_put_data_odd(0x3f, 0xff, t)
|
||||
|
||||
@When("generating a key of OPENPGP.(.*)")
|
||||
def generate_key(openpgp_keyno_str):
|
||||
openpgp_keyno = int(openpgp_keyno_str)
|
||||
pubkey_info = ftc.token.cmd_genkey(openpgp_keyno)
|
||||
scc.data = rsa_keys.calc_fpr(pubkey_info[0], pubkey_info[1])
|
||||
|
||||
@When("put the first data to (.*)")
|
||||
def cmd_put_data_first_with_result(tag_str):
|
||||
tag = int(tag_str, 16)
|
||||
tagh = tag >> 8
|
||||
tagl = tag & 0xff
|
||||
scc.result = ftc.token.cmd_put_data(tagh, tagl, scc.data[0])
|
||||
|
||||
@When("put the second data to (.*)")
|
||||
def cmd_put_data_second_with_result(tag_str):
|
||||
tag = int(tag_str, 16)
|
||||
tagh = tag >> 8
|
||||
tagl = tag & 0xff
|
||||
result = ftc.token.cmd_put_data(tagh, tagl, scc.data[1])
|
||||
scc.result = (scc.result and result)
|
||||
|
||||
@Then("you should get: (.*)")
|
||||
def check_result(v):
|
||||
value = ast.literal_eval("'" + v + "'")
|
||||
assert_equal(scc.result, value)
|
||||
|
||||
@Then("it should get success")
|
||||
def check_success():
|
||||
assert_equal(scc.result, True)
|
||||
|
||||
@Then("you should get NULL")
|
||||
def check_null():
|
||||
assert_equal(scc.result, "")
|
||||
|
||||
@Then("data should match: (.*)")
|
||||
def check_regexp(re):
|
||||
assert_regexp_matches(scc.result, re)
|
||||
|
||||
@Then("results should be same")
|
||||
def check_signature():
|
||||
assert_equal(scc.sig, scc.result)
|
||||
|
||||
@Then("decrypted data should be same as a plain text")
|
||||
def check_decrypt():
|
||||
assert_equal(scc.plaintext, scc.result)
|
||||
25
test/generate_keys.py
Normal file
25
test/generate_keys.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from Crypto import Random
|
||||
from Crypto.PublicKey import RSA
|
||||
from binascii import hexlify
|
||||
|
||||
def print_key_in_hex(k):
|
||||
prv = k.exportKey(format='DER', pkcs=8)
|
||||
n = prv[38:38+256]
|
||||
e = prv[38+256+2:38+256+2+3]
|
||||
p = prv[38+256+2+3+4+257+4:38+256+2+3+4+257+4+128]
|
||||
q = prv[38+256+2+3+4+257+4+128+4:38+256+2+3+4+257+4+128+4+128]
|
||||
n_str = hexlify(n)
|
||||
e_str = hexlify(e)
|
||||
p_str = hexlify(p)
|
||||
q_str = hexlify(q)
|
||||
if int(p_str, 16)*int(q_str, 16) != int(n_str, 16):
|
||||
raise ValueError("wrong key", k)
|
||||
print n_str
|
||||
print e_str
|
||||
print p_str
|
||||
print q_str
|
||||
|
||||
rng = Random.new().read
|
||||
key = RSA.generate(2048, rng)
|
||||
|
||||
print_key_in_hex(key)
|
||||
404
test/gnuk.py
Normal file
404
test/gnuk.py
Normal file
@@ -0,0 +1,404 @@
|
||||
"""
|
||||
gnuk.py - a library for Gnuk Token
|
||||
This tool is for importing certificate, writing serial number, etc.
|
||||
|
||||
Copyright (C) 2011, 2012 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 *
|
||||
import string
|
||||
|
||||
# Assume only single CCID device is attached to computer, and it's Gnuk Token
|
||||
|
||||
import usb
|
||||
|
||||
# USB class, subclass, protocol
|
||||
CCID_CLASS = 0x0B
|
||||
CCID_SUBCLASS = 0x00
|
||||
CCID_PROTOCOL_0 = 0x00
|
||||
|
||||
def icc_compose(msg_type, data_len, slot, seq, param, data):
|
||||
return pack('<BiBBBH', msg_type, data_len, slot, seq, 0, param) + data
|
||||
|
||||
def iso7816_compose(ins, p1, p2, data, cls=0x00):
|
||||
data_len = len(data)
|
||||
if data_len == 0:
|
||||
return pack('>BBBB', cls, ins, p1, p2)
|
||||
else:
|
||||
return pack('>BBBBB', cls, ins, p1, p2, data_len) + data
|
||||
|
||||
def list_to_string(l):
|
||||
return string.join([chr(c) for c in l], '')
|
||||
|
||||
class gnuk_token(object):
|
||||
def __init__(self, device, configuration, interface):
|
||||
"""
|
||||
__init__(device, configuration, interface) -> None
|
||||
Initialize the device.
|
||||
device: usb.Device object.
|
||||
configuration: configuration number.
|
||||
interface: usb.Interface object representing the interface and altenate setting.
|
||||
"""
|
||||
if interface.interfaceClass != CCID_CLASS:
|
||||
raise ValueError("Wrong interface class")
|
||||
if interface.interfaceSubClass != CCID_SUBCLASS:
|
||||
raise ValueError("Wrong interface sub class")
|
||||
self.__devhandle = device.open()
|
||||
try:
|
||||
self.__devhandle.setConfiguration(configuration)
|
||||
except:
|
||||
pass
|
||||
self.__devhandle.claimInterface(interface)
|
||||
self.__devhandle.setAltInterface(interface)
|
||||
|
||||
self.__intf = interface.interfaceNumber
|
||||
self.__alt = interface.alternateSetting
|
||||
self.__conf = configuration
|
||||
|
||||
self.__bulkout = 1
|
||||
self.__bulkin = 0x81
|
||||
|
||||
self.__timeout = 10000
|
||||
self.__seq = 0
|
||||
|
||||
def increment_seq(self):
|
||||
self.__seq = (self.__seq + 1) & 0xff
|
||||
|
||||
def reset_device(self):
|
||||
try:
|
||||
self.__devhandle.reset()
|
||||
except:
|
||||
pass
|
||||
|
||||
def release_gnuk(self):
|
||||
self.__devhandle.releaseInterface()
|
||||
|
||||
def icc_get_result(self):
|
||||
msg = self.__devhandle.bulkRead(self.__bulkin, 1024, self.__timeout)
|
||||
if len(msg) < 10:
|
||||
print msg
|
||||
raise ValueError("icc_get_result")
|
||||
msg_type = msg[0]
|
||||
data_len = msg[1] + (msg[2]<<8) + (msg[3]<<16) + (msg[4]<<24)
|
||||
slot = msg[5]
|
||||
seq = msg[6]
|
||||
status = msg[7]
|
||||
error = msg[8]
|
||||
chain = msg[9]
|
||||
data = msg[10:]
|
||||
# XXX: check msg_type, data_len, slot, seq, error
|
||||
return (status, chain, data)
|
||||
|
||||
def icc_get_status(self):
|
||||
msg = icc_compose(0x65, 0, 0, self.__seq, 0, "")
|
||||
self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
|
||||
self.increment_seq()
|
||||
status, chain, data = self.icc_get_result()
|
||||
# XXX: check chain, data
|
||||
return status
|
||||
|
||||
def icc_power_on(self):
|
||||
msg = icc_compose(0x62, 0, 0, self.__seq, 0, "")
|
||||
self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
|
||||
self.increment_seq()
|
||||
status, chain, data = self.icc_get_result()
|
||||
# XXX: check status, chain
|
||||
self.atr = list_to_string(data) # ATR
|
||||
|
||||
def icc_power_off(self):
|
||||
msg = icc_compose(0x63, 0, 0, self.__seq, 0, "")
|
||||
self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
|
||||
self.increment_seq()
|
||||
status, chain, data = self.icc_get_result()
|
||||
# XXX: check chain, data
|
||||
return status
|
||||
|
||||
def icc_send_data_block(self, data):
|
||||
msg = icc_compose(0x6f, len(data), 0, self.__seq, 0, data)
|
||||
self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
|
||||
self.increment_seq()
|
||||
return self.icc_get_result()
|
||||
|
||||
def icc_send_cmd(self, data):
|
||||
status, chain, data_rcv = self.icc_send_data_block(data)
|
||||
if chain == 0:
|
||||
while status == 0x80:
|
||||
status, chain, data_rcv = self.icc_get_result()
|
||||
return data_rcv
|
||||
elif chain == 1:
|
||||
d = data_rcv
|
||||
while True:
|
||||
msg = icc_compose(0x6f, 0, 0, self.__seq, 0x10, "")
|
||||
self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
|
||||
self.increment_seq()
|
||||
status, chain, data_rcv = self.icc_get_result()
|
||||
# XXX: check status
|
||||
d += data_rcv
|
||||
if chain == 2:
|
||||
break
|
||||
elif chain == 3:
|
||||
continue
|
||||
else:
|
||||
raise ValueError("icc_send_cmd chain")
|
||||
return d
|
||||
else:
|
||||
raise ValueError("icc_send_cmd")
|
||||
|
||||
def cmd_get_response(self, expected_len):
|
||||
result = []
|
||||
while True:
|
||||
cmd_data = iso7816_compose(0xc0, 0x00, 0x00, '') + pack('>B', expected_len)
|
||||
response = self.icc_send_cmd(cmd_data)
|
||||
result += response[:-2]
|
||||
sw = response[-2:]
|
||||
if sw[0] == 0x90 and sw[1] == 0x00:
|
||||
return list_to_string(result)
|
||||
elif sw[0] != 0x61:
|
||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||
else:
|
||||
expected_len = sw[1]
|
||||
|
||||
def cmd_verify(self, who, passwd):
|
||||
cmd_data = iso7816_compose(0x20, 0x00, 0x80+who, passwd)
|
||||
sw = self.icc_send_cmd(cmd_data)
|
||||
if len(sw) != 2:
|
||||
raise ValueError(sw)
|
||||
if not (sw[0] == 0x90 and sw[1] == 0x00):
|
||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||
return True
|
||||
|
||||
def cmd_read_binary(self, fileid):
|
||||
cmd_data = iso7816_compose(0xb0, 0x80+fileid, 0x00, '')
|
||||
sw = self.icc_send_cmd(cmd_data)
|
||||
if len(sw) != 2:
|
||||
raise ValueError, sw
|
||||
if sw[0] != 0x61:
|
||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||
return self.cmd_get_response(sw[1])
|
||||
|
||||
def cmd_write_binary(self, fileid, data, is_update):
|
||||
count = 0
|
||||
data_len = len(data)
|
||||
if is_update:
|
||||
ins = 0xd6
|
||||
else:
|
||||
ins = 0xd0
|
||||
while count*256 < data_len:
|
||||
if count == 0:
|
||||
if len(data) < 128:
|
||||
cmd_data0 = iso7816_compose(ins, 0x80+fileid, 0x00, data[:128])
|
||||
cmd_data1 = None
|
||||
else:
|
||||
cmd_data0 = iso7816_compose(ins, 0x80+fileid, 0x00, data[:128], 0x10)
|
||||
cmd_data1 = iso7816_compose(ins, 0x80+fileid, 0x00, data[128:256])
|
||||
else:
|
||||
if len(data[256*count:256*count+128]) < 128:
|
||||
cmd_data0 = iso7816_compose(ins, count, 0x00, data[256*count:256*count+128])
|
||||
cmd_data1 = None
|
||||
else:
|
||||
cmd_data0 = iso7816_compose(ins, count, 0x00, data[256*count:256*count+128], 0x10)
|
||||
cmd_data1 = iso7816_compose(ins, count, 0x00, data[256*count+128:256*(count+1)])
|
||||
sw = self.icc_send_cmd(cmd_data0)
|
||||
if len(sw) != 2:
|
||||
raise ValueError("cmd_write_binary 0")
|
||||
if not (sw[0] == 0x90 and sw[1] == 0x00):
|
||||
raise ValueError("cmd_write_binary 0", "%02x%02x" % (sw[0], sw[1]))
|
||||
if cmd_data1:
|
||||
sw = self.icc_send_cmd(cmd_data1)
|
||||
if len(sw) != 2:
|
||||
raise ValueError("cmd_write_binary", sw)
|
||||
if not (sw[0] == 0x90 and sw[1] == 0x00):
|
||||
raise ValueError("cmd_write_binary", "%02x%02x" % (sw[0], sw[1]))
|
||||
count += 1
|
||||
|
||||
def cmd_select_openpgp(self):
|
||||
cmd_data = iso7816_compose(0xa4, 0x04, 0x0c, "\xD2\x76\x00\x01\x24\x01")
|
||||
sw = self.icc_send_cmd(cmd_data)
|
||||
if len(sw) != 2:
|
||||
raise ValueError, sw
|
||||
if not (sw[0] == 0x90 and sw[1] == 0x00):
|
||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||
return True
|
||||
|
||||
def cmd_get_data(self, tagh, tagl):
|
||||
cmd_data = iso7816_compose(0xca, tagh, tagl, "")
|
||||
sw = self.icc_send_cmd(cmd_data)
|
||||
if len(sw) != 2:
|
||||
raise ValueError, sw
|
||||
if sw[0] == 0x90 and sw[1] == 0x00:
|
||||
return ""
|
||||
elif sw[0] != 0x61:
|
||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||
return self.cmd_get_response(sw[1])
|
||||
|
||||
def cmd_change_reference_data(self, who, data):
|
||||
cmd_data = iso7816_compose(0x24, 0x00, 0x80+who, data)
|
||||
sw = self.icc_send_cmd(cmd_data)
|
||||
if len(sw) != 2:
|
||||
raise ValueError(sw)
|
||||
if not (sw[0] == 0x90 and sw[1] == 0x00):
|
||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||
return True
|
||||
|
||||
def cmd_put_data(self, tagh, tagl, content):
|
||||
cmd_data = iso7816_compose(0xda, tagh, tagl, content)
|
||||
sw = self.icc_send_cmd(cmd_data)
|
||||
if len(sw) != 2:
|
||||
raise ValueError(sw)
|
||||
if not (sw[0] == 0x90 and sw[1] == 0x00):
|
||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||
return True
|
||||
|
||||
def cmd_put_data_odd(self, tagh, tagl, content):
|
||||
cmd_data0 = iso7816_compose(0xdb, tagh, tagl, content[:128], 0x10)
|
||||
cmd_data1 = iso7816_compose(0xdb, tagh, tagl, content[128:])
|
||||
sw = self.icc_send_cmd(cmd_data0)
|
||||
if len(sw) != 2:
|
||||
raise ValueError(sw)
|
||||
if not (sw[0] == 0x90 and sw[1] == 0x00):
|
||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||
sw = self.icc_send_cmd(cmd_data1)
|
||||
if len(sw) != 2:
|
||||
raise ValueError(sw)
|
||||
if not (sw[0] == 0x90 and sw[1] == 0x00):
|
||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||
return True
|
||||
|
||||
def cmd_reset_retry_counter(self, how, data):
|
||||
cmd_data = iso7816_compose(0x2c, how, 0x00, data)
|
||||
sw = self.icc_send_cmd(cmd_data)
|
||||
if len(sw) != 2:
|
||||
raise ValueError(sw)
|
||||
if not (sw[0] == 0x90 and sw[1] == 0x00):
|
||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||
return True
|
||||
|
||||
def cmd_pso(self, p1, p2, data):
|
||||
cmd_data = iso7816_compose(0x2a, p1, p2, data)
|
||||
sw = self.icc_send_cmd(cmd_data)
|
||||
if len(sw) != 2:
|
||||
raise ValueError(sw)
|
||||
if sw[0] == 0x90 and sw[1] == 0x00:
|
||||
return ""
|
||||
elif sw[0] != 0x61:
|
||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||
return self.cmd_get_response(sw[1])
|
||||
|
||||
def cmd_pso_longdata(self, p1, p2, data):
|
||||
cmd_data0 = iso7816_compose(0x2a, p1, p2, data[:128], 0x10)
|
||||
cmd_data1 = iso7816_compose(0x2a, p1, p2, data[128:])
|
||||
sw = self.icc_send_cmd(cmd_data0)
|
||||
if len(sw) != 2:
|
||||
raise ValueError(sw)
|
||||
if not (sw[0] == 0x90 and sw[1] == 0x00):
|
||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||
sw = self.icc_send_cmd(cmd_data1)
|
||||
if len(sw) != 2:
|
||||
raise ValueError(sw)
|
||||
elif sw[0] != 0x61:
|
||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||
return self.cmd_get_response(sw[1])
|
||||
|
||||
def cmd_internal_authenticate(self, data):
|
||||
cmd_data = iso7816_compose(0x88, 0, 0, data)
|
||||
sw = self.icc_send_cmd(cmd_data)
|
||||
if len(sw) != 2:
|
||||
raise ValueError(sw)
|
||||
if sw[0] == 0x90 and sw[1] == 0x00:
|
||||
return ""
|
||||
elif sw[0] != 0x61:
|
||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||
return self.cmd_get_response(sw[1])
|
||||
|
||||
def cmd_genkey(self, keyno):
|
||||
if keyno == 1:
|
||||
data = '\xb6\x00'
|
||||
elif keyno == 2:
|
||||
data = '\xb8\x00'
|
||||
else:
|
||||
data = '\xa4\x00'
|
||||
cmd_data = iso7816_compose(0x47, 0x80, 0, data)
|
||||
sw = self.icc_send_cmd(cmd_data)
|
||||
if len(sw) != 2:
|
||||
raise ValueError(sw)
|
||||
if sw[0] == 0x90 and sw[1] == 0x00:
|
||||
return ""
|
||||
elif sw[0] != 0x61:
|
||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||
pk = self.cmd_get_response(sw[1])
|
||||
return (pk[9:9+256], pk[9+256+2:9+256+2+3])
|
||||
|
||||
def cmd_get_public_key(self, keyno):
|
||||
if keyno == 1:
|
||||
data = '\xb6\x00'
|
||||
elif keyno == 2:
|
||||
data = '\xb8\x00'
|
||||
else:
|
||||
data = '\xa4\x00'
|
||||
cmd_data = iso7816_compose(0x47, 0x81, 0, data)
|
||||
sw = self.icc_send_cmd(cmd_data)
|
||||
if len(sw) != 2:
|
||||
raise ValueError(sw)
|
||||
elif sw[0] != 0x61:
|
||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||
pk = self.cmd_get_response(sw[1])
|
||||
return (pk[9:9+256], pk[9+256+2:9+256+2+3])
|
||||
|
||||
|
||||
def compare(data_original, data_in_device):
|
||||
i = 0
|
||||
for d in data_original:
|
||||
if ord(d) != data_in_device[i]:
|
||||
raise ValueError, "verify failed at %08x" % i
|
||||
i += 1
|
||||
|
||||
def gnuk_devices():
|
||||
busses = usb.busses()
|
||||
for bus in busses:
|
||||
devices = bus.devices
|
||||
for dev in devices:
|
||||
for config in dev.configurations:
|
||||
for intf in config.interfaces:
|
||||
for alt in intf:
|
||||
if alt.interfaceClass == CCID_CLASS and \
|
||||
alt.interfaceSubClass == CCID_SUBCLASS and \
|
||||
alt.interfaceProtocol == CCID_PROTOCOL_0:
|
||||
yield dev, config, alt
|
||||
|
||||
def get_gnuk_device():
|
||||
icc = None
|
||||
for (dev, config, intf) in gnuk_devices():
|
||||
try:
|
||||
icc = gnuk_token(dev, config, intf)
|
||||
print "Device: ", dev.filename
|
||||
print "Configuration: ", config.value
|
||||
print "Interface: ", intf.interfaceNumber
|
||||
break
|
||||
except:
|
||||
pass
|
||||
if not icc:
|
||||
raise ValueError("No ICC present")
|
||||
status = icc.icc_get_status()
|
||||
if status == 0:
|
||||
pass # It's ON already
|
||||
elif status == 1:
|
||||
icc.icc_power_on()
|
||||
else:
|
||||
raise ValueError("Unknown ICC status", status)
|
||||
return icc
|
||||
4
test/rsa-aut.key
Normal file
4
test/rsa-aut.key
Normal file
@@ -0,0 +1,4 @@
|
||||
9cf7192b51a574d1ad3ccb08ba09b87f228573893eee355529ff243e90fd4b86f79a82097cc7922c0485bed1616b1656a9b0b19ef78ea8ec34c384019adc5d5bf4db2d2a0a2d9cf14277bdcb7056f48b81214e3f7f7742231e29673966f9b1106862112cc798dba8d4a138bb5abfc6d4c12d53a5d39b2f783da916da20852ee139bbafda61d429caf2a4f30847ce7e7ae32ab4061e27dd9e4d00d60910249db8d8559dd85f7ca59659ef400c8f6318700f4e97f0c6f4165de80641490433c88da8682befe68eb311f54af2b07d97ac74edb5399cf054764211694fbb8d1d333f3269f235abe025067f811ff83a2224826219b309ea3e6c968f42b3e52f245dc9
|
||||
010001
|
||||
b5ab7b159220b18e363258f61ebde08bae83d6ce2dbfe4adc143628c527887acde9de09bf9b49f438019004d71855f30c2d69b6c29bb9882ab641b3387409fe9199464a7faa4b5230c56d9e17cd9ed074bc00180ebed62bae3af28e6ff2ac2654ad968834c5d5c88f8d9d3cc5e167b10453b049d4e454a5761fb0ac717185907
|
||||
dd2fffa9814296156a6926cd17b65564187e424dcadce9b032246ad7e46448bb0f9e0ff3c64f987424b1a40bc694e2e9ac4fb1930d163582d7acf20653a1c44b97846c1c5fd8a7b19bb225fb39c30e25410483deaf8c2538d222b748c4d8103b11cec04f666a5c0dbcbf5d5f625f158f65746c3fafe6418145f7cffa5fadeeaf
|
||||
4
test/rsa-dec.key
Normal file
4
test/rsa-dec.key
Normal file
@@ -0,0 +1,4 @@
|
||||
d392714c29738aac6372f2c8654a08c25a1299fed7004bd512cd2452b503ebad6301130816ac525ba528dc155be6347a5c70407fb4fbdaed751dfc0a7cd5e3910272ff236c4ed1ce5de6620b191a172e5b247347b8cab73a43d79221708755c959a2f83f486439da30917384554331532aabc8326db48866f8c91198834a86ab94679f6175db737bdf399e3f0b737dcb1f4208279d3e1cc694e78686785e4f363a377dec912b7c2f757b1422d866fb9fa85c96b83adfd6a223989a9a02988bdee81ad17eff6385e7b38cec8611fdf367ba4ac8e90d5f48ac7715c5f47aea06a4a37cdaa3029ce59d29bc66853bf6758ef4a7da5a5953f5e557a5a22f67c368c3
|
||||
010001
|
||||
dae085952c5beee38f25f09bc37a4ca2434c31f78055469d0d5f0bf3337e3a70ba6c91734f195b742e211a5fe283befdf66820008e6ef2c8ca54a91922838fce07d9e33a331ce20dac36803e777d5ee2195ed28d6a4045e28623a6a60b0661e45f7c4f84ae2b1dfad0cf1ec30605158323382a819e730c09a33fad704dd67501
|
||||
f774be43ea198aa2f089274e4fffd7d0092ee7b35a1d2f854cdb166f698caab72fdeb099e690e78438b2e043e452d4d2f19d7f44ba6b286642f0ce5204966ff98ecd9e3b448877324631365dc860797429b9414a21a7e166d504cace156588b9a145657eeb1afb43b8ff65d8d6d93cea2ba4ef8aab047885c4de64ffef0b49c3
|
||||
4
test/rsa-sig.key
Normal file
4
test/rsa-sig.key
Normal file
@@ -0,0 +1,4 @@
|
||||
c6c877dfd3b441f8fb1b8dc504093a51c2efe4883fe0a6379205acc6e673709905e4d767ddf46143c535cc6d7f10b616f520d8346320ef69ff4a2c4f4a148edc65f7ad24ed7d4fe23bb862a0ae71f4f7904abac0397abf3213df91326b1a25554b3b18cf54584d8bf220169fc92b2aa511e8313983e72b4c9110b3a1aea087aebef95873865608e8faea9ef10e7f7f3a66ca8def2d499c3149c127491e0e4339fd6abe10bfc6c13e43d522004f1485767328eabe35d6ffa8df4c15f0fbcd4eb1c07cc6d85e275139ac69e2962273ae987236926dd6c1144fce3e7ae567fa58ea60620dfafc52f95299fea601739fce27ee71eea978d0074f21e7086f60ba8331
|
||||
010001
|
||||
cc365b5702714bf203e8c49b0b8afa8dad586e929cf5edca38ad07fa45efd5c2d89022d29f40283a57e50ca24c5f28c8e911a74faaf796f112e7e48195956f9a4df7668a5342523b27179cec958f363211ee11d0ec0e0e1b92ca007a61e8c9ac14e00229b9a7624850199e6667afa1a44db8f3c5de0a8eef0e6de050ac0ac633
|
||||
f931a3c12f0e3a5276f712b7706590ba02e14a97ff9b8ce3152af0fc4d9cdc690ea9bc4c82cb16c7d23136cbdab58fbec69880a88bca85c4214df01045082cbe9f4192e3e39c79896533c37dad9eb9e73c2643b9c0a704a4f93d81573537963d6b6e5140a24c702d9f26e06a2095de906daa8824172a6b39f563b7153907050b
|
||||
152
test/rsa_keys.py
Normal file
152
test/rsa_keys.py
Normal file
@@ -0,0 +1,152 @@
|
||||
from binascii import hexlify, unhexlify
|
||||
from time import time
|
||||
from struct import pack
|
||||
from hashlib import sha1, sha256
|
||||
import string
|
||||
from os import urandom
|
||||
|
||||
def read_key_from_file(file):
|
||||
f = open(file)
|
||||
n_str = f.readline()[:-1]
|
||||
e_str = f.readline()[:-1]
|
||||
p_str = f.readline()[:-1]
|
||||
q_str = f.readline()[:-1]
|
||||
f.close()
|
||||
e = int(e_str, 16)
|
||||
p = int(p_str, 16)
|
||||
q = int(q_str, 16)
|
||||
n = int(n_str, 16)
|
||||
if n != p * q:
|
||||
raise ValueError("wrong key", p, q, n)
|
||||
return (unhexlify(n_str), unhexlify(e_str), unhexlify(p_str), unhexlify(q_str), e, p, q, n)
|
||||
|
||||
def calc_fpr(n,e):
|
||||
timestamp = int(time())
|
||||
timestamp_data = pack('>I', timestamp)
|
||||
m_len = 6 + 2 + 256 + 2 + 4
|
||||
m = '\x99' + pack('>H', m_len) + '\x04' + timestamp_data + '\x01' + \
|
||||
pack('>H', 2048) + n + pack('>H', 17) + e
|
||||
fpr = sha1(m).digest()
|
||||
return (fpr, timestamp_data)
|
||||
|
||||
key = [ None, None, None ]
|
||||
fpr = [ None, None, None ]
|
||||
timestamp = [ None, None, None ]
|
||||
|
||||
key[0] = read_key_from_file('rsa-sig.key')
|
||||
key[1] = read_key_from_file('rsa-dec.key')
|
||||
key[2] = read_key_from_file('rsa-aut.key')
|
||||
|
||||
(fpr[0], timestamp[0]) = calc_fpr(key[0][0], key[0][1])
|
||||
(fpr[1], timestamp[1]) = calc_fpr(key[1][0], key[1][1])
|
||||
(fpr[2], timestamp[2]) = calc_fpr(key[2][0], key[2][1])
|
||||
|
||||
def build_privkey_template(openpgp_keyno, keyno):
|
||||
n_str = key[keyno][0]
|
||||
e_str = '\x00' + key[keyno][1]
|
||||
p_str = key[keyno][2]
|
||||
q_str = key[keyno][3]
|
||||
|
||||
if openpgp_keyno == 1:
|
||||
keyspec = '\xb6'
|
||||
elif openpgp_keyno == 2:
|
||||
keyspec = '\xb8'
|
||||
else:
|
||||
keyspec = '\xa4'
|
||||
|
||||
key_template = '\x91\x04'+ '\x92\x81\x80' + '\x93\x81\x80'
|
||||
|
||||
exthdr = keyspec + '\x00' + '\x7f\x48' + '\x08' + key_template
|
||||
|
||||
suffix = '\x5f\x48' + '\x82\x01\x04'
|
||||
|
||||
t = '\x4d' + '\x82\01\16' + exthdr + suffix + e_str + p_str + q_str
|
||||
return t
|
||||
|
||||
def build_privkey_template_for_remove(openpgp_keyno):
|
||||
if openpgp_keyno == 1:
|
||||
keyspec = '\xb6'
|
||||
elif openpgp_keyno == 2:
|
||||
keyspec = '\xb8'
|
||||
else:
|
||||
keyspec = '\xa4'
|
||||
return '\x4d\02' + keyspec + '\0x00'
|
||||
|
||||
def compute_digestinfo(msg):
|
||||
digest = sha256(msg).digest()
|
||||
prefix = '\x30\31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'
|
||||
return prefix + digest
|
||||
|
||||
# egcd and modinv are from wikibooks
|
||||
# https://en.wikibooks.org/wiki/Algorithm_Implementation/Mathematics/Extended_Euclidean_algorithm
|
||||
|
||||
def egcd(a, b):
|
||||
if a == 0:
|
||||
return (b, 0, 1)
|
||||
else:
|
||||
g, y, x = egcd(b % a, a)
|
||||
return (g, x - (b // a) * y, y)
|
||||
|
||||
def modinv(a, m):
|
||||
g, x, y = egcd(a, m)
|
||||
if g != 1:
|
||||
raise Exception('modular inverse does not exist')
|
||||
else:
|
||||
return x % m
|
||||
|
||||
def pkcs1_pad_for_sign(digestinfo):
|
||||
byte_repr = '\x00' + '\x01' + string.ljust('', 256 - 19 - 32 - 3, '\xff') \
|
||||
+ '\x00' + digestinfo
|
||||
return int(hexlify(byte_repr), 16)
|
||||
|
||||
def pkcs1_pad_for_crypt(msg):
|
||||
padlen = 256 - 3 - len(msg)
|
||||
byte_repr = '\x00' + '\x02' \
|
||||
+ string.replace(urandom(padlen),'\x00','\x01') + '\x00' + msg
|
||||
return int(hexlify(byte_repr), 16)
|
||||
|
||||
def compute_signature(keyno, digestinfo):
|
||||
e = key[keyno][4]
|
||||
p = key[keyno][5]
|
||||
q = key[keyno][6]
|
||||
n = key[keyno][7]
|
||||
p1 = p - 1
|
||||
q1 = q - 1
|
||||
h = p1 * q1
|
||||
d = modinv(e, h)
|
||||
dp = d % p1
|
||||
dq = d % q1
|
||||
qp = modinv(q, p)
|
||||
|
||||
input = pkcs1_pad_for_sign(digestinfo)
|
||||
t1 = pow(input, dp, p)
|
||||
t2 = pow(input, dq, q)
|
||||
t = ((t1 - t2) * qp) % p
|
||||
sig = t2 + t * q
|
||||
return sig
|
||||
|
||||
def integer_to_bytes(i):
|
||||
s = hex(i)[2:]
|
||||
s = s.rstrip('L')
|
||||
if len(s) & 1:
|
||||
s = '0' + s
|
||||
return unhexlify(s)
|
||||
|
||||
def encrypt(keyno, plaintext):
|
||||
e = key[keyno][4]
|
||||
n = key[keyno][7]
|
||||
m = pkcs1_pad_for_crypt(plaintext)
|
||||
return '\x00' + integer_to_bytes(pow(m, e, n))
|
||||
|
||||
def encrypt_with_pubkey(pubkey_info, plaintext):
|
||||
n = int(hexlify(pubkey_info[0]), 16)
|
||||
e = int(hexlify(pubkey_info[1]), 16)
|
||||
m = pkcs1_pad_for_crypt(plaintext)
|
||||
return '\x00' + integer_to_bytes(pow(m, e, n))
|
||||
|
||||
def verify_signature(pubkey_info, digestinfo, sig):
|
||||
n = int(hexlify(pubkey_info[0]), 16)
|
||||
e = int(hexlify(pubkey_info[1]), 16)
|
||||
di_pkcs1 = pow(sig,e,n)
|
||||
m = pkcs1_pad_for_sign(digestinfo)
|
||||
return di_pkcs1 == m
|
||||
2
tool/asm-thumb/README
Normal file
2
tool/asm-thumb/README
Normal file
@@ -0,0 +1,2 @@
|
||||
These assembler program are source code of program fragments in
|
||||
stlinkv2.py.
|
||||
19
tool/asm-thumb/blank_check.S
Normal file
19
tool/asm-thumb/blank_check.S
Normal file
@@ -0,0 +1,19 @@
|
||||
/* ARM Thumb Assembler code */
|
||||
// arm-none-eabi-gcc -Wa,-amhls=blank_check.lst -c blank_check.S
|
||||
|
||||
.cpu cortex-m3
|
||||
.thumb
|
||||
ldr r1, .START_ADDR
|
||||
ldr r2, .END_ADDR
|
||||
0: ldr r0, [r1]
|
||||
add r0, r0, #1
|
||||
bne 1f
|
||||
add r1, r1, #4
|
||||
cmp r1, r2
|
||||
bne 0b
|
||||
1: bkpt #0x00
|
||||
// success: r0=0
|
||||
// fail: r0!=0
|
||||
.align 2
|
||||
.START_ADDR: .word 0x08000000
|
||||
.END_ADDR: .word 0x08020000
|
||||
39
tool/asm-thumb/flash_write.S
Normal file
39
tool/asm-thumb/flash_write.S
Normal file
@@ -0,0 +1,39 @@
|
||||
/* ARM Thumb Assembler code */
|
||||
// arm-none-eabi-gcc -Wa,-amhls=flash_write.lst -c flash_write.S
|
||||
|
||||
#define FLASH_CR_PG 0x0001 // == FLASH_SR_BSY
|
||||
#define FLASH_CR_ERRORS 0x0014 // == PGERR | WRPRTERR
|
||||
#define FLASH_SR_BSY 0x0001
|
||||
|
||||
#define FLASH_SR_OFFSET 0x0c
|
||||
#define FLASH_CR_OFFSET 0x10
|
||||
|
||||
#define COUNT 0x1000
|
||||
|
||||
.cpu cortex-m3
|
||||
.thumb
|
||||
movw r2, #COUNT
|
||||
ldr r0, .SRC_ADDR
|
||||
ldr r1, .TARGET_ADDR
|
||||
ldr r4, .FLASH_BASE_ADDR
|
||||
mov r5, #FLASH_CR_PG
|
||||
mov r6, #FLASH_CR_ERRORS
|
||||
mov r7, #0
|
||||
str r5, [r4, #FLASH_CR_OFFSET]
|
||||
0: ldrh r3, [r0, r7]
|
||||
strh r3, [r1, r7]
|
||||
1: ldr r3, [r4, #FLASH_SR_OFFSET]
|
||||
tst r3, r5
|
||||
bne 1b
|
||||
tst r3, r6
|
||||
bne 2f
|
||||
add r7, r7, #0x02
|
||||
cmp r7, r2
|
||||
bne 0b
|
||||
2: mov r7, #0
|
||||
str r7, [r4, #FLASH_CR_OFFSET]
|
||||
bkpt #0x00
|
||||
.align 2
|
||||
.FLASH_BASE_ADDR: .word 0x40022000
|
||||
.SRC_ADDR: .word 0x20000038
|
||||
.TARGET_ADDR: .word 0x08000000
|
||||
29
tool/asm-thumb/opt_bytes_write.S
Normal file
29
tool/asm-thumb/opt_bytes_write.S
Normal file
@@ -0,0 +1,29 @@
|
||||
/* ARM Thumb Assembler code */
|
||||
// arm-none-eabi-gcc -Wa,-amhls=opt_bytes_write.lst -c opt_bytes_write.S
|
||||
|
||||
#define FLASH_CR_OPTPG 0x0010
|
||||
#define FLASH_SR_BSY 0x0001
|
||||
|
||||
#define FLASH_SR_OFFSET 0x0c
|
||||
#define FLASH_CR_OFFSET 0x10
|
||||
|
||||
#define OB_RDP_UNLOCK 0x00a5
|
||||
|
||||
.cpu cortex-m3
|
||||
.thumb
|
||||
movw r0, #OB_RDP_UNLOCK
|
||||
ldr r1, .TARGET_ADDR
|
||||
ldr r2, .FLASH_BASE_ADDR
|
||||
mov r3, #FLASH_CR_OPTPG
|
||||
mov r4, #FLASH_SR_BSY
|
||||
str r3, [r2, #FLASH_CR_OFFSET]
|
||||
strh r0, [r1]
|
||||
1: ldr r0, [r2, #FLASH_SR_OFFSET]
|
||||
tst r0, r4
|
||||
bne 1b
|
||||
mov r0, #0
|
||||
str r0, [r2, #FLASH_CR_OFFSET]
|
||||
bkpt #0x00
|
||||
.align 2
|
||||
.FLASH_BASE_ADDR: .word 0x40022000
|
||||
.TARGET_ADDR: .word 0x1FFFF800
|
||||
673
tool/stlinkv2.py
Executable file
673
tool/stlinkv2.py
Executable file
@@ -0,0 +1,673 @@
|
||||
#! /usr/bin/python
|
||||
|
||||
"""
|
||||
stlinkv2.py - a tool to control ST-Link/V2
|
||||
|
||||
Copyright (C) 2012 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 *
|
||||
import sys, time
|
||||
import usb
|
||||
from colorama import init as colorama_init, Fore, Back, Style
|
||||
|
||||
# INPUT: binary file
|
||||
|
||||
# Assumes only single ST-Link/V2 device is attached to computer.
|
||||
|
||||
|
||||
GPIOA=0x40010800
|
||||
GPIOB=0x40010C00
|
||||
OPTION_BYTES_ADDR=0x1ffff800
|
||||
RDP_KEY=0x00a5 # Unlock readprotection
|
||||
FLASH_BASE_ADDR=0x40022000
|
||||
|
||||
FLASH_KEYR= FLASH_BASE_ADDR+0x04
|
||||
FLASH_OPTKEYR= FLASH_BASE_ADDR+0x08
|
||||
FLASH_SR= FLASH_BASE_ADDR+0x0c
|
||||
FLASH_CR= FLASH_BASE_ADDR+0x10
|
||||
FLASH_AR= FLASH_BASE_ADDR+0x14
|
||||
FLASH_OBR= FLASH_BASE_ADDR+0x1c
|
||||
|
||||
FLASH_KEY1=0x45670123
|
||||
FLASH_KEY2=0xcdef89ab
|
||||
|
||||
FLASH_SR_BSY= 0x0001
|
||||
FLASH_SR_PGERR= 0x0004
|
||||
FLASH_SR_WRPRTERR= 0x0010
|
||||
FLASH_SR_EOP= 0x0020
|
||||
|
||||
FLASH_CR_PG= 0x0001
|
||||
FLASH_CR_PER= 0x0002
|
||||
FLASH_CR_MER= 0x0004
|
||||
FLASH_CR_OPTPG= 0x0010
|
||||
FLASH_CR_OPTER= 0x0020
|
||||
FLASH_CR_STRT= 0x0040
|
||||
FLASH_CR_LOCK= 0x0080
|
||||
FLASH_CR_OPTWRE= 0x0200
|
||||
|
||||
|
||||
SPI1= 0x40013000
|
||||
|
||||
def uint32(v):
|
||||
return v[0] + (v[1]<<8) + (v[2]<<16) + (v[3]<<24)
|
||||
|
||||
## HERE comes: "movw r2,#SIZE" instruction
|
||||
prog_flash_write_body = "\x0A\x48" + "\x0B\x49" + \
|
||||
"\x08\x4C" + "\x01\x25" + "\x14\x26" + "\x00\x27" + "\x25\x61" + \
|
||||
"\xC3\x5B" + "\xCB\x53" + "\xE3\x68" + "\x2B\x42" + "\xFC\xD1" + \
|
||||
"\x33\x42" + "\x02\xD1" + "\x02\x37" + "\x97\x42" + "\xF5\xD1" + \
|
||||
"\x00\x27" + "\x27\x61" + "\x00\xBE" + "\x00\x20\x02\x40" + \
|
||||
"\x38\x00\x00\x20"
|
||||
# .SRC_ADDR: 0x20000038
|
||||
## HERE comes: target_addr in 4-byte
|
||||
# .TARGET_ADDR
|
||||
|
||||
def gen_prog_flash_write(addr,size):
|
||||
return pack("<BBBB", (0x40 | (size&0xf000)>>12), (0xf2 | (size&0x0800)>>9),
|
||||
(size & 0x00ff), (0x02 | ((size&0x0700) >> 4))) + \
|
||||
prog_flash_write_body + pack("<I", addr)
|
||||
|
||||
|
||||
## HERE comes: "movw r0,#VAL" instruction
|
||||
prog_option_bytes_write_body = "\x06\x49" + "\x05\x4A" + "\x10\x23" + \
|
||||
"\x01\x24" + "\x13\x61" + "\x08\x80" + "\xD0\x68" + "\x20\x42" + \
|
||||
"\xFC\xD1" + "\x00\x20" + "\x10\x61" + "\x00\xBE" + "\x00\x20\x02\x40"
|
||||
## HERE comes: target_addr in 4-byte
|
||||
# .TARGET_ADDR
|
||||
|
||||
def gen_prog_option_bytes_write(addr,val):
|
||||
return pack("<BBBB", (0x40 | (val&0xf000)>>12), (0xf2 | (val&0x0800)>>9),
|
||||
(val & 0x00ff), (0x00 | ((val&0x0700) >> 4))) + \
|
||||
prog_option_bytes_write_body + pack("<I", addr)
|
||||
|
||||
prog_blank_check_body = "\x04\x49" + "\x05\x4A" + "\x08\x68" + "\x01\x30" + \
|
||||
"\x02\xD1" + "\x04\x31" + "\x91\x42" + "\xF9\xD1" + "\x00\xBE" + \
|
||||
"\x00\xBF" + "\x00\x00\x00\x08"
|
||||
## HERE comes: end_addr in 4-byte
|
||||
# .END_ADDR
|
||||
|
||||
def gen_prog_blank_check(size):
|
||||
return prog_blank_check_body + pack("<I", 0x08000000 + size)
|
||||
|
||||
|
||||
SRAM_ADDRESS=0x20000000
|
||||
BLOCK_SIZE=16384 # Should be less than (20KiB - 0x0038)
|
||||
BLOCK_WRITE_TIMEOUT=80 # Increase this when you increase BLOCK_SIZE
|
||||
|
||||
|
||||
class TimeOutError(Exception):
|
||||
def __init__(self, msg):
|
||||
self.msg = msg
|
||||
def __str__(self):
|
||||
return repr(self.msg)
|
||||
def __repr__(self):
|
||||
return "TimeoutError(" + self.msg + ")"
|
||||
|
||||
class OperationFailure(Exception):
|
||||
def __init__(self, msg):
|
||||
self.msg = msg
|
||||
def __str__(self):
|
||||
return repr(self.msg)
|
||||
def __repr__(self):
|
||||
return "OperationFailure(" + self.msg + ")"
|
||||
|
||||
|
||||
class stlinkv2(object):
|
||||
def __init__(self, dev):
|
||||
self.__bulkout = 2
|
||||
self.__bulkin = 0x81
|
||||
|
||||
self.__timeout = 1000 # 1 second
|
||||
conf = dev.configurations[0]
|
||||
intf_alt = conf.interfaces[0]
|
||||
intf = intf_alt[0]
|
||||
if intf.interfaceClass != 0xff: # Vendor specific
|
||||
raise ValueError("Wrong interface class.", intf.interfaceClass)
|
||||
self.__devhandle = dev.open()
|
||||
try:
|
||||
self.__devhandle.setConfiguration(conf)
|
||||
except:
|
||||
pass
|
||||
self.__devhandle.claimInterface(intf)
|
||||
# self.__devhandle.setAltInterface(intf) # This is not good for libusb-win32
|
||||
|
||||
def shutdown(self):
|
||||
self.__devhandle.releaseInterface()
|
||||
|
||||
def execute_get(self, cmd, res_len):
|
||||
self.__devhandle.bulkWrite(self.__bulkout, cmd, self.__timeout)
|
||||
res = self.__devhandle.bulkRead(self.__bulkin, res_len, self.__timeout)
|
||||
return res
|
||||
|
||||
def execute_put(self, cmd, data=None):
|
||||
self.__devhandle.bulkWrite(self.__bulkout, cmd, self.__timeout)
|
||||
if (data):
|
||||
self.__devhandle.bulkWrite(self.__bulkout, data, self.__timeout)
|
||||
|
||||
def stl_mode(self):
|
||||
v = self.execute_get("\xf5\x00", 2)
|
||||
return (v[1] * 256 + v[0])
|
||||
|
||||
def exit_from_dfu(self):
|
||||
self.__devhandle.bulkWrite(self.__bulkout, "\xf3\x07", self.__timeout)
|
||||
time.sleep(1)
|
||||
|
||||
def enter_swd(self):
|
||||
self.__devhandle.bulkWrite(self.__bulkout, "\xf2\x20\xa3", self.__timeout)
|
||||
time.sleep(1)
|
||||
|
||||
def get_status(self):
|
||||
v = self.execute_get("\xf2\x01\x00", 2)
|
||||
return (v[1] << 8) + v[0]
|
||||
# RUN:128, HALT:129
|
||||
|
||||
def enter_debug(self):
|
||||
v = self.execute_get("\xf2\x02\x00", 2)
|
||||
return (v[1] << 8) + v[0]
|
||||
|
||||
def exit_debug(self):
|
||||
self.execute_put("\xf2\x21\x00")
|
||||
|
||||
def reset_sys(self):
|
||||
v = self.execute_get("\xf2\x03\x00", 2)
|
||||
return (v[1] << 8) + v[0]
|
||||
|
||||
def read_memory(self, addr, length):
|
||||
return self.execute_get("\xf2\x07" + pack('<IH', addr, length), length)
|
||||
|
||||
def read_memory_u32(self, addr):
|
||||
return uint32(self.execute_get("\xf2\x07" + pack('<IH', addr, 4), 4))
|
||||
|
||||
def write_memory(self, addr, data):
|
||||
return self.execute_put("\xf2\x08" + pack('<IH', addr, len(data)), data)
|
||||
|
||||
def write_memory_u32(self, addr, data):
|
||||
return self.execute_put("\xf2\x08" + pack('<IH', addr, 4),
|
||||
pack('<I', data))
|
||||
|
||||
def read_reg(self, regno):
|
||||
return uint32(self.execute_get("\xf2\x05" + pack('<B', regno), 4))
|
||||
|
||||
def write_reg(self, regno, value):
|
||||
return self.execute_get("\xf2\x06" + pack('<BI', regno, value), 2)
|
||||
|
||||
def run(self):
|
||||
v = self.execute_get("\xf2\x09\x00", 2)
|
||||
return (v[1] << 8) + v[0]
|
||||
|
||||
def core_id(self):
|
||||
v = self.execute_get("\xf2\x22\x00", 4)
|
||||
return v[0] + (v[1]<<8) + (v[2]<<16) + (v[3]<<24)
|
||||
|
||||
# For FST-01-00 and FST-01: LED on, USB off
|
||||
def setup_gpio(self):
|
||||
apb2enr = self.read_memory_u32(0x40021018)
|
||||
apb2enr = apb2enr | 4 | 8 | 0x1000 # Enable port A, port B, and SPI1
|
||||
self.write_memory_u32(0x40021018, apb2enr) # RCC->APB2ENR
|
||||
self.write_memory_u32(0x4002100c, 4|8|0x1000) # RCC->APB2RSTR
|
||||
self.write_memory_u32(0x4002100c, 0)
|
||||
self.write_memory_u32(GPIOA+0x0c, 0xfffffbff) # ODR
|
||||
self.write_memory_u32(GPIOA+0x04, 0x88888383) # CRH
|
||||
self.write_memory_u32(GPIOA+0x00, 0xBBB38888) # CRL
|
||||
self.write_memory_u32(GPIOB+0x0c, 0xffffffff) # ODR
|
||||
self.write_memory_u32(GPIOB+0x04, 0x88888883) # CRH
|
||||
self.write_memory_u32(GPIOB+0x00, 0x88888888) # CRL
|
||||
|
||||
# For FST-01-00 and FST-01: LED off, USB off
|
||||
def finish_gpio(self):
|
||||
apb2enr = self.read_memory_u32(0x40021018)
|
||||
apb2enr = apb2enr & ~(4 | 8 | 0x1000)
|
||||
self.write_memory_u32(0x40021018, apb2enr) # RCC->APB2ENR
|
||||
self.write_memory_u32(GPIOA+0x0c, 0xfffffaff) # ODR
|
||||
self.write_memory_u32(GPIOB+0x0c, 0xfffffffe) # ODR
|
||||
|
||||
def spi_flash_init(self):
|
||||
self.write_memory_u32(SPI1+0x00, 0x0004); # CR1 <= MSTR
|
||||
i2scfgr = self.read_memory_u32(SPI1+0x1c) # I2SCFGR
|
||||
i2scfgr = i2scfgr & 0xf7ff #
|
||||
self.write_memory_u32(SPI1+0x1c, i2scfgr); # I2SCFGR <= SPI mode
|
||||
self.write_memory_u32(SPI1+0x10, 7); # CRCPR <= 7
|
||||
self.write_memory_u32(SPI1+0x04, 0x04); # CR2 <= SSOE
|
||||
self.write_memory_u32(SPI1+0x00, 0x0044); # CR1 <= MSTR | SPE
|
||||
|
||||
def spi_flash_select(self, enable):
|
||||
if enable:
|
||||
self.write_memory_u32(GPIOA+0x0c, 0xffffffef) # ODR
|
||||
else:
|
||||
self.write_memory_u32(GPIOA+0x0c, 0xffffffff) # ODR
|
||||
|
||||
def spi_flash_sendbyte(self, v):
|
||||
i = 0
|
||||
while True:
|
||||
status = self.read_memory_u32(SPI1+0x08) # SR
|
||||
if status & 0x02 != 0: # TXE (Data Empty)
|
||||
break
|
||||
time.sleep(0.01)
|
||||
i = i + 1
|
||||
if i > 10:
|
||||
raise TimeOutError('spi_flash_sendbyte')
|
||||
self.write_memory_u32(SPI1+0x0c, v) # DR
|
||||
i = 0
|
||||
while True:
|
||||
status = self.read_memory_u32(SPI1+0x08) # SR
|
||||
if status & 0x01 != 0: # RXNE (Data Not Empty)
|
||||
break
|
||||
time.sleep(0.01)
|
||||
i = i + 1
|
||||
if i > 10:
|
||||
raise TimeOutError('spi_flash_sendbyte')
|
||||
v = self.read_memory_u32(SPI1+0x0c) # DR
|
||||
return v
|
||||
|
||||
def spi_flash_read_id(self):
|
||||
self.spi_flash_select(True)
|
||||
self.spi_flash_sendbyte(0x9f)
|
||||
t0 = self.spi_flash_sendbyte(0xa5)
|
||||
t1 = self.spi_flash_sendbyte(0xa5)
|
||||
t2 = self.spi_flash_sendbyte(0xa5)
|
||||
self.spi_flash_select(False)
|
||||
return (t0 << 16) | (t1 << 8) | t2
|
||||
|
||||
def protection(self):
|
||||
return (self.read_memory_u32(FLASH_OBR) & 0x0002) != 0
|
||||
|
||||
def blank_check(self):
|
||||
prog = gen_prog_blank_check(0x20000) # 128KiB XXX: table lookup???
|
||||
self.write_memory(SRAM_ADDRESS, prog)
|
||||
self.write_reg(15, SRAM_ADDRESS)
|
||||
self.run()
|
||||
i = 0
|
||||
while self.get_status() == 0x80:
|
||||
time.sleep(0.050)
|
||||
i = i + 1
|
||||
if i >= 10:
|
||||
raise TimeOutError("blank check")
|
||||
|
||||
r0_value = self.read_reg(0)
|
||||
return r0_value == 0
|
||||
|
||||
def option_bytes_read(self):
|
||||
return self.read_memory_u32(OPTION_BYTES_ADDR)
|
||||
|
||||
def option_bytes_write(self,addr,val):
|
||||
self.write_memory_u32(FLASH_KEYR, FLASH_KEY1)
|
||||
self.write_memory_u32(FLASH_KEYR, FLASH_KEY2)
|
||||
self.write_memory_u32(FLASH_SR, FLASH_SR_EOP | FLASH_SR_WRPRTERR | FLASH_SR_PGERR)
|
||||
|
||||
self.write_memory_u32(FLASH_OPTKEYR, FLASH_KEY1)
|
||||
self.write_memory_u32(FLASH_OPTKEYR, FLASH_KEY2)
|
||||
|
||||
prog = gen_prog_option_bytes_write(addr,val)
|
||||
self.write_memory(SRAM_ADDRESS, prog)
|
||||
self.write_reg(15, SRAM_ADDRESS)
|
||||
self.run()
|
||||
i = 0
|
||||
while self.get_status() == 0x80:
|
||||
time.sleep(0.050)
|
||||
i = i + 1
|
||||
if i >= 10:
|
||||
raise TimeOutError("option bytes write")
|
||||
|
||||
status = self.read_memory_u32(FLASH_SR)
|
||||
self.write_memory_u32(FLASH_CR, FLASH_CR_LOCK)
|
||||
if (status & FLASH_SR_EOP) == 0:
|
||||
raise OperationFailure("option bytes write")
|
||||
|
||||
def option_bytes_erase(self):
|
||||
self.write_memory_u32(FLASH_KEYR, FLASH_KEY1)
|
||||
self.write_memory_u32(FLASH_KEYR, FLASH_KEY2)
|
||||
self.write_memory_u32(FLASH_SR, FLASH_SR_EOP | FLASH_SR_WRPRTERR | FLASH_SR_PGERR)
|
||||
|
||||
self.write_memory_u32(FLASH_OPTKEYR, FLASH_KEY1)
|
||||
self.write_memory_u32(FLASH_OPTKEYR, FLASH_KEY2)
|
||||
|
||||
self.write_memory_u32(FLASH_CR, FLASH_CR_OPTER)
|
||||
self.write_memory_u32(FLASH_CR, FLASH_CR_STRT | FLASH_CR_OPTER)
|
||||
|
||||
i = 0
|
||||
while True:
|
||||
status = self.read_memory_u32(FLASH_SR)
|
||||
if (status & FLASH_SR_BSY) == 0:
|
||||
break
|
||||
i = i + 1
|
||||
if i >= 1000:
|
||||
break
|
||||
|
||||
self.write_memory_u32(FLASH_CR, FLASH_CR_LOCK)
|
||||
if (status & FLASH_SR_EOP) == 0:
|
||||
raise OperationError("option bytes erase")
|
||||
|
||||
def flash_write_internal(self, addr, data, off, size):
|
||||
prog = gen_prog_flash_write(addr,size)
|
||||
self.write_memory(SRAM_ADDRESS, prog+data[off:off+size])
|
||||
self.write_reg(15, SRAM_ADDRESS)
|
||||
self.run()
|
||||
i = 0
|
||||
while self.get_status() == 0x80:
|
||||
time.sleep(0.050)
|
||||
i = i + 1
|
||||
if i >= BLOCK_WRITE_TIMEOUT:
|
||||
raise TimeOutError("flash write")
|
||||
status = self.read_memory_u32(FLASH_SR)
|
||||
if (status & FLASH_SR_PGERR) != 0:
|
||||
raise OperationFailure("flash write: write to not erased part")
|
||||
if (status & FLASH_SR_WRPRTERR) != 0:
|
||||
raise OperationFailure("flash write: write to protected part")
|
||||
|
||||
def flash_write(self, addr, data):
|
||||
self.write_memory_u32(FLASH_KEYR, FLASH_KEY1)
|
||||
self.write_memory_u32(FLASH_KEYR, FLASH_KEY2)
|
||||
self.write_memory_u32(FLASH_SR, FLASH_SR_EOP | FLASH_SR_WRPRTERR | FLASH_SR_PGERR)
|
||||
|
||||
off = 0
|
||||
while True:
|
||||
if len(data[off:]) > BLOCK_SIZE:
|
||||
size = BLOCK_SIZE
|
||||
self.flash_write_internal(addr, data, off, size)
|
||||
off = off + size
|
||||
addr = addr + size
|
||||
else:
|
||||
size = len(data[off:])
|
||||
self.flash_write_internal(addr, data, off, size)
|
||||
break
|
||||
|
||||
self.write_memory_u32(FLASH_CR, FLASH_CR_LOCK)
|
||||
|
||||
def flash_erase_all(self):
|
||||
self.write_memory_u32(FLASH_KEYR, FLASH_KEY1)
|
||||
self.write_memory_u32(FLASH_KEYR, FLASH_KEY2)
|
||||
self.write_memory_u32(FLASH_SR, FLASH_SR_EOP | FLASH_SR_WRPRTERR | FLASH_SR_PGERR)
|
||||
|
||||
self.write_memory_u32(FLASH_CR, FLASH_CR_MER)
|
||||
self.write_memory_u32(FLASH_CR, FLASH_CR_STRT | FLASH_CR_MER)
|
||||
|
||||
i = 0
|
||||
while True:
|
||||
status = self.read_memory_u32(FLASH_SR)
|
||||
if (status & FLASH_SR_BSY) == 0:
|
||||
break
|
||||
i = i + 1
|
||||
time.sleep(0.050)
|
||||
if i >= 100:
|
||||
break
|
||||
|
||||
self.write_memory_u32(FLASH_CR, FLASH_CR_LOCK)
|
||||
|
||||
if (status & FLASH_SR_EOP) == 0:
|
||||
raise OperationError("flash erase all")
|
||||
|
||||
def flash_erase_page(self, addr):
|
||||
self.write_memory_u32(FLASH_KEYR, FLASH_KEY1)
|
||||
self.write_memory_u32(FLASH_KEYR, FLASH_KEY2)
|
||||
|
||||
self.write_memory_u32(FLASH_SR, FLASH_SR_EOP | FLASH_SR_WRPRTERR | FLASH_SR_PGERR)
|
||||
|
||||
self.write_memory_u32(FLASH_CR, FLASH_CR_PER)
|
||||
self.write_memory_u32(FLASH_AR, addr)
|
||||
self.write_memory_u32(FLASH_CR, FLASH_CR_STRT | FLASH_CR_PER)
|
||||
|
||||
i = 0
|
||||
while True:
|
||||
status = self.read_memory_u32(FLASH_SR)
|
||||
if (status & FLASH_SR_BSY) == 0:
|
||||
break
|
||||
i = i + 1
|
||||
if i >= 1000:
|
||||
break
|
||||
|
||||
self.write_memory_u32(FLASH_CR, FLASH_CR_LOCK)
|
||||
|
||||
if (status & FLASH_SR_EOP) == 0:
|
||||
raise OperationError("flash page erase")
|
||||
|
||||
def start(self):
|
||||
mode = self.stl_mode()
|
||||
if mode == 2:
|
||||
return
|
||||
elif mode != 1:
|
||||
self.exit_from_dfu()
|
||||
mode = self.stl_mode()
|
||||
print "Change ST-Link/V2 mode to: %04x" % mode
|
||||
self.enter_swd()
|
||||
s = self.get_status()
|
||||
if s != 0x0080:
|
||||
raise ValueError("Status of core is not running.", s)
|
||||
mode = self.stl_mode()
|
||||
if mode != 2:
|
||||
raise ValueError("Failed to switch debug mode.", mode)
|
||||
|
||||
|
||||
USB_VENDOR_ST=0x0483 # 0x0483 SGS Thomson Microelectronics
|
||||
USB_VENDOR_STLINKV2=0x3748 # 0x3748 ST-LINK/V2
|
||||
|
||||
def stlinkv2_devices():
|
||||
busses = usb.busses()
|
||||
for bus in busses:
|
||||
devices = bus.devices
|
||||
for dev in devices:
|
||||
if dev.idVendor != USB_VENDOR_ST:
|
||||
continue
|
||||
if dev.idProduct != USB_VENDOR_STLINKV2:
|
||||
continue
|
||||
yield dev
|
||||
|
||||
def compare(data_original, data_in_device):
|
||||
i = 0
|
||||
for d in data_original:
|
||||
if ord(d) != data_in_device[i]:
|
||||
raise ValueError("Verify failed at:", i)
|
||||
i += 1
|
||||
|
||||
def open_stlinkv2():
|
||||
for d in stlinkv2_devices():
|
||||
try:
|
||||
stl = stlinkv2(d)
|
||||
return stl
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
def help():
|
||||
print "stlinkv2.py [-h]: Show this help message"
|
||||
print "stlinkv2.py [-e]: Erase flash ROM"
|
||||
print "stlinkv2.py [-u]: Unlock flash ROM"
|
||||
print "stlinkv2.py [-s]: Show status"
|
||||
print "stlinkv2.py [-b] [-n] [-r] [-i] FILE: Write content of FILE to flash ROM"
|
||||
print " -b: Blank check before write (auto erase when not blank)"
|
||||
print " -n: Don't enable read protection after write"
|
||||
print " -r: Don't reset after write"
|
||||
print " -i: Don't test SPI flash"
|
||||
|
||||
|
||||
def main(show_help, erase_only, no_protect, spi_flash_check,
|
||||
reset_after_successful_write,
|
||||
skip_blank_check, status_only, unlock, data):
|
||||
if show_help or len(sys.argv) != 1:
|
||||
help()
|
||||
return 1
|
||||
|
||||
stl = open_stlinkv2()
|
||||
if not stl:
|
||||
raise ValueError("No ST-Link/V2 device found.", None)
|
||||
|
||||
stl.start()
|
||||
core_id = stl.core_id()
|
||||
chip_id = stl.read_memory_u32(0xE0042000)
|
||||
|
||||
# FST-01 chip id: 0x20036410
|
||||
print "CORE: %08x, CHIP_ID: %08x" % (core_id, chip_id)
|
||||
print "Flash ROM read protection:",
|
||||
protection = stl.protection()
|
||||
if protection:
|
||||
print "ON"
|
||||
else:
|
||||
print "off"
|
||||
option_bytes = stl.option_bytes_read()
|
||||
print "Option bytes: %08x" % option_bytes
|
||||
if (option_bytes & 0xff) == RDP_KEY:
|
||||
ob_protection_enable = False
|
||||
else:
|
||||
ob_protection_enable = True
|
||||
|
||||
stl.enter_debug()
|
||||
status = stl.get_status()
|
||||
if status != 0x0081:
|
||||
raise ValueError("Status of core is not halt.", status)
|
||||
|
||||
if protection:
|
||||
if status_only:
|
||||
print "The MCU is now stopped."
|
||||
return 0
|
||||
elif not unlock:
|
||||
raise OperationFailure("Flash ROM is protected")
|
||||
else:
|
||||
if not skip_blank_check:
|
||||
blank = stl.blank_check()
|
||||
print "Flash ROM blank check: %s" % blank
|
||||
else:
|
||||
blank = True
|
||||
if status_only:
|
||||
stl.reset_sys()
|
||||
stl.run()
|
||||
stl.exit_debug()
|
||||
return 0
|
||||
elif unlock and not ob_protection_enable:
|
||||
print "No need to unlock. Protection is not enabled."
|
||||
return 1
|
||||
|
||||
if erase_only:
|
||||
if blank:
|
||||
print "No need to erase"
|
||||
return 0
|
||||
|
||||
stl.setup_gpio()
|
||||
|
||||
if unlock:
|
||||
stl.reset_sys()
|
||||
stl.option_bytes_write(OPTION_BYTES_ADDR,RDP_KEY)
|
||||
print "Flash ROM read protection disabled. Reset the board, now."
|
||||
return 0
|
||||
|
||||
if spi_flash_check:
|
||||
stl.spi_flash_init()
|
||||
id = stl.spi_flash_read_id()
|
||||
print "SPI Flash ROM ID: %06x" % id
|
||||
if id != 0xbf254a:
|
||||
raise ValueError("bad spi flash ROM ID")
|
||||
|
||||
if not blank:
|
||||
print "ERASE ALL"
|
||||
stl.reset_sys()
|
||||
stl.flash_erase_all()
|
||||
|
||||
if erase_only:
|
||||
stl.finish_gpio()
|
||||
return 0
|
||||
|
||||
time.sleep(0.100)
|
||||
|
||||
print "WRITE"
|
||||
stl.flash_write(0x08000000, data)
|
||||
|
||||
print "VERIFY"
|
||||
data_received = ()
|
||||
size = len(data)
|
||||
off = 0
|
||||
while size > 0:
|
||||
if size > 1024:
|
||||
blk_size = 1024
|
||||
else:
|
||||
blk_size = size
|
||||
data_received = data_received + stl.read_memory(0x08000000+off, 1024)
|
||||
size = size - blk_size
|
||||
off = off + blk_size
|
||||
compare(data, data_received)
|
||||
|
||||
if not no_protect:
|
||||
print "PROTECT"
|
||||
stl.option_bytes_erase()
|
||||
print "Flash ROM read protection enabled. Reset the board to enable protection."
|
||||
|
||||
if reset_after_successful_write:
|
||||
stl.reset_sys()
|
||||
stl.run()
|
||||
stl.exit_debug()
|
||||
else:
|
||||
stl.finish_gpio()
|
||||
|
||||
stl.shutdown()
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
show_help = False
|
||||
erase_only = False
|
||||
no_protect = False
|
||||
reset_after_successful_write = True
|
||||
skip_blank_check=True
|
||||
status_only = False
|
||||
unlock = False
|
||||
data = None
|
||||
spi_flash_check = True
|
||||
|
||||
while len(sys.argv) > 1:
|
||||
if sys.argv[1] == '-h':
|
||||
sys.argv.pop(1)
|
||||
show_help = True
|
||||
break
|
||||
elif sys.argv[1] == '-e':
|
||||
sys.argv.pop(1)
|
||||
erase_only = True
|
||||
skip_blank_check=False
|
||||
break
|
||||
elif sys.argv[1] == '-u':
|
||||
sys.argv.pop(1)
|
||||
unlock = True
|
||||
break
|
||||
elif sys.argv[1] == '-s':
|
||||
sys.argv.pop(1)
|
||||
status_only = True
|
||||
skip_blank_check=False
|
||||
break
|
||||
elif sys.argv[1] == '-b':
|
||||
skip_blank_check=False
|
||||
elif sys.argv[1] == '-n':
|
||||
no_protect = True
|
||||
elif sys.argv[1] == '-r':
|
||||
reset_after_successful_write = False
|
||||
elif sys.argv[1] == '-i':
|
||||
spi_flash_check = False
|
||||
else:
|
||||
filename = sys.argv[1]
|
||||
f = open(filename,'rb')
|
||||
data = f.read()
|
||||
f.close()
|
||||
sys.argv.pop(1)
|
||||
|
||||
colorama_init()
|
||||
|
||||
try:
|
||||
r = main(show_help, erase_only, no_protect, spi_flash_check,
|
||||
reset_after_successful_write,
|
||||
skip_blank_check, status_only, unlock, data)
|
||||
if r == 0:
|
||||
print Fore.WHITE + Back.BLUE + Style.BRIGHT + "SUCCESS" + Style.RESET_ALL
|
||||
sys.exit(r)
|
||||
except Exception as e:
|
||||
print Back.RED + Style.BRIGHT + repr(e) + Style.RESET_ALL
|
||||
60
tool/usb_strings.py
Executable file
60
tool/usb_strings.py
Executable file
@@ -0,0 +1,60 @@
|
||||
#! /usr/bin/python
|
||||
|
||||
"""
|
||||
usb_strings.py - a tool to dump USB string
|
||||
|
||||
Copyright (C) 2012 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/>.
|
||||
"""
|
||||
|
||||
import usb, sys
|
||||
|
||||
USB_VENDOR_FSIJ=0x234b
|
||||
USB_PRODUCT_GNUK=0x0000
|
||||
|
||||
def gnuk_devices():
|
||||
busses = usb.busses()
|
||||
for bus in busses:
|
||||
devices = bus.devices
|
||||
for dev in devices:
|
||||
if dev.idVendor != USB_VENDOR_FSIJ:
|
||||
continue
|
||||
if dev.idProduct != USB_PRODUCT_GNUK:
|
||||
continue
|
||||
yield dev
|
||||
|
||||
title = [ '', 'Vendor', 'Product', 'Serial', 'Revision', 'Config', 'Sys' ]
|
||||
|
||||
def main(n):
|
||||
for dev in gnuk_devices():
|
||||
handle = dev.open()
|
||||
print "Device: ", dev.filename
|
||||
try:
|
||||
for i in range(1,n):
|
||||
str = handle.getString(i, 512)
|
||||
print "%10s: %s" % (title[i], str)
|
||||
except:
|
||||
pass
|
||||
del dev
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) > 1:
|
||||
n = int(sys.argv[1])
|
||||
else:
|
||||
n = 7 # Gnuk has seven strings
|
||||
main(n)
|
||||
Reference in New Issue
Block a user