diff --git a/README b/README index 035376b..720bc92 100644 --- a/README +++ b/README @@ -1,7 +1,9 @@ Gnuk - software for GPG USB Token - Version 0.0 2010-09-05 - Niibe Yutaka + Version 0.0 + 2010-09-06 + Niibe Yutaka + Free Software Initiative of Japan What's Gnuk =========== @@ -14,8 +16,7 @@ processor. Release notes ============= -This is initial release of Gnuk, and it is experimental yet. -It is not yet daily use. +This is initial release of Gnuk, and it is experimental. Supported and tested features are: @@ -25,7 +26,7 @@ Supported and tested features are: * Password handling (PW1, RC, PW3) - * Single key import + * Single key import for signature. * PSO: Digital Signature @@ -107,6 +108,13 @@ Type: $ make +In the make process, it takes time for the command of + + dd if=/dev/random bs=1 of=random_bits count=1024 + +Don't just wait, but do some other work on your PC. +/dev/random needs entropy to finish. + Then, we will have "gnuk.elf". @@ -134,7 +142,6 @@ virtual COM port by: and you will see debug output of Gnuk. - For libccid, we need following change: --- /etc/libccid_Info.plist.dpkg-dist 2009-07-29 06:50:20.000000000 +0900 @@ -171,7 +178,7 @@ Then, try following to see Gnuk runs: $ gpg --card-status -For more, see doc/HOWTO_GNUK. +For more, see doc/HOWTO-GNUK. diff --git a/doc/HACKING b/doc/HACKING new file mode 100644 index 0000000..aa22d21 --- /dev/null +++ b/doc/HACKING @@ -0,0 +1,30 @@ +* Random Number Generator + +RNG is needed for Data Encryption Key to encrypt private key (P and Q). +It is important to collect enough entropy. Perhaps, it would +be possible to get entropy from USB traffic (of other devices). + + +* RSA + +It would be good not to use malloc. + + +* Manufacture ID + +Get it from FSFE. + + +* Serial number + +Currently, aid[] in openpgp-do.c has serial number 00000001. +It would be good to generate (random) number at compile time. + + +* Flash ROM recover from shutdown + + +* Flash ROM garbage collection + + +* Flash ROM protection diff --git a/doc/NOTES b/doc/NOTES index fe8be97..bd92b13 100644 --- a/doc/NOTES +++ b/doc/NOTES @@ -2,21 +2,20 @@ USB communication ================= * No command chaining, but extended APDU and extended Lc and Le + * dwMaxCCIDMessageLength: 64 OpenPGP card protocol implementation ==================================== -* No clear password(s) - -PW1 - -* Support of Resetting code +I try to follow "no clear password(s)" policy. +After key import, keystrings are also removed. +But because of this, we only support single key for this version. -KEY -======= +How a private key is stored +=========================== KEYPTR ----> [ P ][ Q ][ N ] diff --git a/src/Makefile b/src/Makefile index ce44b19..885879c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -89,7 +89,8 @@ CSRC = $(PORTSRC) \ $(CRYPTSRC) \ main.c usb_lld.c \ hw_config.c usb_desc.c usb_prop.c \ - usb-icc.c openpgp.c ac.c openpgp-do.c flash.c hardclock.c random.c + usb-icc.c openpgp.c ac.c openpgp-do.c flash.c hardclock.c \ + random.c ifneq ($(ENABLE_DEBUG),) CSRC += debug.c @@ -203,3 +204,17 @@ ifeq ($(USE_FWLIB),yes) endif include $(CHIBIOS)/os/ports/GCC/ARM/rules.mk + +OBJS += random-data.o +OUTFILES += random_bits + +random_bits: + dd if=/dev/random bs=1 of=random_bits count=1024 + +random-data.o: random_bits + $(CP) -I binary $< -O elf32-littlearm -B arm \ + --rename-section \ + .data=.gnuk_random,alloc,load,readonly,data,contents \ + $@ + +$(PROJECT).elf: random-data.o diff --git a/src/ac.c b/src/ac.c index f387768..598220e 100644 --- a/src/ac.c +++ b/src/ac.c @@ -34,6 +34,9 @@ verify_pso_cds (const uint8_t *pw, int pw_len) || pw_status_bytes[PW_STATUS_PW1] == 0) /* locked */ return 0; + DEBUG_INFO ("verify_pso_cds\r\n"); + DEBUG_BYTE (pw_len); + keystring[0] = pw_len; sha1 (pw, pw_len, keystring+1); memcpy (pwsb, pw_status_bytes, SIZE_PW_STATUS_BYTES); @@ -43,7 +46,7 @@ verify_pso_cds (const uint8_t *pw, int pw_len) gpg_do_write_simple (NR_DO_PW_STATUS, pwsb, SIZE_PW_STATUS_BYTES); return r; } - else + else if (pwsb[PW_STATUS_PW1] != 3) { pwsb[PW_STATUS_PW1] = 3; gpg_do_write_simple (NR_DO_PW_STATUS, pwsb, SIZE_PW_STATUS_BYTES); @@ -80,7 +83,7 @@ verify_pso_other (const uint8_t *pw, int pw_len) gpg_do_write_simple (NR_DO_PW_STATUS, pwsb, SIZE_PW_STATUS_BYTES); return r; } - else + else if (pwsb[PW_STATUS_PW1] != 3) { pwsb[PW_STATUS_PW1] = 3; gpg_do_write_simple (NR_DO_PW_STATUS, pwsb, SIZE_PW_STATUS_BYTES); @@ -162,7 +165,7 @@ verify_admin_0 (const uint8_t *pw, int buf_len, int pw_len_known) gpg_do_write_simple (NR_DO_PW_STATUS, pwsb, SIZE_PW_STATUS_BYTES); return -1; } - else + else if (pwsb[PW_STATUS_PW3] != 3) { /* OK, the user is now authenticated */ pwsb[PW_STATUS_PW3] = 3; gpg_do_write_simple (NR_DO_PW_STATUS, pwsb, SIZE_PW_STATUS_BYTES); diff --git a/src/flash.c b/src/flash.c index b99914d..e70c105 100644 --- a/src/flash.c +++ b/src/flash.c @@ -231,7 +231,7 @@ flash_do_write (uint8_t nr, const uint8_t *data, int len) } else { - hw |= data[0]<<8; + hw |= (data[0]<<8); if (flash_program_halfword (addr, hw) != FLASH_COMPLETE) return NULL; addr += 2; diff --git a/src/gnuk.h b/src/gnuk.h index 2db6f79..9c876cd 100644 --- a/src/gnuk.h +++ b/src/gnuk.h @@ -185,10 +185,10 @@ extern uint8_t keystring_md_pw3[KEYSTRING_MD_SIZE]; #define SIZE_PW_STATUS_BYTES 7 -extern uint8_t *get_data_encryption_key (void); /* 16-byte random bytes */ -extern void dek_free (uint8_t *); +/* 32-byte random bytes */ extern uint32_t get_random (void); -extern void random_init (void); +extern const uint8_t *random_bytes_get (void); +extern void random_bytes_free (const uint8_t *); extern uint32_t hardclock (void); diff --git a/src/gnuk.ld b/src/gnuk.ld index 0cfea61..393e770 100644 --- a/src/gnuk.ld +++ b/src/gnuk.ld @@ -115,6 +115,11 @@ SECTIONS PROVIDE(end = .); _end = .; + .gnuk_random : ALIGN (1024) + { + *(.gnuk_random) + } > flash + .gnuk_flash : ALIGN (1024) { _do_pool = .; diff --git a/src/main.c b/src/main.c index c0d42fc..694d186 100644 --- a/src/main.c +++ b/src/main.c @@ -173,7 +173,6 @@ main (int argc, char **argv) { eventmask_t m; int count = 0; - uint8_t once = 0; (void)argc; (void)argv; @@ -200,8 +199,6 @@ main (int argc, char **argv) while (1) { - uint32_t r; - #if 0 if (palReadPad(IOPORT1, GPIOA_BUTTON)) palSetPad (IOPORT3, GPIOC_LED); @@ -211,23 +208,15 @@ main (int argc, char **argv) if (m == EV_LED) palClearPad (IOPORT3, GPIOC_LED); - if (once == 0 && bDeviceState == CONFIGURED) - { - random_init (); - once = 1; - r = get_random (); - DEBUG_WORD (r); - } - +#ifdef DEBUG_MORE if (bDeviceState == CONFIGURED && (count % 100) == 0) { - r = get_random (); - - DEBUG_WORD (r); + DEBUG_WORD (count / 100); _write ("\r\nThis is ChibiOS 2.0.2 on Olimex STM32-H103.\r\n" "Testing USB driver.\n\n" "Hello world\r\n\r\n", 47+21+15); } +#endif m = chEvtWaitOneTimeout (ALL_EVENTS, 100); if (m == EV_LED) diff --git a/src/openpgp-do.c b/src/openpgp-do.c index eb68895..770b33d 100644 --- a/src/openpgp-do.c +++ b/src/openpgp-do.c @@ -38,7 +38,7 @@ */ /* AID */ -static const uint8_t const aid[] __attribute__ ((aligned (1))) = { +static const uint8_t aid[] __attribute__ ((aligned (1))) = { 16, 0xd2, 0x76, 0x00, 0x01, 0x24, 0x01, 0x02, 0x00, /* Version 2.0 */ @@ -48,7 +48,7 @@ static const uint8_t const aid[] __attribute__ ((aligned (1))) = { }; /* Historical Bytes (template) */ -static const uint8_t const historical_bytes[] __attribute__ ((aligned (1))) = { +static const uint8_t historical_bytes[] __attribute__ ((aligned (1))) = { 10, 0x00, 0x31, 0x80, /* Full DF name */ @@ -61,7 +61,7 @@ static const uint8_t const historical_bytes[] __attribute__ ((aligned (1))) = { }; /* Extended Capabilities */ -static const uint8_t const extended_capabilities[] __attribute__ ((aligned (1))) = { +static const uint8_t extended_capabilities[] __attribute__ ((aligned (1))) = { 10, 0x30, /* * No SM, No get challenge, @@ -78,7 +78,7 @@ static const uint8_t const extended_capabilities[] __attribute__ ((aligned (1))) }; /* Algorithm Attributes */ -static const uint8_t const algorithm_attr[] __attribute__ ((aligned (1))) = { +static const uint8_t algorithm_attr[] __attribute__ ((aligned (1))) = { 6, 0x01, /* RSA */ 0x08, 0x00, /* Length modulus (in bit): 2048 */ @@ -86,7 +86,7 @@ static const uint8_t const algorithm_attr[] __attribute__ ((aligned (1))) = { 0x00 /* 0: p&q , 3: CRT with N (not yet supported) */ }; -static const uint8_t const do_pw_status_bytes_template[] = +static const uint8_t do_pw_status_bytes_template[] = { 7, 1, /* PW1 valid for several PSO:CDS commands */ @@ -543,7 +543,7 @@ gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, int key_len, const uint8_t *modulus; struct prvkey_data *pd; uint8_t *key_addr; - uint8_t *dek; + const uint8_t *dek; const uint8_t *ks_pw1 = gpg_do_read_simple (NR_DO_KEYSTRING_PW1); const uint8_t *ks_rc = gpg_do_read_simple (NR_DO_KEYSTRING_RC); @@ -584,7 +584,7 @@ gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, int key_len, DEBUG_INFO ("enc..."); - dek = get_data_encryption_key (); /* 16-byte random bytes */ + dek = random_bytes_get (); /* 16-byte random bytes */ encrypt (dek, (uint8_t *)&kd, sizeof (struct key_data)); DEBUG_INFO ("done\r\n"); @@ -594,7 +594,7 @@ gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, int key_len, if (r < 0) { - dek_free (dek); + random_bytes_free (dek); free (pd); return r; } @@ -605,10 +605,12 @@ gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, int key_len, ac_reset_pso_cds (); if (ks_pw1) { + uint8_t ks_pw1_len = ks_pw1[0]; + memcpy (pd->dek_encrypted_1, dek, DATA_ENCRYPTION_KEY_SIZE); encrypt (ks_pw1+1, pd->dek_encrypted_1, DATA_ENCRYPTION_KEY_SIZE); /* Only its length */ - gpg_do_write_simple (NR_DO_KEYSTRING_PW1, ks_pw1, 1); + gpg_do_write_simple (NR_DO_KEYSTRING_PW1, &ks_pw1_len, 1); } else { @@ -624,10 +626,12 @@ gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, int key_len, if (ks_rc) { + uint8_t ks_rc_len = ks_rc[0]; + memcpy (pd->dek_encrypted_2, dek, DATA_ENCRYPTION_KEY_SIZE); encrypt (ks_rc+1, pd->dek_encrypted_2, DATA_ENCRYPTION_KEY_SIZE); /* Only its length */ - gpg_do_write_simple (NR_DO_KEYSTRING_RC, ks_rc, 1); + gpg_do_write_simple (NR_DO_KEYSTRING_RC, &ks_rc_len, 1); } else memset (pd->dek_encrypted_2, 0, DATA_ENCRYPTION_KEY_SIZE); @@ -638,7 +642,7 @@ gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, int key_len, p = flash_do_write (nr, (const uint8_t *)pd, sizeof (struct prvkey_data)); do_ptr[nr] = p; - dek_free (dek); + random_bytes_free (dek); free (pd); if (p == NULL) return -1; @@ -1086,9 +1090,10 @@ void gpg_do_public_key (uint8_t kk_byte) { const uint8_t *do_data; - uint8_t *key_addr; + const uint8_t *key_addr; DEBUG_INFO ("Public key\r\n"); + DEBUG_BYTE (kk_byte); if (kk_byte == 0xb6) do_data = do_ptr[NR_DO_PRVKEY_SIG]; @@ -1104,7 +1109,7 @@ gpg_do_public_key (uint8_t kk_byte) return; } - key_addr = *(uint8_t **)&do_data[1]; + key_addr = *(const uint8_t **)&do_data[1]; res_p = res_APDU; @@ -1200,12 +1205,18 @@ gpg_do_reset_pw_counter (uint8_t which) if (do_data) { memcpy (pwsb, &do_data[1], SIZE_PW_STATUS_BYTES); + if (pwsb[which] == 3) + return; + pwsb[which] = 3; flash_do_release (do_data); } else { memcpy (pwsb, PW_STATUS_BYTES_TEMPLATE, SIZE_PW_STATUS_BYTES); + if (pwsb[which] == 3) + return; + pwsb[which] = 3; } diff --git a/src/openpgp.c b/src/openpgp.c index 7977b2e..677d335 100644 --- a/src/openpgp.c +++ b/src/openpgp.c @@ -42,7 +42,7 @@ #define INS_PUT_DATA 0xda #define INS_PUT_DATA_ODD 0xdb /* For key import */ -static const uint8_t const +static const uint8_t select_file_TOP_result[] __attribute__ ((aligned (1))) = { 0x00, 0x00, /* unused */ 0x0b, 0x10, /* number of bytes in this directory */ @@ -60,7 +60,7 @@ select_file_TOP_result[] __attribute__ ((aligned (1))) = { 0x00, 0x00 /* PIN status: OK, PIN blocked?: No */ }; -static const uint8_t const +static const uint8_t read_binary_result[] __attribute__ ((aligned (1))) = { 0x5a, 0x4, 0x01, 0x02, 0x03, 0x04 }; @@ -89,17 +89,24 @@ cmd_verify (void) int len; uint8_t p2 = cmd_APDU[3]; int r; + int data_start = 5; DEBUG_INFO (" - VERIFY\r\n"); DEBUG_BYTE (p2); len = cmd_APDU[4]; + if (len == 0) /* extended length */ + { + len = (cmd_APDU[5]<<8) | cmd_APDU[6]; + data_start = 7; + } + if (p2 == 0x81) - r = verify_pso_cds (&cmd_APDU[5], len); + r = verify_pso_cds (&cmd_APDU[data_start], len); else if (p2 == 0x82) - r = verify_pso_other (&cmd_APDU[5], len); + r = verify_pso_other (&cmd_APDU[data_start], len); else - r = verify_admin (&cmd_APDU[5], len); + r = verify_admin (&cmd_APDU[data_start], len); if (r < 0) { @@ -152,6 +159,12 @@ cmd_change_password (void) DEBUG_INFO ("Change PW\r\n"); DEBUG_BYTE (who); + if (len == 0) /* extended length */ + { + len = (cmd_APDU[5]<<8) | cmd_APDU[6]; + pw += 2; + } + if (who == 1) /* PW1 */ { const uint8_t *pk = gpg_do_read_simple (NR_DO_KEYSTRING_PW1); @@ -169,6 +182,9 @@ cmd_change_password (void) pw_len = 6; newpw = pw + pw_len; newpw_len = len - pw_len; + + sha1 (newpw, newpw_len, new_ks); + new_ks0[0] = newpw_len; goto no_prvkey; } else @@ -244,22 +260,28 @@ static void cmd_reset_user_password (void) { uint8_t p1 = cmd_APDU[2]; - int len = cmd_APDU[3]; - const uint8_t *pw = &cmd_APDU[4]; + int len = cmd_APDU[4]; + const uint8_t *pw = &cmd_APDU[5]; const uint8_t *newpw; int pw_len, newpw_len; int r; + uint8_t new_ks0[KEYSTRING_MD_SIZE+1]; + uint8_t *new_ks = &new_ks0[1]; DEBUG_INFO ("Reset PW1\r\n"); DEBUG_BYTE (p1); + if (len == 0) /* extended length */ + { + len = (cmd_APDU[5]<<8) | cmd_APDU[6]; + pw += 2; + } + if (p1 == 0x00) /* by User with Reseting Code */ { const uint8_t *pw_status_bytes = gpg_do_read_simple (NR_DO_PW_STATUS); const uint8_t *ks_rc = gpg_do_read_simple (NR_DO_KEYSTRING_RC); uint8_t old_ks[KEYSTRING_MD_SIZE]; - uint8_t new_ks0[KEYSTRING_MD_SIZE+1]; - uint8_t *new_ks = &new_ks0[1]; if (pw_status_bytes == NULL || pw_status_bytes[PW_STATUS_PW1] == 0) /* locked */ @@ -318,46 +340,43 @@ cmd_reset_user_password (void) } else /* by Admin (p1 == 0x02) */ { + const uint8_t *old_ks = keystring_md_pw3; + if (!ac_check_status (AC_ADMIN_AUTHORIZED)) { DEBUG_INFO ("permission denied.\r\n"); GPG_SECURITY_FAILURE (); + return; + } + + newpw_len = len; + newpw = pw; + sha1 (newpw, newpw_len, new_ks); + new_ks0[0] = newpw_len; + r = gpg_change_keystring (3, old_ks, 1, new_ks); + if (r < -2) + { + DEBUG_INFO ("memory error.\r\n"); + GPG_MEMORY_FAILURE (); + } + else if (r < 0) + { + DEBUG_INFO ("security error.\r\n"); + GPG_SECURITY_FAILURE (); + } + else if (r == 0) + { + DEBUG_INFO ("done (no privkey).\r\n"); + gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, KEYSTRING_SIZE_PW1); + ac_reset_pso_cds (); + gpg_do_reset_pw_counter (PW_STATUS_PW1); } else { - const uint8_t *old_ks = keystring_md_pw3; - uint8_t new_ks0[KEYSTRING_MD_SIZE+1]; - uint8_t *new_ks = &new_ks0[1]; - - newpw_len = len; - newpw = pw; - sha1 (newpw, newpw_len, new_ks); - new_ks0[0] = newpw_len; - r = gpg_change_keystring (3, old_ks, 1, new_ks); - if (r < -2) - { - DEBUG_INFO ("memory error.\r\n"); - GPG_MEMORY_FAILURE (); - } - else if (r < 0) - { - DEBUG_INFO ("security error.\r\n"); - GPG_SECURITY_FAILURE (); - } - else if (r == 0) - { - DEBUG_INFO ("done (no privkey).\r\n"); - gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, KEYSTRING_SIZE_PW1); - ac_reset_pso_cds (); - gpg_do_reset_pw_counter (PW_STATUS_PW1); - } - else - { - DEBUG_INFO ("done.\r\n"); - ac_reset_pso_cds (); - gpg_do_reset_pw_counter (PW_STATUS_PW1); - GPG_SUCCESS (); - } + DEBUG_INFO ("done.\r\n"); + ac_reset_pso_cds (); + gpg_do_reset_pw_counter (PW_STATUS_PW1); + GPG_SUCCESS (); } } } @@ -395,7 +414,7 @@ cmd_pgp_gakp (void) if (cmd_APDU[2] == 0x81) /* Get public key */ - gpg_do_public_key (cmd_APDU[5]); + gpg_do_public_key (cmd_APDU[7]); else { /* Generate key pair */ if (!ac_check_status (AC_ADMIN_AUTHORIZED)) diff --git a/src/random.c b/src/random.c index 180c511..629f2a2 100644 --- a/src/random.c +++ b/src/random.c @@ -1,59 +1,51 @@ +/* + * random.c -- get random bytes + * + * Copyright (C) 2010 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * 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 . + * + */ + #include "config.h" #include "ch.h" #include "gnuk.h" -/* - * XXX: I have tried havege_rand, but it requires too much memory... - */ +extern void *_binary_random_bits_start; -/* - * Multiply-with-carry method by George Marsaglia - */ -static uint32_t m_w; -static uint32_t m_z; +const uint8_t * +random_bytes_get (void) +{ + uint32_t addr; + + addr = (uint32_t)&_binary_random_bits_start + ((hardclock () << 5) & 0x3e0); + + return (const uint8_t *)addr; +} + +void +random_bytes_free (const uint8_t *p) +{ + (void)p; +} uint32_t get_random (void) { - m_z = 36969 * (m_z & 65535) + (m_z >> 16); - m_w = 18000 * (m_w & 65535) + (m_w >> 16); - - return (m_z << 16) + m_w; -} - -void -random_init (void) -{ - static uint8_t s = 0; - - again: - if ((s & 1)) - m_w = (m_w << 8) ^ hardclock (); - else - m_z = (m_z << 8) ^ hardclock (); - - s++; - if (m_w == 0 || m_z == 0) - goto again; -} - -uint8_t dek[16]; -uint8_t *get_data_encryption_key (void) -{ - uint32_t r; - r = get_random (); - memcpy (dek, &r, 4); - r = get_random (); - memcpy (dek+4, &r, 4); - r = get_random (); - memcpy (dek+8, &r, 4); - r = get_random (); - memcpy (dek+12, &r, 4); - return dek; -} - -void -dek_free (uint8_t *dek) -{ - (void)dek; + const uint32_t *p = (const uint32_t *)random_bytes_get (); + return *p; } diff --git a/src/usb-icc.c b/src/usb-icc.c index bb529dc..809fee9 100644 --- a/src/usb-icc.c +++ b/src/usb-icc.c @@ -221,7 +221,9 @@ icc_send_status (void) USB_SIL_Write (EP1_IN, icc_tx_data, icc_tx_size); SetEPTxValid (ENDP1); } +#ifdef DEBUG_MORE DEBUG_INFO ("St\r\n"); +#endif } enum icc_state @@ -267,7 +269,9 @@ icc_send_data_block (uint8_t status, uint8_t error, uint8_t chain, icc_tx_size = ICC_MSG_DATA_OFFSET + len; USB_SIL_Write (EP1_IN, icc_tx_data, icc_tx_size); SetEPTxValid (ENDP1); +#ifdef DEBUG_MORE DEBUG_INFO ("DATA\r\n"); +#endif } }