diff --git a/gnuk/AUTHORS b/gnuk/AUTHORS new file mode 100644 index 0000000000..5a7b6cfead --- /dev/null +++ b/gnuk/AUTHORS @@ -0,0 +1,67 @@ +Aurelien Jarno: + Modified: + src/Makefile + src/configure + src/main.c + src/stack-def.h + +Anthony Romano: + Modified: + src/call-rsa.c + src/main.c + src/mod.c + +Jeremy Drake: + Modified: + regnual/regnual.c + +Kaz Kojima: + Added STM32 Primer2 support. + +NIIBE Yutaka: + Founder of the project. + Wrote tools for STLink/V2: + tool/stlinkv2.py + Wrote tools for DfuSe: + tool/dfuse.py + tool/dump_mem.py + tool/intel_hex.py + Wrote a tool for Gnuk: + tool/gnuk_put_binary.py + tool/gnuk_put_binary_libusb.py + tool/gnuk_remove_keys.py + tool/gnuk_upgrade.py + Wrote a tool for USB Hub: + tool/hub_ctrl.py + Wrote a tool for testing card reader with pinpad: + tool/pinpadtest.py + Wrote reGNUal implementation: + regnual/regnual.c + regnual/sys.c + Wrote Gnuk implementation: + gnuk.svg + src/configure + src/ac.c + src/call-rsa.c + src/debug.c + src/flash.c + src/gnuk.h + src/main.c + src/neug.c + src/openpgp-do.c + src/openpgp.c + src/openpgp.h + src/pin-cir.c + src/pin-dial.c + src/pin-dnd.c + src/random.c + src/sys.c + src/usb-icc.c + src/usb-msc.c + src/usb-msc.h + src/usb_ctrl.c + src/usb_desc.c + src/usb_lld.c + src/usb_lld.h + * + and others. diff --git a/gnuk/ChangeLog b/gnuk/ChangeLog new file mode 100644 index 0000000000..e1c63dd9fc --- /dev/null +++ b/gnuk/ChangeLog @@ -0,0 +1,2218 @@ +2017-10-12 Aurelien Jarno + + * polarssl/include/polarssl/bn_mul.h (MULADDC_HUIT_DEAD): Rename + from MULADDC_HUIT. + [__ARM_FEATURE_DSP] (MULADDC_1024_CORE, MULADDC_1024_LOOP) + (MULADDC_INIT, MULADDC_CORE, MULADDC_HUIT, MULADDC_STOP): New. + + * polarssl/library/bignum.c (mpi_montsqr): Check on + POLARSSL_HAVE_ASM and __arm__. + [__ARM_FEATURE_DSP] (mpi_montsqr): New. + (MAX_WSIZE): New. + (mpi_exp_mod): Use MAX_WSIZE. + + * src/Makefile (DEFS): Remove BIGNUM_C_IMPLEMENTATION. + + * src/main.c (HEAP_SIZE): Rename from MEMORY_SIZE. + (HEAP_END, HEAP_ALIGNMENT, HEAP_ALIGN): Likewise. + + * src/stack-def.h (SIZE_3): Depend on MEMORY_SIZE. + * src/configure: Emit DEFS with MEMORY_SIZE. + +2017-10-11 NIIBE Yutaka + + * VERSION: 1.2.6. + + * regnual/Makefile (LDSCRIPT): Move after include. + * regnual/types.h: Add uintptr_t. + + * test/features/002_get_data_static.feature (data object AID): Fix + for any binary value. + * 402_get_data_static.feature: Likewise. + * 802_get_data_static.feature: Likewise. + +2017-10-10 NIIBE Yutaka + + * src/main.c (main): Support --debug option. + * chopstx: Update to 1.5. + +2017-10-06 NIIBE Yutaka + + * src/configure (flash_override): Fix suggested by Jeremy Drake. + (help): STM8S_DISCOVERY is supported again. + +2017-10-06 NIIBE Yutaka + + * src/gnuk.ld.in (.stacks): Specify NOLOAD type. + + * src/configure: Allow not specifying VIDPID. + + * src/main.c [GNU_LINUX_EMULATION] (main): Handle "--vidpid" + option to assign vendor ID and product ID of USB. + + * src/usb_desc.c [GNU_LINUX_EMULATION] (device_desc): Export. + + * GNUK_USB_DEVICE_ID (0000:0000): New. + +2017-10-05 NIIBE Yutaka + + * src/stack-def.h (SIZE_1, SIZE_3): Tweak the size. + + * src/call-rsa.c (rsa_genkey): Single step. + * src/openpgp-do.c (gpg_do_keygen): Do RSA key generation in single + step, using APDU buffer. + * src/openpgp.c (cmd_pgp_gakp): Supply the APDU as a buffer. + + * src/Makefile (install): New target. + + * src/configure (prefix. exec_prefix, libexecdir): Add. + + * src/main.c [GNU_LINUX_EMULATION] (main): Option handling. + + * tool/gnuk-emulation-setup: New. + + * polarssl/library/bignum.c (M_LIMBS, limbs_M, MAX_A_LIMBS) + (limbs_MAX_A, mpi_gen_prime): Fix for 64-bit machine. + +2017-10-04 NIIBE Yutaka + + * src/configure (output_vendor_product_serial_strings): Support + GNU/Linux emulation. + + * polarssl/library/bignum.c (mpi_div_mpi): Fix for 64-bit machine. + + * src/main.c (gnuk_malloc, gnuk_free): Fix for 64-bit machine. + + * src/stack-def.h (SIZE_3): Tweak the size. + + * src/openpgp-do.c (gpg_do_keygen): Do RSA key generation in two + steps. + + * src/call-rsa.c (rsa_genkey_start, rsa_genkey_finish): New. + (rsa_genkey): Remove. + +2017-10-03 NIIBE Yutaka + + * src/call-ec.c (ecc_compute_public): No use of malloc. + * src/call-rsa.c (modulus_calc, rsa_genkey): Likewise. + * src/ecc-edwards.c (eddsa_compute_public_25519): Likewise. + * src/ecc-mont.c (ecdh_compute_public_25519): Likewise. + * src/openpgp-do.c (gpg_do_write_prvkey, gpg_do_chks_prvkey) + (proc_key_import, gpg_do_keygen): Likewise. + + * polarssl/library/rsa.c: Don't include stdlib.h. + * src/gnuk-malloc.h: Rename from stdlib.h. + * polarssl/library/bignum.c: Include gnuk-malloc.h. + + * src/Makefile (build/flash.data): Generate. + + * src/main.c (flash_addr_key_storage_start) + (flash_addr_data_storage_start): New. + (main): Determine flash address. + + * src/flash.c (FLASH_ADDR_KEY_STORAGE_START) + (FLASH_ADDR_DATA_STORAGE_START): New. + (flash_do_storage_init, flash_terminate, flash_activate) + (flash_key_storage_init, flash_copying_gc, flash_do_release) + (flash_key_getpage): Use new macros. + +2017-10-02 NIIBE Yutaka + + * src/main.c (device_initialize_once): Not for GNU/Linux. + + * src/openpgp.c, src/flash.c: Distinguish FLASH_UPGRADE_SUPPORT. + + * src/main.c [GNU_LINUX_EMULATION]: Use emulated_main. + (MEMORY_SIZE, MEMORY_END): Fix for GNU/Linux. + + * src/usb-ccid.c (INTR_REQ_USB): Fix for GNU/Linux. + + * polarssl/library/bignum.c (mpi_montsqr): Easy C implementation. + +2017-09-30 NIIBE Yutaka + + * src/flash.c (flash_terminate, flash_activate) + (flash_copying_gc, flash_do_write_internal, flash_do_release) + (flash_key_write, flash_check_all_other_keys_released) + (flash_key_fill_zero_as_released, flash_key_release) + (flash_key_release_page, flash_clear_halfword) + (flash_put_data_internal, flash_put_data, flash_bool_clear) + (flash_bool_write_internal, flash_bool_write) + (flash_enum_write_internal, flash_enum_write) + (flash_cnt123_write_internal, flash_cnt123_increment) + (flash_cnt123_clear, flash_erase_binary, flash_write_binary): Fix + for GNU/Linux. + + * src/usb-ccid.c (ccid_tx_done): Rename from EP1_IN_Callback. + (ccid_rx_ready): Rename from EP1_OUT_Callback. + +2017-09-29 NIIBE Yutaka + + * src/usb-ccid.c (epo_init, epi_init, ccid_thread): Simplify. + (EP1_IN_Callback, ccid_prepare_receive, EP1_OUT_Callback) + (usb_rx_ready, ccid_error, ccid_power_on, ccid_send_status) + (ccid_send_data_block_internal, ccid_send_data_block_0x9000) + (ccid_send_data_block_gr, ccid_send_params) + (ccid_notify_slot_change, _write) [GNU_LINUX_EMULATION]: Use + different usb driver API. + + * src/usb_ctrl.c (usb_device_reset): Fix control endpoint init. + (gnuk_setup_endpoints_for_interface): Add DEV + argument. + (usb_device_reset) [GNU_LINUX_EMULATION]: Use usb_lld_setup_endp. + +2017-09-29 NIIBE Yutaka + + * src/main.c [FLASH_UPGRADE_SUPPORT] (main): Factor out flash ROM + upgrade support. + (calculate_regnual_entry_address): Likewise. + * src/usb_ctrl.c (usb_setup, download_check_crc32): Likewise. + + * src/openpgp.c (modify_binary): Fix for 64-bit machine. + * src/openpgp-do.c (encrypt, decrypt): Likewise. + (gpg_data_scan): Likewise. + (gpg_do_chks_prvkey): Fix error return path. + + * src/stack-def.h: New. + + * src/gnuk.ld.in: Remove stack definitions. + * src/configure: Remove stack size modifications. + + * src/main.c (STACK_MAIN, STACK_PROCESS_1): Use stack-def.h. + * src/usb-ccid.c (STACK_PROCESS_3): Likewise. + * src/usb-msc.c (STACK_PROCESS_5): Likewise. + * src/pin-cir.c (STACK_PROCESS_6, STACK_PROCESS_7): Likewise. + + * src/usb_ctrl.c (download_check_crc32): Use chrc32_rv_ functions. + + * src/mcu-stm32f103.c (rbit, check_crc32): Remove. + + * src/neug.c: Update from NeuG. + * src/neug.h: Ditto. + +2017-09-28 NIIBE Yutaka + + * src/ec_p256k1.c (coefficient_a): Remove. + + * polarssl/library/bignum.c (mpi_fill_pseudo_random): Fix for + 64-bit machine. + + * src/call-rsa.c (rsa_decrypt): Fix for 64-bit machine. + + * src/flash.c (flash_do_storage_init): Rename from flash_init. + (flash_key_storage_init): Rename from flash_init_keys. + * src/openpgp.c (gpg_init): Use new function names. + + * src/stdlib.h: Update for GNU/Linux emulation. + + * src/Makefile: Support GNU/Linux emulation. + * src/configure: Support GNU/Linux emulation. + * emulation: Remove. + +2017-08-11 NIIBE Yutaka + + * VERSION: 1.2.5. + * chopstx: Update to 1.4. + + * src/gnuk.ld.in (__process3_stack_size__): Tweak the size. + + * src/configure: Define STM32F103_OVERRIDE_FLASH_SIZE_KB for + BULE_PILL. + + * src/configure: Let generate src/config.mk. + * src/Makefile: Rename from src/Makefile.in. + * regnual/Makefile: Use src/config.mk. + +2017-08-03 NIIBE Yutaka + + * src/openpgp.c (cmd_terminate_df): Fix for admin-less mode. + +2017-08-03 Jeremy Drake + + * regnual/regnual.c (main): Allow compile time + flash size definition by STM32F103_OVERRIDE_FLASH_SIZE_KB. + +2017-08-02 Jeremy Drake + + * src/flash.c (flash_terminate): Erase Certificate DO, too. + +2017-08-01 NIIBE Yutaka + + * src/openpgp.c (FILE_CARD_TERMINATED_OPENPGP): Remove. + (cmd_select_file): Don't change file_selection. + +2017-07-19 NIIBE Yutaka + + * src/mod.c (mod_inv): Clear TMP. + + * src/configure (REVISION): Generate even when no git. + + * polarssl/library/bignum.c (mpi_exp_mod): Call mpi_grow for X + after the initialization of RR. + +2017-07-18 NIIBE Yutaka + + * src/configure: Bark when no git available. + +2017-07-18 Anthony Romano + + * docker: New. + +2017-07-18 Anthony Romano + + * src/main.c (MEMORY_SIZE, MEM_HEAD_IS_CORRUPT, MEM_HEAD_CHECK): + New. + (gnuk_malloc, gnuk_free): Add calls to MEM_HEAD_CHECK. + + * src/gnuk.h (FATAL_HEAP): New. + +2017-07-18 Anthony Romano + + * src/openpgp-do.c (gpg_reset_algo_attr): New. + (rw_algorithm_attr): Use gpg_reset_algo_attr. + Fix null dereference. + +2017-07-18 Anthony Romano + + * src/mod.c (mod_reduce): Clean up unused code. + +2017-07-18 Anthony Romano + + * src/call-rsa.c (modulus_calc): Free modulus on error. + (rsa_genkey): Remove bogus check, and call chopstx_cleanup_pop + with 1 to release p_q_modulus on error. Assign NULL to clp.arg + when it's goes with no error. + + * src/main.c (gnuk_free): Allow NULL. + +2017-07-18 NIIBE Yutaka + + * Update chopstx (with USBIP emulation). + +2017-05-12 NIIBE Yutaka + + * VERSION: 1.2.4. + +2017-04-28 NIIBE Yutaka + + * src/mcu-stm32f103.c: New. + (check_crc32, sram_address): New. + + * src/usb_ctrl.c (download_check_crc32): Use check_crc32 and + sram_address. + + * src/openpgp-do.c (gpg_write_digital_signature_counter): Fix + writing lower 10-bit. + +2017-04-27 NIIBE Yutaka + + * src/gnuk.ld.in (_data_pool): Move to the end. + + * src/flash.c (flash_init): Return address of end of data object. + * src/openpgp.c (gpg_init): Get address of end of data object. + * src/openpgp-do.c (gpg_data_scan): Check the end address. + +2017-02-02 NIIBE Yutaka + + * VERSION: 1.2.3. + + * src/gnuk.ld.in (__process1_stack_size__): Increase by 0x20. + * chopstx: Update to 1.3. + * src/configure: Add BLUE_PILL in the help message. + +2017-02-01 NIIBE Yutaka + + * README: Update README. Thanks to Paul Fertser. + +2017-01-02 Szczepan Zalega + + * tool/upgrade_by_passwd.py: Add file extention check. + +2017-02-01 NIIBE Yutaka + + * tool/upgrade_by_passwd.py (main): More verbose messages + suggested by Szczepan Zalega . + + * tool/gnuk_token.py (USB_PRODUCT_LIST): New. + (gnuk_devices_by_vidpid): Support searching by USB_PRODUCT_LIST. + Thanks to Szczepan Zalega . + + * tool/usb_strings.py: Use gnuk_token.py. + +2016-10-21 Niibe Yutaka + + * src/ecc.c (check_secret): Fix condition. + +2016-10-15 NIIBE Yutaka + + * VERSION: 1.2.2. + + * tool/gnuk_put_binary_libusb.py (main): Likewise. + * tool/upgrade_by_passwd.py (main): Add call of cmd_select_openpgp + method. + + * src/openpgp.c (gpg_init): flash_init_keys shoule be after + gpg_data_scan since flash_init_keys accesses Data Object for + key attributes. + + * src/usb-ccid.c (ccid_power_on): Don't waste stack. + +2016-10-14 Niibe Yutaka + + * src/usb-ccid.c (ccid_power_on) [LIFE_CYCLE_MANAGEMENT_SUPPORT]: + Change LCS value in ATR at run time. + + * src/openpgp.c (gpg_init): Handle FILE_CARD_TERMINATED. + (cmd_select_file): Don't return AID. + (cmd_activate_file, cmd_terminate_df): New. + (process_command_apdu): Let return GPG_NO_RECORD() when + not selected. + + * src/openpgp-do.c (gpg_do_terminate): New. + (gpg_data_scan): Handle p_start is NULL. + (do_hist_bytes): Remove. + + * src/flash.c (flash_data): Change the value from 0x0000. + (flash_init): Support termination state. Fix handling + of the boundary case where gen0 is 0xfffe. + (flash_terminate, flash_activate): New. + (flash_copying_gc): Skip 0xffff for generation number. + +2016-10-13 Niibe Yutaka + + * src/status-code.h: Rename from openpgp.h. + + * chopstx: Update to 1.2. + + * tests: New test suite for OpenPGP card with PyTest. + + * src/configure (factory_reset): New. + + * src/usb-ccid.c (ccid_power_on): Use ATR_head and historical + bytes. + + * src/openpgp-do.c (rw_algorithm_attr): Clear fingerprint, timestamp, + and possibly ds_counter. + +2016-10-12 Niibe Yutaka + + * test/features/steps.py (cmd_reset_retry_counter): Fix. + * tool/gnuk_token.py (gnuk_token.cmd_reset_retry_counter): Fix. + (gnuk_token.cmd_select_openpgp): Fix P2. + +2016-09-02 Niibe Yutaka + + * src/configure (REVISION): Fix the detection of .git. + It may be a regular file (if it's created by worktree). + +2016-08-24 Niibe Yutaka + + * test/features/steps.py (ini): Use GLC (the global context), + instead of FTC (the feature context), so that token only is + opened once. + +2016-08-03 Niibe Yutaka + + * tool/hub_ctrl.py: Port to Python 3. + +2016-07-11 NIIBE Yutaka + + * VERSION: 1.2.1. + + * src/usb-ccid.c (ccid_power_on): Fix call of chopstx_create. + * src/usb-msc.c (msc_init): Ditto. + * src/pin-cir.c (cir_init): Ditto. + * src/neug.c (neug_init): Ditto. + * src/main.c (main): Ditto. + + * src/usb-ccid.c (struct ccid): Arrange for smaller footprint. + * src/gnuk.h (struct apdu): Likewise. + + * src/usb-ccid.c (ccid_card_change_signal): Don't touch ccid_state_p. + (ccid_state_p): This is constant. + + * src/configure (output_vendor_product_serial_strings): Add const + qualifier. + + * src/usb-ccid.c (epo_init, epi_init): Simplify without notify method. + (EP1_IN_Callback, EP1_OUT_Callback): Call notify_tx and notify_icc + directly. + +2016-07-09 NIIBE Yutaka + + * src/openpgp.c (openpgp_card_thread): Don't need to get SELF. + +2016-07-06 NIIBE Yutaka + + * src/pin-cir.c (cir_getchar): Use chopstx_poll. + * src/usb-ccid.c (usb_tx_done): Fix ifdef condition. + * src/usb_ctrl.c (usb_ctrl_write_finish): Fix ifdef nesting. + +2016-07-04 NIIBE Yutaka + + * doc/conf.py: Remove 'sphinx.ext.pngmath' and 'sphinx.ext.mathjax'. + Reported by Kenji Rikitake. + +2016-07-01 NIIBE Yutaka + + * chopstx: Update to 1.1. + * src/usb-ccid.c (poll_event_intr, ccid_thread): Follow the + change. + +2016-06-21 Niibe Yutaka + + * doc/index.rst: Update documentation by an example + Ed25519/cv25519. + +2016-06-17 Niibe Yutaka + + * chopstx: Update to 1.0. + +2016-06-15 NIIBE Yutaka + + * src/gnuk.ld.in (__process2_stack_size__): Update + thread size for rng by examining NeuG. + + * src/usb-ccid.c (poll_event_intr): New. + +2016-06-14 Niibe Yutaka + + * regnual/regnual.c (usb_device_reset): Rename from + usb_cb_device_reset. + (usb_ctrl_write_finish): Rename from usb_cb_ctrl_write_finish. + (usb_setup): Rename from usb_cb_setup. + (usb_get_descriptor): Rename from usb_cb_get_descriptor. + (usb_set_configuration): New. + (usb_interrupt_handler): New. + + * src/usb-ccid.c (usb_tx_done): Rename from usb_cb_tx_done. + (usb_rx_ready): Rename from usb_cb_rx_ready. + (usb_event_handle): New. + (ccid_thread): Use usb_event_handle. + + * src/usb-msc.c (EP6_IN_Callback): Update to new USB API. + (EP6_OUT_Callback): Likewise. + + * src/usb_ctrl.c (usb_device_reset): Rename from + usb_cb_device_reset. + (vcom_port_data_setup): Update to new USB API. + (usb_ctrl_write_finish): Rename from usb_cb_ctrl_write_finish. + (usb_setup): Rename from usb_cb_setup. + (usb_set_configuration): New, based on usb_cb_handle_event. + (usb_set_interface): Rename from usb_cb_interface. + (usb_get_interface): New. + (usb_get_status_interface): New. + + * src/usb_desc.c (usb_get_descriptor): Rename from + usb_cb_get_descriptor. + +2016-06-02 Niibe Yutaka + + * regnual/regnual.c (usb_cb_tx_done): Follow the change of USB + API. + + * regnual/reset.c: Rename from sys.c. + +2016-06-01 Niibe Yutaka + + * tool/stlinkv2.py (stlinkv2.__init__): Don't + call setConfiguration. + + * tool/gnuk_token.py (gnuk_token, regnual): Don't + call setAltInterface, it's not needed. + + * src/usb-ccid.c (ccid_notify_slot_change): New. + (ccid_thread): Call ccid_notify_slot_change at + interface_reset and EV_CARD_CHANGE. + +2016-05-31 NIIBE Yutaka + + * src/usb_stm32f103.c, src/stm32f103.h: Remove. + * src/adc_stm32f103.c, src/sys.c: Remove. + + * src/usb_ctrl.c (usb_cb_interface): call ccid_usb_reset. + (usb_cb_handle_event): Likewise. + + * src/usb-ccid.c (ccid_thread): Handle RESET->CONFIGURE process + correctly. + (ccid_thread): Handle SET_INTERFACE correctly. + + * polarssl/library/aes.c (FT0, FT1, FT2): Add "weak" flag. + + * src/neug.c: Update from NeuG. + + * src/usb_desc.c (usb_cb_get_descriptor): Only valid if USE_SYS3. + + * src/Makefile.in (USE_SYS, USE_USB, USE_ADC): Enabled. + (CHIP): Add. + + * src/sys.c, src/sys.h: Remove. + * src/usb_stm32f103.c, src/usb_lld.h: Remove. + * src/adc_stm32f103.c, src/adc.h: Remove. + + * chopstx: Update to 0.12. + +2016-05-21 Niibe Yutaka + + * src/main.c (led_blink, main): Fix LED blink protocol. + +2016-05-20 NIIBE Yutaka + + * VERSION: 1.2.0. + * src/usb-ccid.c (ccid_thread): Fix timeout. + (icc_handle_timeout, icc_send_status): Tweak. + +2016-05-19 Niibe Yutaka + + * src/usb_ctrl.c (usb_cb_ctrl_write_finish): Set bDeviceState. + + * src/usb-ccid.c: Rename from usb-icc.c. + (ccid_thread): Handle reGNUal upgrade. + + * src/Makefile.in (CSRC): Follow the change. + + * chopstx: Update to 0.11. + +2016-05-18 Niibe Yutaka + + * src/gnuk.ld.in: Tweak thread size. + * src/main.c (main): Use chopstx_setpriority. + * src/usb-icc.c (ccid_init): Use new eventflag API. + + * regnual/regnual.c (nvic_enable_intr): New. + (main): Call nvic_enable_intr. + + * chopstx: Update. + +2016-05-16 Niibe Yutaka + + * regnual/regnual.c (usb_cb_rx_ready, usb_cb_tx_done) + (usb_cb_device_reset): Follow the change of USB API. + + * chopstx: Update. + * src/sys.c: Update from Chopstx. + +2016-05-13 Niibe Yutaka + + * src/neug.c (rng): Call chopstx_claim_irq before adc_start. + Remove call of chopstx_release_irq. + +2016-05-12 Niibe Yutaka + + * chopstx: Update. + * src/sys.c: Update from Chopstx. + * src/usb_lld.h: Likewise. + * src/usb_stm32f103.c: Likewise. + + * src/usb_ctrl.c (usb_intr): Follow the change of USB API. + (usb_cb_rx_ready, usb_cb_tx_done): Likewise. + + * src/adc.h: Remove unused declarations. + +2016-03-08 Niibe Yutaka + + * tool/gnuk_token.py (gnuk_token.__init__, regnual.__init__): + Don't call setConfiguration method. + + * src/usb_lld.h (usb_cb_ctrl_write_finish): Change the API of + callback, which possibly needs INDEX, VALUE, and LEN parameters. + (usb_lld_set_data_to_recv): Fix the type of P. + (USB_DEVICE_DESCRIPTOR_TYPE, USB_CONFIGURATION_DESCRIPTOR_TYPE) + (USB_STRING_DESCRIPTOR_TYPE, USB_INTERFACE_DESCRIPTOR_TYPE) + (USB_ENDPOINT_DESCRIPTOR_TYPE): Remove, as we have the enumeration + values for same things. + + * src/usb_stm32f103.c (handle_in0): Follow the change. + * src/usb_ctrl.c (usb_cb_ctrl_write_finish): Likewise. + + * src/usb_desc.c (usb_cb_get_descriptor): Use HID_INTERFACE. + (device_desc, config_desc, string_descriptors) + (usb_cb_get_descriptor): Use the enumeration types. + * src/configure: Use the enumeration types. + + * regnual/regnual.c: Follow the change of usb_lld.h. + +2016-02-09 Niibe Yutaka + + * src/openpgp.c (cmd_verify): Support VERIFY reset, which is + described in the specification V2.2 and V3.1. + + * polarssl/library/bignum.c (mpi_exp_mod): Fix to our local + change. Thanks to Aidan Thornton for the failure test case. + + Fix of mpi_div_mpi from upstream. + * polarssl/library/bignum.c (int_clz, int_div_int): New. + (mpi_div_mpi): Use int_div_int. + +2016-02-09 Niibe Yutaka + + * src/openpgp.c (s2k): Include the unique ID of MCU into the + computation of S2K function. + +2016-02-08 Niibe Yutaka + + * src/modp256r1.c (modp256r1_add, modp256r1_sub): Keep the result + less than P256R1. + (modp256r1_reduce): Fix wrong calculation. + * src/modp256k1.c (modp256k1_add, modp256k1_sub): Likewise. + Thanks to Aidan Thornton. + +2016-02-05 Niibe Yutaka + + * src/configure: Add submodule check suggested by Elliott + Mitchell. + +2015-11-30 perillamint + + * src/openpgp.c (card_thread): Fix offset of bConfirmPIN. + +2015-09-18 Niibe Yutaka + + * VERSION: 1.1.9. + + * src/openpgp-do.c (proc_key_import): Fix error return. + (rw_algorithm_attr): Check it's not ALGO_RSA2K. + +2015-09-17 Niibe Yutaka + + * VERSION: 1.1.8. + +2015-09-15 Niibe Yutaka + + * chopstx: Update to 0.10. + + * src/main.c (main): Don't join after calling ccid_usb_reset. + * src/usb-icc.c (ccid_thread): Don't finish on reset, but + keep running. + + * src/usb_ctrl.c (usb_cb_device_reset): Stop the interface. + + * src/usb_stm32f103.c (std_set_interface): Bug fix for conf. + + * src/gnuk.ld.in (__process3_stack_size__): Increase stack size of + GPG thread. + (__process2_stack_size__): Increase stack size of RNG. + (__process4_stack_size__): Increase stack size of USB. + (__main_stack_size__): Decrease stack size of exception handlers. + (__process1_stack_size__): Decrease stack size of CCID. + +2015-09-14 Niibe Yutaka + + * src/gnuk.h (LED_GNUK_EXEC): New. + * src/main.c, src/usb-icc.c, src/usb_ctrl.c: icc_state_p access + clean up. + +2015-09-11 Niibe Yutaka + + * tool/upgrade_by_passwd.py (main): Loop until finding reGNUal + device. + +2015-09-10 Niibe Yutaka + + * src/call-rsa.c (rsa_cleanup): New. + (rsa_sign, rsa_decrypt, rsa_genkey): Allow cancellation. + * src/openpgp.c (cmd_pso, cmd_internal_authenticate): Cancellation + is handled by each functions in case of RSA. + +2015-09-09 Niibe Yutaka + + * src/sys.h: Update from Chopstx. + * src/adc_stm32f103.c: Update from NeuG. + + * src/openpgp.c (process_command_apdu): Protect command execution + against cancelling the execution thread. + (cmd_pso, cmd_internal_authenticate): Allow cancellation. + + * src/main.c (main): Handle LED_USB_RESET. + + * src/usb-icc.c (ccid_usb_reset): New. + (ccid_thread): Upon receival of EV_USB_RESET, finish + the thread, canceling the card thread. + +2015-09-08 Niibe Yutaka + + * src/gnuk.h (EV_USB_RESET, LED_USB_RESET): New. + + * src/usb_ctrl.c (CDC_CTRL_DTR): New. + (vcom_port_data_setup): Distinguish detail->value for DTR. + + * src/configure (help): Add ST_DONGLE and ST_NUCLEO_F103. + +2015-09-04 Niibe Yutaka + + * src/openpgp-do.c (do_openpgpcard_aid): Use upper bytes of unique + ID of MCU; same as USB serial number. + + * src/configure (help): Add NITROKEY_START. + +2015-08-26 Mateusz Zalega + + * GNUK_USB_DEVICE_ID: Add Nitrokey Start. + +2015-08-05 Niibe Yutaka + + * VERSION: 1.1.7. + +2015-08-04 Niibe Yutaka + + * src/adc_stm32f103.c: Update from NeuG 1.0.3. + + * chopstx: Update to 0.08. + * src/sys.h: Update. + +2015-08-03 Niibe Yutaka + + * test/features/steps.py (set_msg): Python3 fix. + * test/generate_keys.py: Likewise. + * test/rsa_keys.py: Likewise. + + * tool/gnuk_token.py (gnuk_token.download, gnuk_token.execute) + (regnual.download): Python3 fix. + (list_to_string): Remove. + + * tool/upgrade_by_passwd.py (maian): Python3 fix. + * tool/usb_strings.py (main): Python3 fix. + +2015-07-31 Niibe Yutaka + + * src/configure (output_vendor_product_serial_strings): Fix sed + script when string is short. Remove empty line. + + * regnual/regnual.c (usb_cb_ctrl_write_finish, usb_cb_setup) + (usb_cb_get_descriptor, usb_cb_interface): Follow the change + of USB API. + + * tool/stlinkv2.py: Support ST-Link/V2-1. + +2015-07-28 Niibe Yutaka + + * tool/stlinkv2.py: Fix for Python3. Thanks to Bertrand Jacquin. + + * tool/gpg_agent.py: Fix for Python3. + + * src/usb-msc.c: Update from Fraucheky. + + * src/usb_stm32f103.c (struct DATA_INFO): Remove offset. + (struct DEVICE_INFO): Integrate CONTROL_INFO. + +2015-07-27 Niibe Yutaka + + * src/usb_stm32f103.c (usb_lld_reply_request): New. + (usb_lld_set_data_to_send): Remove. + (usb_lld_set_data_to_recv): Not a macro but a function. + (std_get_status): Don't use statically allocated memory. + (std_get_configuration): Use usb_lld_reply_request. + (handle_setup0): Follow the change. + * src/usb_ctrl.c (vcom_port_data_setup, usb_cb_setup) + (usb_cb_interface): Use usb_lld_reply_request. + * src/usb_desc.c (usb_cb_get_descriptor): Likewise. + +2015-07-24 Niibe Yutaka + + * tool/gnuk_put_binary.py: Remove. + * tool/gnuk_remove_keys.py: Remove. + +2015-07-23 Niibe Yutaka + + * src/configure (nl): New. Follow the change of NeuG. + +2015-07-21 Niibe Yutaka + + * VERSION: 1.1.6. + +2015-07-20 Niibe Yutaka + + * src/openpgp-do.c (gpg_do_keygen): Support ECC. + * src/call-ec.c (ecc_check_secret): New. + * src/ecc.c (check_secret): New. + +2015-07-18 Niibe Yutaka + + * src/configure (keygen): It's always enabled. + * src/openpgp-do.c (gpg_do_keygen): Support key generation. + * src/openpgp.c (cmd_pgp_gakp): Likewise. + * src/call-rsa.c (rsa_genkey): Likewise. + * src/random.c (random_gen): Likewise. + * src/Makefile.in (KEYGEN_SUPPORT): Remove. + * polarssl/include/polarssl/config.h (POLARSSL_GENPRIME): Define. + +2015-07-16 Niibe Yutaka + + * src/configure (FLASH_PAGE_SIZE, FLASH_SIZE, MEMORY_SIZE) + [sys1_compat]: Use safe values for common binary. + (TARGET_DEFINE): Remove. + +2015-07-15 Niibe Yutaka + + * tool/usb_strings.py (field): Add 'Board'. + + * regnual/regnual.c (usb_cb_get_descriptor): Update. + * src/usb_ctrl.c (usb_cb_interface): Call usb_lld_write. + * src/usb_desc.c (usb_cb_get_descriptor): Support sys_board_name, + using usb_lld_write. + * src/usb_lld.h (usb_cb_get_descriptor): Add last argument length + for asked length. + * src/usb_stm32f103.c (handle_setup0): Allow setup callback to + call usb_lld_write with ENDP0. + * src/usb_conf.h (NUM_STRING_DESC): Remove. + + * src/configure [!sys1_compat] (CONFIG): Don't include target + board name. + + * src/flash.c: Detect flash_page_size at runtime. + + * src/main.c: Remove dependency to board.h. + + * src/neug.c: Update from NeuG 1.0.2. + * src/adc_stm32f103.c: Update. + + * chopstx: Update to 0.07. + * src/sys.c: Update. + * src/sys.h: Update. + * src/gnuk.ld.in: Update. + + * tool/stlinkv2.py (stlinkv2.get_chip_id): New. Detect flash + size, too. + (main): Call stlinkv2.get_chip_id after MCU reset and stop. + Verify read out fix. + +2015-07-11 Niibe Yutaka + + * src/configure (help): Add STM32_PRIMER2 and CQ_STARM. + + * chopstx: Update to 0.06. + + * tool/stlinkv2.py: Support 512kB version of STM32F103. + The size of executable file should be even. + +2015-07-07 Niibe Yutaka + + * src/Makefile.in (CSRC): Add ecc-mont.c. + + * src/ecc-mont.c (mod25638_mul_121665): Fix. + (ecdh_compute_public_25519, ecdh_decrypt_curve25519): New. + + * src/openpgp.c (cmd_pso): Support ALGO_CURVE25519. + + * src/openpgp-do.c (algorithm_attr_cv25519): New. + (rw_algorithm_attr, get_algo_attr_data_object) + (gpg_get_algo_attr_key_size, gpg_do_write_prvkey) + (proc_key_import, gpg_do_public_key): Support ALGO_CURVE25519. + + * src/gnuk.h (ALGO_CURVE25519): New. + +2015-07-06 Niibe Yutaka + + Enhancement for FSM-55. + * tool/stlinkv2.py (stlinkv2.control_nrst): New. + (stlinkv2.get_rdp_key,has_spi_flash,has_protection): New. + (stlinkv2.get_core_id): Rename. + (stlinkv2.blank_check): Use self.flash_size. + (stlinkv2.start): Call control_nrst. Call get_core_id. + Distinguishing chip, and set rdp_key, flash_size and require_nrst. + (stlinkv2.flash_write): Use self.flash_block_size. + (main): Call control_nrst. + (prog_flash_write_body, prog_option_bytes_write_body) + (prog_blank_check_body): Support Cortex-M0. + (main): Call API V2 halt twice. + * tool/asm-thumb/*.S: Updated for Cortex-M0. + +2015-06-30 Niibe Yutaka + + * src/sys.c: Update from chopstx/example-cdc/sys.c. + + * src/main.c (device_initialize_once): Apply change of NeuG. + +2015-06-03 Niibe Yutaka + + * VERSION: 1.1.5. + + * test/ecc_nistp256_keys.py: New. + + * tool/upgrade_by_passwd.py: Remove -p option and add -f option. + + * tool/gnuk_token.py (gnuk_token.download): Add verbose flag. + (regnual.download): Ditto. + + * tool/gnuk_upgrade.py: Use gnuk_token module. + +2015-06-02 Niibe Yutaka + + * src/openpgp.c (cmd_pso): Support OpenPGPcard spec v3.0. + +2015-04-20 Niibe Yutaka + + * chopstx: Upgrade to 0.05. + +2015-04-19 Niibe Yutaka + + * src/gnuk.h (CCID_CARD_INIT): New. + * src/usb_desc.c (gnukConfigDescriptor): Update dwDefaultClock, + dwMaximumClock, dwFeatures, and bClassEnvelope. + * src/usb_ctrl.c (freq_table): Change the value to 4000MHz. + (usb_cb_handle_event): Call ccid_card_change_signal after configure. + * src/usb-icc.c (ccid_thread): Change EV_CARD_CHANGE handling. + +2015-04-18 Niibe Yutaka + + * src/main.c (main): Call chopstx_main_init. + * src/Makefile.in (DEFS): Remove CHX_PRIO_MAIN. + +2015-04-17 Niibe Yutaka + + * src/configure: Fix shell syntax. + +2015-03-31 Niibe Yutaka + + * src/usb_conf.h (ICC_NUM_INTERFACES, HID_NUM_INTERFACES) + (HID_NUM_INTERFACES, VCOM_NUM_INTERFACES, MSC_NUM_INTERFACES) + (NUM_INTERFACES): Define here (moved from usb_desc.c). + (ICC_INTERFACE, HID_INTERFACE, VCOM_INTERFACE_0, VCOM_INTERFACE_1) + (MSC_INTERFACE): New. + * src/usb_ctrl.c (gnuk_setup_endpoints_for_interface) + (usb_cb_setup, usb_cb_ctrl_write_finish): Use *_INTERFACE. + * src/usb_desc.c (gnukConfigDescriptor): Likewise. + +2015-03-06 Niibe Yutaka + + * src/ecc-edwards.c (eddsa_sign_25519): Return 0. + +2015-02-25 Niibe Yutaka + + * src/openpgp.c (cmd_internal_authenticate): Fix storing to + res_APDU_size. + +2015-02-10 Niibe Yutaka + + * src/openpgp.c (cmd_pso): Fix counter update for EdDSA. Thanks + to Jonathan Schleifer. + + * src/call-rsa.c (rsa_sign): Don't set res_APDU_len. + (rsa_decrypt): Likewise, but get OUTPUT_LEN_P as an argument. + +2015-02-09 Niibe Yutaka + + * src/openpgp.c (cmd_pso): Fix EdDSA. Use GPG_KEY_FOR_SIGNING. + +2014-12-15 Niibe Yutaka + + * VERSION: 1.1.4. + +2014-12-13 Niibe Yutaka + + * src/flash.c (flash_key_getpage, flash_key_release_page): New. + + * src/openpgp-do.c (gpg_do_delete_prvkey): New arg. + (rw_algorithm_attr): Call gpg_do_delete_prvkey with CLEAN_PAGE_FULL. + +2014-12-12 Niibe Yutaka + + * src/Makefile.in (build/bignum.o): Specific OPT for this target. + + * src/configure (target): Default is FST-01. + (--with-dfu): FST-01 doesn't have DFU. If set, it must be + mistake. + + * boards/STBEE_MINI: Remove, since it is now supported by Chopstx. + + * test/features/802_get_data_static.feature: Reflect the change + of extended capabilities. + * test/features/402_get_data_static.feature: Ditto. + * test/features/002_get_data_static.feature: Ditto. + + * test/features/003_keyattr_change.feature: New test. + + * src/usb_lld.h: Don't use 'extern' for function declarations. + * src/usb-icc.c (end_cmd_apdu_data): Fix Le handling. + +2014-12-11 Niibe Yutaka + + * chopstx: Upgrade to 0.04. + * src/syc.c: Update from 0.04. + +2014-12-10 Niibe Yutaka + + * src/ecc-cdh.c: Remove as smartcard only does + a part of ECDH computation as gpg-agent does. + + * src/chconf.h, src/halconf.h: Remove files needed for ChibiOS/RT. + +2014-12-09 Niibe Yutaka + + * src/call-ec.c (ecc_compute_public): Rename, as we share + same routine among ECDSA and ECDH. + (ecdh_decrypt): New. + +2014-12-09 Niibe Yutaka + + * src/ecc.c (compute_kP): Bug fix. It's P, not G. + (point_is_on_the_curve): Bug fix. + +2014-12-03 Niibe Yutaka + + Changes for RSA-4096. + + * src/gnuk.h (MAX_CMD_APDU_DATA_SIZE, MAX_RES_APDU_DATA_SIZE): + Send/Recv buffer should be bigger. + * polarssl/library/bignum.c (mpi_exp_mod): Don't consume much. + * polarssl/library/rsa.c (rsa_rsaes_pkcs1_v15_decrypt): buffer + allocation size should be variable. + +2014-12-01 Niibe Yutaka + + * src/Makefile.in (DEFS): Don't define compile time preference of + key algo attributes. + + * src/openpgp-do.c (proc_key_import): Support modifiable key algo + attributes. + +2014-11-21 Niibe Yutaka + + * src/gnuk.h (ALGO_RSA4K, ALGO_NISTP256R1, ALGO_SECP256K1) + (ALGO_ED25519, ALGO_RSA2K): New. + (struct key_data_internal): Move to ... + * src/openpgp-do.c (struct key_data_internal): ... here. + (CHECKSUM_ADDR, kdi_len): New. + (CKDC_CALC, CKDC_CHECK): New. + (compute_key_data_checksum): Add arg PRVKEY_LEN. + (gpg_do_load_prvkey, gpg_do_delete_prvkey): Support modifiable key + algo attributes. + (gpg_do_write_prvkey, gpg_do_public_key, gpg_do_keygen): Likewise. + (gpg_do_clear_prvkey): Use MAX_PRVKEY_LEN. + + * src/openpgp.c (gpg_init): Call flash_init_keys after + gpg_data_scan. + (cmd_pso): Support modifiable key algo attributes. + (cmd_internal_authenticate): Likewise. + +2014-11-21 Niibe Yutaka + + * src/openpgp-do.c (algorithm_attr_rsa2k): Rename from *_rsa. + (algorithm_attr_rsa4k): New. + (get_algo_attr_pointer, kk_to_nr, gpg_get_algo_attr) + (get_algo_attr_data_object, gpg_get_algo_attr_key_size): New. + (rw_algorithm_attr): New. + (gpg_do_table): Register rw_algorithm_attr for GPG_DO_ALG_*. + (gpg_data_scan, gpg_data_copy): Handle algo attributes. + + (rw_pw_status): Fix checking against redundant write. + +2014-11-20 Niibe Yutaka + + * src/openpgp-do.c (extended_capabilities): Key attributes can be + modifid now. + +2014-11-20 Niibe Yutaka + + * src/gnuk.h (NR_NONE, NR_DO__FIRST__): Remove. + (NR_DO_*): Redefine. + (NR_KEY_ALGO_ATTR_SIG, NR_KEY_ALGO_ATTR_DEC) + (NR_KEY_ALGO_ATTR_AUT): New. + + * src/openpgp-do.c (gpg_do_load_prvkey, gpg_do_delete_prvkey) + (gpg_do_write_prvkey, gpg_do_chks_prvkey, gpg_data_scan) + (gpg_data_copy, gpg_do_read_simple) + (gpg_do_write_simple): Don't use NR_DO__FIRST__. + (gpg_do_put_data): Don't use NR_NONE any more. + (do_tag_to_nr): Use -1 to specify NONE. + + * src/flash.c (flash_enum_clear, flash_enum_write_internal) + (flash_enum_write): New. + +2014-11-19 Niibe Yutaka + + * src/gnuk.h (FIRMWARE_UPDATE_KEY_CONTENT_LEN): New. + (size_of_key): New enum. + + * src/openpgp.c (gpg_get_firmware_update_key) + (cmd_read_binary, cmd_external_authenticate): Use + FIRMWARE_UPDATE_KEY_CONTENT_LEN. + + * src/flash.c (KEY_SIZE): Remove. + (key_available_at): Add new arg as KEY_SIZE. + (flash_check_all_other_keys_released): Likewise. + (flash_key_fill_zero_as_released, flash_key_release): Likewise. + + (flash_init): Move initializing keys into another function. + (flash_init_keys): New function. + + (flash_key_alloc): Use gpg_get_algo_attr_key_size. + (flash_key_write): Add new arg as KEY_DATA_LEN. + + (flash_write_binary): Use FIRMWARE_UPDATE_KEY_CONTENT_LEN. + +2014-09-16 Niibe Yutaka + + * src/gnuk.h (MAX_PRVKEY_LEN): New. + (KEY_CONTENT_LEN): Remove. + + * src/call-rsa.c (RSA_SIGNATURE_LENGTH): Remove. + (rsa_sign, rsa_verify, rsa_genkey): Add new arg: PUBKEY_LEN. + (rsa_decrypt): Don't use KEY_CONTENT_LEN. + +2014-06-19 Niibe Yutaka + + * src/ecc-mont.c (compute_nQ): Add comment. + + * src/mod.c (mod_inv): Fix comment. X^-1 = 0 when X=0 + is important for Montgomery curve computation. + +2014-06-05 Niibe Yutaka + + * tool/add_openpgp_authkey_from_gpgssh.py: New. + +2014-04-17 Niibe Yutaka + + * src/muladd_256.h (MULADD_256_ASM): New. + (MULADD_256): Use MULADD_256_ASM. + * src/ecc-mont.c (mod25638_mul_121665): Add asm implementation. + + * src/bn.c (bn256_add_uint, bn256_sub_uint): Simplify. + * src/mod25638.c (mod25638_add, mod25638_sub): Simplify. + (n25638, add19): Remove. + (ADDWORD_256): Add s_ as source pointer. + (mod25519_reduce): Remove a call to memcpy. Use bn256_add_uint. + * src/ecc-edwards.c (point_double): Simplify. + +2014-04-16 Niibe Yutaka + + * VERSION: 1.1.3. + +2014-04-15 Niibe Yutaka + + * src/ecc-mont.c: New. + + * src/mod25638.c (p25519): Move from ecc-edwards.c. + (mod25519_reduce, add19): Likewise. + (mod25638_reduce) [!ASM_IMPLEMENTATION]: Use bn256_add_uint. + +2014-04-14 Niibe Yutaka + + * src/jpc.c (jpc_to_ac): Error check before mod_inv. + + * src/mod.c (mod_inv): No return value (if N==0 returns ZERO). + + * src/bn.c [BN256_NO_RANDOM]: Only include "random.h" if needed. + +2014-04-08 Niibe Yutaka + + * src/mod.c (mod_inv): Use MAX_GCD_STEPS_BN256. + Return failure or success. + * src/jpc.c (jpc_to_ac): Use mod_inv. + * src/modp256k1.c (modp256k1_inv): Remove. + * src/modp256r1.c (modp256r1_inv): Remove. + +2014-04-07 Niibe Yutaka + + * src/openpgp-do.c (algorithm_attr_ed25519): It's OID only. + +2014-04-03 Niibe Yutaka + + * src/ecc-edwards.c (eddsa_sign_25519): Change type of OUT. + * src/openpgp.c (cmd_internal_authenticate): Have a buffer. + + * src/flash.c (flash_init): Fix key address finder. + +2014-04-02 Niibe Yutaka + + * src/openpgp-do.c (proc_key_import): Handle EdDSA. + (algorithm_attr_ed25519): Fix OID and add ID for SHA512. + +2014-04-01 Niibe Yutaka + + * src/ecc-edwards.c (eddsa_compute_public_25519): New. + + * src/openpgp-do.c (algorithm_attr_ed25519): New. + (gpg_do_write_prvkey, proc_key_import, gpg_do_public_key): Add + EdDSA handling. + +2014-03-31 Niibe Yutaka + + * src/ecc-edwards.c (eddsa_sign_25519): Rename and API change. + + * src/openpgp-do.c (gpg_do_load_prvkey, gpg_do_delete_prvkey) + (gpg_do_write_prvkey, gpg_do_public_key, gpg_do_keygen): Follow + the change of PRVKEY_DATA and KEY_DATA. + + * src/flash.c (key_available_at): New. + (flash_init): Initilize KD. + + * src/gnuk.h (struct prvkey_data): Remove member KEY_ADDR. + (struct key_data): Addd member KEY_ADDR. + + * src/openpgp-do.c (gpg_do_keygen): Bug fix. Reset the signature + counter when new key is generated. + + * src/flash.c (flash_key_alloc): Change API, supply KK. + +2014-03-29 Niibe Yutaka + + * src/ecc-edwards.c (point_double, point_add): Rename. + (mod25519_reduce): New. + +2014-03-28 Niibe Yutaka + + * misc/t-eddsa.c (main): Update for new API of eddsa_25519. + + * src/ecc-edwards.c (compute_kG_25519): Tune for 252-bit. + (eddsa_25519): Public key should be provided by caller. + (eddsa_public_key_25519): New. + +2014-03-27 Niibe Yutaka + + * src/ecc-edwards.c (ed_add_25638): Remove the third argument. + (compute_kG_25519): The curve is complete, we don't need to avoid + identity element as NIST curve or secp256k1 curve. + (eddsa_25519): Change the API, with A and the seed. + +2014-03-26 Niibe Yutaka + + * src/mod25638.c (mod25638_reduce): New. + (mod25638_mul, mod25638_sqr): Use mod25638_reduce. + + * src/ecc-edwards.c (ptc_to_ac_25519): No need to subtract p25519. + +2014-03-25 Niibe Yutaka + + * misc/t-eddsa.c: New. + + * src/ecc-edwards.c (bnX_mul_C, mod_reduce_M): New. + (eddsa_25519): New. + +2014-03-20 Niibe Yutaka + + * src/ecc-edwards.c (ed_add_25638): Fix for X == A. + (main): Compute pre-computed tables. + (precomputed_KG, precomputed_2E_KG): Add. + (compute_kG_25519): New. + +2014-03-19 Niibe Yutaka + + * src/bn.c (bn256_add): Fix for X == B. + (bn256_sub): Likewise. + + * src/ecc-edwards.c: New. + +2014-03-18 Niibe Yutaka + + * src/mod25638.c (mod25638_add, mod25638_sub, mod25638_sqr) + (mod25638_shift): New. + +2014-03-13 Niibe Yutaka + + * src/mod25638.c: Rename from fe25519.c. + * src/mod25638.h: Likewise. + +2014-03-07 Niibe Yutaka + + * VERSION: 1.1.2. + +2014-02-25 Niibe Yutaka + + * src/openpgp-do.c (gpg_do_public_key): Don't put OID. + + * src/configure [certdo] (gnuk.ld): Add TIM_SIZE and EXT_SIZE. + Thanks to Vasily Evseenko for the bug report. + +2014-02-21 Niibe Yutaka + + * src/ecc.c (compute_kG): Compute higer index at first. + (point_is_on_the_curve): Don't use coefficient_a if it's zero. + + * src/jpc.c (jpc_double): Care coefficient A. + + * src/ec_p256r1.c (COEFFICIENT_A_IS_MINUS_3): New. + * src/ec_p256k1.c (COEFFICIENT_A_IS_ZERO): New. + * src/jpc_p256r1.c (COEFFICIENT_A_IS_MINUS_3): Likewise. + * src/jpc_p256k1.c (COEFFICIENT_A_IS_MINUS_3): Likewise. + + * src/modp256k1.c (modp256k1_shift): Bug fix. + +2014-02-20 Niibe Yutaka + + * src/Makefile.in (CSRC): Add files of p256k1. + + * src/openpgp.c (cmd_pso): Support p256k1 for signature. + + * src/openpgp-do.c (algorithm_attr_p256k1): New. + (gpg_do_write_prvkey): Support p256k1 for signature. + (proc_key_import, gpg_do_table, gpg_do_public_key): Likewise. + + * src/Makefile.in (DEFS): Add -DRSA_SIG. + + * src/openpgp-do.c (gpg_do_write_prvkey): Use _p256r1. + * src/openpgp.c (cmd_internal_authenticate): Likewise. + + * src/call-ec_p256k1.c: New. Use call-ec.c. + * src/call-ec_p256r1.c: Use call-ec.c. + * src/call-ec.c: New. + (ecdsa_sign): Change the signature. + +2014-02-19 Niibe Yutaka + + * tool/calc_precompute_table_ecc.py: New. + + * src/ec_p256k1.c: New. Use ecc.c. + * src/ec_p256k1.h: New. + * src/ec_p256r1.c: Use ecc.c. + * src/ecc.c: New. + +2014-02-18 Niibe Yutaka + + * src/jpc_p256k1.c: New. Use jpc.c. + * src/jpc_p256r1.c: Use jpc.c. + * src/jpc.c: New. + + * src/sha256.c (memcpy_output_bswap32): Bug fix. + + * src/modp256k1.h, src/modp256k1.c: New. + +2014-02-17 Niibe Yutaka + + * src/Makefile.in (CSRC): Follow the changes of filenames. + + * src/modp256r1.c (modp256r1_add, modp256r1_sub, S3) + (modp256r1_mul, modp256r1_sqr, modp256r1_inv, modp256r1_shift): + Use new function names. + * src/jpc_p256r1.c (jpc_double_p256r1, jpc_add_ac_signed_p256r1) + (jpc_to_ac_p256r1): Likewise. + * src/ec_p256r1.c (point_is_on_the_curve) + (compute_kG_p256r1, compute_kP_p256r1): Likewise. + * src/call-ec_p256r1.c (ecdsa_sign): Likewise. + + * src/modp256r1.h: Rename from modp256.h. + * src/jpc-ac_p256r1.h: Rename from jpc-ac.h. + * src/ec_p256r1.h: Rename from ec_p256.h. + + * src/modp256r1.c: Rename from modp256.c. + * src/jpc_p256r1.c: Rename from jpc.c. + * src/ec_p256r1.c: Rename from ec_p256.c. + * src/call-ec_p256r1.c: Rename from call-ec_p256.c. + +2014-02-05 NIIBE Yutaka + + * src/sha512.h, src/sha512.c: New. + + * src/sha256.c (initial_state): Don't export, it's internal. + (memcpy_output_bswap32): Rename and remove last argument. + +2014-01-28 Niibe Yutaka + + * src/muladd_256.h: New. + * src/bn.c (bn256_mul, bn256_sqr): Assembler implementation. + + * src/ec_p256.c (get_vk_kP): Bug fix. + (compute_kP): Bug fix for index table. + +2014-01-27 Niibe Yutaka + + * src/ec_p256.c (get_vk_kP): New. + (naf4_257_set, naf4_257_get, compute_naf4_257): Remove. + (compute_kP): Change the argument, fixing for constant time. + +2014-01-24 Niibe Yutaka + + * src/ec_p256.c (get_vk): New. + (compute_kG): Fix for constant time. + (compute_kP): Simplify. + +2014-01-23 Niibe Yutaka + + * src/jpc.c (jpc_add_ac_signed): Fix for constant time. + + * src/ec_p256.c (ecdsa): Bug fix for k selection. + +2014-01-22 Niibe Yutaka + + * src/modp256.c (modp256_inv): Fix for constant time. + + * src/bn.c (bn256_sqr): Fix for constant time. + + * src/mod.c (mod_inv): Fix for constant time. + + * src/ec_p256.c (compute_kG): Simplify. + + * src/jpc.c (jpc_double): Support calling with A = infinity. + +2014-01-21 Niibe Yutaka + + * src/jpc.c (jpc_add_ac_signed): Bug fix for A check. + + * src/ec_p256.c (ecdsa): Fix for constant time. + + * src/modp256.c (modp256_add, modp256_sub, modp256_reduce) + (modp256_shift): Fix for constant time. + (modp256_inv): Likewise (not fully constant time, yet). + + * src/mod.c (mod_reduce): Fix for constant time. + (mod_inv): Likewise (not fully constant time, yet). + + * src/bn.h (bn256, bn512): words -> word. + * src/ec_p256.h (naf4_257): Likewise. + +2014-01-20 Niibe Yutaka + + * src/fe25519.h, src/fe25519.c: New. + +2014-01-15 Niibe Yutaka + + * src/bn.c (bn256_is_zero, bn256_is_ge, bn256_cmp): Computation + should be constant time. + +2013-12-25 Niibe Yutaka + + * VERSION: 1.1.1. + + * tool/gnuk_token.py (gnuk_token.__init__, regnual.__init__): Fix + the argument of setAltInterface. + * tool/gnuk_upgrade.py: Likewise. + * tool/dfuse.py (DFU_STM32.__init__): Likewise. + * tool/stlinkv2.py (stlinkv2.__init__): Likewise. + +2013-12-24 Niibe Yutaka + + * polarssl/include/polarssl/bn_mul.h (MULADDC_1024_CORE) + (MULADDC_1024_LOOP): Use younger number registers more for shorter + instructions and better performance. + * polarssl/library/bignum.c (mpi_montsqr): Likewise. Change loop + structure and conditional branch for better performance. + +2013-12-23 Niibe Yutaka + + * polarssl/library/bignum.c (mpi_montmul): Computation + time should not depends on input. + (mpi_montmul, mpi_montred, mpi_montsqr): Change the API. + (mpi_exp_mod): Follow the change of the API. Allocate memory on + stack instead of malloc. + + * src/gnuk.ld.in (__process3_stack_size__): Increase stack size. + +2013-12-20 Niibe Yutaka + + * Version 1.1.0. + + * src/usb_ctrl.c (USB_FSIJ_GNUK_CARD_CHANGE): New. + (usb_cb_setup): Support USB_FSIJ_GNUK_CARD_CHANGE. + * src/usb-icc.c (ccid_card_change_signal): New argument HOW. + +2013-12-20 Niibe Yutaka + + * polarssl/include/polarssl/bn_mul.h (MULADDC_1024_CORE) + (MULADDC_CORE): Reorder instructions for more speed up. + * polarssl/library/bignum.c (mpi_montsqr): Likewise. + +2013-12-19 Niibe Yutaka + + * src/configure (--enable-hid-card-change): New (experimental). + * src/config.h.in (HID_CARD_CHANGE_DEFINE): New. + * src/usb_ctrl.c (gnuk_setup_endpoints_for_interface) + (usb_cb_setup, usb_cb_ctrl_write_finish): Conditionalize + HID_CARD_CHANGE_SUPPORT. + * src/usb_desc.c (gnukDeviceDescriptor, usb_cb_get_descriptor): + Likewise. + +2013-12-19 Niibe Yutaka + + * src/openpgp.c (S2KCOUNT): It's now 192, as the threat model + of Gnuk Token is different. + +2013-12-19 Niibe Yutaka + + * polarssl/library/bignum.c (mpi_montsqr): New. + (mpi_exp_mod): Use mpi_montsqr. + + Note that this change introduces a vulnerability by the + Yarom/Falkner flush+reload cache side-channel attack. When this + code is used on general purpose computer where we can observe + which code is executed (sqr or mul), it's not safe. + +2013-12-16 Niibe Yutaka + + * polarssl/include/polarssl/bn_mul.h (MULADDC_1024_CORE) + (MULADDC_1024_LOOP, MULADDC_HUIT, MULADDC_INIT, MULADDC_CORE) + (MULADDC_STOP) [__arm__]: The value of input B won't change. + More acculate specification for asm statement. + + * polarssl/library/bignum.c (mpi_cmp_abs_limbs): New. + (mpi_montmul): Change the signature and use the upper half of T. + (mpi_montred): Likewise. + (mpi_exp_mod): Use improved mpi_montmul and mpi_montred. + (mpi_sub_hlp, mpi_mul_hlp): Add const qualifier for S. + +2013-12-13 Niibe Yutaka + + * polarssl/library/bignum.c (mpi_exp_mod): Initialize lower + half of T with zero. + (mpi_montmul): Don't need to clear lower half of + T, as we keep zero. Call mpi_sub_hlp with upper half of T. + (mpi_montred): Ditto. + + * polarssl/library/bignum.c (mpi_montmul, mpi_montred): Minimize + number of limbs for T. + (mpi_exp_mod): Only allocate N-n * 2 for T. + Only allocate N->n for X, W[i], and RR. + +2013-12-13 Niibe Yutaka + + * tool/upgrade_by_passwd.py (main): Support -k to specify KEYNO. + +2013-12-13 Niibe Yutaka + + * src/usb_ctrl.c (HID_LED_STATUS_CARDCHANGE): Rename from + HID_LED_STATUS_NUMLOCK. + + * tool/gnuk_token.py (gnuk_token.stop_gnuk): Detach kernel + driver of HID. + +2013-12-12 Niibe Yutaka + + * src/openpgp-do.c (do_openpgpcard_aid): Coerce to volatile to + force memory access at run time. + +2013-12-11 Niibe Yutaka + + * regnual/sys.c (entry): Fix relocation calculation. + +2013-11-27 Niibe Yutaka + + * src/stm32f103.h (AFIO_MAPR_SWJ_CFG_DISABLE): New. + * src/sys.c: Likewise. + +2013-11-26 Niibe Yutaka + + * src/usb_desc.c (hid_report_desc): New. + (ICC_TOTAL_LENGTH): Update. + (HID_TOTAL_LENGTH, HID_NUM_INTERFACES): New. + (W_TOTAL_LENGTH, NUM_INTERFACES): Update. + (gnukConfigDescriptor): Add IN2 interrupt endpoint descriptor. + Add HID interface descriptor. + (usb_cb_get_descriptor): Handle HID. + + * src/usb_ctrl.c (NUM_INTERFACES, MSC_INTERFACE_NO): Add 1. + (USB_HID_REQ_*, HID_LED_STATUS_NUMLOCK): New. + (gnuk_setup_endpoints_for_interface): Add ENDP2 interrupt + endpoint. + (usb_cb_setup): Handle HID requests. + (usb_cb_ctrl_write_finish): Likewise. + + * src/usb-icc.c (ccid_card_change_signal): New. + (ccid_thread): Handle card change. + (icc_error, icc_send_status): Handle ICC_STATE_NOCARD state. + (icc_handle_data): Add the case of ICC_STATE_NOCARD. + (EP2_IN_Callback): New. + +2013-11-26 Niibe Yutaka + + * src/pin-dial.c: Remove. + * src/configure: Remove pin-dial support. + +2013-11-25 Niibe Yutaka + + * src/Makefile.in (HEXOUTPUT_MAKE_OPTION): New. + * src/configure (HEXOUTPUT_MAKE_OPTION): New. + * src/main.c: Include board.h. + * src/stm32f103.h (EXTI0_IRQ, EXTI1_IRQ): New. + +2013-11-18 Niibe Yutaka + + * regnual/sys.c (entry): Bug fix of clearing BSS. + + * src/usb_stm32f103.c: Update from NeuG. + (usb_handle_transfer): Add argument ISTR_VALUE. + + * src/openpgp.c (card_thread): Add noinline attribute. + + * src/usb-icc.c (ccid_thread): Join the OpenPGP thread. + Add noinline attribute. + +2013-11-15 Niibe Yutaka + + * src/configure (options): Add --enable-sys1-compat. + +2013-11-12 Niibe Yutaka + + * chopstx: Upgrade to 0.03. + + * src/usb_desc.c (usb_initial_feature): Remove. + (USB_SELF_POWERED): Move to ... + * src/usb_conf.h (USB_SELF_POWERED): ... here. + * src/usb_ctrl.c (usb_cb_device_reset, usb_intr): Follow the + change. + +2013-11-11 Niibe Yutaka + + * src/adc_stm32f103.c (adc_wait_completion): Update from NeuG 1.0. + +2013-11-03 Niibe Yutaka + + * regnual/regnual.c (usb_cb_get_descriptor): Update to new API. + + * src/usb_lld.h (usb_initial_feature): Remove. + + * chopstx: Update to 0.01. + * src/pin-cir.c: Chatter fix to 200ms. + * src/main.c: Fix bDeviceState. + +2013-11-02 Niibe Yutaka + + * src/usb_lld.h, src/usb_stm32f103.c (std_get_descriptor): Change + the API of usb_cb_get_descriptor. + * src/usb_desc.c: Follow the change. + * src/usb_conf.h: Modify for CCID INT and HID usage. + +2013-11-02 Niibe Yutaka + + * src/pin-cir.c: Port to Chopstx. + + * chopstx: Update. + * src/configure (TIM_SIZE, EXT_SIZE): New. + * src/gnuk.ld.in (__process6_stack_size__) + (__process7_stack_size__): New. + * src/main.c (main): Call cir_init. + * src/openpgp.c (openpgp_card_thread): Rename from GPGthread. + * src/usb-icc.c (icc_power_on): Follow the change. + +2013-11-01 Niibe Yutaka + + * src/sys.c: Update from Chopstx. + * src/usb_lld.h: Remove interrupt definition. + * src/stm32f103.h: Add AFIO, EXTI, and TIMER constants. + +2013-10-31 Niibe Yutaka + + * src/main.c (main): Call msc_init before USB interrupt thread. + * src/gnuk.h, src/usb-msc.h, src/usb-msc.c, src/pin-dnd.c: Port to + Chipstx. + * src/openpgp.c (get_pinpad_input): Follow the change. + * src/usb_ctrl.c (gnuk_setup_endpoints_for_interface): Don't stall + RX of ENDP6. + +2013-10-24 Niibe Yutaka + + * src/Makefile.in (DEFS): Add -DCHX_PRIO_MAIN=5 for LED blink. + * src/main.c (PRIO_CCID): It's now 3 (was: 2). + +2013-10-24 Niibe Yutaka + + * src/gnuk.ld.in (.gnuk_flash): Three pages for three keys. + * src/flash.c (FLASH_KEYSTORE_SIZE): Likewise. + (flash_keystore_release): Remove. + (flash_key_fill_zero_as_released) + (flash_check_all_other_keys_released, flash_key_release): New. + (flash_init, flash_key_alloc): New method to handle free space. + * src/openpgp-do.c (fetch_four_bytes): New. + (gpg_do_load_prvkey, gpg_do_delete_prvkey, gpg_do_public_key): Use + fetch_four_bytes. + (gpg_do_delete_prvkey): Call flash_key_release. + +2013-10-23 Niibe Yutaka + + * test/features/010_setup_passphrase.feature + * test/features/030_key_registration.feature + * test/features/040_passphrase_change.feature + * test/features/410_setup_passphrase.feature + * test/features/430_key_registration.feature + * test/features/201_keygen.feature + * test/features/601_keygen.feature: Modified to support new way of + pass phrase reset by key import / key generation. + + * test/features/201_keygen.feature + * test/features/601_keygen.feature + * test/features/202_setup_passphrase.feature + * test/features/602_setup_passphrase.feature: Rename to change + order of execution. + +2013-10-23 Niibe Yutaka + + * src/openpgp-do.c (gpg_do_write_prvkey): Bug fix of adding + num_prv_keys. + +2013-10-22 Niibe Yutaka + + * src/openpgp-do.c (gpg_do_write_prvkey): Bug fix. + +2013-10-15 Niibe Yutaka + + * src/openpgp.c (cmd_change_password, cmd_reset_user_password): It + is now error to change User's pass phrase with no keys. + + * src/openpgp-do.c (proc_resetting_code): Likewise for resetting + code. + (gpg_do_delete_prvkey): New. + (gpg_do_write_prvkey): Make sure to delete the key before writing. + User's pass phrase is always the one of factory setting. + (gpg_do_chks_prvkey): Support removing the key. + (proc_key_import): Use gpg_do_delete_prvkey. + (gpg_do_keygen): Use factory setting pass phrase. + +2013-10-11 Niibe Yutaka + + * src/ac.c (verify_user_0, verify_admin_00): Fix conditions. + + * src/openpgp-do.c (gpg_do_write_prvkey): Delete keystring + information from data object of NR_DO_KEYSTRING_PW3. + Fix conditions. + (gpg_do_keygen): Likewise. + + * src/openpgp.c (cmd_reset_user_password): Likewise. + +2013-10-10 Niibe Yutaka + + * src/gnuk.h (S2K_ITER): Remove. It's determined at compile time. + + * src/openpgp-do.c (proc_resetting_code, gpg_do_write_prvkey) + (proc_key_import): Remove "iteration" field. + + * src/openpgp.c (cmd_change_password): Likewise. + +2013-10-10 Niibe Yutaka + + * src/openpgp-do.c (gpg_do_write_prvkey): Access of data object + considering garbage collection. + + * src/openpgp.c (cmd_change_password): Call gpg_do_write_simple + after accessing the data object (it may cause garbage collection). + +2013-10-10 Niibe Yutaka + + * polarssl/library/bignum.c (mpi_montred): Constant time for + carry propagation. Bug fix for carry propagation. + (mpi_exp_mod): Bug fix. Shrink the size of RR as same as X. + +2013-10-09 Niibe Yutaka + + * src/ac.c (verify_user_0, verify_admin_00, verify_admin_0): Add a + flag to save into keystring_md_pw3. Add SALT handling. + (decode_iterate_count, calc_md, gpg_set_pw3): Remove. + + * src/openpgp-do.c (proc_resetting_code, gpg_do_write_prvkey) + (gpg_do_keygen): Add SALT handling. + * src/openpgp.c (cmd_change_password, cmd_reset_user_password) + (s2k): Ditto. + * src/random.c (random_get_salt): Rename from get_salt. + +2013-10-09 Niibe Yutaka + + * src/openpgp-do.c (gpg_do_write_prvkey): Remove information (but + pass phrase length) for admin from keystring data object. + (proc_key_import): Recover admin keystring to DO when key deletion. + +2013-10-09 Niibe Yutaka + + * src/ac.c (verify_user_0, verify_admin_00): Handle PW_LEN_MASK. + * src/openpgp-do.c (proc_resetting_code, gpg_do_write_prvkey): + Likewise. + + * src/openpgp.c (cmd_change_password, cmd_reset_user_password): + Handle PW_LEN_KEYSTRING_BIT. + +2013-10-09 Niibe Yutaka + + * src/ac.c (verify_admin_00): New. Add authentication by loading + signature key. + (verify_admin_0): Use verify_admin_00. + + * src/openpgp.c (cmd_change_password): Admin keystring handling as + same as user's. + +2013-10-08 Niibe Yutaka + + * src/openpgp.c (modify_binary): Allow odd size of certificate. + + * polarssl/library/rsa.c: Update from PolarSSL 1.2.10. + * polarssl/include/polarssl/rsa.h: Ditto. + +2013-10-07 Niibe Yutaka + + * polarssl/library/bignum.c (mpi_sub_hlp): Return CARRY. + (mpi_sub_abs): Carry propagatoin is done here. + (mpi_mul_hlp_mm): Remove. + (mpi_mul_hlp): Return CARRY, computation in constant time. + (mpi_mul_mpi): Change the order of computation not to propagate + carry. + (mpi_montmul): Minimum zero-ing of D and reduce usage of temporary + memory, by one word. Use carry of mpi_mul_hlp. Use + NEED_SUBTRACTION against timing attack. + (mpi_exp_mod): Minimum usage of temporary memory. + +2013-10-06 Niibe Yutaka + + * polarssl/library/bignum.c (mpi_mul_hlp_mm): New. Handle + extra-carry in constant time to mitigate timing attack. + (mpi_montmul): Use mpi_mul_hlp_mm. + * src/call-rsa.c (rsa_sign, rsa_decrypt, rsa_verify): Don't + use RSA blinding. + +2013-10-05 Niibe Yutaka + + * polarssl/include/polarssl/aes.h: Update from PolarSSL 1.2.9. + * polarssl/include/polarssl/bignum.h: Ditto. + * polarssl/include/polarssl/config.h: Ditto. + * polarssl/include/polarssl/rsa.h: Ditto. + * polarssl/library/aes.c, polarssl/library/bignum.c: Ditto. + * polarssl/library/rsa.c: Ditto. Fix rsa_free. + * src/call-rsa.c (rsa_sign, modulus_calc, rsa_decrypt) + (rsa_verify): Follow changes of PolarSSL 1.2.9 with RSA blinding. + Better error checking. + +2013-10-04 Niibe Yutaka + + * src/main.c (gnuk_malloc): Update ->neighbor field of a chunk on + the free list. + (gnuk_free): Access free list after getting the lock. + +2013-10-01 Niibe Yutaka + + * src/random.c (random_gen): Bug fix for INDEXed copy. + + * src/call-rsa.c (rsa_genkey): Call neug_flush and prng_seed. + * polarssl/library/bignum.c (small_prime): More constants. + (prng_seed, jkiss, mpi_fill_pseudo_random): New. + (mpi_is_prime): Use mpi_fill_pseudo_random. + +2013-09-30 Niibe Yutaka + + * polarssl/library/bignum.c (mpi_is_prime): Enable trial divisions + by small integers. + Add Fermat primality test. + (mpi_gen_prime): Limit random value so that two MSBs of result will + be 0x11. + +2013-09-27 Niibe Yutaka + + * polarssl/include/polarssl/bignum.h (mpi_is_prime): ifdef-out. + * polarssl/library/bignum.c (mpi_is_prime): It's now internal + function, assuming we already know its coprime to small primes. + (M): New constant MPI. Multiply primes 2*...*691. + (MAX_A): New constant MPI. 2^1024 / M - 1. + (mpi_gen_prime): Specialize for 1024-bit, using Fouque-Tibouchi + method. + +2013-09-25 Niibe Yutaka + + * src/sha256.h, src/adc.h + * src/neug.c, src/adc_stm32f103.c: Update from NeuG 0.11. + + * chopstx: Upgrade to new Chopstx 0.00. + + * VERSION: New file. + + * src/configure (SERIALNO, SERIALNO_STR_LEN_DEFINE): New. + (REVISION): Use the file VERSION if it doesn't have .git. + Thanks to Sumedha Widyadharma for the bug report. + + * src/config.h.in (SERIALNO_STR_LEN_DEFINE): New. + * src/main.c (ID_OFFSET): Use SERIALNO_STR_LEN. + * src/usb_desc.c (gnukStringSerial): Remove. It's now + generated in usb-strings.c.inc. + + * src/ec_p256.c (compute_kP): Fix for impossible cases. + (point_is_on_the_curve): New. + (coefficient_a, coefficient_b): New. + +2013-09-20 Niibe Yutaka + + * src/call-ec_p256.c (ecdsa_compute_public): Handle possible + error (where key_data is the order). + + * src/ec_p256.c (compute_kG, compute_kP): Handle errors. + + * src/jpc.c (jpc_to_ac): Return -1 on error. + (jpc_add_ac_signed): Handle the case where A=inf. + + * src/modp256.c (modp256_inv): Handle error case. + + * src/bn.c (bn256_cmp): New. + +2013-07-19 Niibe Yutaka + + * src/gnuk.ld.in: Layout change following NeuG. + (_end): Add alignment of 16. + + * src/neug.c, src/adc.h, src/adc_stm32f103.c: Update from NeuG. + + * src/main.c [DFU_SUPPORT] (main): Fix calling + flash_erase_all_and_exec. + + * src/openpgp-do.c (gpg_do_write_prvkey, gpg_do_keygen): Fix + allocated memory handling. Clean up before free. + * src/call-rsa.c (modulus_calc, rsa_genkey): Fix removing const. + * src/call-ec_p256.c (ecdsa_compute_public): Likewise. + +2013-07-18 Niibe Yutaka + + Port to Chopstx. + * src/Makefile.in: Change for Chopstx. + * src/configure: Likewise. + * src/gnuk.h, src/gnuk.ld.in: Likewise. + * src/ac.c: Include stdint.h and string.h, not ch.h. + * src/call-rsa.c, src/debug.c, src/flash.c: Likewise. + * src/call-ec_p256.c, src/usb_desc.c + * src/openpgp-do.c, src/random.c: Likewise. + * src/openpgp.c: Likewise. Use eventflag of Chopstx. + * src/usb-icc.c: Likewise. + * src/usb_ctrl.c: Update for Chopstx. + * src/debug.h: New. + * src/stdlib.h: Use gnuk_malloc and gnuk_free for malloc/free. + * src/config.h.in: Move FLASH_PAGE_SIZE in board.h. + * polarssl/library/aes.c (FT0, FT1, FT2): Export (for sys 2.0). + * src/main.c (struct stdout, _write, EP3_IN_Callback) + (EP5_OUT_Callback): Rewrite for Chopstx. No independent thread + any more. + (display_fatal_code, emit_led, display_status_code, led_blink): + Use primitives of Chopstx. + (main): Changes for Chopstx. + (gnuk_malloc_init, sbrk, gnuk_malloc, gnuk_free): New. + +2013-06-20 Niibe Yutaka + + * src/sys.c, src/sys.h, src/neug.c, src/adc.h + * src/adc_stm32f103.c, src/usb_stm32f103.c: Update from NeuG 0.10. + * src/stm32f103.h: New. From NeuG 0.10. + +2013-06-18 Niibe Yutaka + + * src/openpgp-do.c (gpg_do_write_prvkey, proc_key_import, gpg_do_table) + (gpg_do_public_key) [RSA_AUTH]: Conditional compilation for RSA/ECDSA. + * src/openpgp.c (cmd_internal_authenticate) [RSA_AUTH]: Likewise. + + * src/modp256.c (p256): Add const qualifier. + +2013-03-19 Niibe Yutaka + + * src/random.c (random_gen): New (was: random_byte). + + * src/call-rsa.c (rsa_sign): Follow change of API. + (rsa_genkey): Use random_gen. + (modulus_calc, rsa_decrypt, rsa_verify): Follow change of API. + * src/openpgp-do.c (encrypt, decrypt): Likewise. + + * polarssl/include/polarssl/aes.h: Updated from PolarSSL 1.2.6. + * polarssl/library/aes.c: Ditto. + * polarssl/include/polarssl/rsa.h: Ditto. + * polarssl/library/rsa.c: Ditto. + * polarssl/include/polarssl/bignum.h: Ditto. + * polarssl/library/bignum.c: Ditto. + * polarssl: Move from polarssl-0.14.0, and needed files only. + +2013-03-15 Niibe Yutaka + + * regnual/regnual.ld (.bss): Put at RAM1. This makes reGNUal can + be loaded on the lower address. + + * regnual/sys.c (entry): Don't change SP. Put alignment. + + * regnual/regnual.c (usb_cb_get_descriptor): Fix adding break. + +2013-03-14 Niibe Yutaka + + * tool/stlinkv2.py (stlinkv2.start): Call write_debug_reg to run + the core again. + +2013-03-12 Niibe Yutaka + + * src/gnuk.ld.in (__process_stack_size__): Increase (was: 0x200). + + * tool/stlinkv2.py (stlinkv2.exit_from_debug_swd) + (stlinkv2.exit_from_debug_swim): New. + (stlinkv2.start): Call exit_from_debug_swd or + exit_from_debug_swim. + +2013-03-09 Niibe Yutaka + + * src/openpgp-do.c (gpg_do_public_key): Add OID for ECDSA. + (gpg_do_write_prvkey): Add PUBKEY_LEN for ECDSA. + + * src/flash.c (flash_key_write): Argument change for ECDSA key. + + * src/main.c (calculate_regnual_entry_address): New. + (main): Use calculate_regnual_entry_address for entry point. + + * src/openpgp-do.c (gpg_do_write_prvkey): Coerce KDI.DATA to + uint8_t *. + + * src/usb_stm32f103.c (handle_setup0): Fix selecting handler. + +2013-03-08 Niibe Yutaka + + Relocatable reGNUal. + + * regnual/regnual.ld (MEMORY): 0x1400 was the value of Gnuk 1.0.1. + Keep this value. + (.text): Include .text.entry next to the .vectors. + (.got): New. + + * regnual/sys.c (entry): Now, it's at .text.entry section. + Do relocations. + Don't use absolute values which causes relocations, but + access at GOT. + + * regnual/Makefile (CFLAGS): Add -fpie. + +2013-03-07 Niibe Yutaka + + Follow the USB stack change. + + * regnual/regnual.c (usb_cb_device_reset): Rename from + regnual_device_reset. + (mem): Change type to uint32_t. + (mem_info): Removed. + (fetch): Avoid pointer punning. + (usb_cb_ctrl_write_finish): Rename from regnual_ctrl_write_finish. + (usb_cb_setup): Rename from regnual_setup. + (usb_cb_get_descriptor): Rename from regnual_get_descriptor. + (usb_cb_handle_event): Rename regnual_usb_event. + (usb_cb_interface): Rename regnual_interface. + (Device_Method): Remove. + (usb_cb_get_descriptor): Not use struct Descriptor. + +2013-03-06 Niibe Yutaka + + USB stack implementation improvement. + + * src/usb_stm32f103.c (Device_Method, method_p): Remove. + (usb_interrupt_handler): Call usb_cb_device_reset. + (std_get_descriptor): Call usb_cb_get_descriptor. + (std_set_configuration): Call usb_cb_handle_event. + (std_get_status, std_get_interface, std_set_interface): Call + usb_cb_interface. + (handle_setup0): Call usb_cb_setup. + (handle_in0): Call usb_cb_handle_event and + usb_cb_ctrl_write_finish. + (request_handler): Remove. + (handle_setup0): Call std_* directly, not indirectly by + request_handler. + (ep_intr_handler_IN, ep_intr_handler_OUT): Remove. + (usb_handle_transfer): Call EP*_Callback directly, not indirectly + by ep_intr_handler_IN, ep_intr_handler_OUT. + + * src/usb_lld.h (struct usb_device_method, Device_Method): Remove. + (usb_cb_device_reset, usb_cb_ctrl_write_finish) + (usb_cb_setup, usb_cb_get_descriptor, usb_cb_handle_event) + (usb_cb_interface): Define callbacks. + (usb_initial_feature): New. + (struct Descriptor): Move to ... + * src/usb_desc.c: ... here. + (usb_initial_feature): New. + (usb_cb_get_descriptor): Rename from gnuk_get_descriptor and move + from usb_ctrl.c. + + * src/usb_ctrl.c (usb_cb_device_reset): Rename from + gnuk_device_reset. + (usb_cb_setup): Rename from gnuk_setup. + (usb_cb_ctrl_write_finish): Rename from gnuk_ctrl_write_finish. + (usb_cb_event): Rename from gnuk_usb_event. + (usb_cb_interface): Rename from gnuk_interface. + (Device_Method): Remove. + + * src/main.c (main): Use usb_initial_feature. + +2013-02-27 Niibe Yutaka + + * src/usb-icc.c (set_sw1sw2): Arguments are C and CHUNK_LEN. + Fix reporting remaining bytes. + (icc_send_data_block_gr): Follow the arguments change of + set_sw1sw2. + +2013-02-26 Niibe Yutaka + + * regnual/regnual.ld (MEMORY): Fix start address. + + * src/random.c (random_fini): New. + * src/main.c (main): Call random_fini. + +2013-02-25 Niibe Yutaka + + * src/configure: Correct typo in help text. + + * src/gnuk.h (struct key_data_internal): Use uint32_t. + * src/openpgp-do.c (do_openpgpcard_aid): Fix calculation of VID. + (compute_key_data_checksum): Don't use type-punning pointer. + (gpg_do_write_prvkey): Use coercing to char *. + +2013-02-22 Niibe Yutaka + + * src/openpgp-do.c (gpg_do_public_key): Add header of EC point. + + * src/openpgp-do.c (GPG_DO_DISCRETIONARY, cmp_discretionary): New. + (cmp_app_data): Change to factor out GPG_DO_DISCRETIONARY. + (gpg_do_table): Add GPG_DO_DISCRETIONARY. + +2013-02-21 Niibe Yutaka + + * src/gnuk.ld.in (MEMORY): Fix adding FLASH_SIZE unit. + + * src/call-ec_p256.c (ecdsa_sign): Fix secret key access. + +2013-02-20 Niibe Yutaka + + * src/openpgp.c (cmd_internal_authenticate): Support ECDSA for + authentication. + + * src/openpgp-do.c (algorithm_attr_ecdsa): New. + (algorithm_attr_rsa): Rename (was: algorithm_attr). + (gpg_do_table): Change for GPG_DO_ALG_AUT. + (gpg_do_write_prvkey): Support ECDSA key for authentication. + (proc_key_import): Likewise. + (gpg_do_public_key): Likewise. + + * src/call-ec_p256.c: New. + * src/Makefile.in: Add call-ec_p256.c. + * src/call-rsa.c (modulus_free): Remove. + +2013-02-19 Niibe Yutaka + + * regnual/regnual.ld (MEMORY): Fix address of regnual. + + * regnual/Makefile (MCFLAGS): Remove -mfix-cortex-m3-ldrd. + (CFLAGS): Add output to .lst. + * src/Makefile.in (MCFLAGS): Remove. + + * src/sha256.c: Update from NeuG 0.05. + + * ChibiOS_2.0.8: Remove. + +2013-02-18 Niibe Yutaka + + Changes for new ChibiOS/RT. + * src/main.c: Include adc.h. + (main): Call halInit, adc_init, and chSysInit (change for + ChibiOS/RT 2.4.x). + * src/random.h: New. + * src/ac.c, src/bn.c, src/call-rsa.c, src/main.c: Include random.h. + * src/openpgp.c, src/openpgp-do.c: Likewise. + * src/configure, src/gnuk.ld.in: Add MEMORY_SIZE. + * src/ec_p256.c: Fix call of bn256_add_uint. + * boards/STM8S_DISCOVERY/*: Update for ChibiOS/RT 2.4.x. + * boards/CQ_STARM/*: Likewise. + * boards/FST_01_00/*: Likewise. + * boards/OLIMEX_STM32_H103/*: Likewise. + * boards/STBEE/*: Likewise. + * boards/STBEE_MINI/*: Likewise. + * boards/STM32_PRIMER2/*: Likewise. + + Merge ec_p256 branch. + * src/Makefile.in: Add ECC files. + * src/bn.h, src/bn.c: New. + * src/jpc-ac.h, src/jpc.c: New. + * src/ec_p256.h, src/ec_p256.c, src/ecc-cdh.c: New. + * src/mod.h, src/mod.c, src/modp256.h, src/modp256.c: New. + +2013-02-17 Niibe Yutaka + + * chibios: New submodule for ChibioS/RT 2.4.x. + * boards/FST_01/*: Update for ChibiOS/RT 2.4.x. + * boards/common/mcuconf-common.h: Ditto. + + * src/chconf.h, src/halconf.h, src/Makefile.in, src/gnuk.ld.in: + Update for ChibiOS/RT 2.4.x. + + * src/main.c, src/openpgp.c, src/usb-icc.c: Follow the change of + ChibiOS/RT 2.4.x. + * boards/common/board-common.c: Rename from hwinit.c. + * src/usb_stm32f103.c: Rename from usb_lld.c. + + * src/neug.h, src/neug.c: Update NeuG 0.05. + * src/adc_stm32f103.c, src/adc.h: New from NeuG 0.05. + + * src/random.c: Follow the change of NeuG 0.05. diff --git a/gnuk/ChangeLog-1_0 b/gnuk/ChangeLog-1_0 new file mode 100644 index 0000000000..84b5bc682f --- /dev/null +++ b/gnuk/ChangeLog-1_0 @@ -0,0 +1,2477 @@ +2013-02-15 Niibe Yutaka + + * Version 1.0.2. + * src/usb_desc.c (gnukStringSerial): Updated. + +2013-02-14 Niibe Yutaka + + * test/features/002_get_data_static.feature: Value of extended + capabilities changed. + * test/features/402_get_data_static.feature: Ditto. + * test/features/802_get_data_static.feature: Ditto. + + * src/openpgp.c (cmd_write_binary): Move erasing page of update + keys to... + (modify_binary): ...here. + + * src/flash.c (flash_write_binary): Handle removal of update keys. + +2013-02-13 Niibe Yutaka + + * src/openpgp.c (cmd_get_challenge): Handle Le field. + + * src/openpgp-do.c (extended_capabilities): Fix for GET CHALLENGE. + + * src/gnuk.h (CHALLENGE_LEN): Moved here (was: openpgp.c). + + * tool/gnuk_token.py (iso7816_compose): Add Le field. + +2013-01-30 Niibe Yutaka + + * src/openpgp.c (cmd_external_authenticate): Fix off-by-one error. + + * tool/gnuk_token.py (gnuk_token.cmd_external_authenticate): Add + KEYNO to the arguments. + + * tool/upgrade_by_passwd.py (main): Explicitly say it's KEYNO. + +2013-01-28 Niibe Yutaka + + * src/openpgp-do.c (gpg_pw_get_retry_counter): New. + * src/openpgp.c (cmd_verify): Implement VERIFY with empty data. + +2013-01-22 Niibe Yutaka + + * tool/pinpadtest.py (Card.cmd_vega_alpha_disable_empty_verify): + New. + (main): call cmd_vega_alpha_disable_empty_verify if it's + COVADIS_VEGA_ALPHA. + +2013-01-21 Niibe Yutaka + + * tool/pageant_proxy_to_gpg.py: New. + * tool/sexp.py: New. + +2013-01-20 Niibe Yutaka + + * tool/gpg_agent.py: New. + +2013-01-11 Niibe Yutaka + + * tool/pinpadtest.py: Add fixed length input. + +2012-12-25 Niibe Yutaka + + * tool/rsa.py: New. + + * tool/rsa_example.key: New. Example RSA key information. + + * tool/upgrade_by_passwd.py: New. + +2012-12-19 Niibe Yutaka + + * src/Makefile.in (USE_OPT): -O3 and -Os (was: -O2). + + * tool/gnuk_token.py (gnuk_token.stop_gnuk, gnuk_token.mem_info) + (gnuk_token.download, gnuk_token.execute) + (gnuk_token.cmd_get_challenge) + (gnuk_token.cmd_external_authenticate): New. + (gnuk_devices_by_vidpid): New. + (regnual): New. + +2012-12-18 Niibe Yutaka + + * test/gnuk.py: Remove. + + * test/features/steps.py: Use tool/gnuk_token.py. + + * tool/gnuk_put_binary_libusb.py: Use gnuk_token.py. + (main): Follow the API change. + + * tool/gnuk_token.py (list_to_string): New. + (gnuk_token.get_string, gnuk_token.increment_seq) + (gnuk_token.reset_device, gnuk_token.release_gnuk): New. + (gnuk_token.icc_power_on): Set self.atr and it's now string. + (gnuk_token.icc_send_cmd): Handle time extension. + (gnuk_token.cmd_get_response): Return string (was: list). + (gnuk_token.cmd_get_data): Return "" when success. + (gnuk_token.cmd_change_reference_data, gnuk_token.cmd_put_data) + (gnuk_token.cmd_put_data_odd) + (gnuk_token.cmd_reset_retry_counter, gnuk_token.cmd_pso) + (gnuk_token.cmd_pso_longdata) + (gnuk_token.cmd_internal_authenticate, gnuk_token.cmd_genkey) + (gnuk_token.cmd_get_public_key): New. + (compare): New. + (get_gnuk_device): New. + +2012-12-14 Niibe Yutaka + + * src/openpgp.c (cmd_change_password): Check password length + for admin less mode. + +2012-12-13 Niibe Yutaka + + * src/openpgp-do.c (gpg_do_put_data): Add GPG_SUCCESS for + completeness (it worked because of lower layer goodness). + +2012-12-12 Niibe Yutaka + + * tool/gnuk_token.py: Add module imports. + + * tool/gnuk_remove_keys.py (main): Fix data object number + for KGTIME_SIG, KGTIME_DEC and KGTIME_AUT. + + * tool/gnuk_remove_keys_libusb.py (main): Likewise. + +2012-12-05 Niibe Yutaka + + * tool/gnuk_remove_keys_libusb.py: New. + * tool/gnuk_token.py: New. + +2012-11-07 Niibe Yutaka + + * src/usb-icc.c (icc_send_data_block_internal): New. + (icc_send_data_block_time_extension): New. + (icc_handle_timeout): Use icc_send_data_block_time_extension. + (icc_send_data_block): Only one argument. + (USBthread): Follow the change. + +2012-11-01 Niibe Yutaka + + * tool/gnuk_upgrade.py (main): New option '-k' to specify keygrip + for non-smartcard key. + (gpg_sign): Support non-smartcard key. + +2012-10-31 Niibe Yutaka + + * tool/get_raw_public_key.py: New. + +2012-10-26 Niibe Yutaka + + * GNUK_USB_DEVICE_ID (Product_STRING): It's considered better not + to include vendor name. Change the name to "Gnuk Token" (was: + FSIJ USB Token). + +2012-10-13 Niibe Yutaka + + * boards/STBEE_MINI/board.c [!DFU_SUPPORT] (hwinit1): Don't run + when "user switch" is pushed. This is for JTAG/SWD debugger. + +2012-09-25 Niibe Yutaka + + * tool/stlinkv2.py (main): Print out option bytes value. + Call reset_sys before blank_check. + +2012-09-18 Niibe Yutaka + + * tool/stlinkv2.py (stlinkv2.option_bytes_erase) + (stlinkv2.flash_erase_all, stlinkv2.flash_erase_page): : Fix + OperationFailure (was OperationError). + (main): Call option_bytes_erase if it's not 0xff. + +2012-09-12 Niibe Yutaka + + * src/sha256.c: Include . + + * src/sha256.h (SHA256_DIGEST_SIZE, SHA256_BLOCK_SIZE): Move + from sha256.c. + +2012-08-29 Niibe Yutaka + + * tool/hub_ctrl.py (__main__): Fix to busnum (was: bunum). + Thanks to Henry Hu. + +2012-08-03 Niibe Yutaka + + * Version 1.0.1. + * src/usb_desc.c (gnukStringSerial): Updated. + * src/main.c (ID_OFFSET): Fix. + +2012-08-02 Niibe Yutaka + + * test/gnuk.py (gnuk_token.get_string): New. + * test/features/991_version_string.feature: New. + +2012-07-21 Niibe Yutaka + + * Version 1.0. + * src/usb_desc.c (gnukStringSerial): Updated. + + Documentation by Sphinx. + * doc/Makefile: New. + * doc/note: Old notes are moved here. + +2012-07-20 Niibe Yutaka + + * test/features/002_get_data_static.feature: Support CERTDO enabled + Gnuk for the test of extended capabilities. + * test/features/802_get_data_static.feature: Ditto. + * test/features/402_get_data_static.feature: Ditto. + +2012-07-10 Niibe Yutaka + + * test/features/*: Add test cases for PW1/PW3 of factory settings. + + * test/features/202_keygen.feature: Add PSO signature test after + keygen. + * test/features/602_keygen.feature: Ditto. + + Bug fix. + * src/openpgp-do.c (gpg_do_write_prvkey): Don't call ac_reset_* + here. + (proc_key_import): But call ac_reset_* here. + (gpg_do_keygen): Load private key for signing. + + * tool/stlinkv2.py (stlinkv2.usb_disconnect): New. + +2012-07-09 Niibe Yutaka + + * src/openpgp.c (cmd_pso): For decryption, return error sooner for + invalid data. + + * tool/stlinkv2.py (stlinkv2.setup_gpio): Fix GPIOB_CRL. + + * test/rsa_keys.py (integer_to_bytes_256): Rename from + integer_to_bytes and it should be exactly 256-byte long. + +2012-07-06 Niibe Yutaka + + * 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 + + * 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 + + 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 + + * 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 + + * tool/stlinkv2.py (stlinkv2.__init__): Don't call setAltInterface. + +2012-06-30 Niibe Yutaka + + * 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 + + * regnual/Makefile: Don't copy usb_lld.c. + +2012-06-28 Niibe Yutaka + + * 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 + + * 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 + + * test: New. + +2012-06-25 Niibe Yutaka + + * tool/usb_strings.py: New. + +2012-06-22 Niibe Yutaka + + * tool/stlinkv2.py (stlinkv2.blank_check): Add blank check of + Flash ROM. + +2012-06-21 Niibe Yutaka + + * tool/asm-thumb/blank_check.S: New. + +2012-06-20 Niibe Yutaka + + 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 + + * Version 0.20. + + * src/usb_desc.c (gnukStringSerial): Updated. + +2012-06-18 Niibe Yutaka + + LED display output change. + * src/main.c (MAIN_TIMEOUT_INTERVAL): New. + (LED_TIMEOUT_INTERVAL, etc.): New values. + (main_mode, display_interaction): Remove. + (led_inverted, emit_led): New. + (display_status_code): Use emit_led. + (led_blink): Use LED_* for SPEC. + (main, fatal): New LED display output. + * src/gnuk.h (LED_ONESHOT, LED_TWOSHOTS, LED_SHOW_STATUS) + (LED_START_COMMAND, LED_FINISH_COMMAND, LED_FATAL): New semantics. + (main_thread): Remove. + * src/openpgp-do.c (gpg_do_keygen): Don't touch LED here. + * src/openpgp.c (get_pinpad_input): Call led_blink. + (cmd_pso, cmd_internal_authenticate): Don't touch LED here. + (GPGthread): Call led_blink. + * src/pin-cir.c (pinpad_getline): Change arg of led_blink. + * src/pin-dnd.c (pinpad_getline): Ditto. + * src/usb-icc.c (icc_handle_timeout): Ditto. + (icc_send_status): Call led_blink. + * src/usb_ctrl.c (gnuk_usb_event): Don't touch LED here. + +2012-06-16 Niibe Yutaka + + Use SHA256 format for "external authenticate". + * tool/gnuk_upgrade.py (gpg_sign): SHA256 sign by "SCD PKAUTH". + (main): Not specify keygrip, but always use key for authentication. + * src/call-rsa.c (rsa_verify): It is SHA256 format (was: SHA1). + * src/openpgp.c (cmd_get_challenge): Don't add chip-id prefix. + (cmd_external_authenticate): Likewise. + +2012-06-15 Niibe Yutaka + + * src/random.c (random_bytes_free): Clear out random bytes. + + More protection improvements. + * src/random.c (RANDOM_BYTES_LENGTH): It's 32 now (was: 16). + * src/gnuk.h (struct key_data_internal): Remove check, random, + magic. Add checksum. + (struct prvkey_data): Remove crm_encrypted. Add iv and + checksum_encrypted. + * src/openpgp-do.c (encrypt, decrypt): Add IV argument. + (encrypt_dek, decrypt_dek): New. It's in ECB mode. + (compute_key_data_checksum): New. + (gpg_do_load_prvkey): Handle initial vector and checksum. + Use decrypt_dek to decrypt DEK. Clear DEK after use. + (calc_check32):Remove. + (gpg_do_write_prvkey): Use encrypt_dek to encrypt DEK. + (gpg_do_chks_prvkey): Likewise. + + * polarssl-0.14.0/include/polarssl/aes.h (aes_crypt_cbc) + * polarssl-0.14.0/library/aes.c (aes_crypt_cbc): ifdef-out. + + * src/configure (--enable-pinpad): Deprecate DND. + +2012-06-14 Niibe Yutaka + + Protection improvement. + * src/openpgp.c (resetcode_s2k): New. + (cmd_reset_user_password): Use resetcode_s2k. + * src/openpgp-do.c (proc_resetting_code): Likewise. + + * src/sha256.c (sha256_finish): Clear out CTX at the end. + + * src/call-rsa.c (rsa_sign, rsa_decrypt, rsa_verify): Use + mpi_lset (was: mpi_read_string). + * polarssl-0.14.0/library/bignum.c (mpi_get_digit) + (mpi_read_string): ifdef-out. + + KDF is now SHA-256 (was: SHA1). + * src/sha256.c: New file. Based on the implementation by Dr Brian + Gladman. + * src/openpgp.c (cmd_change_password, cmd_reset_user_password): + Use sha256. + * src/openpgp-do.c (proc_resetting_code, gpg_do_write_prvkey): Likewise. + * src/ac.c (verify_user_0, calc_md, verify_admin): Likewise. + * src/crypt.mk (CRYPTSRC): Add sha256.c, removing sha1.c. + * src/gnuk.h (KEYSTRING_MD_SIZE): It's 32 for SHA-256. + +2012-06-13 Niibe Yutaka + + Bug fixes. + * src/main.c (display_interaction): Assign to main_mode. + * src/openpgp.c (cmd_change_password): Bug fix for admin less mode + to admin full mode. Variable who_old should be admin_authorized. + + Key generation is configure option. + * src/configure (keygen): Add --enable-keygen option. + * src/Makefile.in (UDEFS): Add definition of KEYGEN_SUPPORT. + * src/call-rsa.c [KEYGEN_SUPPORT] (rsa_genkey): Conditionalize. + * src/random.c [KEYGEN_SUPPORT] (random_byte): Ditto. + * src/openpgp.c [KEYGEN_SUPPORT] (cmd_pgp_gakp): Ditto. + * src/openpgp-do.c [KEYGEN_SUPPORT] (gpg_do_keygen): Ditto. + * polarssl-0.14.0/include/polarssl/config.h: Ditto. + * polarssl-0.14.0/library/bignum.c [POLARSSL_GENPRIME] + (mpi_inv_mod): Unconditionalize. + +2012-06-08 Niibe Yutaka + + * polarssl-0.14.0/library/bignum.c (mpi_cmp_mpi): Bug fix. + Though it doesn't matter for Gnuk usage. + + Emit LED light while computation (or asking user input). + * src/usb-icc.c (icc_handle_timeout): Call led_blink. + * src/openpgp.c (cmd_pso, cmd_internal_authenticate): Call + LED_WAIT_MODE, LED_STATUS_MODE to show "it's under computation". + * src/openpgp-do.c (gpg_do_keygen): Ditto. + * src/gnuk.h (LED_WAIT_MODE): Rename (was: LED_INPUT_MODE). + * src/main.c (display_interaction): Change the behavior of LED, + now, it's mostly ON (was: mostly OFF). + +2012-06-07 Niibe Yutaka + + * src/openpgp.c (cmd_internal_authenticate): Add check for input + length. + + Implement key generation. + * src/openpgp.c (cmd_pgp_gakp): Call gpg_do_keygen. + * src/openpgp-do.c (proc_key_import): Call with modulus = NULL. + (gpg_do_keygen): New function. + (gpg_reset_digital_signature_counter): New function. + (gpg_do_write_prvkey): New argument MODULUS. Call + gpg_reset_digital_signature_counter. + * src/call-rsa.c (rsa_genkey): New function. + * src/random.c (random_byte): New function. + + PolarSSL modification. + * polarssl-0.14.0/library/rsa.c (rsa_gen_key): Don't set D, DP, + DQ, and QP. It's only for key generation. + * polarssl-0.14.0/library/rsa.c (rsa_gen_key, rsa_pkcs1_encrypt): + Change f_rng function return type. + * polarssl-0.14.0/include/polarssl/rsa.h: Likewise. + * polarssl-0.14.0/library/bignum.c (mpi_is_prime, mpi_gen_prime): + Change f_rng function return type. + * polarssl-0.14.0/include/polarssl/bignum.h: Likewise. + +2012-06-06 Niibe Yutaka + + * Version 0.19. + + * src/usb_desc.c (gnukStringSerial): Updated. + + * regnual/regnual.c (fetch): Better implementation. + +2012-06-05 Niibe Yutaka + + Firmware update key handling. + * tool/gnuk_put_binary.py (GnukToken.cmd_get_response): Handle + larger data such as card holder certificate. + (GnukToken.cmd_write_binary): Bug fix for cert do write. + (GnukToken.cmd_read_binary): New. + (main): Support firmware update key. + + Take advantage of the Thumb-2 "rbit" instruction. + * regnual/regnual.c (fetch): Reverse bits. + * src/usb_ctrl.c (rbit): New. Deleted reverse32. + (download_check_crc32): Use rbit. + * tool/gnuk_upgrade.py (crc32): Just use binascii.crc32. + (crctab): Remove. + +2012-06-04 Niibe Yutaka + + Card holder certificate data object bug fixes. + * tool/gnuk_put_binary_libusb.py (gnuk_token.cmd_get_response): + Handle larger data such as card holder certificate. + * src/flash.c (flash_write_binary): Bug fix. Call + flash_check_blank with p + offset. + * src/gnuk.h (FLASH_CH_CERTIFICATE_SIZE): Define here (was: flash.c). + + Implement CRC32 check for firmware update. + * src/usb_ctrl.c (download_check_crc32): New. + * regnual/regnual.c (calc_crc32): New. + (regnual_ctrl_write_finish): Call calc_crc32. + * tool/gnuk_upgrade.py (crc32): New. + (regnual.download): Check crc32code. + + * regnual/regnual.c (regnual_ctrl_write_finish): Bug fix. + +2012-06-01 Niibe Yutaka + + Support firmware update with public key authentication. + * tool/gnuk_upgrade.py (gpg_sign): New. + * tool/gnuk_put_binary_libusb.py (main): Support firmware update + key registration. + + Update of reGNUal. + * regnual/regnual.c (main): Follow the change of usb_lld_init. + (regnual_config_desc): Include interface descriptor. + (usb-strings.c.inc): Change the file name. + * regnual/Makefile (regnual.o): Depend on sys.h. + * src/configure (usb-strings.c.inc): ifdef-out + gnuk_revision_detail and gnuk_config_options (for reGNUal). + * src/usb_desc.c (USB_STRINGS_FOR_GNUK): Define. + + USB bug fixes. + * src/usb_ctrl.c (gnuk_usb_event): Bug fix for handling + USB_EVENT_CONFIG. Do nothing when current_conf == value. + * src/usb_lld.c (std_clear_feature): Bug fix. Always clear DTOG. + (usb_lld_init): New argument for FEATURE. + +2012-05-31 Niibe Yutaka + + * polarssl-0.14.0/library/rsa.c (rsa_pkcs1_verify): BUF size is + 256 (was: 1024). + * src/call-rsa.c (rsa_verify): It's SIG_RSA_SHA1. + * src/openpgp.c (cmd_external_authenticate): Follow the change of + rsa_verify. + + Support "firmware update" keys. + * src/flash.c (flash_write_binary): Support update keys. + * src/gnuk.h (FILEID_UPDATE_KEY_0, FILEID_UPDATE_KEY_1) + (FILEID_UPDATE_KEY_2,FILEID_UPDATE_KEY_3): New. + * src/gnuk.ld.in (_updatekey_store): New. + * src/openpgp.c (FILE_EF_UPDATE_KEY_0, FILE_EF_UPDATE_KEY_1) + (FILE_EF_UPDATE_KEY_2, FILE_EF_UPDATE_KEY_3): New. + (gpg_get_firmware_update_key): New. + (cmd_read_binary): Support update keys and certificate. + (modify_binary): New. + (cmd_update_binary, cmd_write_binary): Use modify_binary. + (cmd_external_authenticate): Support up to four keys. + + Version string of system service is now USB string. + * src/sys.h (unique_device_id): Define here, not as system + service. + * src/sys.c (sys_version): Version string for system service. + * src/usb_desc.c (String_Descriptors): Add sys_version. + * src/usb_conf.h (NUM_STRING_DESC): 7 (was: 6). + * src/gnuk.ld.in (.sys.version): New section. + +2012-05-30 Niibe Yutaka + + * src/openpgp.c (CHALLENGE_LEN): New. + (cmd_external_authenticate): Authentication by response with + public key. + (cmd_get_challenge): 16-byte is enough for challenge. + +2012-05-29 Niibe Yutaka + + * src/call-rsa.c (rsa_verify): New function. + + * polarssl-0.14.0/include/polarssl/rsa.h (rsa_pkcs1_verify) + * polarssl-0.14.0/library/rsa.c (rsa_pkcs1_verify): Fix API. + + * src/usb_conf.h (NUM_STRING_DESC): Incremented to 6 (was: 4). + * src/configure: Generate strings for revision detail and config + options. + * src/usb_desc.c (gnuk_revision_detail, gnuk_config_options): New. + + * src/main.c (main) [DFU_SUPPORT]: Kill DFU and install .sys. + + * src/config.h.in (FLASH_PAGE_SIZE): New. + * src/configure: Support FLASH_PAGE_SIZE for config.h + * boards/*/board.h (FLASH_PAGE_SIZE): Remove. + * src/flash.c (FLASH_PAGE_SIZE): Remove. + + * src/sys.c (reset): Don't depend if DFU_SUPPORT or not. + (flash_erase_all_and_exec): Rename and change the argument. + * src/gnuk.ld.in (__flash_start__): Real flash ROM address, + regardless of DFU_SUPPORT. + * src/main.c (main): Call flash_erase_all_and_exec with SRAM + address. + + * polarssl-0.14.0/library/aes.c (FT0, FT1, FT2): Specify sections + in detail, so that addresses won't be affected by compiler. + * src/gnuk.ld.in (.sys): Define sections in detail. + + * boards/STBEE_MINI/board.h (SET_USB_CONDITION, GPIO_USB) + (IOPORT_USB, SET_LED_CONDITION, GPIO_LED, IOPORT_LED) + (FLASH_PAGE_SIZE): New. + * boards/STBEE_MINI/board.c (USB_Cable_Config, set_led): Remove. + + * boards/STBEE/board.h (SET_USB_CONDITION, GPIO_USB, IOPORT_USB) + (SET_LED_CONDITION, GPIO_LED, IOPORT_LED, FLASH_PAGE_SIZE): New. + * boards/STBEE/board.c (USB_Cable_Config, set_led): Remove. + + * boards/CQ_STARM/board.h (SET_USB_CONDITION) + (SET_LED_CONDITION, GPIO_LED, IOPORT_LED, FLASH_PAGE_SIZE): New. + * boards/CQ_STARM/board.c (USB_Cable_Config, set_led): Remove. + +2012-05-28 Niibe Yutaka + + * boards/*/board.c (hwinit0): Removed... + * boards/common/hwinit.c (hwinit0): ... and define here. + (hwinit0) [DFU_SUPPORT]: Don't set SCB->VTOR here. + * src/sys.c (reset) [DFU_SUPPORT]: Set SCB->VTOR here. + (flash_write): Range check. + + * polarssl-0.14.0/library/aes.c (FT0, FT1, FT2): Specify the + section ".sys", so that we will have more room for flash ROM. + * src/gnuk.ld.in (.sys): Add alignment settings. + + * tool/gnuk_upgrade.py (main): First 4096-byte of Gnuk is system + block. Don't send it to reGNUal. + + * regnual/sys.c (entry): Fix clearing BSS. It is called with all + interrupts disabled. + + * regnual/regnual.ld (_flash_start): It's 0x08001000 now, because + there is system block now (was: 0x08000000). + +2012-05-26 Niibe Yutaka + + * src/sys.c (reset): Set SCR->VCR here. + +2012-05-25 Niibe Yutaka + + * src/gnuk.ld.in (.sys): New section. + + * boards/OLIMEX_STM32_H103/board.h (SET_USB_CONDITION, GPIO_USB) + (IOPORT_USB, SET_LED_CONDITION, GPIO_LED, IOPORT_LED) + (FLASH_PAGE_SIZE): New. + * boards/OLIMEX_STM32_H103/board.c (USB_Cable_Config, set_led): + Remove. + + * boards/STM32_PRIMER2/board.h (SET_USB_CONDITION, GPIO_USB) + (IOPORT_USB, SET_LED_CONDITION, GPIO_LED, IOPORT_LED) + (FLASH_PAGE_SIZE): New. + * boards/STM32_PRIMER2/board.c (USB_Cable_Config, set_led): + Remove. + + * boards/FST_01_00/board.h (SET_USB_CONDITION, GPIO_USB) + (IOPORT_USB, SET_LED_CONDITION, GPIO_LED, IOPORT_LED) + (FLASH_PAGE_SIZE): New. + * boards/FST_01_00/board.c (USB_Cable_Config, set_led): Remove. + + * boards/FST_01/board.h (SET_USB_CONDITION, GPIO_USB, IOPORT_USB) + (SET_LED_CONDITION, GPIO_LED, IOPORT_LED, FLASH_PAGE_SIZE): New. + * boards/FST_01/board.c (USB_Cable_Config, set_led): Remove. + + * regnual/sys-stm8s-discovery.h, sys-stbee.h: Remove. + + * boards/STM8S_DISCOVERY/board.h (SET_USB_CONDITION) + (SET_LED_CONDITION, GPIO_LED, IOPORT_LED, FLASH_PAGE_SIZE): New. + * boards/STM8S_DISCOVERY/board.c (USB_Cable_Config, set_led): + Remove. + + * regnual/Makefile: Add -I ../src to CFLAGS. + + * regnual/regnual.ld (vector): New. + + * regnual/sys.c: Remove implementation, but jump to vector by sys.h. + + * src/Makefile.in: Follow change of files. + + * src/configure: Undo changes of 2012-05-22. + + * boards/common/hw_config.c: Remove. Mov function to sys.c. + * src/flash.c: Move functions to sys.c. + * src/sys.c: New. + + * src/main.c (main): Call flash_mass_erase_and_exec. + + * src/usb_lld.c: Include sys.h. + + * src/usb_lld_sys.c: Remove. Move interrupt handler to... + * src/usb_ctrl.c: ... this file. + + * regnual/sys.c (clock_init, gpio_init, flash_unlock): Removed. + (entry): Rename (was: reset). + +2012-05-24 Niibe Yutaka + + * src/main.c (good_bye): Care LSB of function pointer. + (flash_mass_erase_and_exec): Implemented in C. + +2012-05-23 Niibe Yutaka + + * regnual/sys-stm8s-discovery.h: New. + + * src/main.c (flash_mass_erase_and_exec, good_bye): New. + (main): Call good_bye. + + * tool/gnuk_upgrade.py (regnual.protect): New. + (main): Call regnual.protect(). + + * regnual/regnual.c (regnual_setup): Don't call flash_write here. + (regnual_ctrl_write_finish): But call here. + (USB_REGNUAL_RESULT): New. + + * regnual/sys.c (flash_protect): New. + +2012-05-22 Niibe Yutaka + + * src/configure (../regnual/sys.h): Create symblic link. + + * src/usb_ctrl.c: Rename (was: usb_prop.c). + + * regnual/types.h, regnual/sys.c, regnual/regnual.ld: New. + * regnual/regnual.c, regnual/Makefile: New. + * regnual/sys-stbee.h: New. + + * src/usb_lld.c: Support FREE_STANDING environment as well as + under ChibiOS/RT. + (usb_lld_init): Call usb_lld_sys_init. Don't call user defined + method. Call usb_lld_set_configuration. + (usb_lld_shutdown): Call usb_lld_sys_shutdown. + (Vector90): Move to usb_lld_sys.c. + (usb_interrupt_handler): Export to global. + + * src/usb_lld_sys.c: New. + + * src/usb_prop.c (Device_Method): Remove gnuk_device_init. + (gnuk_device_init): Remove. + +2012-05-19 Niibe Yutaka + + * src/usb_lld.c (handle_datastage_in): Bug fix, erable RX when + sending ZLP. It will be possible to get OUT transaction soon + after IN transaction. + +2012-05-18 Niibe Yutaka + + * src/usb_lld.c (handle_datastage_out): Fix rx copying. + (handle_setup0): Bug fix not stalling TX, it will be possible + to go IN transaction, soon after OUT transaction. + + * src/usb_lld.h (USB_SETUP_SET, USB_SETUP_GET): New. + (usb_device_method.ctrl_write_finish): New. + (usb_device_method.setup): Merge setup_with_data, and + setup_with_nodata. + + * src/usb_lld.c (usb_lld_shutdown, usb_lld_prepare_shutdown): New. + (handle_setup0): Call ->setup. + (handle_in0): Call ->ctrl_write_finish. + + * src/usb_prop.c (vcom_port_data_setup): Merge + vcom_port_setup_with_nodata. + (download_check_crc32): New. + (gnuk_setup): Merge gnuk_setup_with_data and + gnuk_setup_with_nodata. + (gnuk_ctrl_write_finish): New. + +2012-05-17 Niibe Yutaka + + * tool/gnuk_upgrade.py: New tool. + + * src/gnuk.h (ICC_STATE_EXITED, ICC_STATE_EXEC_REQUESTED): New. + + * src/openpgp.c (INS_EXTERNAL_AUTHENTICATE) + (cmd_external_authenticate): New. + (INS_GET_CHALLENGE, cmd_get_challenge): New. + + * src/usb-icc.c (USBthread): Finish the thread with + ICC_STATE_EXITED, after EXTERNAL_AUTHENTICATE. + + * src/usb_prop.c (gnuk_setup_endpoints_for_interface): Add STOP + argument. + (gnuk_usb_event): Disable all endpoints when configure(0). + (vcom_port_data_setup): Check direction and support + USB_CDC_REQ_SET_LINE_CODING. + (vcom_port_setup_with_nodata): Check direction. + (gnuk_setup_with_data): Check direction and add FSIJ_GNUK device + requests. + (gnuk_setup_with_nodata): Likewise. + + * src/usb_lld.c (LAST_OUT_DATA): Remove. + (handle_datastage_out): Cleanup and call st103_ep_set_rxtx_status. + (handle_datastage_in): Call st103_ep_set_rxtx_status and + st103_ep_set_tx_status. + (handle_setup0): Likewise. + (handle_out0): Remove LAST_OUT_DATA. + (std_none, std_get_status, std_clear_feature, std_set_feature) + (std_set_address, std_get_descriptor, std_get_configuration) + (std_set_configuration, std_get_interface, std_set_interface): + Check direction. + (handle_setup0): Add length for setup_with_data. + +2012-05-16 Niibe Yutaka + + * tool/gnuk_put_binary.py (main): Fix fileid. + * tool/gnuk_put_binary_libusb.py: Ditto. + + * src/openpgp.c (FILE_EF_RANDOM): Remove. + (cmd_update_binary, cmds): ifdef CERTDO_SUPPORT. + (cmd_write_binary): Fix fileid. + + * src/flash.c (flash_check_blank): Always enable. + (flash_erase_binary): ifdef CERTDO_SUPPORT. + (flash_write_binary): Call flash_check_blank. + +2012-05-15 Niibe Yutaka + + * Version 0.18. + + * src/usb_desc.c (gnukStringSerial): Updated. + + * src/main.c (EP3_IN_Callback, EP5_OUT_Callback): Move from + usb_endp.c. + + * src/usb_endp.c: Remove. + +2012-05-14 Niibe Yutaka + + * tool/gnuk_remove_keys.py: New. + + * src/openpgp-do.c (proc_key_import): Fix checking extended header. + + * src/hardclock.c: Remove. + + * src/usb_prop.c (MSC_INTERFACE_NO): New. + (gnuk_setup_endpoints_for_interface): Cleanup with MSC_INTERFACE_NO. + (gnuk_setup_with_data, gnuk_setup_with_nodata): Likewise. + + * src/usb-msc.c: Rename from usb_msc.c. + + * src/usb-msc.h: Rename from usb_msc.h. + + * src/Makefile.in: Follow the rename of usb-msc.c and remove of + hardclock.c. + + * src/pin-dnd.c, src/usb_prop.c: Follow the rename of usb-msc.h. + +2012-05-12 Niibe Yutaka + + * src/usb_msc.c (ep6_out): Rename (was: ep7_out). + (usb_start_receive): Use ep6_out and ENDP6. + (EP6_OUT_Callback): Rename (was: EP7_OUT_Callback). + Use ep6_out and ENDP6. + (msc_handle_command): Use ep6_out and ENDP6. + + * src/main.c (main): Wait USB reset. + + * src/usb-icc.c (EP1_OUT_Callback): Rename from EP2_OUT_Callback. + (USBthread): Use ENDP1 for both of epi_init and epo_init. + + * src/usb_conf.h (ENDP1_RXADDR, ENDP2_TXADDR, ENDP6_RXADDR): New. + (ENDP3_TXADDR, ENDP4_TXADDR, ENDP5_RXADDR): New value. + (ENDP7_RXADDR): Remove. + + * src/usb_desc.c (gnukConfigDescriptor): Use endpoint OUT1 (was + IN2), endpoint OUT6 (IN7). + + * src/usb_prop.c (gnuk_setup_endpoints_for_interface): Use ENDP1 + and ENDP6 for both directions. + +2012-05-11 Niibe Yutaka + + * src/configure (--vidpid): New mandatory option. + + * GNUK_USB_DEVICE_ID: New file. + + * src/usb_desc.c (gnukDeviceDescriptor): Include + usb-vid-pid-ver.c.inc. + (gnukStringVendor, gnukStringProduct): Remove. It's in the + file, usb-string-vender-product.c.inc. + + * src/Makefile.in (distclean): Delete *.inc. + + * src/usb_prop.c (vcom_port_setup_with_nodata) Rename. + (vcom_port_data_setup): Rename and fix return value. + + * src/usb-cdc.h (VIRTUAL_COM_PORT_DATA_SIZE) + (VIRTUAL_COM_PORT_INT_SIZE): New. + + * src/main.c (#include): Add usb-cdc.h. + * src/usb_desc.c (#include): Add usb-cdc.h. + * src/usb_endp.c (#include): Add usb_lld.h. + + * src/configure ($help): Add FST_01. + +2012-05-10 Niibe Yutaka + + * STM32_USB-FS-Device_Driver, Virtual_COM_Port: Remove. + + * src/usb_lld.c (#include): Don't include usb_lib.h. + (RECIPIENT, REG_BASE PMA_ADDR, CNTR, ISTR, FNR, DADDR, BTABLE) + (ISTR_CTR, ISTR_DOVR, ISTR_ERR, ISTR_WKUP, ISTR_SUSP, ISTR_RESET) + (ISTR_SOF, ISTR_ESOF, ISTR_DIR, ISTR_EP_ID, CLR_CTR, CLR_DOVR) + (CLR_ERR, CLR_WKUP, CLR_SUSP, CLR_RESET, CLR_SOF, CLR_ESOF) + (CNTR_CTRM, CNTR_DOVRM, CNTR_ERRM, CNTR_WKUPM, CNTR_SUSPM) + (CNTR_RESETM, CNTR_SOFM, CNTR_ESOFM, CNTR_RESUME, CNTR_FSUSP) + (CNTR_LPMODE, CNTR_PDWN, CNTR_FRES, DADDR_EF, DADDR_ADD) + (EP_CTR_RX, EP_DTOG_RX, EPRX_STAT, EP_SETUP, EP_T_FIELD, EP_KIND) + (EP_CTR_TX, EP_DTOG_TX, EPTX_STAT, EPADDR_FIELD, EPREG_MASK) + (EP_TX_DIS, EP_TX_STALL, EP_TX_NAK, EP_TX_VALID, EPTX_DTOG1) + (EPTX_DTOG2, EP_RX_DIS, EP_RX_STALL, EP_RX_NAK, EP_RX_VALID) + (EPRX_DTOG1, EPRX_DTOG2): New. Compatible to ST's USB-FS-Device_Lib. + (CH_IRQ_HANDLER): Call usb_interrupt_handler (was: USB_Istr). + (EP1_IN_Callback, EP2_IN_Callback, EP3_IN_Callback) + (EP4_IN_Callback, EP5_IN_Callback, EP6_IN_Callback) + (EP7_IN_Callback, EP1_OUT_Callback, EP2_OUT_Callback) + (EP3_OUT_Callback, EP4_OUT_Callback, EP5_OUT_Callback) + (EP6_OUT_Callback, EP7_OUT_Callback): New. Implement here. + Compatible to ST's USB-FS-Device_Lib. + (USB_MAX_PACKET_SIZE): New. + (GET_STATUS, CLEAR_FEATURE, RESERVED1, SET_FEATURE, RESERVED2) + (SET_ADDRESS, GET_DESCRIPTOR, SET_DESCRIPTOR, GET_CONFIGURATION) + (SET_CONFIGURATION, GET_INTERFACE, SET_INTERFACE) + (SYNCH_FRAME,TOTAL_REQUEST): New for USB control transfer. + (enum CONTROL_STATE): New for state machine of control pipe. + (enum FEATURE_SELECTOR): New. + (struct DATA_INFO, struct CONTROL_INFO, struct DEVICE_INFO): New. + (ctrl_p, dev_p, data_p, Control_Info, Device_Info, Data_Info): + New. + (usb_lld_stall_tx, usb_lld_stall_rx) + (usb_lld_tx_data_len, usb_lld_txcpy, usb_lld_tx_enable) + (usb_lld_write, usb_lld_rx_enable, usb_lld_rx_data_len) + (usb_lld_rxcpy): Move from usb_lld.h and not inline. + (usb_lld_reset, usb_lld_setup_endpoint) + (usb_lld_set_configuration, usb_lld_current_configuration) + (usb_lld_set_feature, usb_lld_set_data_to_send): New. + (usb_lld_to_pmabuf, usb_lld_from_pmabuf): Clean up. + (usb_lld_init): New implementation. + (st103_set_btable, st103_get_istr, st103_set_istr, st103_set_cntr) + (st103_set_daddr, st103_set_epreg, st103_get_epreg) + (st103_set_tx_addr, st103_get_tx_addr, st103_set_tx_count) + (st103_get_tx_count, st103_set_rx_addr, st103_get_rx_addr) + (st103_set_rx_buf_size, st103_get_rx_count, st103_ep_clear_ctr_rx) + (st103_ep_clear_ctr_tx, st103_ep_set_rxtx_status) + (st103_ep_set_rx_status, st103_ep_get_rx_status) + (st103_ep_set_tx_status, st103_ep_get_tx_status) + (st103_ep_clear_dtog_rx, st103_ep_clear_dtog_tx): New lower-level + functions for USB related registers access. + (usb_interrupt_handler, usb_handle_transfer) + (handle_datastage_out, handle_datastage_in, handle_setup0) + (handle_in0, handle_out0) + (std_none, std_get_status, std_clear_feature, std_set_feature, + std_set_address, std_get_descriptor, std_get_configuration, + std_set_configuration, std_get_interface, std_set_interface) + (std_request_handler): New USB stack implementation. + + * src/usb_lld.h (usb_lld_stall_tx, usb_lld_stall_rx) + (usb_lld_tx_data_len, usb_lld_txcpy, usb_lld_tx_enable) + (usb_lld_write, usb_lld_rx_enable, usb_lld_rx_data_len) + (usb_lld_rxcpy): Those are not inline functions anymore. + (USB_DEVICE_DESCRIPTOR_TYPE, USB_CONFIGURATION_DESCRIPTOR_TYPE) + (USB_STRING_DESCRIPTOR_TYPE, USB_INTERFACE_DESCRIPTOR_TYPE) + (USB_ENDPOINT_DESCRIPTOR_TYPE, STANDARD_ENDPOINT_DESC_SIZE) + (ENDP0, ENDP1, ENDP2, ENDP3, ENDP4, ENDP5, ENDP6, ENDP7) + (EP_BULK, EP_CONTROL, EP_ISOCHRONOUS, EP_INTERRUPT) + (DEVICE_RECIPIENT, INTERFACE_RECIPIENT, ENDPOINT_RECIPIENT) + (ENDPOINT_RECIPIENT, OTHER_RECIPIENT) + (DEVICE_DESCRIPTOR, CONFIG_DESCRIPTOR, STRING_DESCRIPTOR) + (INTERFACE_DESCRIPTOR, ENDPOINT_DESCRIPTOR) + (REQUEST_TYPE, STANDARD_REQUEST, CLASS_REQUEST, VENDOR_REQUEST) + (USB_UNSUPPORT, USB_SUCCESS) + (USB_EVENT_RESET, USB_EVENT_ADDRESS, USB_EVENT_CONFIG) + (USB_EVENT_SUSPEND, USB_EVENT_WAKEUP, USB_EVENT_STALL) + (USB_SET_INTERFACE, USB_GET_INTERFACE, USB_QUERY_INTERFACE) + (UNCONNECTED, ATTACHED, POWERED, SUSPENDED, ADDRESSED) + (CONFIGURED, USB_Cable_Config): New. Compatible to ST's + USB-FS-Device_Lib. + (struct Descriptor, struct usb_device_method) + (Device_Descriptor, Config_Descriptor, String_Descriptors) + (STM32_USB_IRQ_PRIORITY, bDeviceState, Device_Method) + (usb_lld_init, usb_lld_reset, usb_lld_setup_endpoint) + (usb_lld_set_configuration, usb_lld_current_configuration) + (usb_lld_set_feature, usb_lld_set_data_to_send): New API. + + * src/usb_prop.c(#include): Only include usb_lld.h for USB. + (SetEPRxCount_allocated_size): Remove. + (struct line_coding, line_coding, Virtual_Com_Port_Data_Setup) + (Virtual_Com_Port_NoData_Setup): Add from usb-cdc-vport.c. + (gnuk_device_init, gnuk_device_reset, gnuk_setup_with_data) + (gnuk_setup_with_nodata): Follow the API change of USB stack. + (gnuk_setup_endpoints_for_interface, gnuk_get_descriptor) + (gnuk_usb_event, gnuk_interface): New. + (gnuk_device_SetConfiguration, gnuk_device_SetInterface) + (gnuk_device_SetDeviceAddress, gnuk_device_Status_In) + (gnuk_device_Status_Out, gnuk_device_GetDeviceDescriptor) + (gnuk_device_GetConfigDescriptor, gnuk_device_GetStringDescriptor) + (gnuk_device_Get_Interface_Setting, gnuk_clock_frequencies) + (gnuk_data_rates, msc_lun_info, Device_Table) + (User_Standard_Requests): Remove. + (Device_Method): Replace Device_Property. + + * src/usb_msc.c (#include): Only include usb_lld.h for USB. + + * src/usb_endp.c (#include): Only include usb_lld.h for USB. + (EP5_OUT_Callback): Follow the API change of USB stack. + + * src/usb_desc.c (#include): Only include usb_lld.h for USB. + Add usb_conf.h. + (Device_Descriptor, Config_Descriptor): Follow the API change + of USB stack. + (String_Descriptors): New, rename from String_Descriptor. + + * src/usb_conf.h (EP_NUM, BTABLE_ADDRESS, IMR_MSK): Remove. + (NUM_STRING_DESC): Add. + + * src/usb-icc.c (#include): Only include usb_lld.h for USB. + + * src/usb-cdc-vport.c, src/usb_prop.h: Remove. + + * src/stmusb.mk, src/vcomport.mk: Remove. + + * src/main.c (#include): Only include usb_lld.h for USB. + (main): Remove call to USB_Init. + + * src/Makefile.in (include): Remove stmusb.mk, vcomport.mk. + (VCOMSRC) [ENABLE_VCOMPORT]: Add. + (INCDIR): Remove STMUSBINCDIR and VCOMDIR. + + * boards/common/hw_config.c (Enter_LowPowerMode) + (Leave_LowPowerMode): Remove. + +2012-02-02 Niibe Yutaka + + * Version 0.17. + + * src/usb_desc.c (gnukStringSerial): Updated. + (gnukConfigDescriptor): Short APDU only. + + * tool/gnuk_put_binary.py (cmd_get_response): New. + (cmd_select_openpgp, cmd_get_data): Call cmd_get_response. + +2012-01-30 Niibe Yutaka + + * src/usb-icc.c (struct ccid): Add chained_cls_ins_p1_p2. + (end_cmd_apdu_head, icc_cmd_apdu_data, icc_handle_data): Add checking + CMD APDU head for command chaining. + +2012-01-20 Niibe Yutaka + + Short APDU only CCID driver. + * STM32_USB-FS-Device_Driver/src/usb_core.c (DataStageOut) + (DataStageIn): Use usb_lld_to_pmabuf and usb_lld_from_pmabuf. + + * src/configure (CERTDO_SUPPORT): Comment fix. + + * src/gnuk.h (struct adpu): expected_res_size has type uint16_t. + (MAX_CMD_APDU_DATA_SIZE, MAX_RES_APDU_DATA_SIZE): New. + (MAX_CMD_APDU_SIZE, MAX_RES_APDU_SIZE, USB_BUF_SIZE): Remove. + (icc_state_p): New. + (set_res_sw): Rename from set_res_apdu. + + * src/call-rsa.c (rsa_decrypt): Use MAX_RES_APDU_DATA_SIZE. + + * src/openpgp.c (set_res_sw): Rename from set_res_apdu. + * src/openpgp.h: Use set_res_sw. + + * src/main.c: Handle icc_state_p. + + * src/openpgp-do.c (historical_bytes): command chaining but short + APDU only. + (extended_capabilities): Change for short APDU only. + + * src/usb-icc.c (USB_BUF_SIZE): Define here (was in gnuk.h). + (struct ep_in, epi_init, struct ep_out, epo_init, endpoint_out) + (endpoint_in, icc_state_p, struct ccid, APDU_STATE_WAIT_COMMAND) + (APDU_STATE_COMMAND_CHAINING, APDU_STATE_COMMAND_RECEIVED) + (APDU_STATE_RESULT, APDU_STATE_RESULT_GET_RESPONSE, ccid_reset) + (ccid_init, CMD_APDU_HEAD_SIZE, apdu_init, notify_tx, no_buf) + (set_sw1sw2, get_sw1sw2, notify_icc, end_icc_rx, end_abdata) + (end_cmd_apdu_head, end_nomore_data, end_cmd_apdu_data) + (nomore_data, INS_GET_RESPONSE, icc_cmd_apdu_data, icc_abdata) + (icc_send_data_block_0x9000, icc_send_data_block_gr, ccid): New. + (icc_data_size, icc_seq, icc_next_p, icc_chain_p, icc_tx_size) + (icc_thread, icc_state, gpg_thread, ICC_RESPONSE_MSG_DATA_SIZE): + Remove. + (EP1_IN_Callback): Rewrite using epi. + (EP2_OUT_Callback): Rewrite using epo. + (icc_prepare_receive): Rewrite using epo and struct ccid. + (ATR): Change ofr short APDU only. + (icc_error, icc_power_on, icc_send_status, icc_power_off) + (icc_send_data_block, icc_send_params, icc_handle_data) + (icc_handle_timeout, USBthread): Rewrite using struct ccid. + + * src/usb_desc.c (gnukConfigDescriptor): dwFeatures: Short APDU + level, dwMaxCCIDMessageLength: 271. + + * src/usb_lld.c (usb_lld_to_pmabuf, usb_lld_from_pmabuf): New. + * src/usb_lld.h (usb_lld_txcpy, void usb_lld_write) Use + usb_lld_to_pmabuf. + (usb_lld_rxcpy): Use usb_lld_from_pmabuf. + + * src/stmusb.mk (usb_mem.c): Remove. + + * gnuk_put_binary.py (cmd_select_openpgp): No response APDU data. + (cmd_verify, cmd_write_binary): Send short APDU. + (__main__): Remove RANDOM_NUMBER_BITS support. + + Bug fix for CERTDO_SUPPORT. + * src/gnuk.ld.in: Add missing alignment for _data_pool (when no + CERTDO_SUPPORT). + +2012-01-19 Niibe Yutaka + + * src/usb-icc.c (icc_handle_data): Handle the case when it only + sends 0x90 and 0x00 correctly. + + * src/openpgp-do.c (gpg_do_get_data): Fix res_apdu_data_len. + +2012-01-18 Niibe Yutaka + + Clean up API between application layer and CCID layer. + * tool/gnuk_put_binary.py, gnuk_put_binary_libusb.py: Don't append + 0x9000 at the data, any more. + * src/usb-icc.c (icc_data_size, icc_buffer, icc_seq): Make them + internal. + (res_APDU_size, res_APDU_pointer): Removed. + (icc_handle_data, USBthread): Follow new API of struct apdu. + * src/call-rsa.c (rsa_sign, rsa_decrypt): Likewise. + * src/openpgp.c (CLS, INS, P1, P2): New. + (set_res_apdu, cmd_verify, cmd_change_password) + (cmd_reset_user_password, cmd_put_data, cmd_pgp_gakp) + (cmd_read_binary, cmd_select_file, cmd_pso) + (cmd_internal_authenticate, cmd_update_binary, cmd_write_binary) + (process_command_apdu, GPGthread): Follow new API of struct apdu. + * src/openpgp-do.c (gpg_do_get_data, gpg_do_public_key): Follow + new API of struct apdu. + * src/gnuk.h (struct apdu, apdu): New. + (cmd_APDU, icc_data_size, cmd_APDU_size, icc_buffer): Removed. + (res_APDU, res_APDU_size): Use members of struct apdu. + +2012-01-16 Niibe Yutaka + + Adopt new USB API. + * src/usb_msc.c (usb_start_transmit): Use usb_lld_write. + (EP6_IN_Callback): Use usb_lld_tx_data_len and usb_lld_write. + (usb_start_receive): Use usb_lld_rx_enable. + (EP7_OUT_Callback): Use usb_lld_rx_data_len, usb_lld_rxcpy + and usb_lld_rx_enable + (msc_handle_command): Use usb_lld_stall_rx and usb_lld_stall_tx. + + * src/usb_lld.h (usb_lld_stall_tx, usb_lld_stall_rx) + (usb_lld_tx_data_len): New. + + * src/main.c (STDOUTthread): Use usb_lld_write. + + * src/usb-icc.c (EP1_IN_Callback, icc_error, icc_power_on) + (icc_send_status, icc_send_data_block, icc_send_params): Use + usb_lld_write (was: USB_SIL_Write). + (EP2_OUT_Callback): Use usb_lld_rx_data_len, usb_lld_rxcpy, + and usb_lld_rx_enable (was: USB_SIL_Read and SetEPRxValid). + (icc_prepare_receive): Use usb_lld_rx_enable. + + * src/stmusb.mk (STMUSBSRC): Dont' include usb_sil.c. + + * src/usb_lld.h (usb_lld_txcpy, usb_lld_tx_enable) + (usb_lld_write, usb_lld_rx_enable, usb_lld_rx_data_len) + (usb_lld_rxcpy): New. + + * src/usb_prop.c (SetEPRxCount_allocated_size): Fix the + implementation. (ST's SetEPRxCount is actually meant to + setup allocated size, which is confusing). + (gnuk_device_init): Don't call USB_SIL_Init. + +2012-01-10 Niibe Yutaka + + * src/openpgp.c (GPGthread): Allow INS_RESET_RETRY_COUNTER and + INS_PUT_DATA for pinentry targets. + +2012-01-05 Niibe Yutaka + + * src/openpgp.c (cmd_select_file): Check DF name. + + * tool/pinpadtest.py: Rename from pinpad-test.py. + +2011-12-28 Niibe Yutaka + + * src/usb_prop.c (SetEPRxCount_allocated_size): New. + (gnuk_device_reset): Use SetEPRxCount_allocated_size. + * src/usb_msc.c (usb_start_receive): Don't set RxCount register + here. + * STM32_USB-FS-Device_Driver/src/usb_core.c (Standard_ClearFeature) + (Post0_Process): Don't need to set RxCount register. + + * src/usb_prop.c (msc_lun_info) [PINPAD_DND_SUPPORT]: ifdef-out. + + * src/usb-icc.c (EP2_OUT_Callback): Fix apdu size == 49 bug, + we don't assume host sends ZLP (But accepts ZLP, just in case). + +2011-12-22 Niibe Yutaka + + * src/openpgp-do.c (extended_capabilities) [CERTDO_SUPPORT]: + conditionalize. + +2011-12-21 Niibe Yutaka + + * src/openpgp-do.c (gpg_do_get_data) [CERTDO_SUPPORT]: ifdef out. + + * src/gnuk.ld.in (.gnuk_ch_certificate): Only valid + when --enable-certdo. + + * src/flash.c (flash_check_blank) [CERTDO_SUPPORT]: ifdef out. + (flash_erase_binary) [CERTDO_SUPPORT]: Likewise. + (flash_write_binary) [CERTDO_SUPPORT]: Likewise. + + * src/configure (certdo): New. + (--enable-certdo, --disable-certdo): New options. + Remove cheking for /dev/random. + + * src/config.h.in (@CERTDO_DEFINE@): New. + +2011-12-20 Niibe Yutaka + + * src/usb_msc.c (msc_handle_command): SCSI_START_STOP_UNIT command + with stop/eject/close means cancelling pinentry. + + * src/pin-dnd.c (pinpad_finish_entry, parse_directory_sector): + Implement "cancel". + (pinpad_getline): Likewise. + (msc_scsi_stop): New. + +2011-12-16 Niibe Yutaka + + * tool/gnuk_put_binary_libusb.py (gnuk_token.cmd_select_openpgp): + Fix apdu parameter. + + * tool/gnuk_put_binary.py (GnukToken.cmd_select_openpgp): Ditto. + + * tool/pinpad-test.py: New. + +2011-12-14 Niibe Yutaka + + * Version 0.16. + + * src/usb_desc.c (gnukStringSerial): Updated. + + * boards/STM8S_DISCOVERY/board.h, board.c: Fix for PINPAD_SUPPORT. + * boards/STBEE_MINI/board.h, board.c: Likewise. + * boards/STBEE/board.h, board.c: Likewise. + * boards/FST_01/board.c: Likewise. + +2011-12-13 Niibe Yutaka + + Add pinpad DND support. + * src/Makefile.in (CSRC) [ENABLE_PINPAD]: Add usb_msc.c. + * src/configure (pinpad): Add dnd support. + * src/gnuk.h [PINPAD_DND_SUPPORT]: Add declarations. + * src/main.c (STDOUTthread): Add PUSH packet. + (main) [PINPAD_DND_SUPPORT]: Call msc_init. + * src/usb_conf.h (EP_NUM): Add the case of PINPAD_DND_SUPPORT. + (ENDP6_TXADDR, ENDP7_RXADDR): New. + (ENDP4_TXADDR, ENDP5_RXADDR): Changed for smaller buffer. + * src/usb_desc.c (gnukConfigDescriptor): Add Mass storage device. + * src/usb_msc.c, src/usb_msc.h, src/pin-dnd.c: New. + * src/usb_prop.c: Include "usb_msc.h". + (gnuk_device_reset): Add initialization of ENDP6 and ENDP7. + (gnuk_device_SetInterface): Add initialization of ENDP6 and ENDP7. + (NUM_INTERFACES): Handle cases for PINPAD_DND_SUPPORT. + (msc_lun_info): New. + (gnuk_setup_with_data, gnuk_setup_with_nodata): Handle standard + request for Mass storage device. + * Virtual_COM_Port/usb_desc.h (VIRTUAL_COM_PORT_DATA_SIZE): Since + there isn't enough hardware buffer, smaller value (was: 64). + + * src/ac.c (verify_user_0): Add access argument. + (verify_pso_cds, verify_other, verify_admin_0): Follow the change. + * src/openpgp.c (cmd_change_password): Likewise. + +2011-12-08 Niibe Yutaka + + * src/usb-icc.c: Not include "usb_desc.h". + + * src/usb_endp.c (EP5_OUT_Callback): Fix minor bug. + +2011-12-07 Niibe Yutaka + + * src/usb_desc.c (gnukDeviceDescriptor): Changed bcdUSB = 1.1. + Gnuk device conforms to USB 2.0 full speed device, but when it was + 2.0, some OS informs users, "you can connect the device to 2.0 + compliant hub so that it can have better bandwidth", which is not + the case for full speed device. + + * src/openpgp.c (GPGthread): Handle bConfirmPIN parameter. + + * src/usb-icc.c (icc_handle_data): Pass PC_to_RDR_Secure + information to gpg_thread using memory of cmd_APDU. + +2011-12-01 Niibe Yutaka + + * src/gnuk.h (EV_PINPAD_INPUT_DONE, EV_NOP, EV_CMD_AVAILABLE) + (EV_VERIFY_CMD_AVAILABLE, EV_MODIFY_CMD_AVAILABLE): New. + * src/usb-icc.c (icc_power_off, icc_handle_data): Use EV_NOP, + EV_CMD_AVAILABLE, EV_VERIFY_CMD_AVAILABLE, and EV_MODIFY_CMD_AVAILABLE. + * src/pin-cir.c (cir_timer_interrupt): Use EV_PINPAD_INPUT_DONE. + * src/pin-dial.c (dial_sw_interrupt, pinpad_getline): Ditto. + (EV_SW_PUSH): Remove. + + * src/openpgp.h (GPG_FUNCTION_NOT_SUPPORTED): New. + (GPG_CONDITION_NOT_SATISFIED): New. + * src/openpgp.c (cmd_change_password): Use GPG_FUNCTION_NOT_SUPPORTED. + + * src/openpgp.c (cmd_verify, cmd_change_password) + (cmd_reset_user_password, cmd_put_data): Remove pinpad handling... + (GPGthread): ... and implement pinpad handling here. + +2011-11-29 Niibe Yutaka + + * src/openpgp.c (cmd_put_data) [PINPAD_SUPPORT]: Support pinpad + input (for reset code). + +2011-11-24 Niibe Yutaka + + * Version 0.15. + * src/usb_desc.c (gnukStringSerial): Updated. + +2011-11-22 Niibe Yutaka + + * tool/dfuse.py (DFU_STM32.download, DFU_STM32.verify): Support + unaligned write and hole. + +2011-11-14 Niibe Yutaka + + * boards/FST_01/{mcuconf.h,board.h,board.c}: New. + +2011-11-01 Niibe Yutaka + + * src/pin-dial.c (pinpad_getline): New. + (pin_main): Remove. + + * boards/STBEE_MINI/board.h (TIMx): Define. + boards/STBEE/board.h (TIMx): Ditto. + boards/STM8S_DISCOVERY/board.h: Ditto. + + * src/pin-cir.c (pinpad_getline): New. + (cir_timer_interrupt, cir_ext_interrupt): Use TIMx. + (cir_key_is_backspace, cir_key_is_enter, pin_main, pindisp): + Remove. + (cir_codetable_dell_mr425, cir_codetable_aquos) + (cir_codetable_regza, cir_codetable_bravia, ch_is_backspace) + (ch_is_enter, find_char_codetable, hex, cir_getchar): New. + (cir_timer_interrupt): Don't filter out ADDRESS. + + * src/openpgp.c (get_pinpad_input): Don't invoke thread, + but just call pinpad_getline. + + * src/main.c (display_interaction, display_fatal_code) + (display_status_code, led_blink): New. + (main): Call display_* routine. + (fatal): Notify main thread. + * src/usb_prop.c (gnuk_device_SetConfiguration): Notify main + thread. + + * src/pin-cir.c (pindisp): Remove. + + * boards/FST_01_00: New (for 8MHz FST-01). + + * src/ac.c (calc_md): Fix comparison. + + * src/call-rsa.c (RSA_SIGNATURE_LENGTH): Use KEY_CONTENT_LEN. + (rsa_sign, rsa_decrypt): Likewise. + (modulus_calc): Don't assume it's 2048-bit. + + * src/ac.c (verify_user_0): Fix for non-initialized PW1. + + * src/Makefile.in (MCFLAGS): Override MCFLAGS option for newer + GCC of summon-arm-toolchain to add -mfix-cortex-m3-ldrd. + NOTE: This should not be needed (as -mcpu=cortex-m3 defaults + to -mfix-cortex-m3-ldrd for GCC-proper), but it is needed + to select arm-none-eabi/lib/thumb2/libc.a correctly. + +2011-10-14 NIIBE Yutaka + + * src/gnuk.ld.in (__main_stack_size__): It's 1KB (was 512 byte). + +2011-10-07 NIIBE Yutaka + + * Version 0.14. + * src/usb_desc.c (gnukStringSerial): Updated. + + * src/random.c (random_init): Call neug_prng_reseed. + +2011-10-06 NIIBE Yutaka + + * src/Makefile.in (random_bits): Remove. + + * src/openpgp.c (GPGthread): Remove unused event message. + + * src/main.c (main): Call random_init. + + * src/gnuk.ld.in (__process_stack_size__): Fix. + (.gnuk_random): Removed. + + * src/flash.c (flash_erase_binary, flash_write_binary): Remove + support of random_byte in flash ROM. + + * src/neug.c (adccb): Use old API (was: chEvtSignalFlagsI). + (adccb_err): Remove. + (rng_gen, rng): Add the last argument adccb for adcStartConversion: + This is old API of ADC driver. + (adcgrpcfg): Remove callbacks, add CONT and SWSTART: This is old + API of ADC driver. + (adccb): Remove the first argument: This is old API of ADC driver. + (neug_wait_full): New. + + * ChibiOS_2.0.8/os/hal/platforms/STM32/adc_lld.h (ADC_SAMPLE_1P5): + Add (from new API). + + * src/random.c (random_init): New. + (random_bytes_get, random_bytes_free, get_salt): Use NeuG. + + * src/Makefile.in (CSRC): Add neug.c. + + * src/neug.c: New. Verbatim copy of NeuG/src/random.c. + + * boards/common/mcuconf-common.h (USE_STM32_ADC1): TRUE for NewG RNG. + * src/chconf.h (CH_USE_SEMAPHORES): TRUE as ADC driver requires it. + * src/halconf.h (CH_HAL_USE_ADC); TRUE for NewG RNG. + +2011-07-22 NIIBE Yutaka + + * boards/OLIMEX_STM32_H103/board.h (BOARD_NAME): Fixed. + + * boards/STBEE_MINI/mcuconf.h: Added missing include of + mcuconf-common.h. + +2011-07-04 NIIBE Yutaka + + * ChibiOS_2.0.8/os/ports/GCC/ARMCMx/chcore_v7m.c + (_port_irq_epilogue, _port_switch_from_isr): Apply a patch of 2.2.6. + + * ChibiOS_2.0.8/os/hal/platforms/STM32/adc_lld.h: Apply a patch of + ADC from the branch of ChibiOS_2.0.X. + +2011-06-15 NIIBE Yutaka + + * Version 0.13. + * src/usb_desc.c (gnukStringSerial): Updated. + +2011-06-08 NIIBE Yutaka + + * polarssl-0.14.0/include/polarssl/bn_mul.h [__arm__] + (MULADDC_1024_CORE, MULADDC_1024_LOOP): New. + * polarssl-0.14.0/library/bignum.c (mpi_mul_hlp): Use + MULADDC_1024_LOOP. + +2011-05-31 NIIBE Yutaka + + * polarssl-0.14.0/include/polarssl/bn_mul.h [__arm__] + (MULADDC_HUIT, MULADDC_INIT, MULADDC_CORE, MULADDC_STOP): Tweak. + +2011-05-27 NIIBE Yutaka + + * tool/gnuk_put_binary.py (main): Confirm Serial ID is written + correctly. + + * src/openpgp.c (cmd_write_binary): Fix FILE_EF_SERIAL comparison. + + * src/gnuk.ld.in (.gnuk_random, .gnuk_ch_certificate): Put LONG to + have CONTENTS. + + * polarssl-0.14.0/include/polarssl/bn_mul.h [__arm__] + (MULADDC_HUIT): New. + +2011-05-26 NIIBE Yutaka + + * polarssl-0.14.0/include/polarssl/bn_mul.h [__arm__] + (MULADDC_INIT): Add ADDS instruction to clear of carry flag. + (MULADDC_CORE): Tune to 6 instructions and less registers. + (MULADDC_STOP): Add ADC instruction to save carry flag. + +2011-05-25 NIIBE Yutaka + + * tool/hub_ctrl.py: New. Port of original C implementation. + +2011-05-16 NIIBE Yutaka + + * src/main.c (main): Call flash_unlock at the beginning. + (device_initialize_once): Don't call flash_unlock here. + * src/flash.c (flash_init): Likewise. + + * src/openpgp.c (cmd_select_file): Don't use write_res_apdu. + (set_res_apdu): Rename from write_res_apdu. Just SW1 and SW2. + +2011-05-13 NIIBE Yutaka + + * Version 0.12. + +2011-05-12 NIIBE Yutaka + + * src/openpgp.c (cmd_pso, cmd_internal_authenticate) + (cmd_update_binary, cmd_write_binary): Don't check pw locked. + + * tool/dfuse.py (DFU_STM32.verify): Add missing colon. + * tool/dfuse.py (get_device): Restrict to STMicro DfuSe. + + * tool/gnuk_put_binary.py (main): Add -p option to enter password. + + * src/ac.c (verify_user_0): New. + (verify_pso_cds, verify_admin_0): Use verify_user_0. + * src/openpgp.c (cmd_change_password): Use verify_user_0. + + * src/random.c (get_salt): Rename from get_random. + (random_bytes_get, random_bytes_free): It's 16-byte. + + * src/ac.c (verify_admin_0): Use PW_ERR_PW1 counter when + authenticated by PW1. + +2011-05-11 NIIBE Yutaka + + * src/ac.c (verify_pso_cds, verify_other): Fail (with no counter + update) if key is not registered yet. + (verify_admin_0): Compare to OPENPGP_CARD_INITIAL_PW3 when empty + PW3 and non-empty PW1 but signing key is not registered yet. + + * tool/gnuk_put_binary.py: New implementation by pyscard. + + * src/main.c (device_initialize_once): New. + * src/usb_prop.c (gnukStringSerial): Move to... + * src/usb_desc.c (gnukStringSerial): here. Bump version to 0.12. + Fill by 0xff. + * src/usb_prop.c (gnuk_device_init) + (gnuk_device_GetStringDescriptor): Don't use RAM for + gnukStringSerial, use ROM like other string descriptor. + * src/usb_desc.c (String_Descriptor): Add gnukStringSerial. + + * src/openpgp-do.c (gpg_get_pw1_lifetime): Make static. + (gpg_do_load_prvkey, gpg_do_write_prvkey): Use kdi. + (gpg_increment_digital_signature_counter): Call gpg_get_pw1_lifetime. + * src/openpgp.c (cmd_pso): Follow the change. + * src/flash.c (keystore_pool): Remove. Use &_keystore_pool. + * src/ac.c (auth_status): Don't assign 0 as it's automatically + cleared. + +2011-05-10 NIIBE Yutaka + + * src/openpgp-do.c (gpg_pw_locked): Rename from gpg_passwd_locked. + (gpg_pw_get_err_counter): Rename from gpg_get_pw_err_counter. + (gpg_pw_reset_err_counter): Rename from gpg_reset_pw_err_counter. + (gpg_pw_increment_err_counter): Rename from gpg_increment_err_counter. + * src/ac.c, src/openpgp.c, src/gnuk.h: Follow the change. + + Bug fixes. + * src/openpgp.c (cmd_reset_user_password, cmd_change_password) + * src/openpgp-do.c (proc_resetting_code): Fix check of return value. + * src/ac.c (ac_fini): Clear keystring_md_pw3. + + Prevent observation of PW3 is emptiness by PW3's error counter. + Support verify_admin by PW1 when PW3 is empty. + * src/ac.c (admin_authorized): New. + (verify_admin_0): Set admin_authorized. + * src/openpgp-do.c (proc_resetting_code): Use admin_authorized. + (gpg_do_write_prvkey): Clear dek_encrypted_3 when keystring_admin + is NULL. + (proc_key_import): Checking admin_authorized, set keystring_admin. + * src/openpgp.c (cmd_reset_user_password): Use admin_authorized. + +2011-04-18 NIIBE Yutaka + + * gnuk.svg: Updated. + +2011-04-15 NIIBE Yutaka + + * Version 0.11. + + * src/usb_prop.c (gnukStringSerial): Updated. + +2011-04-11 NIIBE Yutaka + + * tool/dfuse.py (DFU_STM32.verify): support data size of non-1-KiB. + +2011-02-24 NIIBE Yutaka + + * src/usb_prop.c (gnuk_device_SetInterface): Fix argument to + ClearDTOG_TX. + +2011-02-10 NIIBE Yutaka + + * Version 0.10. + + * src/configure, src/Makefile.in (BOARD_DIR): New. + + * boards/CQ_STARM/board.mk, boards/OLIMEX_STM32_H103/board.mk: + Removed. + * boards/STBEE/board.mk, boards/STBEE_MINI/board.mk: Removed. + * boards/STM32_PRIMER2/board.mk, boards/STM8S_DISCOVERY/board.mk: + Removed. + + * src/Makefile.in (OUTFILES): Don't include random_bits. + +2011-02-09 NIIBE Yutaka + + * src/usb_prop.c (gnukStringSerial): Updated. + + * tool/gnuk_put_binary.py (gnuk_token.__del__): Removed. + Releasing the interface is done in PyUSB. + + * tool/dfuse.py (DFU_STM32.__del__): Removed. + + * src/openpgp.c (cmd_write_binary): Support random bits and card + holder certificate as well. + + * src/openpgp-do.c (do_openpgpcard_aid): Add volatile to prevent + compiler optimization to access AID. + +2011-02-08 NIIBE Yutaka + + * tool/gnuk_put_binary.py: Renamed (was: gnuk_update_binary.py). + (gnuk_token.cmd_write_binary): New. + (main): Support writing serial number. + + * GNUK_SERIAL_NUMBER: Renamed (was: FSIJ_SERIAL_NUMBER). + + * src/config.h.in (@SERIAL_DEFINE@): Removed. + + * src/gnuk.h (FILEID_SERIAL_NO): New. + + * src/openpgp.c (INS_WRITE_BINARY, cmd_write_binary): New. + + * src/configure: Remove --with-fixed-serial support. + + * src/openpgp-do.c (do_openpgpcard_aid): Remove support of + SERIAL_NUMBER_IN_AID. + + * src/flash.c (flash_write_binary): Support FILEID_SERIAL_NO. + +2011-02-04 NIIBE Yutaka + + * tool/gnuk_update_binary.py: Support updating random bits. + + * src/random.c (random_bits_start): Renamed. + (random_bytes_get): Check initial erased state. + + * src/Makefile.in (random-data.o): Removed. + + * src/gnuk.ld.in (.gnuk_random): Don't have .gnuk_random any more. + + * src/flash.c (flash_erase_binary): Support FILEID_RANDOM. + (flash_write_binary): Ditto. + + * src/openpgp.c (cmd_reset_user_password): Fix PINPAD_SUPPORT case + with reset code. + +2011-02-01 NIIBE Yutaka + + * Version 0.9. + + * src/openpgp-do.c (extended_capabilities): Change value for card + holder certificate. + + * src/usb_prop.c (gnuk_device_SetInterface): New. + +2011-01-29 NIIBE Yutaka + + * src/usb_prop.c (gnuk_device_Get_Interface_Setting): Handle the + case where we have multiple interfaces. + +2011-01-28 NIIBE Yutaka + + * tool/gnuk_update_binary.py: New. + + * src/openpgp-do.c (gpg_do_get_data): Fix length adding two for + status word at the end and adding four for the tag and the length. + + * src/usb-icc.c (icc_handle_data): Fix decrementing res_APDU_size. + (icc_power_off): Status should be the one *after* power off. + + * src/openpgp.c (cmd_update_binary): Fix return code. + +2011-01-27 NIIBE Yutaka + + * src/usb-icc.c (res_APDU_pointer): New. + (icc_handle_data, USBthread): Handle res_APDU_pointer. + + * src/openpgp.h (GPG_COMMAND_NOT_ALLOWED): New. + + * src/openpgp.c (INS_UPDATE_BINARY, FILE_EF_CH_CERTIFICATE) + (FILE_EF_RANDOM, cmd_update_binary): New. + (process_command_apdu): Initialize res_APDU_pointer. + + * src/openpgp-do.c (gpg_do_get_data): Handle GPG_DO_CH_CERTIFICATE. + + * src/gnuk.ld.in (.gnuk_ch_certificate): New. + + * src/flash.c (flash_check_blank, flash_erase_binary) + (flash_write_binary): New. + + * src/openpgp-do.c (gpg_do_table): Exclude GPG_DO_CH_CERTIFICATE. + + * src/openpgp.c (cmd_reset_user_password): Add PINPAD_SUPPORT. + + * src/gnuk.ld.in: Fix alignment and filling. + +2011-01-26 NIIBE Yutaka + + * boards/STBEE/mcuconf.h: New. + * boards/STBEE/board.mk: New. + * boards/STBEE/board.h: New. + * boards/STBEE/board.c: New. + + * tool/dfuse.py (DFU_STM32.verify): Add double ll_clear_status. + + * src/configure (target): Add STBEE. + +2011-01-25 NIIBE Yutaka + + * src/openpgp.c (cmd_pso): Support DigestInfo by MD5 (for opensc). + +2011-01-22 NIIBE Yutaka + + * src/openpgp.c (cmd_pgp_gakp): Handle case of non-extended Lc. + (cmd_select_file): Return DF name when FCI is requested. + + * src/openpgp-do.c (copy_do): Don't add tag if not requested. + + * src/gnuk.h (memmove): Add declaration. + +2011-01-21 NIIBE Yutaka + + * src/openpgp-do.c (copy_do): Fix off-by-one error. + + * src/openpgp.c (get_pinpad_input): Ifdef-out PINPAD_SUPPORT. + +2011-01-19 NIIBE Yutaka + + * Version 0.8. + + * src/pin-cir.c (pin_main): Fix typo, call cir_ext_disable. + + * src/usb_prop.c (gnukStringSerial): Updated. + + * src/pin-dial.c: New. + + * boards/STBEE_MINI/board.c (hwinit1): Add PINPAD_DIAL_SUPPORT. + (dial_sw_disable, dial_sw_enable, EXTI2_IRQHandler): New. + + * src/gnuk.h: Add PINPAD_DIAL_SUPPORT. + + * src/usb-icc.c (icc_handle_data): Handle PIN modification. + + * src/usb_desc.c (gnukConfigDescriptor): bPinSupport = 3 when + PINPAD_DIAL_SUPPORT is enabled. + +2011-01-18 NIIBE Yutaka + + * src/pin-cir.c (pin_main): Call cir_ext_disable at the end. + +2011-01-17 NIIBE Yutaka + + * src/gnuk.h (PIN_INPUT_CURRENT, PIN_INPUT_NEW) + (PIN_INPUT_CONFIRM): New. + + * src/pin-cir.c (pin_main): New argument MSG_CODE. + + * src/openpgp.c (get_pinpad_input): New. + (cmd_verify): Use get_pinpad_input. + (cmd_change_password): Added PINPAD_SUPPORT. + + * src/openpgp.c (cmd_nop): Removed. + + * src/config.h.in: ifdef-out (not for ASSEMBLER). + +2011-01-15 NIIBE Yutaka + + * Version 0.7. + + * src/usb-icc.c (icc_handle_data): Bug fix: add break for case + ICC_STATE_SEND. + +2011-01-14 NIIBE Yutaka + + * Version 0.6. + + * src/usb_prop.c (gnukStringSerial): Include version number (again). + + * boards/STM8S_DISCOVERY/board.c (hwinit1): Initialize TIM3 and + remap TIM3. + (cir_ext_disable, cir_ext_enable, EXTI9_5_IRQHandler) + (TIM3_IRQHandler): New. + + * boards/STBEE_MINI/board.h (HAVE_7SEGLED): New. + + * boards/STM8S_DISCOVERY/board.h: Include "config.h". + (VAL_GPIOBODR): PB0 (TIM3_CH3) is pull-down for PINPAD_SUPPORT. + + * src/pin-cir.c (pindisp): Handle the board with no 7 segment + display. + +2011-01-11 NIIBE Yutaka + + * src/openpgp-do.c (do_openpgpcard_aid): Fix length of res_p; + +2011-01-08 NIIBE Yutaka + + * src/usb-icc.c (icc_handle_data): Handle the case of + ICC_STATE_SEND (back again to the implementation of v0.4). + (USBthread): Don't send back larger block (for libccid 1.3.11). + +2011-01-07 NIIBE Yutaka + + * src/openpgp.c (cmd_read_binary): Call gpg_do_get_data for AID. + + * src/openpgp-do.c (gpg_do_get_data): Added new argument WITH_TAG. + + * src/usb_prop.c (gnuk_device_init) + (gnuk_device_GetStringDescriptor): gnukStringSerial with unique + chip ID. + + * src/openpgp-do.c (do_openpgpcard_aid): New. + (openpgpcard_aid): Removed. + + * boards/common/hw_config.c (unique_device_id): New. + +2011-01-06 NIIBE Yutaka + + * src/config.h.in (PINPAD_MORE_DEFINE): Added. + + * src/configure: Requiring bash (for variable substitution), added + PINPAD. + + * src/Makefile.in: Support PINPAD. + + * src/pin-cir.c (cir_timer_interrupt): Support Sharp protocol. + +2011-01-04 NIIBE Yutaka + + * src/chconf.h (CH_USE_DYNAMIC): It's TRUE now. + + * src/usb_desc.c (gnukConfigDescriptor): Added PINPAD_SUPPORT. + + * src/pin-cir.c (cir_timer_interrupt): Added CIR_PERIOD_INHIBIT_CHATTER. + +2010-12-29 NIIBE Yutaka + + * src/pin-cir.c (cir_timer_interrupt): Support Philips RC-5 protocol. + +2010-12-28 NIIBE Yutaka + + * src/pin-cir.c (cir_timer_interrupt): Support Philips RC-6 protocol. + +2010-12-27 NIIBE Yutaka + + * src/pin-cir.c (cir_timer_interrupt): Support Sony protocol. + +2010-12-24 NIIBE Yutaka + + * src/pin-cir.c: New file. + +2010-12-20 NIIBE Yutaka + + * src/openpgp.c (GPGthread): Added PINPAD_SUPPORT. + * boards/STBEE_MINI/mcuconf.h: Simplified. + * boards/STBEE_MINI/board.h: Include config.h. + (PINPAD_SUPPORT): Added. + * boards/STBEE_MINI/board.c (hwinit1): Added PINPAD_SUPPORT. + +2010-12-15 NIIBE Yutaka + + * src/configure (FLASH_SIZE): Without 'k'. + * src/gnuk.ld.in (MEMORY): Append "k" here. + (.gnuk_flash): End point should be aligned too. + + * src/config.h.in (@PINPAD_DEFINE@): New. + * src/Makefile.in (@PINPAD_MAKE_OPTION@): New. + * src/configure (PINPAD_MAKE_OPTION, PINPAD_DEFINE): New. + +2010-12-14 NIIBE Yutaka + + * src/configure (FLASH_PAGE_SIZE): Always set. + +2010-12-13 NIIBE Yutaka + + * Version 0.5. + + * src/usb_desc.c (gnukStringSerial): Updated. + +2010-12-10 NIIBE Yutaka + + * src/usb-cdc-vport.c (Virtual_Com_Port_Data_Setup) + (Virtual_Com_Port_NoData_Setup): No check for class&interface + request. + + * src/usb-icc.c (ATR): Fixed. + + * src/usb_desc.c (/* ICC Descriptor*/): bcdCCID = 1.1. + dwDefaultClock = dwMaximumClock = 3571. + dwFeatures 0x00040842. + + * src/usb_prop.c (gnuk_clock_frequencies, gnuk_data_rates): New. + (gnuk_nothing_todo): Removed. + (gnuk_setup_with_data, gnuk_setup_with_nodata): New. + (Device_Property): Changed to call gnuk_setup_with_data and + gnuk_setup_with_nodata. + +2010-12-09 NIIBE Yutaka + + * src/usb-icc.c (icc_power_off): Set icc_data_size = 0 to specify + no command APDU. Signal GPGThread. + (icc_handle_data, USBthread): Don't signal main thread any more. + + * src/openpgp.c (GPGthread): Only process the command APDU, if any. + + * src/openpgp-do.c (do_tag_to_nr): Don't call fatal. + * src/main.c (fatal_code): New. + (main): Implemented 1-bit LED status display. + (fatal): Added argument CODE. + * src/flash.c (flash_data_pool_allocate): Supply argument FATAL_FLASH. + * src/random.c (random_bytes_get): Supply argument FATAL_RANDOM. + * src/ac.c (auth_status): Added volatile, and remove static. + +2010-12-08 NIIBE Yutaka + + * src/gnuk.h (AC_OTHER_AUTHORIZED): Renamed (was: + AC_PSO_OTHER_AUTHORIZED). + * src/ac.c (ac_reset_other): Renamed (was: ac_reset_pso_other). + (verify_other): Renamed (was: verify_pso_other). + (ac_reset_admin): New. + * src/openpgp.c (cmd_change_password): Call ac_reset_admin. + + * src/main.c (main): Don't create GPGThread here. + * src/usb-icc.c (icc_power_on): But create here, when requested. + (icc_power_off): Terminate GPGThread. + * src/openpgp.c (gpg_init, gpg_fini): New. + (GPGthread): Check chThdShouldTerminate. Call gpg_init and gpg_fini. + +2010-12-07 NIIBE Yutaka + + USB CCID/ICC implementation changes. + * src/usb_desc.c (dwMaxCCIDMessageLength): Updated. + * src/usb-icc.c (EV_TX_FINISHED): New. + (icc_rcv_data, icc_tx_data): Removed. + (icc_buffer, icc_seq): New. + (icc_next_p, icc_chain_p): New. + (icc_tx_ready): Removed. + (EP1_IN_Callback): Handle multiple transactions. + (icc_prepare_receive): New. + (EP2_OUT_Callback): Handle multiple transactions. + (icc_error, icc_send_status): Handle the case of receive in chain. + (icc_power_on, icc_send_params): Specify it's a single transaction. + (icc_send_data_block_filling_header): New. + (icc_send_data_block): Simplify. + (icc_handle_data): Removed the case of ICC_STATE_SEND. + Handle buffer of multiple transactions. + (USBthread): Don't use sending in chain. + * src/gnuk.h (USB_LL_BUF_SIZE): New. + (USB_BUF_SIZE): Now, it's larger value. + * src/configure: Echo for --enable-debug. + * src/call-rsa.c (rsa_sign): Use temp[] buffer as rsa_pkcs1_sign + writes OUTPUT in early stage. + +2010-12-04 NIIBE Yutaka + + * src/flash.c (flash_keystore_release): Reset keystore storage. + +2010-12-03 NIIBE Yutaka + + Keystore management changes. + * src/flash.c (flash_key_alloc): Check FLASH_KEYSTORE_SIZE. + (flash_key_release): Removed. + (flash_keystore_release): New function. + * src/openpgp-do.c (gpg_do_write_prvkey): Make it static. + When there is a key already, return as error. + (proc_key_import): Call flash_keystore_release when all keys removed. + * src/gnuk.ld.in (_keystore_pool): Size of keystore is now 1.5KB. + +2010-11-30 NIIBE Yutaka + + Flash ROM fixes for STM32F10X_HD. + * src/gnuk.ld.in (.gnuk_flash): Use FLASH_PAGE_SIZE. + * src/configure (FLASH_PAGE_SIZE): Defined for gnuk.ld. + * src/flash.c (FLASH_PAGE_SIZE): New define. + (FLASH_DATA_POOL_SIZE): Use FLASH_PAGE_SIZE. + + Import changes of ChibiOS_2.0.8. + * ChibiOS_2.0.8/os/hal/include/pwm.h + * ChibiOS_2.0.8/os/hal/platforms/STM32/pwm_lld.c + * ChibiOS_2.0.8/os/hal/platforms/STM32/pwm_lld.h + * ChibiOS_2.0.8/os/hal/src/pwm.c + * ChibiOS_2.0.8/os/hal/templates/pwm_lld.c + * ChibiOS_2.0.8/os/hal/templates/pwm_lld.h + * ChibiOS_2.0.8/os/kernel/include/ch.h + * ChibiOS_2.0.8/os/kernel/src/chevents.c + * ChibiOS_2.0.8/os/kernel/src/chthreads.c + * ChibiOS_2.0.8/boards/OLIMEX_LPC_P2148/board.h + * ChibiOS_2.0.8/readme.txt + * ChibiOS_2.0.8/test/testdyn.c + * ChibiOS_2.0.8/docs/*/*: Updated. + + New private key management. + * src/ac.c (ac_reset_pso_cds, ac_reset_pso_other): Call + gpg_do_clear_prvkey. + (verify_pso_other): load private keys here. + * src/openpgp-do.c (kd): Keydata for Signing, Decryption, and + Authentication. + (gpg_do_load_prvkey, gpg_do_write_prvkey): Use kd[]. + (gpg_do_clear_prvkey): New function. + * src/openpgp.c (cmd_pso, cmd_internal_authenticate): Use new API + of rsa_sign and rsa_decrypt. + (cmd_pso): Fixed bug of checking return value of gpg_get_pw1_lifetime. + * src/call-rsa.c (rsa_sign): New argument KD. + (rsa_decrypt): Likewise. + + Don't use malloc/free in C library. + * src/stdlib.h (malloc, free): Use chHeapAlloc and chHeapFree. + +2010-11-26 NIIBE Yutaka + + * boards/STM8S_DISCOVERY/*: New. + * src/configure: STM8S_DISCOVERY only has 64KB flash memory. + + * boards/STBEE_MINI/board.h (CPU_WITH_NO_GPIOE): New define. + * ChibiOS_2.0.6/os/hal/platforms/STM32/hal_lld.c: Use it. + * ChibiOS_2.0.6/os/hal/platforms/STM32/pal_lld.c: Likewise. + * ChibiOS_2.0.6/os/hal/platforms/STM32/pal_lld.h: Likewise. + + * src/openpgp.c (cmd_pso): DigestInfo by SHA224/SHA384/SHA512 is + supported. + +2010-11-22 NIIBE Yutaka + + Import changes of ChibiOS_2.0.6. + * ChibiOS_2.0.6/demos/ARM7-AT91SAM7X-LWIP-GCC/chconf.h + * ChibiOS_2.0.6/os/hal/include/can.h + * ChibiOS_2.0.6/os/hal/platforms/AT91SAM7/hal_lld.c + * ChibiOS_2.0.6/os/hal/platforms/AT91SAM7/serial_lld.c + * ChibiOS_2.0.6/os/hal/platforms/LPC214x/serial_lld.c + * ChibiOS_2.0.6/os/hal/platforms/STM32/hal_lld_f103.h + * ChibiOS_2.0.6/os/hal/platforms/STM32/hal_lld_f105_f107.h + * ChibiOS_2.0.6/os/hal/platforms/STM32/pwm_lld.c + * ChibiOS_2.0.6/os/hal/platforms/STM32/serial_lld.h + * ChibiOS_2.0.6/os/hal/platforms/STM32/spi_lld.h + * ChibiOS_2.0.6/os/hal/src/adc.c + * ChibiOS_2.0.6/os/hal/src/spi.c + * ChibiOS_2.0.6/os/kernel/include/ch.h + * ChibiOS_2.0.6/os/kernel/include/chinline.h + * ChibiOS_2.0.6/os/kernel/include/chioch.h + * ChibiOS_2.0.6/os/kernel/include/chstreams.h + * ChibiOS_2.0.6/os/kernel/include/chthreads.h + * ChibiOS_2.0.6/os/kernel/src/chlists.c + * ChibiOS_2.0.6/os/kernel/src/chschd.c + * ChibiOS_2.0.6/os/kernel/src/chthreads.c + * ChibiOS_2.0.6/os/ports/GCC/ARM/rules.mk + * ChibiOS_2.0.6/os/ports/GCC/ARM7/chcore.h + * ChibiOS_2.0.6/os/ports/GCC/ARM7/port.dox + * ChibiOS_2.0.6/os/ports/GCC/ARMCMx/chcore_v6m.c + * ChibiOS_2.0.6/os/ports/GCC/ARMCMx/chcore_v6m.h + * ChibiOS_2.0.6/os/ports/GCC/ARMCMx/chcore_v7m.c + * ChibiOS_2.0.6/os/ports/GCC/ARMCMx/chcore_v7m.h + * ChibiOS_2.0.6/os/ports/GCC/ARMCMx/old/chcore_v7m.h + * ChibiOS_2.0.6/os/ports/GCC/AVR/chcore.h + * ChibiOS_2.0.6/os/ports/GCC/AVR/port.dox + * ChibiOS_2.0.6/os/ports/GCC/MSP430/chcore.c + * ChibiOS_2.0.6/os/ports/GCC/MSP430/chcore.h + * ChibiOS_2.0.6/os/ports/GCC/MSP430/port.dox + * ChibiOS_2.0.6/os/ports/GCC/PPC/chcore.h + * ChibiOS_2.0.6/os/ports/GCC/PPC/port.dox + * ChibiOS_2.0.6/os/ports/RC/STM8/port.dox + * ChibiOS_2.0.6/os/various/memstreams.h + * ChibiOS_2.0.6/readme.txt + * ChibiOS_2.0.6/docs/*/*: Updated + +2010-11-14 NIIBE Yutaka + + * src/openpgp.c (cmd_pso): DigestInfo by SHA256 is supported. + +2010-11-12 NIIBE Yutaka + + * src/usb_desc.c (gnukConfigDescriptor): Change dwFeatures. + + * src/usb-icc.c (icc_send_params): Always return fixed result. + (icc_handle_data): Support ICC_GET_PARAMS. + +2010-11-10 NIIBE Yutaka + + * src/usb_desc.c (gnukConfigDescriptor): Fix bmAttributes. + +2010-11-09 NIIBE Yutaka + + * Version 0.4. + + * src/usb_desc.c (gnukStringSerial): Updated. + + * ChibiOS_2.0.2/os/hal/platforms/STM32/pal_lld.h (PALConfig): + STBee Mini uses STM32F103CBT6 which expose no GPIO E port. + * ChibiOS_2.0.2/os/hal/platforms/STM32/pal_lld.c (_pal_lld_init): + Likewise. + +2010-11-08 NIIBE Yutaka + + * tool/dump_mem.py: New tool. + + Implement GC for data pool in flash memory. + * src/openpgp-do.c (gpg_write_digital_signature_counter): New. + (gpg_increment_digital_signature_counter): Fix for GC. + (gpg_data_scan): Rename from gpg_do_table_init. + (gpg_data_copy): New function for copying GC. + * src/main.c (main): Call gpg_data_scan with the address which + flash_init returns. + * src/flash.c (flash_erase_page): New function. + (FLASH_DATA_POOL_SIZE): data_pool is 2KiB now. + (flash_data): Put a header (GC generation). + (flash_init): Implement choosing a data pool page. + (flash_data_pool): Removed. + (flash_copying_gc): New function. + (flash_data_pool_allocate): Call flash_copying_gc when full. + (flash_do_write_internal, flash_put_data_internal) + (flash_bool_write_internal, flash_cnt123_write_internal): New + * src/gnuk.ld.in (gnuk_flash): data_pool is 2KiB now. + + Bug fixes. + * src/openpgp.c (cmd_change_password, cmd_reset_user_password): + Write to APDU correctly. + * src/flash.c (flash_warning): Make it public. + * src/openpgp-do.c (do_hist_bytes, do_fp_all, do_cafp_all) + (do_kgtime_all, do_ds_count): Fix return value. + (rw_pw_status): Correctly return value. + (proc_resetting_code): Change func proto. to return success/failure. + (proc_key_import): Ditto. + (gpg_do_put_data): Handle return values. + (gpg_do_write_simple): Don't write to APDU. + +2010-11-05 NIIBE Yutaka + + Bug fixes. + * src/openpgp.c (gpg_change_keystring): Handle + GPG_KEY_FOR_AUTHENTICATION. + * src/openpgp-do.c (gpg_do_write_prvkey): Remove multiple call + of flash_do_release. + + Bug fix. + * src/openpgp-do.c (gpg_do_write_prvkey): Don't hardcode 6, but + use strlen. + + * src/flash.c, src/gnuk.ld.in: Rename "Flash DO Pool" to "Flash + Data Pool", because it's not only DO. + * src/gnuk.h, src/opengpg-do.c: Cleanup. + + Digital Signature Counter implementation improvement. + * src/gnuk.h (NR_DO_DS_COUNT): Removed. + (NR_COUNTER_DS, NR_COUNTER_DS_LSB): New. + * src/openpgp-do.c (do_ds_count_initial_value): Removed. + (gpg_do_increment_digital_signature_counter): Removed. + (digital_signature_counter): New variable. + (do_ds_count, gpg_increment_digital_signature_counter): New functions. + (gpg_do_table): Change the entry for GPG_DO_DS_COUNT as DO_PROC_READ. + (gpg_do_table_init): Handle digital_signature_counter. + * src/flash.c (flash_data_pool_allocate, flash_put_data): New. + + Password status implementation improvement. + * src/gnuk.h (PW_STATUS_PW1, PW_STATUS_RC, PW_STATUS_PW3): Removed. + (PW_ERR_PW1, PW_ERR_RC, PW_ERR_PW3): New define. + (NR_COUNTER_123, NR_BOOL_PW1_LIFETIME): New define. + (NR_NONE, NR_EMPTY): New define. + * src/flash.c (flash_bool_clear, flash_bool_write) + (flash_cnt123_get_value, flash_cnt123_increment) + (flash_cnt123_clear): New functions. + * src/openpgp-do.c (do_pw_status_bytes_template): Removed. + (PW_STATUS_BYTES_TEMPLATE, gpg_do_reset_pw_counter): Removed. + (PASSWORD_ERRORS_MAX, PW_LEN_MAX): New define. + (pw1_lifetime_p, pw_err_counter_p): New variables. + (gpg_get_pw1_lifetime): New function. + (gpg_get_pw_err_counter, gpg_passwd_locked, gpg_reset_pw_counter) + (gpg_increment_pw_counter): New functions. + (rw_pw_status): Use pw1_lifetime_p and pw_err_counter_p. + (gpg_do_table_init): Handle NR_COUNTER_123 and NR_BOOL_PW1_LIFETIME. + * src/ac.c (verify_pso_cds, verify_pso_other, verify_admin_0): + Follow the changes. + * src/openpgp.c (cmd_change_password, cmd_reset_user_password) + (cmd_pso, cmd_internal_authenticate): Likewise. + +2010-11-04 NIIBE Yutaka + + * src/flash.c (flash_warning): New. + (flash_do_pool): Added header for DO pool. + (flash_do_release): Fill zero. + (flash_do_write): Change DO format in flash. + * src/openpgp-do.c (gpg_do_table_init, copy_do_1) + (gpg_do_read_simple): Follow the change of DO format in flash. + + * src/openpgp-do.c (DO_CMP_READ): Renamed. + (cmp_ch_data, cmp_app_data, cmp_ss_temp): Likewise. + (with_tag): Removed static global variable. + (do_hist_bytes, do_fp_all, do_cafp_all, do_kgtime_all) + (rw_pw_status, copy_do_1, copy_do, gpg_do_get_data): Added + with_tag argument. + (gpg_do_put_data): length > 255 will be error. + +2010-11-03 NIIBE Yutaka + + Bug fixes. + * src/ac.c (verify_admin_0): Initialize pwsb earlier. + * src/openpgp-do.c (copy_do_1): Access do_data[0] (was: do_data[1]). + +2010-11-02 NIIBE Yutaka + + DfuSe support. + * tool/dfuse.py (DFU_STM32.download): Put '#' for each 4-KiB. + Added 0-length write to finish download. + Take intel_hex object as argument. + (DFU_STM32.ll_upload_block): New method. + (DFU_STM32.dfuse_read_memory): New method. + (DFU_STM32.verify): New method. + (get_device): Support DFU_STM32PROTOCOL_0 too (for CQ STARM). + + * tool/dfuse.py: Renamed from dfu_stmicroelectronics_extention.py. + +2010-11-01 NIIBE Yutaka + + * tool/intel_hex.py: New file. + * tool/dfu_stmicroelectronics_extention.py: New file. + +2010-10-28 NIIBE Yutaka + + * src/gnuk.h (OPENPGP_CARD_INITIAL_PW3): New. + * src/ac.c (verify_admin_0): Use OPENPGP_CARD_INITIAL_PW3. + +2010-10-23 NIIBE Yutaka + + * Version 0.3. + + * src/usb_desc.c (gnukStringSerial): Updated. + +2010-10-22 NIIBE Yutaka + + * src/gnuk.ld.in (.gnuk_random): Fix description so that + padding with 0xffffffff will be in gnuk.hex. + + * src/openpgp.c (file_selection): Change type (was: int). + (FILE_NONE..FILE_EF_SERIAL): Change the values. + + * src/configure: Added STBee Mini support. + * boards/STBEE_MINI/mcuconf.h: New. + * boards/STBEE_MINI/board.mk: New. + * boards/STBEE_MINI/board.h: New. + * boards/STBEE_MINI/board.c: New. + + * ChibiOS_2.0.2/os/hal/platforms/STM32/hal_lld.c + (pal_default_config): STBee Mini uses STM32F103CBT6 which expose + no GPIO E port. + +2010-10-21 NIIBE Yutaka + + * boards/common/hw_config.c (Get_SerialNum): Removed. + * src/usb_prop.c (gnuk_device_init): Remove calling Get_SerialNum. + * src/usb_desc.c (gnukStringSerial): Updated. + * boards/CQ_STARM/board.c (set_led): Fix polarity. + +2010-10-20 NIIBE Yutaka + + * FSIJ_SERIAL_NUMBER: New. + * src/configure (with_fsij): Added FSIJ serial number support. + * src/config.h.in (@FSIJ_DEFINE@, @SERIAL_NUMBER_FOUR_BYTES@): New. + + * src/configure: Added CQ STARM target. + * boards/CQ_STARM/mcuconf.h: New. + * boards/CQ_STARM/board.mk: New. + * boards/CQ_STARM/board.h: New. + * boards/CQ_STARM/board.c: New. + +2010-10-19 NIIBE Yutaka + + * boards/STM32_PRIMER2/board.mk (BOARDSRC): Use common/hw_config.c. + * boards/OLIMEX_STM32_H103/board.mk (BOARDSRC): Likewise. + + * boards/common/hw_config.c: Move board specific functions to ... + * boards/STM32_PRIMER2/board.c (USB_Cable_Config, set_led): ... here. + * boards/OLIMEX_STM32_H103/board.c (USB_Cable_Config, set_led): Ditto. + + * boards/{OLIMEX_STM32_H103,STM32_PRIMER2}/hw_config.c: Removed. + * boards/common/hw_config.c: New file (was: boards/*/hw_config.c). + + * .gitignore: New file. + +2010-10-16 NIIBE Yutaka + + Implement "INTERNAL AUTHENTICATE" command. + + * src/gnuk.h (BY_USER, BY_RESETCODE, BY_ADMIN): New defines. + (NUM_ALL_PRV_KEYS): Now it's 3 (was: 2). + + * src/openpgp.c (INS_INTERNAL_AUTHENTICATE): New define. + (cmd_internal_authenticate): New function. + (cmds): Added INS_INTERNAL_AUTHENTICATE. + (cmd_change_password): Use BY_USER. + (cmd_reset_user_password): Use BY_USER, BY_RESETCODE, BY_ADMIN. + (cmd_pso): Load GPG_KEY_FOR_DECRYPTION here. + (cmd_pso): Removed adding status word into res_APDU... + * src/call-rsa.c (rsa_sign): and moved adding status word into + res_APDU here. + + * src/ac.c (pw1_keystring): New variable. + (ac_reset_pso_other): Clear pw1_keystring. + (verify_pso_cds): Use BY_USER. + (verify_pso_other): Just check the length of password here, and + defer real check to cmd_pso or cmd_internal_authenticate. + +2010-10-14 NIIBE Yutaka + + Adding 'configure' support. + * src/configure: New file. + * src/Makefile.in: Renamed from src/Makefile. + * src/config.h: Renamed from src/config.h. + * src/gnuk.ld: Renamed from src/gnuk.ld. + + Adding DFU_SUPPORT. + * boards/common/hwinit0.c: New file adding DFU_SUPPORT. + * boards/common/hwinit1.c: New file. + * boards/OLIMEX_STM32_H103/board.c: Include config.h. + Use common/hwinit0.c and common/hwinit1.c. + * boards/STM32_PRIMER2/board.c: Likewise. + +2010-09-16 NIIBE Yutaka + + * src/usb-icc.c (icc_error): New function. + (icc_handle_data): Call icc_error. + Don't go to STATE_START on errors. + +2010-09-13 NIIBE Yutaka + + * Version 0.2. + + * src/openpgp.c (cmd_select_file): Override data of number_of_bytes. + + * src/openpgp-do.c (gpg_do_table_init): Calculate number of byte + which Data Objects consumes. + +2010-09-12 Kaz Kojima + + * src/call-rsa.c (rsa_decrypt): Debug output only when DEBUG. + + * boards/STM32_PRIMER2/hw_config.c (USB_Cable_Config): Fix GPIO. + (set_led): Ditto. + + * boards/STM32_PRIMER2/board.c (hwinit1): Added LED initialization. + +2010-09-11 NIIBE Yutaka + + * src/usb-icc.c (ATR): Fixed. + (icc_send_params): New function. + (icc_handle_data): Handle ICC_SET_PARAMS request. + + * src/random.c (random_bytes_get, random_bytes_free, get_random): + Clear used random bytes. + + * src/flash.c (flash_clear_halfword): New function. + +2010-09-10 NIIBE Yutaka + + * Version 0.1. + + * src/usb_desc.c (gnukStringSerial): Change the value so that + libccid doesn't get confused. + + * src/openpgp.c (gpg_change_keystring): Support key for decryption + as well. + (cmd_read_binary): Use openpgpcard_aid. + (cmd_pso): call ac_reset_pso_other. + + * src/openpgp-do.c (openpgpcard_aid): Renamed from aid, and exported. + (do_ds_count_initial_value): New const variable. + (num_prv_keys): New variable. + (gpg_do_write_prvkey): Remove contents of keystring only if + ++num_prv_keys == NUM_ALL_PRV_KEYS. + (gpg_do_chks_prvkey): Call flash_do_release. + (gpg_do_table_init): Initialize with do_ds_count_initial_value. + Initialize num_prv_keys. + (gpg_do_write_simple): Support removing DO. + (gpg_do_increment_digital_signature_counter): Call flash_do_release. + + * src/gnuk.h (NUM_ALL_PRV_KEYS): New definition. + (OPENPGP_CARD_INITIAL_PW1): New definition. + (enum kind_of_key): Rename. + + * src/ac.c (ac_reset_pso_cds): New function. + +2010-09-09 Kaz Kojima + + * boards/STM32_PRIMER2/{board.c,board.h,board.mk,hw_config.c,mcuconf.h}: + New files. + + * boards/OLIMEX_STM32_H103/{mcuconf.h,hw_config.c}: Moved from src. + + * src/main.c (main): Use set_led instead of palClearPad directly. + +2010-09-08 NIIBE Yutaka + + * src/ac.c (calc_md): Make SHA1 variable auto. + + * src/debug.c (put_int): New. + + * src/gnuk.ld (__process_stack_size__): Removed. + + * src/main.c (STDOUTthread): Use Event. + (main): Make LED ON during command execution, blink usually. + + * src/openpgp-do.c (encrypt, decrypt): Make AES variables auto. + (gpg_do_table): GPG_DO_ALG_AUT is NULL. + + * src/openpgp.c (cmd_pso): Bug fix for extended Lc. + + * src/usb-icc.c (icc_power_off): Make LED ON during command + execution. + (USB_ICC_TIMEOUT): Longer value (was: 1000). + + * src/usb_desc.c (gnukConfigDescriptor): Fix bcdCCID value. + + * src/vcomport.mk (VCOMSRC): Use our own usb_endp.c. + + * src/usb_desc.c (gnukConfigDescriptor): ICC Descriptor is + Revision 1.0. + + * polarssl-0.14.0/include/polarssl/config.h: Commend out + POLARSSL_SELF_TEST. + + * polarssl-0.14.0/library/rsa.c (rsa_private): Don't check input, + so that we don't access ctx->N. + (rsa_pkcs1_decrypt): size of BUF is enough as 256. + + * polarssl-0.14.0/library/sha1.c (sha1_file): #if-out to avoid + stdio of libc. + + * polarssl-0.14.0/library/bignum.c (mpi_write_hlp) + (mpi_write_string, mpi_read_file, mpi_read_file): #if-out to avoid + stdio of libc. + +2010-09-07 NIIBE Yutaka + + * gnuk.svg: New file. + +2010-09-06 NIIBE Yutaka + + * Initial version 0.0. diff --git a/gnuk/FSIJ-s.png b/gnuk/FSIJ-s.png new file mode 100644 index 0000000000..783bf3bcc9 Binary files /dev/null and b/gnuk/FSIJ-s.png differ diff --git a/gnuk/GNUK_SERIAL_NUMBER b/gnuk/GNUK_SERIAL_NUMBER new file mode 100644 index 0000000000..8e06dae008 --- /dev/null +++ b/gnuk/GNUK_SERIAL_NUMBER @@ -0,0 +1,2 @@ +# Email # 6-byte serial number, separated by ':' +gniibe@fsij.org f5:17:00:00:00:01 diff --git a/gnuk/GNUK_USB_DEVICE_ID b/gnuk/GNUK_USB_DEVICE_ID new file mode 100644 index 0000000000..003f4eeaef --- /dev/null +++ b/gnuk/GNUK_USB_DEVICE_ID @@ -0,0 +1,5 @@ +# VID:PID bcdDev Product_STRING Vendor_STRING +0000:0000 0200 Gnuk Emulation Free Software Initiative of Japan +234b:0000 0200 Gnuk Token Free Software Initiative of Japan +20a0:4211 0200 Nitrokey Start Nitrokey +########## ## ########## ################# diff --git a/gnuk/LICENSE b/gnuk/LICENSE new file mode 100644 index 0000000000..94a9ed024d --- /dev/null +++ b/gnuk/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/gnuk/NEWS b/gnuk/NEWS new file mode 100644 index 0000000000..d8429b60e4 --- /dev/null +++ b/gnuk/NEWS @@ -0,0 +1,787 @@ +Gnuk NEWS - User visible changes + +* Major changes in Gnuk 1.2.6 + + Released 2017-10-11, by NIIBE Yutaka + +** Port to GNU/Linux emulation +We can "run" Gnuk Token on GNU/Linux by emulation through USBIP. + +** Upgrade of Chopstx +We use Chopstx 1.5. + + +* Major changes in Gnuk 1.2.5 + + Released 2017-08-11, by NIIBE Yutaka + +** "factory-reset" fix +Gnuk's behavior was implemented by referring the gpg implementation. +It found that gpg implementation was not good from the viewpoint of +the OpenPGP card specification. GnuPG was fixed to match the OpenPGP +card specification already. Thus, Gnuk is now fixed. + +** Upgrade of Chopstx +We use Chopstx 1.4. + + +* Major changes in Gnuk 1.2.4 + + Released 2017-05-12, by NIIBE Yutaka + +** Flash ROM security fix +The partial content of flash ROM might be exposed when scanning of +data object had a problem. Added boundary check and changed layout of +flash ROM. + + +* Major changes in Gnuk 1.2.3 + + Released 2017-02-02, by NIIBE Yutaka + +** ECC key generation on the device +Bug fixed. + +** Upgrade of Chopstx +We use Chopstx 1.3. + + +* Major changes in Gnuk 1.2.2 + + Released 2016-10-15, by NIIBE Yutaka + +** Change of SELECT FILE behavior +Gnuk used to reply AID upon SELECT FILE command. Now, to be compatible +to original OpenPGP card, it returns nothing but status code of 9000. + +** Added feature of Factory Reset as compile time option +Original OpenPGP card has the feature, and Gnuk is now configurable to +support the feature. + +** Upgrade of Chopstx +We use Chopstx 1.2. + + +* Major changes in Gnuk 1.2.1 + + Released 2016-07-11, by NIIBE Yutaka + +** Upgrade of Chopstx +We use Chopstx 1.1. + + +* Major changes in Gnuk 1.2.0 + + Released 2016-05-20, by NIIBE Yutaka + +** Upgrade of Chopstx +We use Chopstx 0.11. + +** Support authentication status reset by VERIFY command. +This feature is described in the OpenPGPcard specification V2.2 and +V3.1, which allow user to reset authentication status. + +** S2K algorithm tweak to defeat "copycat" service of MCU. +Even if the existence of some services copying MCU, your private key +will not be controled by others, in some cases. + +** Bug fix for secp256k1 and NIST P-256. +Bugs in basic computation were fixed. + +** Bug fix for bignum routines. +Bignum routine update from upstream (failure doesn't occur for our RSA +computation, though). Another fix for mpi_exp_mod. + + +* Major changes in Gnuk 1.1.9 + + Released 2015-09-18, by NIIBE Yutaka + +** Bug fix for Ed25519 and Curve25519 +When registering key, wrong operations were not detected correctly. +This is fixed. + + +* Major changes in Gnuk 1.1.8 + + Released 2015-09-17, by NIIBE Yutaka + +** Upgrade of Chopstx +We use Chopstx 0.10, which supports Nitrokey-Start. + +** Card serial number +The way to determine a serial number of Gnuk Token for card has been +changed. It uses the 96-bit unique bits of MCU, but the portion for +use is changed. + +** USB Reset handling +USB reset lets Gnuk Token restart. It would not be perfect, when it's +during computation of some function, but most parts are protected by +Chopstx's feature of cancellation. + + +* Major changes in Gnuk 1.1.7 + + Released 2015-08-05, by NIIBE Yutaka + +** Upgrade of Chopstx +We use Chopstx 0.08, which supports STM32 Nucleo and ST Dongle. + + +* Major changes in Gnuk 1.1.6 + + Released 2015-07-21, by NIIBE Yutaka + +** USB SerialNumber String +The way to determine a serial number of Gnuk Token has been changed. +It uses the 96-bit unique bits of MCU, but the portion for use is +changed. + +** Upgrade of Chopstx +We use Chopstx 0.07, which supports STM32 Primer2 and CQ STARM, too. + +** Experimental Curve25519 support. + +Gnuk can support Curve25519 (for decryption). Note that this is +pretty much experimental, and subjects to change. The low level code +is somehow stable, but there are no consensus in higer level. +Especially, OID in the key attribute would be changed in future. + +** No --enable-keygen option +It is now standard feature included always. Note that it doesn't mean +this feature is stable now. It is becoming stable, hopefully. + + +* Major changes in Gnuk 1.1.5 + + Released 2015-06-03, by NIIBE Yutaka + +** upgrade_by_passwd.py is not so noisy any more. +Since it's getting stable, no debug output any more. + +** Maple mini support. +Although its random number generation is not tested, Maple mini +support is added. + +** Windows interoperability fix. +1.1.x (0 to 4) didn't work with Windows because of INTERRUPT transfer. +It's fixed and it works now. + +** OpenPGPcard specification v3.0 compatibility. +OpenPGPcard specification v3.0 now include NIST curves (and other +curves) and ECDSA and ECDH operations are defined. Gnuk follows +this specification. + + +* Major changes in Gnuk 1.1.4 + + Released 2014-12-15, by NIIBE Yutaka + +** Experimental RSA-4096 support. +Although it takes too long (more than 8.7 second), RSA-4096 +is now implemented. + +** ECDH support. +ECDH is now supported. You need development branch (master) +of GnuPG to use this feature. + +** ECDSA and EdDSA is not that experimental. +You don't need to edit DEFS variable in src/Makefile. + +** STM8S_DISCOVERY is not supported any more. +It's flash ROM size (64KiB) is a bit small to have all features of +Gnuk now. If you manually edit code to limit the size of executable, +it still could run Gnuk, though. + +** configure's default target is now FST-01. +Receiving reports from those who complain default target, I +reconsidered. Those who has Olimex STM32 H103 usually has JTAG +debugger, while FST-01 users don't. So, to be safe, the default +target is now FST-01, instead of Olimex STM32 H103. + + +* Major changes in Gnuk 1.1.3 + + Released 2014-04-16, by NIIBE Yutaka + +** Experimental EdDSA support. +After configure, you can edit the DEFS variable in src/Makefile, so +that Gnuk can support EdDSA with Ed25519 (for authentication). Note +that this is pretty much experimental, and subjects to change. + + +* Major changes in Gnuk 1.1.2 + + Released 2014-03-07, by NIIBE Yutaka + +** Experimental ECC support for secp256k1. +After configure, you can edit the DEFS variable in src/Makefile, so +that Gnuk can support ECDSA with NIST P256 (for authentication), and +ECDSA with secp256k1 (for signature). Note that this is pretty much +experimental, and subjects to change. + + +* Major changes in Gnuk 1.1.1 + + Released 2013-12-25, by NIIBE Yutaka + +** Tools and test suite now work with PyUSB 1.0, too. +It only worked with PyUSB 0.4.3, but it works with PyUSB 1.0 too. + +** Improved RSA routine +Working memory for RSA computation is taken from stack instead of +malloc (mostly). + + +* Major changes in Gnuk 1.1.0 + + Released 2013-12-20, by NIIBE Yutaka + +** Overriding key import / generation (Incompatible Change) +Gnuk supports overriding key import or key generation even if keys are +already installed. Note that it will result password reset of user. + +** RSA key generation improvement +Prime number generation is done by Fouque-Tibouchi method. + +** Security fix for RSA computation +PolarSSL had a vulnerability against timing attack. For detail, +please see: + + http://www.gniibe.org/memo/development/gnuk/polarssl/polarssl-rsa-blinding + +** Improved RSA routine +RSA computation has been improved using MPI square routine. Note that +you should not adopt this modification for general purpose computer, +as this change is weak against the Yarom/Falkner flush+reload cache +side-channel attack. + +** Upgrade of NeuG +The true random number generator was upgraded to the one of NeuG 1.0. + +** Replacement of kernel (thread library) +Instead of ChibiOS/RT, we now use Chopstx. + +** Removal of obsolete features +The feature named pin-dial, which is pin input with hardware +enhancement (with rotary encoder) is removed. + + +* Major changes in Gnuk 1.0.4 + + Released 2013-03-15, by NIIBE Yutaka + +** Relocatable reGNUal, really +In 1.0.3, reGNUal was not fully relocatable. It worked loaded on higher +address, but didn't work for lower address. This was fixed. + + +* Major changes in Gnuk 1.0.3 + + Released 2013-03-14, by NIIBE Yutaka + +** Relocatable reGNUal +The upgrade helper, reGNUal, is now relocatable (other than the first +vector table). It runs well when loaded at different address. This +makes the upgrade procedure more stable. + +** Compilation by newer GNU Toolchain +Now, Gnuk can be compiled with newer GNU Toolchain, specifically GCC +4.7.x and GNU Binutils 2.22. Old versions of Gnuk had problem for +ChibiOS_2.0.8/os/ports/GCC/ARMCMx/cmsis/core_cm3.c, which was fixed. + +** Data object 0x0073 +Data object 0x0073 is now available. + + +* Major changes in Gnuk 1.0.2 + + Released 2013-02-15, by NIIBE Yutaka + +** Product string is now "Gnuk Token" (was: "FSIJ USB Token") +Since the USB ID Repository suggests not including vendor name +in product string, we changed the product string. + +** New tool (experimental): test/upgrade_by_passwd.py +This is the tool to install new firmware to Gnuk Token, provided +that it's just shipped from factory (and nothing changed). It +authenticate as admin by factory setting, register a public key +for firmware upgrade, and then, does firmware upgrade. + +** tool/gnuk_upgrade.py supports '-k' option +It now supports RSA key on the host PC (not the one on the Token). + +** New tool: tool/get_raw_public_key.py +This is a script to dump raw data of RSA public key, which is useful +to register to Gnuk Token as a firmware upgrade key. + +** New tool: tool/gnuk_remove_keys_libusb.py +This tool is libusb version of gnuk_remove_keys.py. Besides, a bug in +gnuk_remove_keys.py was fixed. + +** CCID protocol fix +When time extension is requested by Gnuk Token to host PC, argument +field was 0, which was wrong (but it works for most PC/SC +implementations and GnuPG internal driver). Now it's 1, which means +1*BWT. + +** OpenPGP card protocol enhancement +Now, VERIFY command accepts empty data and returns remaining trial +counts, or 0x9000 (OK) when it's already authenticated. This is +useful for application to synchronize card's authentication status. + + +* Major changes in Gnuk 1.0.1 + + Released 2012-08-03, by NIIBE Yutaka + +** USB SerialNumber String +In 1.0, it has a bug for USB SerialNumber String. It has been fixed +in 1.0.1. + + +* Major changes in Gnuk 1.0 + + Released 2012-07-21, by NIIBE Yutaka + +This is bug fixes only release. + + +* 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 + +** Key generation feature added +Finally, key generation is supported. Note that it may be very slow. +It may take a few minutes (or more) to generate two or three keys, +when you are unlucky. + +** DnD pinentry support is deprecated +Once, DnD pinentry was considered a great feature, but it found that +it is difficult to remember moves of folders. + +** gnuk_upgrade.py assumes using another token for authentication +Use of another token for authentication is assumed now. This is +incompatible change. Note that when you upgrade a token of version +0.19 to 0.20 (or later), you need gnuk_upgrade.py of version 0.19. + +** KDF (Key Derivation Function) is now SHA-256 +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 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 +easily). + +** LED display output change +LED display output by Gnuk is now more reactive. It shows status code +when it gets GET_STATUS message of CCID. When you communicate Gnuk by +internal CCID driver of GnuPG (instead of PC/SC), and enable +'debug-disable-ticker' option in .gnupg/scdaemon.conf, it is more +silent now. + + +* Major changes in Gnuk 0.19 + + Released 2012-06-06, by NIIBE Yutaka + +** Firmware upgrade feature +Firmware upgrade is now possible after the public key authentication +using EXTERNAL AUTHENTICATE command of ISO 7816. Firmware upgrade is +done together with reGNUal, the firmware upgrade program. + +** System service blocks at the beginning of flash ROM. +Once flash ROM is protected, first 4-KiB cannot be modified. Gnuk +use this area for "system service". Note that this area will not +be able to be modified by firmware upgrade (or by any method). + +** New tool: gnuk_upgrade.py +The tool gnuk_upgrade.py is to do public key authentication using +gpg-agent and send reGNUal to Gnuk. Then, we put new Gnuk binary +into the device with reGNUal. + +** USB strings for revision detail, configure options, and system service. +USB strings now have more information. There are revision detail +string, configure options string, system service version string, as +well as vendor string and product string. These strings could be +examined to check Gnuk Token. + + +* Major changes in Gnuk 0.18 + + Released 2012-05-15, by NIIBE Yutaka + +** New mandatory option '--vidpid' for configure +You must specify USB vendor ID and product ID for Gnuk. +The file GNUK_USB_DEVICE_ID lists valid USB device IDs. + +** New tool: gnuk_remove_keys.py +The tool gnuk_remove_keys.py is to remove all keys in Gnuk Token +and reset PW1 and RC (if any). + +** New USB stack +Gnuk used to use USB stack of USB-FS-Device_Lib by ST. Now, it has +original implementation. Hopefully, size and quality are improved. + + +* Major changes in Gnuk 0.17 + + Released 2012-02-02, by NIIBE Yutaka + +** USB CCID/ICCD protocol implementation change +Gnuk now only supports short APDU level exchange, not supporting +extended APDU level exchange. Thus, Gnuk could be compatible to older +host side software implementation. + +** ISO 7816 SELECT command behavior is somewhat strict now +Old implementations do not check DF name for SELECT command. +This causes some trouble when Gnuk Token is identified as if it were +different card/token. Now, DF name of OpenPGP card is checked. + +** USB CCID/ICCD low-level bug is fixed +When the size of command APDU data is just 49, the lower level packet +size is 64. This is maximum size of BULK-OUT transfer packet, and +caused trouble in the past implementations. Example is setting url +(0x5f50) as: http://www.gniibe.org/adpu-string-size-is-just-49 +This is because the past implementations expect ZLP (zero length +packet). Now, it has been fixed. You can use any size of string. + +** CERT.3 Data Object (0x7f21) is now optional +As there's no valid use case for this data object and it does not +work as current version of GnuPG, this is now optional feature. +You can enable this data object by specifying --enable-certdo at +configure time. + +** With DnD pinentry, user can cancel pin input +Now, user can cancel pin input by unmounting device before finishing +DnD. + +** New tool: pinpadtest.py +The tool pinpadtest.py is PC/SC test tool for pinentry of pinpad with +OpenPGP card v2. + + +* Major changes in Gnuk 0.16 + + Released 2011-12-14, by NIIBE Yutaka + +** DnD pinentry support is added and it's default to pinentry support +DnD pinentry support doesn't require any hardware extension, but +emulates mass storage class device of USB. User inputs pass phrase +by "drag and drop"-ing folders using file manager or something. + +** Bug fix for VERIFY for CHV2 +With no keys, VERIFY command for CHV2 used to fail even if pass phrase +is correct. It was intentional, because CHV2 verification would be +useless with no keys. But there is a corner case for PRIVATE-DOs, +which may requires CHV2 verification. Even though Gnuk doesn't +support any PRIVATE-DOs, it is good to be fixed. + +** Changed bcdUSB = 1.1 +Gnuk device conforms to USB 2.0 full speed device, but when it was +2.0, some OS informs users, "you can connect the device to 2.0 +compliant hub so that it can have better bandwidth", which is not +the case for full speed device. + + +* Major changes in Gnuk 0.15 + + Released 2011-11-24, by NIIBE Yutaka + +** New targets: FST_01 and FST_01_00 +Flying Stone Technology's open hardware, Flying Stone Tiny 01 is +supported. + +** Flash writing tool for "DfuSe" is improved +Now, it supports holes and unaligned blocks in hex file. + +** Experimental PIN-pad support (by TV controller) change +Now, Gnuk has codetables for conversion from CIR code to ASCII code. +Note that only four controllers (of Dell, Sharp, Sony, and Toshiba) +are supported and tested. + +** It is possible for users to keep using OPENPGP_CARD_INITIAL_PW1 +With a bug fix of verify_user_0, it's now possible. Although it's not +recommended. + +** Important bug fix and a workaround +In version 0.14, __main_stack_size__ (for interrupt handler) was too +small for some cases. This is fixed in 0.15. + +In src/Makefile.in, added -mfix-cortex-m3-ldrd for correctly linking C +library for thumb2. This is needed for newer summon-arm-toolchain. + + +* Major changes in Gnuk 0.14 + + Released 2011-10-07, by NIIBE Yutaka + +** Random number generator change +NeuG, Gniibe's True RNG implementation for STM32F103, has been +integrated to Gnuk. It is not needed to put random number bytes +(generated by host) to Token any more. + + +* Major changes in Gnuk 0.13 + + Released 2011-06-15, by NIIBE Yutaka + +** Improved RSA routine. +About 20% speed improvement. + +** New tool: hub_ctrl. +It is a Python implementation ported from original C implementation. +It is useful for development of USB target if you have a good hub. +You can power off/on the port to reset Gnuk Token. + + +* Major changes in Gnuk 0.12 + + Released 2011-05-13, by NIIBE Yutaka + +** Admin-less mode is supported. +The OpenPGP card specification assumes existence of a security officer +(admin), who has privilege to manage the card. On the other hand, +many use cases of Gnuk are admin == user. + +Thus, Gnuk now supports "admin-less" mode. In this mode, user can get +privilege with the password of PW1. + +At the initialization of the card, Gnuk becomes compatible mode by +setting PW3. Without setting PW3, it becomes "admin-less" mode +by setting PW1. + +** Important two bug fixes. +Gnuk (<= 0.11) has a bug which makes possible for attacker to change +user password to unknown state without knowing original password (when +no keys are loaded yet). No, attacker could not steal your identity +(cannot sign or decrypt), but it would be possible to disturb you. + +Gnuk (<= 0.11) has a bug which makes possible for attacker to guess +admin password easily. When admin password is not set (the default +value of factory setting), failure of VERIFY doesn't increment error +counter in older versions. Observing no increment of error counter, +attacker could know that admin password is the one of factory setting. + +** tool/gnuk_put_binary.py now uses pyscard. +Instead of PyUSB, it uses Python binding of PC/SC. PyUSB version is +still available as tool/gnuk_put_binary_libusb.py. + +** Logo for Gnuk is updated. + +** Gnuk Sticker SVG is available. + + +* Major changes in Gnuk 0.11 + + Released 2011-04-15, by NIIBE Yutaka + +This is bug fixes only release. + + +* Major changes in Gnuk 0.10 + + Released 2011-02-10, by NIIBE Yutaka + +** The executable can be installed to multiple devices. +So far, users of Gnuk should have not shared single executable among +multiple devices because the executable includes random bits (or +fixed serial number). Now, random_bits and fixed serial number are +configured *after* compilation, we can install single executable image +to multiple devices. Note that we need to configure random_bits for +each device. + +** Removed configure option: --with-fixed-serial +It is not compile time option any more. After installation, we can +modify serial number in AID by tool/gnuk_put_binary.py. Modification +is possible only once. If you don't modify, Gnuk uses unique chip ID +of STM32 processor for AID. + + +* Major changes in Gnuk 0.9 + + Released 2011-02-01, by NIIBE Yutaka + +** Card Holder Certificate is supported (still this is experimental). +Gnuk can support card holder certificate now. Note that GnuPG is not +ready yet. The tool/gnuk_update_binary.py is for writing card holder +certificate to Gnuk Token. + +** Better interoperability to OpenSC. +Gnuk is not yet supported by OpenSC, but it could be. With the +changes in Gnuk, it could be relatively easily possible to support +Gnuk Token by OpenSC with a few changes to libopensc/card-openpgp.c, +and libopensc/pkcs15-openpgp.c. + +** New board support "STBee" +STBee is a board by Strawberry Linux Co., Ltd., and it has +STM32F103VET6 on the board. The chip is High Density CPU with 512KB +flash memory and many I/O. If you want to connect sensor, display, +etc., this board would be a good candidate. + +** Experimental PIN-pad modification(unblock) support is added. +PIN-pad modification(unblock) is supported. + + +* Major changes in Gnuk 0.8 + + Released 2011-01-19, by NIIBE Yutaka + +** Experimental PIN-pad modification support is added. +PIN input using rotally encoder and push switch is tested with STBee +Mini. By this hardware, PIN-pad modification is supported. + + +* Major changes in Gnuk 0.7 + + Released 2011-01-15, by NIIBE Yutaka + +** Bug fix only. +In version 0.6, a severe bug was introduced in usb-icc.c when adding a +work around for libccid 1.3.11. The fix is one-liner, but it is worth +to release newer version. + + +* Major changes in Gnuk 0.6 + + Released 2011-01-14, by NIIBE Yutaka + +** Experimental PIN-pad support is added. +Local PIN-pad input is suppored for boards which have input hardware. +PIN input using consumer IR receive module is tested with STBee Mini +and STM8S Discovery. + +** USB device serial number is virtually unique now. +STM32F103 has 96-bit unique chip identifier. We take advantage of +this, Gnuk Token has virtually unique USB serial number. + +** Card serial number is determined at run time by chip identifier. +Until version 0.5, card serial number was compile time option. If we +used same binary for different devices, card serial number was same. +Now, we use STM32F103's 96-bit unique chip identifier for card serial +number (when you don't use --with-fixed-serial option). + +** More improved USB-CCID/ICCD implementation. +The changes in 0.5 was not that good for libccid 1.3.11, which has +small buffer (only 262-byte APDU). Workaround for libccid 1.3.11 is +implemented. + + +* Major changes in Gnuk 0.5 + + Released 2010-12-13, by NIIBE Yutaka + +** LED blink +LED blink now shows status output of the card. It shows the status of +CHV3, CHV2, and CHV1 when GPG is accessing the card. + +** New board support "STM8S Discovery" +ST-Link part (with STM32F103C8T6) of STM8S Discovery board is now supported. + +** Digital signing for SHA224/SHA256/SHA384/SHA512 digestInfo is now possible. + +** Fixes for password management +Now, you can allow the token to do digital signing multiple times with +single authentication. You can use "forcesig" subcommand in card-edit +of GnuPG to enable the feature. + +** Key management changes +If you remove all keys, it is possible to import keys again. + +** More improved USB-CCID/ICCD implementation. +Gnuk works better with GPG's in-stock protocol stack. You can do +digital signing (not decryption, key import, or get_public_key in +GPG2). For decryption, key import and get_public_key, changes are +needed for GPG (scd/ccid-driver.c) to support the case of extended +APDU. In short, you can sign with Gnuk by GPG. + +** Windows support. +Gnuk Token could run with GPG4WIN on MS Windows. GPG4WIN runs with +"usbccid" driver and "winscard" driver. + + +* Major changes in Gnuk 0.4 + + Released 2010-11-09, by NIIBE Yutaka + +** New board support "STBee Mini". + +** Flash writing tool for "DfuSe" is included now. + +** Since Flash GC is now implemented, it can be used longer. + + +* Major changes in Gnuk 0.3 + + Released 2010-10-23, by NIIBE Yutaka + +** Now we have 'configure' script to select target. + +** Support system with DFU (Device Firmware Upgrade) downloader. + +** New board support "CQ STARM". + +** Improved USB-ICCD implementation. Works fine with GPG's protocol stack. + + +* Major changes in Gnuk 0.2 + + Released 2010-09-13, by NIIBE Yutaka + +** With DEBUG=1, timeout is more than 3 seconds. + +** Flash ROM entries for random numbers are cleared after use. + +** Board support "STM32 Primer 2" now works. + + +* Major changes in Gnuk 0.1 + + Released 2010-09-10, by NIIBE Yutaka + +** Enabled force_chv1 (in the pw_status_bytes), so that the decipher works. + +** Support both of key for digital signing and key for decryption. + +** Decipher is supported. + +** New board support "STM32 Primer 2" is added by Kaz Kojima. + +** LED behavior is meaningful now. "ON" during execution. + +** Fixed bcdCCID revision number. + +** Logo. + + +* Major changes in Gnuk 0.0 + + Released 2010-09-06, by NIIBE Yutaka + +** This is initial release. Only it supports digital signing. + +Local Variables: +mode: outline +End: diff --git a/gnuk/README b/gnuk/README new file mode 100644 index 0000000000..600f07a0ff --- /dev/null +++ b/gnuk/README @@ -0,0 +1,638 @@ +Gnuk - An Implementation of USB Cryptographic Token for GnuPG + + Version 1.2.6 + 2017-10-11 + Niibe Yutaka + Free Software Initiative of Japan + +Release Notes +============= + +This is the release of Gnuk, version 1.2.6, which has major +incompatible changes to Gnuk 1.0.x. Specifically, it now supports +overriding key import, but importing keys (or generating keys) results +password reset. Also, you need to import private keys before changing +your password. Please update your documentation for Gnuk Token, so +that the instruction of importing keys won't cause any confusion. + +It has supports of EdDSA, ECDSA (with NIST P256 and secp256k1), and +ECDH (with X25519, NIST P256 and secp256k1), but this ECC feature is +somehow experimental, and it requires modern GnuPG 2.2 with libgcrypt +1.7.0 or later. + +It also supports RSA-4096, but users should know that it takes more +than 8 seconds to sign/decrypt. Key generation of RSA-4096 just fails, +because the device doesn't have enough memory. + +With this release, you can test how Gnuk Token works on GNU/Linux, +without real hardware, by USBIP emulation (--target=GNU_LINUX). +Please note that this emulation is intended only for testing. When +Gnuk does crypto computation on host, it is vulnerable by side channel +attacks. + + +What's Gnuk? +============ + +Gnuk is an implementation of USB cryptographic token for GNU Privacy +Guard. Gnuk supports OpenPGP card protocol version 3, and it runs on +STM32F103 processor. + +I wish that Gnuk will be a developer's soother who uses GnuPG. I have +been nervous of storing secret key(s) on usual secondary storage. +There is a solution with OpenPGP card, but it is not the choice for +me, as card reader is not common device. With Gnuk, this issue will +be solved by a USB token. + +Please look at the graphics of "gnuk.svg" for the software name. My +son used to be with his NUK(R), always, everywhere. Now, I am with a +USB Cryptographic Token by "Gnuk", always, everywhere. + + +FAQ +=== + +Q0: How Gnuk USB Token is superior than other solutions (OpenPGP + card 2.0, YubiKey, etc.) ? + https://www.g10code.de/p-card.html + https://www.yubico.com/ +A0: Good points of Gnuk are: + * If you have skill of electronics and like DIY, you can build + Gnuk Token cheaper (see Q8-A8). + * You can study Gnuk to modify and to enhance. For example, you + can implement your own authentication method with some sensor + such as an acceleration sensor. + * It is "of Free Software"; Gnuk is distributed under GPLv3+, + "by Free Software"; Gnuk development requires only Free Software + (GNU Toolchain, Python, etc.), + "for Free Software"; Gnuk supports GnuPG. + +Q1: What kind of key algorithm is supported? +A1: Gnuk version 1.0 only supports RSA-2048. + Gnuk version 1.2.x supports 255-bit EdDSA, as well as RSA-4096. + (Note that it takes long time to sign with RSA-4096.) + +Q2: How long does it take for digital signing? +A2: It takes a second and a half or so for RSA-2048. + It takes more than 8 secondd for RSA-4096. + +Q3: What's your recommendation for target board? +A3: Orthodox choice is Olimex STM32-H103. + FST-01 (Flying Stone Tiny 01) is available for sale, and it is a + kind of the best choice, hopefully. + If you have a skill of electronics, STM32 Nucleo F103 is the best + choice for experiment. + +Q4: What's version of GnuPG are you using? +A4: In Debian GNU/Linux system, I use GnuPG modern 2.1.18 in + unstable. + +Q5: What's version of pcscd and libccid are you using? +A5: I don't use them, pcscd and libccid are optional, you can use Gnuk + Token without them. + I tested pcscd 1.5.5-4 and libccid 1.3.11-2 which were in Debian + squeeze. + +Q6: What kinds of hardware is required for development? +A6: You need a target board plus a JTAG/SWD debugger. If you just + want to test Gnuk for target boards with DfuSe, JTAG debugger is + not the requirement. Note that for real use, you need JTAG/SWD + debugger to enable flash ROM protection. + +Q7: How much does it cost? +A7: Olimex STM32-H103 plus ARM-USB-TINY-H cost 70 Euro or so. + +Q8: How much does it cost for DIY version? +A8: STM32 Nucleo F103 costs about $10 USD. + +Q9: I got an error like "gpg: selecting openpgp failed: ec=6.108", what's up? +A9: Older GnuPG's SCDaemon has problems for handling insertion/removal of + card/reader. When your newly inserted token is not found by + GnuPG, try killing scdaemon and let it to be invoked again. I do: + + $ gpg-connect-agent "SCD KILLSCD" "SCD BYE" /bye + + and confirm scdaemon doesn't exist, then, + + $ gpg-connect-agent learn /bye + +Qa: With GNOME 2, I can't use Gnuk Token for SSH. How can we use it for SSH? +Aa: You need to deactivate seahorse-agent and gnome-keyring, but use + gpg-agant for the role of ssh-agent. For gnome-keyring please do: + + $ gconftool-2 --type bool --set /apps/gnome-keyring/daemon-components/ssh false + +Qb: With GNOME 3.0, I can't use Gnuk Token at all. Why? +Ab: That's because gnome-keyring-daemon interferes GnuPG. Type: + + $ gnome-session-properties + + and at the tab of "Startup Programs", disable check buttons for + "GPG Password Agent" and "SSH Key Agent". + +Qc: With GNOME 3.x (x >= 8?), I can't use Gnuk Token at all. Why? +Ac: That's because gnome-keyring-daemon interferes GnuPG. Please + disable the invocation of gnome-keyring-daemon. In Debian + wheezy, it's in the files /etc/xdg/autostart/gnome-keyring-ssh.desktop + and /etc/xdg/autostart/gnome-keyring-gpg.desktop. + We have a line something like: + + OnlyShowIn=GNOME;Unity;MATE; + + Please edit this line to: + + OnlyShowIn= + +Qd: Do you know a good SWD debugger to connect FST-01 or something? +Ad: ST-Link/V2 is cheap one. We have a tool/stlinkv2.py as flash ROM + writer program. STM32 Nucleo F103 comes with the valiant of + ST-Link/V2. However, the firmware of ST-Link/V2 is proprietary. + Now, I develop BBG-SWD, SWD debugger by BeagleBone Green. + + +Tested features +=============== + +Gnuk is tested by test suite. Please see the test directory. + + * Personalization of the card + * Changing Login name, URL, Name, Sex, Language, etc. + * Password handling (PW1, RC, PW3) + * Key import for three types: + * key for digital signing + * key for decryption + * key for authentication + * PSO: Digital Signature + * PSO: Decipher + * INTERNAL AUTHENTICATE + * Changing value of password status bytes (0x00C4): forcesig + * Verify with pin pad + * Modify with pin pad + * Card holder certificate (read) + * Removal of keys + * Key generation on device side for RSA-2048 + * Overriding key import + +Original features of Gnuk, tested manually lightly: + + * OpenPGP card serial number setup + * Card holder certificate (write by UPDATE BINARY) + * Upgrading with "EXTERNAL AUTHENTICATE" by reGNUal + +It is known not-working well: + + * It is known that the specific combination of libccid 1.4.1 + (or newer) with libusb 1.0.8 (or older) had a minor problem. + It is rare but it is possible for USB communication to be + failed, because of a bug in libusb implementation. Use + libusbx 1.0.9 or newer, or don't use PC/SC, but use internal + CCID driver of GnuPG. + + +Targets +======= + +We use Olimex STM32-H103 board and Flying Stone Tiny 01 (FST-01). + +With DfuSe support, STBee is also our targets. But this target with +DfuSe is for experiment only, because it is impossible for DfuSe to +disable read from flash. For real use, please consider killing DfuSe +and enabling read protection using JTAG debugger. + +For experimental PIN-pad support, I connect a consumer IR receive +module to FST-01, and use controller for TV. PIN verification is +supported by this configuration. Yes, it is not secure at all, since +it is very easy to monitor IR output of the controllers. It is just +an experiment. Note that hardware needed for this experiment is only +a consumer IR receive module which is as cheap as 50 JPY. + +Note that you need pinpad support for GnuPG to use PIN-pad enabled +Gnuk. The pinpad support for GnuPG is only available in version 2. + + +Build system and Host system +============================ + +Makefile is written for GNU make. You need Bash 4.x for configure. + +If your bash is not installed as /bin/bash, you need to run configure +script prepending 'bash' before './configure'. + +Some tools are written in Python. If your Python is not installed as +/usr/bin/python, please prepend 'python' for your command invocation. +Python 2.7 and PyUSB 0.4.3 is assumed. + + +Souce code +========== + +Gnuk source code is under src/ directory. + +Note that SHA-2 hash function implementation, src/sha256.c, is based +on the original implementation by Dr. Brian Gladman. See: + + http://brg.a2hosted.com//oldsite/cryptography_technology/sha/index.php +(was at: + http://gladman.plushost.co.uk/oldsite/cryptography_technology/sha/index.php) + + +License +======= + +It is distributed under GNU General Public Licence version 3 or later +(GPLv3+). Please see src/COPYING. + +Please note that it is distributed with external source code too. +Please read relevant licenses for external source code as well. + +The author(s) of Gnuk expect users of Gnuk will be able to access the +source code of Gnuk, so that users can study the code and can modify +if needed. This doesn't mean person who has a Gnuk Token should be +able to access everything on the Token, regardless of its protections. +Private keys, and other information should be protected properly. + + +External source code +==================== + +Gnuk is distributed with external source code. + +* chopstx/ -- Chopstx 1.5 + + We use Chopstx as the kernel for Gnuk. + + Chopstx is distributed under GPLv3+ (with a special exception). + + +* polarssl/ -- based on PolarSSL 1.2.10 (now mbedTLS) + + Souce code taken from: http://polarssl.org/ + + We use PolarSSL for RSA computation, and AES encryption/decryption. + + PolarSSL is distributed under GPLv2+. We use PolarSSL under GPLv3 + as our options. + + The file include/polarssl/bn_mul.h is heavily modified for ARM + Cortex-M3. + + The function rsa_private in polarssl/library/rsa.c is modified so + that it doesn't check T against N. The function rsa_pkcs1_sign is + modified to avoid warnings in case of !POLARSSL_PKCS1_V21. + + The functions rsa_pkcs1_verify and rsa_rsassa_pkcs1_v15_verify in + include/polarssl/rsa.h and polarssl/library/rsa.c are modified + (fixed) for last argument SIG, as the memory at SIG aren't modified + by those routines. + + The constant POLARSSL_MPI_MAX_SIZE in include/polarssl/bignum.h is + modified for 2048-bit keys only Gnuk. + + The function mpi_mul_hlp in library/bignum.c is modified for more + optimization for ARM Cortex-M3. Functions mpi_montred, mpi_sub_hlp, + mpi_sub_abs, mpi_mul_mpi, mpi_montmul, and mpi_exp_mod are modified + to avoid side channel attacks. Note that we don't use RSA-blinding + technique for Gnuk. Function mpi_gen_prime and mpi_is_prime are + modified to use Fouque-Tibouchi method. Function mpi_exp_mod is + modified to use new function mpi_montsqr for speed up. + + The file library/aes.c is modified so that some constants can + go to .sys section. + + The file include/polarssl/config.h are modified not to define + POLARSSL_HAVE_LONGLONG to avoid linking libgcc, to define + POLARSSL_AES_ROM_TABLES to have AES tables, not to define + POLARSSL_CIPHER_MODE_CTR, POLARSSL_FS_IO, POLARSSL_PKCS1_V21, + POLARSSL_SELF_TEST, and POLARSSL_PADLOCK_C, and only define + POLARSSL_GENPRIME when defined KEYGEN_SUPPORT. + + And polarssl/library/bignum.c is modified to work on 64-bit machine. + + Aurelien Jarno also modified: + + polarssl/include/polarssl/bn_mul.h + polarssl/library/bignum.c + + See ChangeLog (and/or history of git) for detail. + + +USB vendor ID and product ID (USB device ID) +============================================ + +When you have a vendor ID and assign a product ID for Gnuk, edit the +file GNUK_USB_DEVICE_ID and add an entry for yours. In this case, +please contact Niibe, so that it is listed to the file in the official +release of the source code. + +When you are modifing Gnuk and installing the binary to device, you +should replace the vendor string and serial number to yours (in the +file GNUK_USB_DEVICE_ID and SERIALNO of the script of src/configure), +so that users can see it's not by original vendor, and it is modified +version. + +FSIJ allows you to use USB device ID of FSIJ (234b:0000) for devices +with Gnuk under one of following conditions: + + * For everyone for experimental purpose: + + - You must not distribute a binary with FSIJ's USB device ID, but + must use the binary by yourself only for your experiment. Note + that "Distributing binary" includes distributing a device which + holds the binary. + + * For general individuals: + + - You must use your Gnuk device with a card serial number which is + *not* by FSIJ. Easy one would be a card serial number generated + by chip unique ID. + + * For individuals with explicit permission from FSIJ. + + - You should have an assigned card serial number by FSIJ, + please use that number for your device. + (There a file 'GNUK_SERIAL_NUMBER' in the official release.) + +FSIJ could give companies or business entities "second source +manufacturer" license to use USB device ID of FSIJ for devices with +unmodified version of Gnuk, provided they support Free Software and +respect users' freedom for computing. Please ask FSIJ for the +license. + +Otherwise, companies which want to distribute Gnuk devices, please use +your own USB vendor ID and product ID. Please replace vendor string +and possibly product string to yours, when you modify Gnuk. + + +Host Requirements +================= + +For GNU/Linux, PC/SC service is an option, you can use GnuPG's +internal CCID driver instead. If you chose using PC/SC service, +libccid version >= 1.3.11 is recommended for GNU/Linux. + + +How to compile +============== + +You need GNU toolchain and newlib for 'arm-none-eabi' target. + +On Debian we can install the packages of gcc-arm-none-eabi, +gdb-arm-none-eabi and its friends. I'm using: + + binutils-arm-none-eabi 2.28-4+9+b3 + gcc-arm-none-eabi 15:5.4.1+svn241155-1 + gdb-arm-none-eabi 7.12-6+9+b2 + libnewlib-arm-none-eabi 2.4.0.20160527-2 + +Or else, see https://launchpad.net/gcc-arm-embedded for preparation of +GNU Toolchain for 'arm-none-eabi' target. + +Change directory to `src': + + $ cd gnuk-VERSION/src + +Then, run `configure': + + $ ./configure --vidpid= + +Here, you need to specify USB vendor ID and product ID. For FSIJ's, +it's: --vidpid=234b:0000 . Please read section 'USB vendor ID and +product ID' above. + + +Then, type: + + $ make + +Then, we will have "gnuk.elf" under src/build directory. + + +How to install +============== + +Olimex STM32-H103 board +----------------------- + +If you are using Olimex JTAG-Tiny, type following to invoke OpenOCD +and write "gnuk.elf" to Flash ROM: + + $ openocd -f interface/ftdi/olimex-jtag-tiny.cfg \ + -f board/olimex_stm32_h103.cfg \ + -c "program build/gnuk.elf verify reset exit" + +Command invocation is assumed in src/ directory. + + +Flying Stone Tiny 01 +-------------------- + +If you are using Flying Stone Tiny 01, you need a SWD writer. + +OpenOCD 0.9.0 now supports ST-Link/V2. We can use it like: + + $ openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg \ + -c "program build/gnuk.elf verify reset exit" + + + +STBee +----- + +Reset the board with "USER" switch pushed. Type following to write +to flash: + + # cd ../tool + # ./dfuse.py ../src/build/gnuk.hex + +Then, reset the board. + + +How to protect flash ROM +======================== + +To protect, invoke OpenOCD like (for FST-01): + + $ openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg \ + -c init -c "reset halt" -c "stm32f1x lock 0" -c reset -c exit + +After power-off / power-on sequence, the contents of flash ROM cannot +be accessible from JTAG debugger. + +Unprotecting is: + + $ openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg \ + -c init -c "reset halt" -c "stm32f1x unlock 0" -c reset -c exit + +Upon unprotection, flash is erased. + +Note that it would be still possible for some implementation of DfuSe +to access the contents, even if it's protected. If you really want to +protect, killing DfuSe and accessing by JTAG debugger is recommended. + + +(Optional) Configure serial number and X.509 certificate +======================================================== + +This is completely optional. + +For this procedure, you need python and pyscard (python-pyscard +package in Debian) or PyUSB 0.4.3 (python-usb package in Debian). + +(1) [pyscard] Stop scdaemon + [PyUSB] Stop the pcsc daemon. + +If scdaemon is running, please kill it, or you will get "Smartcard +Exception" by "Sharing violation". + + $ gpg-connect-agent "SCD KILLSCD" "SCD BYE" /bye + +In case of PyUSB tool, you need to stop pcscd. + + # /etc/init.d/pcscd stop + + +(2) [Optional] Write fixed serial number + +If you use fixed serial number in the file 'GNUK_SERIAL_NUMBER', you can do: + + $ EMAIL= ../tool/gnuk_put_binary_usb.py -s ../GNUK_SERIAL_NUMBER + Writing serial number + ... + +(3) [Optional] Write card holder certificate + +If you have card holder certificate binary file, you can do: + + $ ../tool/gnuk_put_binary_usb.py ../../.bin + ../../.bin: + Updating card holder certificate + ... + + +How to run +========== + +Debug enabled +------------- + +If you compiled with --enable-debug option, Gnuk has two interfaces +(one is CCID/ICCD device and another is virtual COM port). Open +virtual COM port by: + + $ cu -l /dev/ttyACM0 + +and you will see debug output of Gnuk. + + +Testing Gnuk +------------ + +Type following command to see Gnuk runs: + + $ gpg --card-status + + +Besides, there is a functionality test under test/ directory. See +test/README. + + +Personalize the Token, import keys, and change the password +----------------------------------------------------------- + +You can personalize the token, putting your information like: Name, +Login name, Sex, Languages, URL. To do so, GnuPG command is: + + $ gpg --card-edit + +Note that the factory setting of user password is "123456" and admin +password is "12345678" as the specification. + +It is recommended to create your keys on your computer, and import +them to Gnuk Token. After you create your keys (they must be 2048-bit +RSA), you can import them. + +Gnuk supports key generation, but this feature is young and should be +considered experimental. + +For detail, please see documentation under doc/. You can see the HTML +version at: https://www.fsij.org/doc-gnuk/ + + +How to debug +============ + +We can use GDB. + + $ arm-none-eabi-gdb gnuk.elf + + +Inside GDB, we can connect OpenOCD by: + + (gdb) target remote localhost:3333 + +or + + (gdb) target extended-remote localhost:3333 + + +You can see the output of PCSCD: + + # /etc/init.d/pcscd stop + # LIBCCID_ifdLogLevel=7 /usr/sbin/pcscd --debug --foreground + + +You can observe the traffic of USB using "usbmon". See the file: +linux/Documentation/usb/usbmon.txt + + +Firmware update +=============== + +See doc/note/firmware-update. + + +Git Repositories +================ + +Please use: https://anonscm.debian.org/cgit/gnuk/gnuk/ + +You can get it by: + + $ git clone git://anonscm.debian.org/gnuk/gnuk/gnuk.git + +It's also available at: www.gniibe.org +You can browse at: https://git.gniibe.org/gitweb?p=gnuk/gnuk.git;a=summary + +I put Chopstx as a submodule of Git. Please do this: + + $ git submodule update --init + + +Information on the Web +====================== + +For more information, please visit: https://www.fsij.org/gnuk/ + +Please see the FST-01 support pages: + + https://www.gniibe.org/category/fst-01.html + +Please consider to join Gnuk-users mailing list: + + https://lists.alioth.debian.org/mailman/listinfo/gnuk-users + +The mailing list will be moved to lists.debian.org. + + +Your Contributions +================== + +FSIJ welcomes your contributions. Please assign your copyright +to FSIJ (if possible), as I do. + + +Foot note +========== + +* NUK(R) is a registered trademark owend by MAPA GmbH, Germany. +-- diff --git a/gnuk/README.md b/gnuk/README.md new file mode 100644 index 0000000000..d53fc064d6 --- /dev/null +++ b/gnuk/README.md @@ -0,0 +1,6 @@ +# An unofficial clone of FST Gnuk Cryptographic Token device for GnuPG + +* See `README` for the original README instruction. +* Note well that the `master` branch is modified by @jj1bdx. +* For the original master branch of NeuG, see `gniibe-master` branch. + diff --git a/gnuk/THANKS b/gnuk/THANKS new file mode 100644 index 0000000000..de1c6b198c --- /dev/null +++ b/gnuk/THANKS @@ -0,0 +1,43 @@ + -*- coding: utf-8 -*- + +We would like to express our gratitudes to Werner Koch for GnuPG, and +Giovanni Di Sirio for ChibiOS/RT. + +Gnuk was originally written by NIIBE Yutaka. People contributed by +encouraging the development, testing the implementation, suggesting +improvements, or fixing bugs. Here is a list of those people. + +Aurelien Jarno aurelien@aurel32.net +Achim Pietig achim@pietig.com +Aidan Thornton +Anibal Monsalve Salazar anibal@debian.org +Andre Zepezauer andre.zepezauer@student.uni-halle.de +Anthony Romano anthony.romano@coreos.com +Bertrand Jacquin bertrand@jacquin.bzh +Clint Adams clint@softwarefreedom.org +Daniel Kahn Gillmor dkg@fifthhorseman.net +Elliott Mitchell +Hironobu SUZUKI hironobu@h2np.net +Jan Suhr jan@suhr.info +Jeremy Drake jeremydrake+gnuk@eacceleration.com +Jonathan McDowell noodles@earth.li +Kaz Kojima kkojima@rr.iij4u.or.jp +Kenji Rikitake +Ludovic Rousseau ludovic.rousseau@free.fr +Luis Felipe R. Murillo luisfelipe@ucla.edu +Mateusz Zalega mateusz@nitrokey.com +MATSUU Takuto matsuu@gentoo.org +Micah Anderson micah@debian.org +NAGAMI Takeshi nagami-takeshi@aist.go.jp +Nguyễn Hồng Quân quannguyen@mbm.vn +Nico Rikken nico@nicorikken.eu +NOKUBI Takatsugu knok@daionet.gr.jp +Paul Fertser +Paul Bakker polarssl_maintainer@polarssl.org +Santiago Ruano Rincón santiago@debian.org +Shane Coughlan scoughlan@openinventionnetwork.com +Stanislas Bach sbach@0g.re +Szczepan Zalega szczepan@nitrokey.com +Vasily Evseenko +Werner Koch wk@gnupg.org +Yuji Imai ug@xcast.jp diff --git a/gnuk/VERSION b/gnuk/VERSION new file mode 100644 index 0000000000..de53852c11 --- /dev/null +++ b/gnuk/VERSION @@ -0,0 +1 @@ +release/1.2.6 diff --git a/gnuk/chopstx/.gitignore b/gnuk/chopstx/.gitignore new file mode 100644 index 0000000000..208d3c7e98 --- /dev/null +++ b/gnuk/chopstx/.gitignore @@ -0,0 +1,3 @@ +*/build +*/.dep +doc/chopstx.info diff --git a/gnuk/chopstx/.gitlab-ci.yml b/gnuk/chopstx/.gitlab-ci.yml new file mode 100644 index 0000000000..a829d2c780 --- /dev/null +++ b/gnuk/chopstx/.gitlab-ci.yml @@ -0,0 +1,15 @@ +image: debian:stable + +before_script: + - apt-get update && apt-get -y install texi2html + +pages: + stage: deploy + script: + - mkdir -p html && (cd html && texi2html ../doc/chopstx.texi) + - mv html/ public + artifacts: + paths: + - public + only: + - master diff --git a/gnuk/chopstx/AUTHORS b/gnuk/chopstx/AUTHORS new file mode 100644 index 0000000000..3168c052d7 --- /dev/null +++ b/gnuk/chopstx/AUTHORS @@ -0,0 +1,76 @@ +Aidan Thornton: + Added Maple Mini support. + board/board-maple-mini.h + +Jeremy Drake: + Modified STM32F103 support. + mcu/sys-stm32f103.c + +Kaz Kojima: + Added STM32 Primer2 support. + board/board-stm32-primer2.h + example-primer2 + +Kenji Rikitake: + Added ST Dongle support. + board/board-st-dongle.h + Added ST Nucleo F103 support. + board/board-st-nucleo-f103.h + +Kiwamu Okabe: + Wrote an OpenOCD scirpt: + example-fsm-55/stlink-v2.cfg + +Mateusz Zalega: + Added Nitrokey-Start support. + board/board-nitrokey-start.h + +NIIBE Yutaka: + Wrote the library: + chopstx.c, chopstx.h, + chopstx-cortex-m.c, chopstx-cortex-m.h, + chopstx-gnu-linux.c, chopstx-gnu-linux.h, + entry.c, + eventflag.c, eventflag.h + Wrote the drivers mcu/*: + chx-gnu-linux.c, chx-mkl27z.c, chx-stm32f0.c, chx-stm32f103.c, + clk_gpio_init-mkl27z.c, clk_gpio_init-stm32.c, + cortex-m.h, mkl27z.h, stm32.h, stm32f103.h, + sys-gnu-linux.c,sys-gnu-linux.h, + sys-mkl27z.c, sys-mkl27z.h, + sys-stm32f0.c, sys-stm32f0.h + sys-stm32f103.c, sys-stm32f103.h, + usb-stm32f103.c, usb-mkl27z.c + Wrote the drivers: + controb/adc-mkl27z.c + Drew the logo: + chopstx.svg, chopstx.png + Wrote examples: + example-led, example-cdc, example-fsm-55, example-fs-bb48, + example-usb-serial, example-cdc-gnu-linux + Wrote board/*: + board-fst-01sz.h, + board-fst-01g.h, board-fst-01.h, board-fst-01-00.h, + board-olimex-stm32-h103.h, board-stm8s-discovery.h + board-cq-starm.h, board-stbee-mini.h, board-stbee.h, + + board-stm32f0-discovery.h, board-fsm-55.h, + + board-fs-bb48.h + + For Free Software Initiative of Japan, wrote: + contrib/adc-stm32f103.c, + contrib/adc-gnu-linux.c + + Under contract of g10 Code GmbH, wrote: + mcu/usb-usbip.c + contrib/usart-stm32f103.c + contrib/ackbtn-stm32f103.c + +Paul Fertser: + Added Blue Pill support. + board/board-blue-pill.h + +Szczepan Zalega: + Modified Nitrokey-Start support. + board/board-nitrokey-start.h diff --git a/gnuk/chopstx/COPYING b/gnuk/chopstx/COPYING new file mode 100644 index 0000000000..94a9ed024d --- /dev/null +++ b/gnuk/chopstx/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/gnuk/chopstx/ChangeLog b/gnuk/chopstx/ChangeLog new file mode 100644 index 0000000000..58c6fd2302 --- /dev/null +++ b/gnuk/chopstx/ChangeLog @@ -0,0 +1,1387 @@ +2019-03-02 NIIBE Yutaka + + * VERSION: 1.14. + * doc/chopstx.texi (VERSION): 1.14. + +2019-03-01 NIIBE Yutaka + + * contrib/usart-stm32f103.c (usart_config): Fix SCEN setting + procedure. + +2019-03-01 NIIBE Yutaka + + * contrib/usart-stm32f103.c (usart_config): Support parity error + interrupt setting. + (usart_init): Don't call usart_config here. + * example-usb-serial/sample.c: It's application calling + usart_config. + +2019-02-28 NIIBE Yutaka + + * contrib/usart.h (BSCARD): New. + * contrib/usart-stm32f103.c (BSCARD): Baudrate for smartcard. + (usart_config_clken): New. + (usart_config): Fix for MODE_SMARTCARD. + + * board/board-st-nucleo-f103.h: Define pins for smartcard + interface. + +2019-02-21 NIIBE Yutaka + + * contrib/usart.h (MODE_SMARTCARD, MODE_IRDA, MODE_IRDA_LP) + (MASK_MODE): New. + + * contrib/usart-stm32f103.c (usart_config): Add support for + those modes. + +2018-12-19 NIIBE Yutaka + + * VERSION: 1.13. + * doc/chopstx.texi (VERSION): 1.13. + +2018-12-08 NIIBE Yutaka + + * chopstx.c (chx_timer_dequeue): Fix calculation of return value. + +2018-12-07 NIIBE Yutaka + + * chopstx.c (chx_timer_dequeue): Return ticks remained. + (chx_wakeup, chopstx_mutex_lock): Use return value of + chx_timer_dequeue. + (chx_snooze): Update *USEC_P, accordingly. + +2018-11-12 NIIBE Yutaka + + * VERSION: 1.12. + * doc/chopstx.texi (VERSION): 1.12. + + * contrib/ackbtn-stm32f103.c (ackbtn_init): Support FST-01 and + FST-01G, using PA2. + (ackbtn_disable): Fix to correctly clear pending interrupt. + +2018-11-09 NIIBE Yutaka + + * eventflag.c (eventflag_set_mask): New. + +2018-10-02 NIIBE Yutaka + + * VERSION: 1.11. + * doc/chopstx.texi (VERSION): 1.11. + +2018-10-01 NIIBE Yutaka + + * contrib/ackbtn-stm32f103.c (PINCFG_EDGE): New. + (ackbtn_init): Use PINCFG_EDGE bit. + (ackbtn_enable): Configure the edge detector here. + (ackbtn_disable): Disable the edge detector. + + * chopstx.c (chopstx_poll): Don't clear ->ready here. + (chx_cond_hook): Set ->ready = 0 when condition does + not meet. + (chopstx_claim_irq): Make sure clearing the interrupt. + (chx_intr_hook): Add the case when ->ready == 1. + (chopstx_intr_done): Set ->ready = 0. + (chx_join_hook): Set ->ready = 0 when it is still alive. + +2018-09-29 NIIBE Yutaka + + * VERSION: 1.10. + * doc/chopstx.texi (VERSION): 1.10. + +2018-09-27 NIIBE Yutaka + + * contrib/ackbtn-stm32f103.c: New. + * rules.mk [USE_ACKBTN] (CSRC): Add ack-button support. + +2018-09-26 NIIBE Yutaka + + Fix for chopstx_intr_done. No spurious interrupts. + * contrib/adc-mkl27z.c (adc_wait_completion): Fix. + * contrib/adc-stm32f103.c (adc_wait_completion): Likewise. + * contrib/usart-stm32f103.c (usart_main): Likewise. + * example-cdc-gnu-linux/usb-cdc.c (tty_main): Likewise. + * example-cdc/usb-cdc.c (tty_main): Likewise. + * example-fraucheky/main.c (usb_main): Likewise. + * example-fs-bb48/touch.c (touch_get): Likewise. + * example-fs-bb48/usb-cdc.c (tty_main): Likewise. + * example-primer2/primer2-ts.c (adc3_conversion): Likewise. + * example-usb-serial/usb-cdc.c (cdc_main): Likewise. + +2018-09-26 NIIBE Yutaka + + * chopstx.h (chopstx_intr_done): New function. + * chopstx.c (chopstx_intr_done): New function. + (chopstx_poll): Don't call chx_clr_intr. + Ensure data memory barrier for interrupt handling. + + * chopstx-cortex-m.c (chx_dmb): New static function. + * chopstx-gnu-linux.c (chx_dmb): Ditto. + +2018-09-21 NIIBE Yutaka + + * mcu/stm32.h (struct GPIO, struct FLASH): Moved from... + * mcu/clk_gpio_init-stm32.c: ... here. + (AFIO_MAPR_SWJ_CFG_JTAGDISABLE): Move to ... + * mcu/stm32f103.h: ... here. + * mcu/sys-stm32f103.c: Don't include "mcu/cortex-m.h". + + * board/board-fst-01sz.h: New. + * mcu/sys-stm32f103.h (BOARD_ID_FST_01SZ): New. + * contrib/adc-stm32f103.c (get_adc_config): Add BOARD_ID_FST_01SZ. + +2018-08-20 NIIBE Yutaka + + * mcu/usb-stm32f103.c (usb_lld_ctrl_send): Fix for ZLP. + * mcu/usb-usbip.c (usb_lld_ctrl_send): Likewise. + * mcu/usb-mkl27z.c (usb_lld_ctrl_send): Likewise. + +2018-05-09 NIIBE Yutaka + + * VERSION: 1.9. + * doc/chopstx.texi (VERSION): 1.9. + +2018-05-08 NIIBE Yutaka + + * board/board-blue-pill.h (GPIO_USB_SET_TO_ENABLE): Remove. + + * doc/chopstx.texi (Compile-time macro): New chapter. + + * mcu/sys-stm32f103.c (usb_lld_sys_init): Use MHZ. + (flash_protect): Fix for GD32F103. + +2018-04-26 NIIBE Yutaka + + * board/board-blue-pill-g.h: New. Define STM32_ADCPRE and + STM32_USBPRE for 96MHz clock. + * mcu/stm32.h (STM32_USBPRE_DIV2): New for GD32F103. + * mcu/clk_gpio_init-stm32.c: Allow constants, which are defined by + board.h (STM32_ADCPRE and STM32_USBPRE). + * mcu/chx-stm32f103.c: Use STM32_ADCPRE and STM32_USBPRE. + + * mcu/usb-stm32f103.c (usb_lld_init): BTABLE setting at + initialization. + (usb_lld_reset): Not at each reset. + + * contrib/adc-stm32f103.c [MCU_STM32F1_GD32F1]: Use continuous + sampling with no DELIBARATELY_DO_IT_WRONG_START_STOP. + (adc_init): Wait after ADC_CR2_ADON. + (adc_start): Likewise. Enabling by ADC_CR2_ADON after all other + registers configuration. + +2018-01-19 NIIBE Yutaka + + * VERSION: 1.8. + * doc/chopstx.texi (VERSION): 1.8. + +2018-01-18 NIIBE Yutaka + + * mcu/stm32f103.h: Include stm32.h and cortex-m.h. + +2018-01-09 Anthony Romano + + * contrib/adc-stm32f103.c: Support IN8 config. + +2017-12-19 NIIBE Yutaka + + * VERSION: 1.7. + * doc/chopstx.texi (VERSION): 1.7. + +2017-12-18 NIIBE Yutaka + + * contrib/usart-stm32f103.c: New. + * example-usb-serial: New example. + +2017-12-16 NIIBE Yutaka + + * chopstx.c (chx_timer_expired): Bug fix of wake up. + (chx_mutex_unlock): Avoid non-deterministic thing. + +2017-12-14 NIIBE Yutaka + + * board/board-st-nucleo-f103.h: Update. + + * mcu/sys-stm32f103.h (BOARD_ID_FST_01G): New. + + * contrib/adc-stm32f103.c (get_adc_config): Add a case for + BOARD_ID_FST_01G and BOARD_ID_ST_NUCLEO_F103. + +2017-11-24 NIIBE Yutaka + + * VERSION: 1.6. + * doc/chopstx.texi (VERSION): 1.6. + * doc/chopstx-api.texi: Regenerated. + +2017-11-21 NIIBE Yutaka + + * example-fsm-55: Update using stack-def.h. + +2017-11-20 NIIBE Yutaka + + * example-led: Update using stack-def.h. + + * chopstx-cortex-m.c [__ARM_ARCH_6M__] (chx_sched): Fix. + +2017-11-18 NIIBE Yutaka + + * example-fsm-55/hh.c (main): Just return. + +2017-11-17 NIIBE Yutaka + + * mcu/chx-stm32f103.c (configure_clock): Change prescaler on + sleep. + + * mcu/clk_gpio_init-stm32.c (clock_init): Don't turn on CRC + module. + + * mcu/clk_gpio_init-stm32.c (STM32_*): Move to... + * mcu/stm32.h (STM32_*): ... this header file. + + * mcu/stm32f103.h, mcu/clk_gpio_init-stm32.c (RCC): Move to... + * mcu/stm32.h (RCC): ... this header file. + + * example-fsm-55: Update for new sleep API. + + * rules.mk (CSRC): Add mcu/chx-$(CHIP).c. + + * chopstx-cortex-m.c (idle, chx_sleep_mode): Remove. + (chx_sched, preempt): Call chx_idle. + * mcu/chx-mkl27z.c, mcu/chx-stm32f0.c, mcu/chx-stm32f103.c: New. + * mcu/stm32.h: New. + + * chopstx-gnu-linux.c (chx_sleep_mode): Move to... + * mcu/chx-gnu-linux.c: Here. + + * mcu/stm32f103.h (DBGMCU): New. + + * mcu/cortex-m.h: New. + * mcu/sys-stm32f0.c, mcu/sys-stm32f103.c: Use mcu/cortex-m.h. + + * chopstx.c (chopstx_conf_idle): Call chx_sleep_mode before + changing chx_allow_sleep. + + * board/board-blue-pill.h, board/board-cq-starm.h, + board/board-fst-01-00.h, board/board-fst-01.h, + board/board-fst-01g.h, board/board-maple-mini.h, + board/board-nitrokey-start.h, board/board-olimex-stm32-h103.h, + board/board-st-dongle.h, board/board-st-nucleo-f103.h, + board/board-stbee-mini.h, board/board-stbee.h, + board/board-stm32-primer2.h: Add MCU_STM32F1. + +2017-11-16 NIIBE Yutaka + + * chopstx.h (chopstx_poll): API change. + * chopstx.c (chopstx_poll): Use const pointer. + + * chopstx-cortex-m.c (idle): Support WFE. + [!MCU_STM32F0] (chx_sleep_mode): Clock feed by HSI. + +2017-11-15 NIIBE Yutaka + + * chopstx.c (chopstx_conf_idle): Call chx_sleep_mode. + * chopstx-cortex-m.c (chx_sleep_mode): New. + * chopstx-gnu-linux.c (chx_sleep_mode): New. + +2017-11-15 NIIBE Yutaka + + * example-fsm-55/hh.c (main): Add call to chopstx_conf_idle. + * example-fsm-55/Makefile (DEFS): Remove USE_WFI_FOR_IDLE. + + * chopstx.c (chx_init): Initialize sleep lock. + (chopstx_conf_idle): New. + * chopstx-cortex-m.c (idle): Support sleep. + + * mcu/usb-stm32f103.c (usb_lld_event_handler): Fix suspend/resume. + +2017-11-14 NIIBE Yutaka + + * usb_lld.h (DEVICE_STATE): Add prefix USB_DEVICE_STATE_. + (USB_DEVICE_STATE_DEFAULT): New. + + * mcu/usb-stm32f103.c (usb_lld_init): Suspend/resume init. + (usb_lld_event_handler): Add suspend/resume support. + + * example-cdc/usb-cdc.c: Follow the change of usb_lld.h. + * example-cdc-gnu-linux/usb-cdc.c: Ditto. + * example-fs-bb48/usb-cdc.c: Ditto. + * example-fraucheky/main.c: Ditto. + * example-fraucheky/Makefile (USE_SYS): Add for debug. + +2017-11-13 NIIBE Yutaka + + * mcu/usb-stm32f103.c (usb_lld_init): Set up interrupt on errors. + + * usb_lld.h (USB_EVENT_DEVICE_WAKEUP): New. + (INTR_REQ_USB): Define here. + * mcu/usb-usbip.c, example-cdc/usb-cdc.c, + example-fraucheky/main.c, example-fs-bb48/usb-cdc.c: Change + for INTR_REQ_USB. + +2017-11-10 NIIBE Yutaka + + * chopstx-cortex-m.c (svc): Replace stack top to TP. + (chx_sched): Return ->v correctly. + + * chopstx.c (chx_timer_expired): Fix timeout. + +2017-10-11 NIIBE Yutaka + + * mcu/sys-stm32f103.h (nonreturn_handler0, nonreturn_handler1): New. + +2017-10-10 NIIBE Yutaka + + * mcu/usb-usbip.c (usbip_run_server): Shutdown support. + (usb_lld_shutdown): Shutdown glace-fully. + +2017-10-10 NIIBE Yutaka + + * VERSION: 1.5. + * doc/chopstx.texi (VERSION): 1.5. + * doc/chopstx-api.texi: Regenerated. + +2017-10-09 NIIBE Yutaka + + * example-cdc-gnu-linux/sample.c (main): Support debug option. + + * mcu/sys-gnu-linux.h (debug): New. + * mcu/sys-gnu-linux.c: Use debug. + * mcu/usb-usbip.c: Use debug. + + * example-cdc/sample.ld (.process_stack, .main_stack): Add NOLOAD. + +2017-09-29 NIIBE Yutaka + + * mcu/sys-stm32f103.c (flash_program_halfword, flash_erase_page) + (flash_write, flash_erase_all_and_exec): Use uintptr_t. + +2017-09-08 NIIBE Yutaka + + * mcu/sys-gnu-linux.c: Flash emulation implemented. + +2017-09-05 NIIBE Yutaka + + * example-cdc: Use stack-def.h. + * example-cdc/stack-def.h: New. + +2017-09-04 NIIBE Yutaka + + * mcu/sys-stm32f103.h (nvic_system_reset): Add noreturn attribute. + + * sys.h: Add mcu/sys-gnu-linux.h. + + * mcu/sys-gnu-linux.c: New. + * mcu/sys-gnu-linux.h: New. + +2017-08-11 NIIBE Yutaka + + * VERSION: 1.4. + * doc/chopstx.texi (VERSION): 1.4. + +2017-08-03 Jeremy Drake + + * mcu/sys-stm32f103.c (flash_write): Allow compile time + flash size definition by STM32F103_OVERRIDE_FLASH_SIZE_KB. + +2017-08-02 NIIBE Yutaka + + * contrib/adc-gnu-linux.c: New. + +2017-07-07 NIIBE Yutaka + + * mcu/usb-usbip.c (usbip_handle_data_urb): Fix error return value. + (notify_hostcontroller): New. + (usb_lld_stall_tx, usb_lld_stall_rx): Notify host controller side. + +2017-07-06 NIIBE Yutaka + + * mcu/usb-usbip.c (hc_handle_data_urb): Fix the condition of the + end of transaction. + (read_data_transaction): Allow partial read by host. + +2017-07-05 NIIBE Yutaka + + * example-fraucheky: New. + +2017-06-28 NIIBE Yutaka + + * mcu/usb-usbip.c (struct usbip_msg_cmd, struct usbip_msg_rep): + Use different struct for command and reply. + +2017-06-27 NIIBE Yutaka + + * example-cdc-gnu-linux/README: New. + +2017-06-26 NIIBE Yutaka + + * board/board-gnu-linux.h: New. + + * mcu/usb-usbip.c: New. + + * rules.mk: Support EMULATION. + + * entry.c (main): Support GNU_LINUX_EMULATION. + +2017-06-23 NIIBE Yutaka + + * chopstx-gnu-linux.c, chopstx-gnu-linux.h: New. + + * chopstx.c (chx_init): Use chx_init_arch. + (chopstx_create): Use chopstx_create_arch. + (chx_systick_reset, chx_systick_reload, chx_systick_get) + (usec_to_ticks, chx_enable_intr, chx_clr_intr, chx_disable_intr) + (chx_set_intr_prio, chx_prio_init, chx_cpu_sched_lock) + (chx_cpu_sched_unlock, idle, chx_handle_intr) + (chx_request_preemption, chx_sched, preempt, svc): Move to... + * chopstx-cortex-m.c: ... here. + (chx_init_arch, chopstx_create_arch): New. + * chopstx-cortex-m.h: New for tcontext_t. + +2017-06-22 NIIBE Yutaka + + * chopstx.c (chx_sched): Use ->V for return value. + (svc): Ditto. + (chx_wakeup): Set -> for the return value of chx_sched. + (chopstx_mutex_lock, chopstx_cancel): Likewise. + (chopstx_exit): Remove naked attribute. + +2017-06-21 NIIBE Yutaka + + * chopstx.c (struct chx_pq): Use uintptr_t for V. + (struct chx_px): Likewise. + (struct chx_thread): Likewise. + (chx_sched): Use uintptr_t for the return value. + (chx_exit): Use the field V for holding exit value. + (chopstx_create): Use uintptr_t for STACK_ADDR. + (chopstx_join): Use the field V for exit value. + +2017-06-16 NIIBE Yutaka + + * chopstx.c (chx_wakeup): Fix for RUNNING==NULL. + + * mcu/usb-mkl27z.c (std_get_status): Use staic var, not auto. + +2017-05-18 NIIBE Yutaka + + * chopstx.c (chx_wakeup): Fix access to REG_R0. + +2017-02-02 NIIBE Yutaka + + * VERSION: 1.3. + * doc/chopstx.texi (VERSION): 1.3. + +2017-02-01 Paul Fertser + + * board/board-blue-pill.h: New. + +2016-12-08 Szczepan Zalega + + * board/board-nitrokey-start.h (VAL_GPIO_USB_ODR): Switch off red + LED for Nitrokey Start. + +2016-10-13 NIIBE Yutaka + + * VERSION: 1.2. + * doc/chopstx.texi (VERSION): 1.2. + +2016-10-12 NIIBE Yutaka + + * chopstx.c (chopstx_join, chopstx_cancel): chopstx_join is + cancellation point. + +2016-07-11 NIIBE Yutaka + + * mcu/stm32f103.h: Add more from Gnuk 1.1.9. + * example-cdc, example-fs-bb48, example-fsm-55, example-led: + Update for stack addr/size. + +2016-07-01 NIIBE Yutaka + + * VERSION: 1.1. + * doc/chopstx.texi (VERSION): 1.1. + + * chopstx.c (chopstx_setpriority): Change the API. + +2016-06-30 NIIBE Yutaka + + * example-cdc/usb-cdc.c (tty_recv, tty_main): Follow the change of + chopstx_poll. + +2016-06-29 NIIBE Yutaka + + * example-fs-bb48: Update. + * example-fs-bb48/touch.c: New. + + * chopstx.c (chopstx_setpriority): Fix sched_lock/unlock. + +2016-06-28 NIIBE Yutaka + + * chopstx.h (struct chx_poll_head): Declare here. + + * chopstx.c (chopstx_poll): Don't use varargs, but use + an array of pointer. + (chopstx_intr_wait): Follow the change of chopstx_poll. + * eventflag.c (eventflag_wait_timeout): Likewise. + * contrib/adc-stm32f103.c (adc_wait_completion): Likewise. + * contrib/adc-mkl27z.c (adc_wait_completion): Likewise. + +2016-06-16 Niibe Yutaka + + * VERSION: 1.0. + * doc/chopstx.texi (VERSION): 1.0. + + * board/board-fst-01g.h: New. + +2016-06-15 Niibe Yutaka + + * chopstx.c (chopstx_exit): Add "naked" attribute. + +2016-06-14 Niibe Yutaka + + * mcu/usb-stm32f103.c (usb_lld_ctrl_recv): Fix the state and make + the endpoint 0 RX_VALID. + +2016-06-10 NIIBE Yutaka + + * usb_lld.h (USB_EVENT_OK): Rename. + * mcu/usb-stm32f103.c: Update. + * mcu/usb-mkl27z.c: Likewise. + + * example-cdc/usb-cdc.c: Follow the change of API. + * example-fs-bb48/usb-cdc.c: Likewise. + +2016-06-09 NIIBE Yutaka + + * mcu/usb-stm32f103.c (usb_lld_ctrl_recv): Rename. + (usb_lld_ctrl_send): Rename. + (usb_lld_ctrl_ack): Rename and let have return value. + * mcu/usb-mkl27z.c: Likewise. + + * example-cdc/usb-cdc.c: Follow the change of API. + * example-fs-bb48/usb-cdc.c: Likewise. + +2016-06-08 NIIBE Yutaka + + * mcu/usb-stm32f103.c: Rewrite to be event driven API. + * mcu/usb-mkl27z.c: Likewise. + + * example-cdc/usb-cdc.c: Update to new USB API. + * example-fs-bb48/usb-cdc.c: Likewise. + + * example-cdc/tty.h: Change tty API to be C string (char*) + friendly. + * example-fs-bb48/tty.h: Ditto. + +2016-06-02 Niibe Yutaka + + * contrib/adc-mkl27z.c: Move from mcu. + * contrib/adc-stm32f103.c: Move from mcu. + + * example-led/Makefile (CHIP): Define as stm32f0. + + * mcu/sys-stm32f0.h: Rename from sys-stm32f030.h, + as we share it among STM32F030 and STM32F050. + * mcu/sys-stm32f0.c: Likewise. + + * board/board-stm32f0-discovery.h (MCU_STM32F0): Define. + + * sys.h: Support STM32F0. + +2016-06-01 NIIBE Yutaka + + * chopstx.h (chx_fatal): Remove weak attribute from declaration. + * chopstx.c (chx_fatal): Add weak attribute to implementation. + +2016-05-31 NIIBE Yutaka + + * VERSION: 0.12. + * doc/chopstx.texi (VERSION): 0.12. + + * sys.h: New. + + * example-cdc, example-fs-bb48: Update. + * example-fsm-55, example-led: Update. + +2016-05-30 NIIBE Yutaka + + * mcu/usb-stm32f103.c (usb_lld_setup_endpoint): Start with + EP_RX_NAK. + + * mcu/usb-mkl27z.c (handle_transaction): Handle NAK case. + (usb_lld_setup_endp): Rename from usb_lld_setup_endpoint. + (usb_lld_rx_enable_buf): Rename from usb_lld_rx_enable. + (usb_lld_tx_enable_buf): Rename from usb_lld_tx_enable. + + * mcu/adc-stm32f103.c: New from NeuG. + * mcu/stm32f103.h: New from NeuG. + + * example-cdc: Update. + +2016-05-30 NIIBE Yutaka + + * mcu/adc-mkl27z.c, mcu/sys-mkl27z.c, mcu/usb-mkl27z.c: Moved from + example-fs-bb48. + + * mcu/adc-stm32f103.c, mcu/sys-stm32f103.c, mcu/usb-stm32f103.c: + Moved from example-cdc. + +2016-05-30 NIIBE Yutaka + + * example-fs-bb48/sys.c (flash_do_internal, flash_do) + (flash_erase_page, flash_program_word): New. + * example-fs-bb48/command.c (cmd_fes, cmd_fww): New. + +2016-05-27 NIIBE Yutaka + + * example-fs-bb48/command.c (cmd_sysinfo): New. + * example-fs-bb48/sample.ld: Update. + * example-fs-bb48/sys.h: New. + Move crc32 functions declarations here. + * example-fs-bb48/sys.c: Rename from first-pages.c. + Include mcu/clk_gpio_init-kl.c to define clock_init and + gpio_init. + (set_led): New. + (reset): Initialize MSP. + (flash_config): Include comparison key. + (crc32_init, crc32_u8, crc32_u32): Change the API. + Write them in assembler so that the size of functions + keep unchanged. + (sys_version, sys_board_id, sys_board_name, sys_vector): New. + +2016-05-26 NIIBE Yutaka + + * entry.c: Follow the move of clk_gpio_init*.c + * example-cdc/sys.c: Likewise. + * example-led/sys.c: Likewise. Update to 3.0. + * example-primer2/sys.c: Update. + + * mcu/clk_gpio_init-stm32.c: Moved. + * mcu/clk_gpio_init-kl.c: Moved. + * mcu/kl_sim.h: New (was in example-fs-bb48). + + * example-fs-bb48/adc_kl27z.c: Use DMA0 and DMA1. + * example-fs-bb48/adc_kl27z.c (adc_wait_completion): Use only + least significant 8-bit (most significant for randomness). + * example-fs-bb48/command.c (cmd_adc): Rename from + cmd_temperature. + +2016-05-24 NIIBE Yutaka + + * example-fs-bb48/command.c: New. + * example-fs-bb48: Update example CDC device. + +2016-05-23 NIIBE Yutaka + + * chopst.x (chx_sched) [__ARM_ARCH_6M__]: Maintain PSR. + +2016-05-23 NIIBE Yutaka + + * chopstx.c (chopstx_poll): Fix a race. Check COUNTER. + + * example-cdc/usb-cdc.c (tty_input_char): Include newline. + * example-cdc/sample.c: Handle newline. + + * example-fs-bb48/usb-cdc.c: Update from example-cdc. + * example-fs-bb48/tty.h: Likewise. + * example-fs-bb48/sample.c: Follow the change. + +2016-05-20 NIIBE Yutaka + + * chopstx.c (chopstx_usec_wait_var): internal function. + (chopstx_wakeup_usec_wait): Remove. + +2016-05-19 NIIBE Yutaka + + * VERSION: 0.11. + * doc/chopstx.texi (VERSION): 0.11. + +2016-05-18 NIIBE Yutaka + + * eventflag.c: Update using chopstx_poll and offer API for poll. + + * chopstx.c (requeue): New. + (chopstx_mutex_lock, chopstx_join): Fix by requeue. + (chopstx_main_init): Remove. + (chopstx_setpriority): New. + + * example-cdc/usb-cdc.c: Prepare for multiple TTYs. + +2016-05-17 NIIBE Yutaka + + * example-cdc/usb-cdc.c: Update as TTY input/output with line + editing support. + * example-cdc/sample.c: Likewise. + * example-cdc/tty.h: Rename from stream.h. + + * chopstx.c (chopstx_poll): Set ->ready = 0. + Add spinlock for ll_dequeue. + +2016-05-16 NIIBE Yutaka + + * example-cdc/sys.c (nvic_enable_vector): Remove. + (usb_lld_sys_init): Don't setup NVIC priority. + (sys_version): sys version 3.0. + +2016-05-16 NIIBE Yutaka + + * chopstx.h (CHOPSTX_EXIT_SUCCESS, CHOPSTX_EXIT_CANCELED) + (CHOPSTX_EXIT_CANCELED_IN_SYNC): Remove. + (CHOPSTX_CANCELED): New. + + * chopstx.c (chopstx_testcancel): Use CHOPSTX_CANCELED. + (chx_sched, chx_snooze): Re-define return values cleanly. + (chopstx_cancel): Use return value of chx_sched to cancel. + (chopstx_cond_wait, chopstx_join, chopstx_poll): Implement + cancellation rather easier way with return value of chx_sched. + +2016-05-15 NIIBE Yutaka + + * chopstx.c (chopstx_claim_irq): Don't register clean up function. + (chx_release_irq): Remove. + (chopstx_cancel): Disable IRQ when canceled at POLL. + (chopstx_poll): Fix IRQ handling. + +2016-05-13 NIIBE Yutaka + + * chopstx.c (chx_handle_intr): Call chx_request_preemption. + (chx_wakeup, chopstx_cond_signal, chopstx_cond_broadcast) + (chx_intr_hook, chopstx_poll): Cleanup. + +2016-05-13 NIIBE Yutaka + + * chopstx.c (chopstx_exit): Don't call chx_release_irq_thread. + (chx_release_irq_thread): Remove. + (q_intr): New variable. + (intr_top): Remove. + (chx_handle_intr, chx_init, chopstx_claim_irq) + (chopstx_intr_wait): Use Q_INTR. + (chx_intr_hook): New. + (chopstx_poll): Support CHOPSTX_POLL_INTR. + (chopstx_release_irq): Remove. + (chx_release_irq): New internal function. + (THREAD_WAIT_INT): Remove. + (chopstx_intr_wait): Rewrite by chopstx_poll. + +2016-05-13 NIIBE Yutaka + + * chopstx.c (chx_sched) [__ARM_ARCH_6M__]: Fix asm. + (chx_handle_intr, chx_wakeup, chopstx_cancel): Fix for polling + with no timeout. + + * example-cdc/sample.c (main): Update chopstx_poll example. + * example-fs-bb48/sample.c (main): Ditto. + + * chopstx.c (struct chx_px): Add READY_P and LOCK. + (chx_proxy_init): Add initialization of new members. + (chx_wakeup): Spinlock PX. + (chopstx_mutex_lock): Add protection by splinlock. + (chx_cond_hook, chx_join_hook): Change arguments. + (chx_cond_unhook, chx_join_unhook): Remove. + (chopstx_claim_irq): Compatible to chx_poll. + (chopstx_join): Fix spinlocking. + (chopstx_cancel): Fix spinlocking. + +2016-05-12 NIIBE Yutaka + + * example-cdc/sample.c: Update using chopstx_poll. + * example-cdc/sample.ld: Likewise. + * example-cdc/stream.h: Likewise. + * example-cdc/usb-cdc.c: Likewise. + + * chopstx.c (chopstx_mutex_init): Initialize OWNER, too. + (preempt) [__ARM_ARCH_7M__]: Bug fix for register 0 to inhibit + scheduling. + (chx_sched) [__ARM_ARCH_6M__]: Return YIELD normally. + (MAX_USEC_FOR_TIMER): Guarantee 24-bit tick. + (chx_snooze): Revert the change of 2016-04-24 and modify + chopstx_poll instead. + (chopstx_poll): Support waiting forever. + +2016-04-24 Niibe Yutaka + + * chopstx.c (chx_snooze): Wait forever when it's POLL and usec==0. + +2016-04-22 Niibe Yutaka + + * example-fs-bb48/first-pages.c: Rename from reset.c and merge + crc32.c. + * example-fs-bb48/sample.ld: Define section for first two pages. + * example-fs-bb48/usb_kl27z.c (__usb_buf__): Remove. + + * chopstx.c (chx_cond_hook): Rename from chopstx_cond_hook and make + it internal. + (chx_cond_unhook): Likewise. + (chx_join_hook, chx_join_unhook): New. + (chopstx_poll): Change API, not exposing internals of hook/unhook. + + * example-fs-bb48/sample.c (main): Follow the API change of + chopstx_poll. + +2016-04-21 Niibe Yutaka + + * chopstx.c (chx_snooze, chx_wakeup): New. + (chopstx_cond_hook, chopstx_cond_unhook): New. + (chopstx_cond_signal, chopstx_cond_broadcast): Fix for poll. + (chx_proxy_init): Initialize with RUNNING. + (chopstx_poll): Fix with chx_snooze. + (chx_exit): Use chx_wakeup. + + * example-fs-bb48/sample.c (main): Update with chopstx_poll. + * example-fs-bb48/usb-cdc.c (stream_recv): Fix the loop clear + FLAG_RECV_AVAIL only after we process it. + +2016-04-20 Niibe Yutaka + + * example-cdc/usb_stm32f103.c (usb_lld_reset): Supply FEATURE + argument. Call usb_lld_set_configuration internally and set + FEATURE. + (usb_lld_set_feature): Remove. + * example-cdc/usb-cdc.c (usb_cb_device_reset): Update with new + API. + + * example-cdc/sys.c: Include clk_gpio_init-stm32.c. + * example-led/sys.c, example-primer2/sys.c: Ditto. + +2016-04-19 Niibe Yutaka + + * example-fs-bb48/sample.c (main): Change the example to display + CRC32 value from input line. + +2016-04-18 Niibe Yutaka + + * example-fs-bb48: New directory for FS-BB48. + + * clk_gpio_init-kl.c: New. + * clk_gpio_init-stm32.c: Rename from clk_gpio_init.c. + +2016-04-07 Niibe Yutaka + + * example-fsm-55/sys.c: Update for non-SVC Chopstx. + +2016-04-07 Niibe Yutaka + + * example-cdc/usb-cdc.c: Update. + + * example-cdc/usb_stm32f103.c (usb_handle_transfer): Don't use + weak symbols for callbacks, but use explicit callbacks of + usb_cb_tx_done and usb_cb_rx_ready. + + * example-cdc/usb_lld.h (usb_cb_tx_done, usb_cb_rx_ready): New + callbacks. + +2016-04-07 Niibe Yutaka + + * chopstx.c (chx_cpu_sched_lock, chx_cpu_sched_unlock): Use SVC + for Cortex-M3, because of ICI/IT of ESPR. + (chx_sched): Invoke svc for Cortex-M3. + (preempt, svc): Change back for Cortex-M3. + +2016-04-07 Niibe Yutaka + + * entry.c (vector_table): Since IDLE thread runs with PSP now, use + different value for MSP. + + * chopstx.c (chx_sched): Push LR value to PC slot on stack, so + that it returns directly to caller. + +2016-04-06 Niibe Yutaka + + * chopstx.c (struct chx_pq): New struct for priority queue. + (struct chx_px): New struct for proxy. + (struct chx_thread): New member FLAG_IS_PROXY. + (ll_dequeue, ll_insert, ll_pop, ll_prio_push, ll_prio_enqueue) + Change API. + (THREAD_WAIT_POLL): New thread status. + (chx_ready_pop, chx_ready_push, chx_ready_enqueue): Type coercion. + (chx_timer_insert, chx_timer_dequeue, chx_timer_expired): Use + chx_pq. + (chx_handle_intr, chopstx_cancel): Handle THREAD_WAIT_POLL. + (chx_init): Type coercion. + (chx_exit): Handle proxy for join. + (chopstx_create): Initialize the member FLAG_IS_PROXY. + (chopstx_cond_signal) Handle proxy. + (chx_proxy_init, chopstx_poll): New. + +2016-04-06 Niibe Yutaka + + * chopstx.h (struct chx_qh): New struct. + + * chopstx.c (struct chx_queue): Use chx_qh. + (struct chx_thread): New member PARENT. + (ll_empty): Use chx_qh. + (ll_prio_push, ll_prio_enqueue): Set the member PARENT. + (chx_ready_pop, chx_ready_push, chx_ready_enqueue): Use the + member Q. + (chx_timer_insert): Return TP. + (chx_timer_dequeue, chx_timer_expired): Use the member Q. + (chx_init): Initialize change for Q_READY, Q_TIMER, Q_JOIN. + (chx_sched, preempt): Handle return value of chx_timer_insert. + (chx_exit, chopstx_mutex_init, chopstx_cond_init): Use the member Q. + (chx_mutex_unlock): Type coercion to CHX_THREAD. + (chopstx_create): Initialize PARENT field. + (chopstx_mutex_lock): Use the PARENT field. + (chopstx_join): Use ll_prio_enqueue, instead of ll_insert. + (chopstx_wakeup_usec_wait): Set REG_R0 on the stack. + +2016-04-06 Niibe Yutaka + + * chopstx.c (struct chx_thread): Move tcontext field. + (chx_cpu_sched_lock, chx_cpu_sched_unlock): Use CPSID/CPSIE. + (chx_sched): Don't use SVC. Return integer value. + (chopstx_usec_wait_var): Don't use R8. + (preempt): Modify so that we don't use SVC. + +2016-04-05 Niibe Yutaka + + * chopstx.c (struct NVIC): Add volatile qualifier to members. + (chx_spin_init): New. + (chopstx_mutex_init, chopstx_cond_init): Call chx_spin_init. + (chx_init): Initialize the spinlocks for INTR_LOCK, Q_READY, + Q_TIMER, and Q_JOIN. + +2016-03-08 Niibe Yutaka + + * chopstx.h (CHOPSTX_THREAD_SIZE): Align by 8. + * chopstx.c (struct chx_thread): Add W field to align. This is to + comply AAPCS (ARM Architecture Procedure Call Standard). + (chx_init): Initialize W. + + * example-cdc/usb_stm32f103.c, usb_lld.h: Update from Gnuk. + * example-cdc/usb-cdc.c: Update. + +2015-11-05 Niibe Yutaka + + * example-cdc/sample.c: Enhanced to be echo service. + +2015-09-15 Niibe Yutaka + + * VERSION: 0.10. + * doc/chopstx.texi (VERSION): 0.10. + + * example-cdc/usb_stm32f103.c: Update from Gnuk. + +2015-09-14 Niibe Yutaka + + * chopstx.c (q_exit): Remove. + (chx_init, chx_exit): Remove access to Q_EXIT. + (chx_release_irq_thread): Fix removing from the list. + +2015-09-11 Niibe Yutaka + + * chopstx.c (chx_init, chopstx_create): Fix the initial value + of flag_cancelable. + +2015-09-10 Niibe Yutaka + + * VERSION: 0.09. + * doc/chopstx.texi (VERSION): 0.09. + + * chopstx.c (chx_release_irq_thread): Rename. + +2015-09-09 Niibe Yutaka + + * example-cdc/usb-cdc.c (usb_cb_ctrl_write_finish): Distinguish + DTR signal. + (usb_cb_device_reset): Fix USB reset handling. + + * chopstx.c (chopstx_usec_wait_var, chopstx_cond_wait) + (chopstx_intr_wait): Call chopstx_testcancel. + (chopstx_setcancelstate): New. + + * chopstx.c (chx_systick_reset, chx_systick_reload) + (chx_systick_get): Factor out systick functions. + (chx_prio_init): Factor out. + +2015-09-08 Niibe Yutaka + + * chopstx.c (chx_request_preemption): Add PRIO argument and check + the condition inside. + (chx_timer_expired, chx_handle_intr): Call unconditionally. + (intr_lock): New variable. + (chx_handle_intr, chopstx_claim_irq, chopstx_release_irq) + (chopstx_release_irq_thread): Add spin lock with intr_lock. + + * chopstx.h (chx_intr): Remove member LOCK. + +2015-09-07 Niibe Yutaka + + * example-primer2: New from Kazumoto Kojima. + + * board/board-nitrokey-start.h (VAL_GPIO_USB_ODR) + (VAL_GPIO_USB_CRL): Fix the values for NeuG settings. + +2015-09-04 Niibe Yutaka + + * example-cdc/sys.h (BOARD_ID_NITROKEY_START): New. + * board/board-nitrokey-start.h (BOARD_ID): Fix the value. + * clk_gpio_init.c (AFIO_MAPR_SWJ_CFG_JTAGDISABLE): New. + +2015-08-07 Mateusz Zalega + + * board/board-nitrokey-start.h: New. + +2015-08-06 Niibe Yutaka + + * example-fsm-55/README: Updated. + +2015-07-31 Niibe Yutaka + + * VERSION: 0.08. + * doc/chopstx.texi (VERSION): 0.08. + + * example-fsm-55/Makefile (DEFS): Remove HAVE_SYS_H. + Add MAKE_ENTRY_PUBLIC. + * example-fsm-55/hacker-emblem.ld: Put vectors on ROM. + * example-fsm-55/sys.c: No system services. + * entry.c (entry): Can be public. + * clk_gpio_init.c (clock_init) [MCU_STM32F0]: Don't change CFGR1. + +2015-07-30 Niibe Yutaka + + * board/board-st-nucleo-f103.h: New. Contributed by Kenji + Rikitake. + +2015-07-29 Niibe Yutaka + + * board/board-st-dongle.h: New. Contributed by Kenji Rikitake. + + * board/board-*.h (FLASH_PAGE_SIZE): Remove. + +2015-07-28 Niibe Yutaka + + * example-cdc/usb_stm32f103.c: Update from Gnuk. + * example-cdc/usb_lld.h: Ditto. + * example-cdc/usb-cdc.c: Follow the change. + +2015-07-15 Niibe Yutaka + + * VERSION: 0.07. + * doc/chopstx.texi (VERSION): 0.07. + +2015-07-14 Niibe Yutaka + + * board/board-*.h (BOARD_ID): New. + * example-cdc/sys.c (sys_board_id): New. + * example-cdc/sample.ld (.sys.board_id): New. + (__flash_start__, __flash_end__): Remove. + * entry.c (vector_table) [HAVE_SYS_H]: By undefining STM32F10X_MD, + prepare for high density device even compiled for MD device. + +2015-07-13 Kaz Kojima + + * board/board-stm32-primer2.h: Update. + * entry.c (vector_table): Less or more. + +2015-07-13 Niibe Yutaka + + * board/board-*.h (BOARD_NAME): New. + (STM32F10X_MD): Define for medium-density devices. + * example-led/sys.c, sample.ld: Update. + * example-fsm-55/sys.c, hacker-emblem.ld: Update. + * example-cdc/sys.c (sys_board_name): New. + * example-cdc/sample.ld: Update. + +2015-07-08 Niibe Yutaka + + * VERSION: 0.06. + * doc/chopstx.texi (VERSION): 0.06. + +2015-07-07 Niibe Yutaka + + * example-fsm-55/sys.c: Include clk_gpio_init.c. + * board/board-stm32f0-discovery.h, board/board-fsm-55.h: Update. + * clk_gpio_init.c (gpio_init): Use VAL_GPIO_LED_*. + +2015-06-29 Niibe Yutaka + + * board/board-cq-starm.h: New for CQ STARM. + + * clk_gpio_init.c (gpio_init): LED is mandatory, but USB enabler + is optional. + * board/*.h: Update. + +2015-06-22 Niibe Yutaka + + * clk_gpio_init.c: New, adding ports E/F/G. + * entry.c: Include clk_gpio.init.c if not HAVE_SYS_H. + * example-led/sys.c: Include clk_gpio_init.c. + * example-cdc/sys.c: Ditto. + + * board/board-stm32-primer2.h: New from Kaz Kojima. + +2015-04-20 Niibe Yutaka + + Merge cortex-m0-support branch. + + * VERSION: 0.05. + + * example-led/sys.c (gpio_init): Support MCU_STM32F0. + (reset): Support __ARM_ARCH_6M__. + * example-led/sample.ld: Change for Cortex-M0. + + * example-fsm-55/*: New example for FSM-55. + + * entry.c (STM32_PPRE1, STM32_PLLSRC, STM32_FLASHBITS) + (STM32_PLLCLKIN): Support MCU_STM32F0. + (struct RCC, RCC_*): Support MCU_STM32F0. + (struct SYSCFG) [MCU_STM32F0]: New. + (struct GPIO, GPIO*): Support MCU_STM32F0. + (clock_init, gpio_init): Support MCU_STM32F0. + (hard_fault, vectors_in_ram, entry): Support Cortex-M0. + + * chopstx.c (chx_cpu_sched_lock, chx_cpu_sched_unlock) + (sched, preempt, svc): Support Cortex-M0. + + * board/board-fsm-55.h: New. + * board/board-stm32f0-discovery.h: New. + +2015-04-17 Niibe Yutaka + + * chopstx.c (CHX_PRIO_MAIN_INIT): New, removing CHX_PRIO_MAIN. + (chopstx_main_init): New. + (chx_init): Use CHX_PRIO_MAIN_INIT. + +2015-04-08 Niibe Yutaka + + * board/board-maple-mini.h: New from Aidan Thornton. + +2015-03-17 Niibe Yutaka + + * VERSION: 0.04a. + +2014-12-10 Niibe Yutaka + + * Version 0.04. + * doc/chopstx.texi (VERSION): 0.04. + + * board/*.h: Updated. + + * example-cdc/sys.c: Copied from example-led. + + * example-led/sys.c (clock_init, GPIO_USB): Follow the change of + entry.c. + (gpio_init): Use RCC_ENR_IOP_EN and RCC_RSTR_IOP_RST. + (reset): Use ldr instead of mov.w and movt. + + * entry.c (GPIO_USB) [GPIO_USB_BASE]: Conditionally defined. + + * chopstx.c (preempt): Add ! for stm. + (chx_exit): Make sure RETVAL is saved onto r8. + (chopstx_usec_wait_var): Make sure USEC_P is saved onto r8. + +2013-11-27 Niibe Yutaka + + * board/board-stbee-mini.h: New. + + * entry.c (AFIO_MAPR_SWJ_CFG_DISABLE): New. + +2013-11-26 Niibe Yutaka + + * board/board-stbee.h: New. + + * rules.mk (OUTFILES) [ENABLE_OUTPUT_HEX]: Add hex generation. + +2013-11-21 Niibe Yutaka + + * example-cdc/usb_stm32f103.c, example-cdc/usb_lld.h: Update from + Gnuk. + +2013-11-08 Niibe Yutaka + + * Version 0.03. + * doc/chopstx.texi (VERSION): 0.03. + + * chopstx.c (preempt): Disable interrupt. + +2013-11-08 Niibe Yutaka + + * Version 0.02. + * doc/chopstx.texi (VERSION): 0.02. + + * example-cdc/usb_stm32f103.c: Updated from NeuG. + + * chopstx.c (CPU_EXCEPTION_PRIORITY_SYSTICK): Equals to + CPU_EXCEPTION_PRIORITY_INTERRUPT. + +2013-11-03 Niibe Yutaka + + * Version 0.01. + * doc/chopstx.texi (VERSION): 0.01. + + * chopstx.c (chx_clr_intr): New. + (chopstx_intr_wait): Call chopstx.c. + (chx_enable_intr): Let chx_clr_intr clear pending-bit. + +2013-11-02 Niibe Yutaka + + * example-cdc/usb_lld.h, example-cdc/usb_stm32f103.c: Update from + Gnuk. + * example-cdc/usb-cdc.c (usb_cb_get_descriptor): Follow the + change. + + * example-cdc/sys.c, example-led/sys.c: Update from Gnuk. + + * chopstx.c (CPU_EXCEPTION_PRIORITY_INTERRUPT) + (CPU_EXCEPTION_PRIORITY_PENDSV): Change the value, so that + interrupt priority matches USB interrupt priority of old SYS 1.0 + implementation. + (chx_enable_intr, chx_disable_intr): Clear pending bit at + chx_enable_intr. + (chopstx_intr_wait): Call chx_enable_intr only when it's going to + sleep. + * entry.c (RCC_APB2RSTR_AFIORST, define RCC_APB2ENR_AFIOEN): New. + (AFIO, GPIO_OTHER): New. + (gpio_init): Handle AFIO and GPIO_OTHER. + * board/board-fst-01.h, board/board-stm8s-discovery.h: Add pin-cir + usage. + +2013-08-21 Niibe Yutaka + + * Version 0.00. + + * doc: Documentation. + * example-led/sys.c: Update from NeuG. + * example-cdc/sys.c: Likewise. + * example-cdc/usb_stm32f103.c: Likewise. + * example-cdc/usb_lld.h: Likewise. + * example-cdc/usb-cdc.c: Likewise. + +2013-08-19 Niibe Yutaka + + * rules.mk (%.elf): Support OBJS_ADD. + +2013-08-16 Niibe Yutaka + + * chopstx.c (chopstx_usec_wait_var): Check usec_p at the beginning + of the loop, bug fix of sched-unlocking from sched-unlocked state. + +2013-06-19 Niibe Yutaka + + * chopstx.h (CHOPSTX_THREAD_SIZE): New. + + * rules.mk (CSRC): Add eventflag.c. + * eventflag.h, eventflag.c: New. + + * chopstx.c (chopstx_main): New. + (chx_init): Initialize chopstx_main. + + * example-cdc/sample.c (usb_intr): No disable/enable interrupt, + but call the handler. + +2013-06-13 Niibe Yutaka + + * chopstx.c (chopstx_usec_wait_var): Rename (was: + chopstx_usec_wait_internal), as it's user visible. + +2013-06-12 Niibe Yutaka + + * chopstx.c (chx_init): Inhibit preemption for main thread. + +2013-06-11 Niibe Yutaka + + * chopstx.c (chx_timer_expired): Use uint16_t here. + (chx_cpu_sched_lock, chx_cpu_sched_unlock): Change only when + its priority is lower than CHOPSTX_PRIO_INHIBIT_PREEMPTION. + +2013-06-06 Niibe Yutaka + + * entry.c: Include sys.h for clock_init and gpio_init. + + * chopstx.c (PREEMPTION_USEC): Fix the value. + (sched): Check tp != NULL to see flag_sched_rr. + +2013-06-05 Niibe Yutaka + + * chopstx.c (sched, preempt, svc, chx_timer_expired, chx_exit) + (chopstx_usec_wait, chopstx_mutex_lock, chopstx_cond_wait) + (chopstx_intr_wait, chopstx_join): Implement SCHED_RR. + (chopstx_create): Change API. + (chopstx_wakeup_usec_wait): New. + (chopstx_usec_wait): Accept wakeup to break. + (chopstx_usec_wait_internal): New. + (CHX_FLAGS_MAIN): New. + (chx_systick_init): Add chx_timer_insert for main. + (chx_set_timer): Don't set thread state. + (chopstx_usec_wait_internal): Set thread state to THREAD_WAIT_TIME. + +2013-06-04 Niibe Yutaka + + * chopstx.c (AIRCR): New. + (chx_init): Setting for PRIGROUP 2-bit:2-bit. + (sched): Unlock cpu_sched_lock on return to thread. + (preempt, chx_timer_expired, chx_handle_intr): Those can be + considered holding cpu_sched_lock (by its equal exception + priorities), thus no acquiring lock required. + (CHX_PRIO_MAIN): New macro. + (chopstx_create): Change API. + (chopstx_attr_init, chopstx_attr_setschedparam) + (chopstx_attr_setstack): Remove. + (chopstx_intr_wait, chopstx_join): Call chx_cpu_sched_unlock. + (chopstx_cleanup_push, chopstx_cleanup_pop): New. + (chopstx_exit): Call cleanup routines. + +2013-06-03 Niibe Yutaka + + * chopstx.c (chx_spin_lock, chx_spin_unlock): Rename. + (chx_cpu_sched_lock, chx_cpu_sched_unlock): New. + + * chopstx.c (chx_exit): Use R8. + +2013-05-29 Niibe Yutaka + + * entry.c (entry): Initialize data section. + + * chopstx.c (svc): Implement race avoidance between + chx_handle_intr. + (chx_handle_intr): Increment ->ready. Put to ready queue + only when it's not running. + (chx_sched): Add an argument for race avoidance. + (chopstx_intr_wait): Fix race condition. + (chopstx_cond_broadcast): Fix initial value. + (chopstx_join): Check RET. + (chx_init): Initialize q_exit and q_join. + (chopstx_release_irq, chopstx_release_irq_thread): Enable IRQ on + release (as system reset default). + + * board/board-olimex-stm32-h103.h (NEUG_ADC_SETTING2_*): Add. + + * chopstx.c (chx_mutex_unlock, chopstx_exit): New. + (chopstx_mutex_unlock): Use chx_mutex_unlock. + (chopstx_cond_wait): Fix race condition. + +2013-05-28 Niibe Yutaka + + * chopstx.c (chopstx_intr_wait): Rename from chopstx_wait_intr. + (chopstx_usec_wait): Rename from chosptx_usleep. + (chopstx_cancel, chopstx_testcancel): New. + (idle): Moved from entry.c and use WFI if USE_WFI_FOR_IDLE. + +2013-05-27 Niibe Yutaka + + * chopstx.c (chx_fatal, chopstx_exit, chopstx_join): New. + (struct chx_thread): Independent member of state. Use bit fields. + (chx_timer_expired, chx_handle_intr): Check priority before + calling chx_request_preemption. + (chx_disable_intr): Clear pending interrupt too. + (chopstx_claim_irq): Rename from chopstx_intr_register. + (chopstx_release_irq): New. + (chopstx_join): Promote priority of thread to be joined. + +2013-05-24 Niibe Yutaka + + * chopstx.c (chx_request_preemption): Rename from chx_preempt. + + * entry.c: "sys.h" is only for to new sys 2.0, keeping + compatibility to 1.0, it is if-def out now. + +2013-05-22 Niibe Yutaka + + * chopstx.c (chx_enable_intr, chx_disable_intr): Implement. + (chx_set_intr_prio): New. + (chx_handle_intr): Use list to register interrupt threads. + (chx_init): Set PendSV priority (as minimum: bigger). + + * entry.c (vector_table): set chx_handle_intr for all interrupts. + +2013-05-21 Niibe Yutaka + + * Initial commit. + diff --git a/gnuk/chopstx/EXCEPTION b/gnuk/chopstx/EXCEPTION new file mode 100644 index 0000000000..9f220df664 --- /dev/null +++ b/gnuk/chopstx/EXCEPTION @@ -0,0 +1,14 @@ +For released versions of Chopstx, it is OK to distribute a binary +without the copy of the GNU GPL, as the license notice of Chopstx +include following exception to GNU GPL. + +-------------------- +As additional permission under GNU GPL version 3 section 7, you may +distribute non-source form of the Program without the copy of the GNU +GPL normally required by section 4, provided you inform the receipents +of GNU GPL by a written offer. +-------------------- + +Note that "written offer" is still needed when you distribute binary +only. +-- diff --git a/gnuk/chopstx/NEWS b/gnuk/chopstx/NEWS new file mode 100644 index 0000000000..2a34557f70 --- /dev/null +++ b/gnuk/chopstx/NEWS @@ -0,0 +1,455 @@ +NEWS - Noteworthy changes + + +* Major changes in Chopstx 1.14 + + Released 2019-03-02 + +** Enhancement of driver: USART for STM32 +Now, it supports smartcard communication. + + +* Major changes in Chopstx 1.13 + + Released 2018-12-19 + +** API fix (redefinition): chopstx_poll +In old implementations, when chopstx_poll returns by non-timeout +event, *USEC_P is not updated. Now, it is updated. + + +* Major changes in Chopstx 1.12 + + Released 2018-11-12 + +** Enhance API of eventflag +New function eventflag_set_mask is added, so that we can only handle +specified events. See Gnuk 1.2.12 for an example (while USB Tx is +busy, the USB thread only accepts EV_TX_FINISHED event, leaving +other events). + +** Acknowledge button support for FST-01 and FST-01G +While FST-01 and FST-01G don't have any button in the original design, +it may be PA2 when user put a hall sensor or a switch. + + +* Major changes in Chopstx 1.11 + + Released 2018-10-02 + +** Support calling chopstx_poll with intr->ready==1 +In version <= 1.10, it assumed that all events should be handled after +chopstx_poll, before calling chopstx_poll again. With having +chopstx_intr_done, it's OK now that chopstx_poll can be called again +not examining/handling all poll descriptors, but only parts of them. + +** Acknowledge button change +In 1.10, the action was able to be "memorized" by the edge detector. +Now, the edge detector is disabled by ackbtn_disable, and it is +enabled by ackbtn_enable. So, the status is cleared correctly. + +** New board support: FST-01SZ +It's still under development. Programming-wise, it will be kept same. + + +* Major changes in Chopstx 1.10 + + Released 2018-09-29 + +** Function chopstx_intr_wait is not deprecated, now +Once, it was said that it's deprecated, but it's active again +to match the new function of chopstx_intr_done. + +** API change: chopstx_poll, chopstx_intr_wait, chopstx_intr_done +To avoid spurious interrupt, we introduce new function +chopstx_intr_done, which should be called after interrupt handling. + +** New driver: Acknowledge button for FST-01SZ +The use case is waiting user's acknowledge. We use EXTI interrupt +feature of STM32. + + +* Major changes in Chopstx 1.9 + + Released 2018-05-09 + +** GD32F103 support +GD32F103 is an alternative implementation of STM32F103 by Giga Device, +which can run at 96MHz. + +** Minor USB driver fix for STM32F103/GD32F103 +BTABLE setting should be done at initialization, not at USB RESET. + +** Minor SYS driver fix for GD32F103 +flash_protect should check FLASH_CR_OPTWRE. + +** Minor ADC driver change for GD32F103 +ADC on GD32F103 is another implementation and its behavior is somewhat +different. It requires waits after enabling. So, we use continuous +sampling, instead of start and stop for each sample. Still, we +observe enough noise (> 4.7 bit/byte) for each ADC sampling. + + +* Major changes in Chopstx 1.8 + + Released 2018-01-19 + +** Minor driver API fix +In version 1.6, part of mcu/stm32f103.h was moved into mcu/cortex-m.h +and mcu/stm32.h. Now, mcu/stm32f103.h automatically includes +mcu/cortex-m.h and mcu/stm32.h, so that it doesn't break existing +applications. + + +* Major changes in Chopstx 1.7 + + Released 2017-12-19 + +** Fix: timer wakeup +Timer expiration had a bug. When it is waken up, the wake up doesn't +handle as a timer expiration when there are multiple threads on same +timing of expire. It confuses as if it were forced wakeup. + +** New driver: USART for STM32 +USART driver for STM32 is added. + + +* Major changes in Chopstx 1.6 + + Released 2017-11-24 + +** Fix cancellation +In Chopstx 1.4 and 1.5, cancellation doesn't work for real MCU. This +is due to the change of chx_sched interface, introduced for GNU/Linux +emulation. This bug is fixed. + +** New feature: sleep mode +New function chopstx_conf_idle is added to support sleep. Note that +use of sleep by this feature requires careful preparation. For +example, enabling sleep, a board with no RESET pin cannot be debugged +by JTAG/SWD. Setting of DBGMCU_CR (0xE0042004) is required beforehand +(hardware default is zero). + +** API change: chopstx_poll +This is a kind of clarification. The third argument is now an array +of constant pointers. We don't touch the array itself, just use it. +This allows having the array in read-only memory and can contribute +less use of RAM. + +** USB API changes +INTR_REQ_USB is now defined by usb_lld.h. Enumeration type of +DEVICE_STATE now has USB_DEVICE_STATE_ prefix. + +** USB driver change +USB suspend and wakeup events are supported for STM32F103. + + +* Major changes in Chopstx 1.5 + + Released 2017-10-10 + +** Stack size definition by stack-def.h +Stack size of each thread was used to be defined in ldscript. +While emulation on GNU/Linux doesn't use ldscript, it is better +to put those definitions in independent header file. Please see +example-cdc/stack-def.h and example-cdc/sample.ld. + +** More support for emulation on GNU/Linux +We have SYS driver for emulation on GNU/Linux. It has flash ROM +emulation. + + +* Major changes in Chopstx 1.4 + + Released 2017-08-11 + +** New port: Emulation on GNU/Linux +Now, user can run Chopstx application on GNU/Linux. Its USB driver is +by USBIP. Its ADC driver is dummy with random(3). + + +* Major changes in Chopstx 1.3 + + Released 2017-02-02 + +** New board support: Blue Pill +It is contributed by Paul Fertser. + + +* Major changes in Chopstx 1.2 + + Released 2016-10-13 + +** Fix: chopstx_join +chopstx_join is now cancellation point. + + +* Major changes in Chopstx 1.1 + + Released 2016-07-01 + +** API change: chopstx_poll +In version 1.0, chopstx_poll has variable arguments. It found that +it's challenging for ffi or lower level C implementation, if C +compiler is new for the specific MCU target. Another example is that +a program touches FPU registers for varargs, even if no argument is +float. So, we decided to avoid use of varargs in Chopstx. + +** API change: chopstx_setpriority +In version 1.0, chopstx_setpriority does not return value. It is +changed to return old value of the priority. + + +* Major changes in Chopstx 1.0 + + Released 2016-06-16 + +** New USB API +Now, USB driver is included in Chopstx. So, it should be good one. +It used to be the code which was derived from interrupt driven API +with callbacks. It's changed to event driven API, so that a user can +do as wish, beyond the restriction of callbacks. + +** New board support: FST-01G +FST-01G is a new revision of original FST-01 with fixed pull-up of +D+ line. + + +* Major changes in Chopstx 0.12 + + Released 2016-05-31 + +** Provide drivers of SYS, USB and ADC +Those were only offered as examples, but now, Chopstx provides drivers +of SYS, USB, and ADC. Please note that the ADC driver is not for +general use (it's specific to NeuG to get noise). To use them, enable +variables in Makefile, like following. +------------ +CHIP=stm32f103 +USE_ADC = yes +USE_USB = yes +USE_SYS = yes +DEFS = -DUSE_SYS3 +------------ + +** Removal of chopstx_usec_wait_var chopstx_wakeup_usec_wait +This API was used when we need to wait something with timeout. +Now, we have better API with chopstx_poll. Please use chopstx_poll +and chopstx_cond_signal. + + +* Major changes in Chopstx 0.11 + + Released 2016-05-19 + +** New feature: polling +New function chopstx_poll is added to watch multiple condition +variables, threads' exit, or IRQ, simultaneously with timeout. + +** Change API of eventflag +The initialization function eventflag_init only has an argument of EV. +An eventflag can be waited with timeout or can be waited with no +timeout, as caller like. It is not determined at initialization time +now. Besides, the eventflag can be waited by any threads. Functions +to poll eventflag together with other events (cond, join, and IRQ) are +provided. + +** Removal of the function chopstx_release_irq +IRQ is enabled only when a thread is blocked in polling. When it (the +thread in polling) is canceled, IRQ is disabled. + +** Removal of the function chopstx_main_init +It is removed because it's too special. Please use +chopstx_setpriority instead. + +** New function: chopstx_setpriority +This function is not recommended in general. It is only added to +support the usage when main thread wants to change the schedule +priority after creating other threads. + +** Function chopstx_intr_wait is deprecated +Use of chopstx_poll is recommended. + +** FS-BB48: Kinetis L MCU +Support for FS-BB48 board with Kinetis L MCU is added. + +** No HardFault at context switch on Cortex-M0 +By its design, Chopstx does context switch holding the scheduler lock. +This is implemented with the feature of BASEPRI on Cortex-M3. Because +Cortex-M0 doesn't have support of BASEPRI, the context switch (before +version 0.11) always caused HardFault exception. Since Cortex-M0 +doesn't have complex exception mechism of ICI/IT (which is supported +on Cortex-M3), it is actually possible to implement the context switch +in user mode. This is done. + +** New sys.c (3.0) +Don't touch NVIC in usb_lld_sys_init. + + +* Major changes in Chopstx 0.10 + + Released 2015-09-15 + +** Thread cancellation bug fix +Thread cancellation didn't work well with 0.09 because +of initial configuration mistake. It's fixed. + +** Interrupt handler bug fix +Interrupt handler wasn't unregistered on exit well. +It's fixed. + + +* Major changes in Chopstx 0.09 + + Released 2015-09-10 + +** New board support: Nitrokey-Start +It is contributed by Mateusz Zalega. + +** Thread cancellation +Add new API: chopstx_setcancelstate. + + +* Major changes in Chopstx 0.08 + + Released 2015-07-31 + +** New board support: ST Nucleo F103 +It is contributed by Kenji Rikitake. + +** New board support: ST Dongle +It is contributed by Kenji Rikitake. +It's the ST-Link/V2-1 part of ST Nucleo F103. + + +* Major changes in Chopstx 0.07 + + Released 2015-07-15 + +** New Board macro definitions +Each board-*.h should have BOARD_ID and BOARD_NAME now. +FLASH_PAGE_SIZE and NEUG_ADC_SETTING2_* are deprecated. + +** New sys.c (2.1) +Flash memory size is probed at runtime now. System +service flash pages now include sys_board_id and sys_board_name. + + +* Major changes in Chopstx 0.06 + + Released 2015-07-08 + +** New file: clk_gpio_init.c +To avoid duplication of code, clock and GPIO initialization code +is now in this file. + +** New board support: STM32 Primer2 +It is contributed by Kaz Kojima. + +** New board support: CQ STARM +The old board which was "published" by CQ Publishing in 2008 is added. + + +* Major changes in Chopstx 0.05 + + Released 2015-04-20, by NIIBE Yutaka + +** New function: chopstx_main_init +chopstx_main_init is the function to change the schedule priority of +main thread. This is useful to enter main loop after initialization +of other threads. + +** The use of CHX_PRIO_MAIN +CHX_PRIO_MAIN is deprecated. Instead, please use the function +chopstx_main_init. + +** Cortex-M0 support +Cortex-M0 support has been added. + +** New board support: Maple mini +It is contributed by Aidan Thornton. + +** New board support: FSM-55 and STM32F0 Discovery +Those boards with STM32F0 (Cortex-M0) are now supported. + + +* Major changes in Chopstx 0.04 + + Released 2014-12-10, by NIIBE Yutaka + +** new board.h macros and sys.c in example +In board.h, RCC_APB2ENR_IOP_EN was renamed to RCC_ENR_IOP_EN and +RCC_APB2RSTR_IOP_RST was renamed to RCC_RSTR_IOP_RST. Example sys.c +was changed accordingly. + +** Bug fix of chopstx_wakeup_usec_wait +chopstx_usec_wait_var/chopstx_usec_wait won't be woken up +by chopstx_wakeup_usec_wait. This is now fixed in 0.04. + +** Board support STBee and STBee Mini +The board STBee and STBee Mini are now supported. + + +* Major changes in Chopstx 0.03 + + Released 2013-11-08, by NIIBE Yutaka + +** Bug fix of preemption +In the implementation of preemption, there was a bug which might cause +not to schedule proper thread. This is because the routine preepmt +itself might be interrupted. This is now fixed in 0.03. + + +* Major changes in Chopstx 0.02 + + Released 2013-11-08, by NIIBE Yutaka + +** Bug fix of priority +There was a severe bug about the configuraion of priority setting of +exceptions. As we don't use any inter-lock between interrupts hander +and timer expiration handler, these priorities should be equal. If +not, timer expiration handler might interrupt the execution of +interrupts handers. + + +* Major changes in Chopstx 0.01 + + Released 2013-11-03, by NIIBE Yutaka + +** Interrupt handling change +There was a bug in 0.00, which caused spurious interrupts. Every +interrupt event caused two events. Specifically, after valid +interrupt event (for the caller of chopstx_intr_wait), another +spurious event was always occurred. This was fixed. + +In the design of Chopstx, interrupt handling is done by a thread. +Lower level interrupt handler just accepts interrupt, disabling the +interrupt, and switchs the control to the thread. It is the thread to +check the cause of interrupt, to process it, and to clear the cause. +Next call to chopstx_intr_wait will enable the interrupt again. + +The bug was related to pending interrupt flag. Pending interrupt flag +for a specific interrupt is set, on return from handler mode if the +cause is still active. With traditional interrupt handling, lower +level interrupt handler processes it and clears the cause. Thus, +pending interrupt flag is not set on return. + +In Chopstx, pending interrupt flag was always set, because the control +goes from lower level interrupt handler (in handler mode) to a +interrupt handling thread which processes the interrupt. In 0.01, new +internal routine chx_clr_intr is introduced, and pending interrupt +flag is cleared within chopstx_intr_wait after waked up. + +For detail of interrupt operation, see the section B.3.4, Nested +Vectored Interrupt Controller (NVIC), in the ARM v7-M Architecture +Reference Manual. The subsection, B3.4.1, Theory of operation, +explains how it works. + +** gpio_init change +Now, gpi_init support AFIO mapping and another GPIO (GPIO_OTHER) +settings. + + +# Local Variables: +# mode: outline +# End: diff --git a/gnuk/chopstx/README b/gnuk/chopstx/README new file mode 100644 index 0000000000..5663ebf79a --- /dev/null +++ b/gnuk/chopstx/README @@ -0,0 +1,70 @@ +Chopstx - Threads and only Threads + Version 1.14 + 2018-03-02 + Niibe Yutaka + Flying Stone Technology + +What's Chopstx? +=============== + +Chopstx is an RT thread library for STM32F103 and GD32F103 (ARM +Cortex-M3), STM32F030 (ARM Cortex-M0), MKL27Z (ARM Cortex-M0plus), and +emulation on GNU/Linux. + +While most RTOSes come with many features, drivers, and protocol +stacks, Chopstx just offers a simple RT thread library. + +With Chopstx, interrupt handling is also done by a thread. This +enables coherent code for ease of maintenance. + +While threads are important, we don't need more threads than +necessary. Chopstx provides a feature of poll, so that we can +minimize use of threads. + + +Note that this library is _not_ related to the hand game: + + https://en.wikipedia.org/wiki/Chopsticks_(hand_game) + +Thanks to Yao Wei and Enrico Zini for giving me an opportunity +visiting the wiki page above, when my children were playing the game. + + +License +======= + +It is distributed under GNU General Public Licence version 3 or later +(GPLv3+), with additional permission. Please see COPYING and +EXCEPTION. + + +Example code +============ + +We have some examples in this distribution; Useful ones are LED +blinker and USB CDC-ACM device. For STM32F103, you can build it USB +CDC-ACM demo by: + + $ cd example-cdc + $ ln -sf ../board/board-olimex-stm32-h103.h board.h + $ make + +If you want to try GD32F103, Add -DMHZ=96 to DEFS in Makefile. + +For a specific board named FSM-55, an example of LED matrix dynamic +driver is provided. See the directory: example-fsm-55. + +For STM32 Primer2, see the directory: example-primer2. + + +Future Works +============ + +Convenience function to determine the bottom of thread stack, +configuration of thread size by compiler's output would be next things +to be done. + +Experimental SMP port for Cortex-A7 is under development. For SMP, +more careful considerations for shared access to objects of struct +chx_pq is needed. So, modifications required will not be small. +-- diff --git a/gnuk/chopstx/VERSION b/gnuk/chopstx/VERSION new file mode 100644 index 0000000000..3cb3b856bb --- /dev/null +++ b/gnuk/chopstx/VERSION @@ -0,0 +1 @@ +release/1.14 diff --git a/gnuk/chopstx/adc.h b/gnuk/chopstx/adc.h new file mode 100644 index 0000000000..1a009f7639 --- /dev/null +++ b/gnuk/chopstx/adc.h @@ -0,0 +1,8 @@ +int adc_init (void); +void adc_start (void); +void adc_stop (void); + +extern uint32_t adc_buf[64]; + +void adc_start_conversion (int offset, int count); +int adc_wait_completion (void); diff --git a/gnuk/chopstx/board/board-blue-pill-g.h b/gnuk/chopstx/board/board-blue-pill-g.h new file mode 100644 index 0000000000..c78e38b9c2 --- /dev/null +++ b/gnuk/chopstx/board/board-blue-pill-g.h @@ -0,0 +1,42 @@ +#define BOARD_NAME "Blue Pill GD32F103" +/* http://wiki.stm32duino.com/index.php?title=Blue_Pill */ +/* echo -n "Blue Pill GD32F103" | shasum -a 256 | sed -e 's/^.*\(........\) -$/\1/' */ +#define BOARD_ID 0xed415594 + +#define MCU_STM32F1_GD32F1 1 +#define STM32_USBPRE STM32_USBPRE_DIV2 +#define STM32_ADCPRE STM32_ADCPRE_DIV8 + +#define MCU_STM32F1 1 +#define STM32F10X_MD /* Medium-density device */ + +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 12 +#define STM32_HSECLK 8000000 + +#define GPIO_LED_BASE GPIOC_BASE +#define GPIO_LED_CLEAR_TO_EMIT 13 +#define GPIO_USB_BASE GPIOA_BASE +#undef GPIO_OTHER_BASE + +/* + * Port A setup. + * PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM) + * PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP) + * + * Port C setup. + * PC13 - Push pull output 50MHz (LED 1:ON 0:OFF) + * ------------------------ Default + * PAx - input with pull-up + * PCx - input with pull-up + */ +#define VAL_GPIO_USB_ODR 0xFFFFE7FF +#define VAL_GPIO_USB_CRL 0x88888888 /* PA7...PA0 */ +#define VAL_GPIO_USB_CRH 0x88811888 /* PA15...PA8 */ + +#define VAL_GPIO_LED_ODR 0xFFFFFFFF +#define VAL_GPIO_LED_CRL 0x88888888 /* PC7...PC0 */ +#define VAL_GPIO_LED_CRH 0x88388888 /* PC15...PC8 */ + +#define RCC_ENR_IOP_EN (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN) +#define RCC_RSTR_IOP_RST (RCC_APB2RSTR_IOPARST | RCC_APB2RSTR_IOPCRST) diff --git a/gnuk/chopstx/board/board-blue-pill.h b/gnuk/chopstx/board/board-blue-pill.h new file mode 100644 index 0000000000..db54a9576b --- /dev/null +++ b/gnuk/chopstx/board/board-blue-pill.h @@ -0,0 +1,38 @@ +#define BOARD_NAME "Blue Pill" +/* http://wiki.stm32duino.com/index.php?title=Blue_Pill */ +/* echo -n "Blue Pill" | shasum -a 256 | sed -e 's/^.*\(........\) -$/\1/' */ +#define BOARD_ID 0xa1099d43 + +#define MCU_STM32F1 1 +#define STM32F10X_MD /* Medium-density device */ + +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 9 +#define STM32_HSECLK 8000000 + +#define GPIO_LED_BASE GPIOC_BASE +#define GPIO_LED_CLEAR_TO_EMIT 13 +#define GPIO_USB_BASE GPIOA_BASE +#undef GPIO_OTHER_BASE + +/* + * Port A setup. + * PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM) + * PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP) + * + * Port C setup. + * PC13 - Push pull output 50MHz (LED 1:ON 0:OFF) + * ------------------------ Default + * PAx - input with pull-up + * PCx - input with pull-up + */ +#define VAL_GPIO_USB_ODR 0xFFFFE7FF +#define VAL_GPIO_USB_CRL 0x88888888 /* PA7...PA0 */ +#define VAL_GPIO_USB_CRH 0x88811888 /* PA15...PA8 */ + +#define VAL_GPIO_LED_ODR 0xFFFFFFFF +#define VAL_GPIO_LED_CRL 0x88888888 /* PC7...PC0 */ +#define VAL_GPIO_LED_CRH 0x88388888 /* PC15...PC8 */ + +#define RCC_ENR_IOP_EN (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN) +#define RCC_RSTR_IOP_RST (RCC_APB2RSTR_IOPARST | RCC_APB2RSTR_IOPCRST) diff --git a/gnuk/chopstx/board/board-cq-starm.h b/gnuk/chopstx/board/board-cq-starm.h new file mode 100644 index 0000000000..7fcba5ceb1 --- /dev/null +++ b/gnuk/chopstx/board/board-cq-starm.h @@ -0,0 +1,47 @@ +#define BOARD_NAME "CQ STARM" +#define BOARD_ID 0xc5480875 + +#define MCU_STM32F1 1 +#define STM32F10X_MD /* Medium-density device */ + +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 9 +#define STM32_HSECLK 8000000 + +#define GPIO_LED_BASE GPIOC_BASE +#define GPIO_LED_SET_TO_EMIT 6 +#undef GPIO_USB_BASE /* No external DISCONNECT/RENUM circuit. */ +#define GPIO_OTHER_BASE GPIOA_BASE + +/* + * Port A setup. + * PA0 - input with pull-up. AN0 + * PA1 - input with pull-up. AN1 + * PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM) + * PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP) + * ------------------------ Default + * PAx - input with pull-up + */ +#define VAL_GPIO_OTHER_ODR 0xFFFFE7FF +#define VAL_GPIO_OTHER_CRL 0x88888888 /* PA7...PA0 */ +#define VAL_GPIO_OTHER_CRH 0x88811888 /* PA15...PA8 */ + +/* + * Port C setup. + * PC0 - Push Pull output 50MHz. + * PC1 - Push Pull output 50MHz. + * Everything input with pull-up except: + * PC4 - Normal input (ADC_IN14 : VoutY of LIS344ALH). + * PC5 - Normal input (ADC_IN15 : VoutZ of LIS344ALH). + * PC6 - Push Pull output (LED). + * (PC9 - SDCard CD) + * (PC12 - SDCard CS) + * PC14 - Normal input (XTAL). + * PC15 - Normal input (XTAL). + */ +#define VAL_GPIO_LED_CRL 0x83448833 /* PC7...PC0 */ +#define VAL_GPIO_LED_CRH 0x44888888 /* PC15...PC8 */ +#define VAL_GPIO_LED_ODR 0xFFFFFFFF + +#define RCC_ENR_IOP_EN (RCC_APB2ENR_IOPAEN|RCC_APB2ENR_IOPCEN) +#define RCC_RSTR_IOP_RST (RCC_APB2RSTR_IOPARST|RCC_APB2RSTR_IOPCRST) diff --git a/gnuk/chopstx/board/board-fs-bb48.h b/gnuk/chopstx/board/board-fs-bb48.h new file mode 100644 index 0000000000..cdf1dfa47d --- /dev/null +++ b/gnuk/chopstx/board/board-fs-bb48.h @@ -0,0 +1,5 @@ +#define BOARD_NAME "FS-BB48" +#define BOARD_ID 0xd1f5119c +/* echo -n "FST-01" | sha256sum | sed -e 's/^.*\(........\) -$/\1/' */ + +#define MCU_KINETIS_L 1 diff --git a/gnuk/chopstx/board/board-fsm-55.h b/gnuk/chopstx/board/board-fsm-55.h new file mode 100644 index 0000000000..edf3ceaae3 --- /dev/null +++ b/gnuk/chopstx/board/board-fsm-55.h @@ -0,0 +1,42 @@ +#define BOARD_NAME "FSM-55" +#define BOARD_ID 0x83433c76 + +/* + * Running at 48MHz with HSI as clock source. + * + */ +#define MCU_STM32F0 1 +/* __ARM_ARCH_6M__ */ + + +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 12 +#define STM32_HSICLK 8000000 + +#define GPIO_LED_BASE GPIOA_BASE +#define GPIO_LED_SET_TO_EMIT 5 +#define GPIO_OTHER_BASE GPIOF_BASE /* USER BUTTON */ + +/* + * Port A setup. + * PA5 - ON (LED 1:ON 0:OFF) + * PA4 - Pull DOWN + */ +#define VAL_GPIO_LED_MODER 0x00145555 /* Output Pin0-7, Pin9 and Pin10 */ +#define VAL_GPIO_LED_OTYPER 0x0000001f /* Open-drain for Pin0-4, Push-Pull*/ +#define VAL_GPIO_LED_OSPEEDR 0x003cffff /* High speed */ +#define VAL_GPIO_LED_PUPDR 0x00000000 /* No pull-up/pull-down */ + + +#define RCC_ENR_IOP_EN (RCC_AHBENR_IOPAEN | RCC_AHBENR_IOPFEN) +#define RCC_RSTR_IOP_RST (RCC_AHBRSTR_IOPARST | RCC_AHBRSTR_IOPFRST) + +/* + * Port F setup. + * PF0 - USER Button + * PF1 - SPEAKER + */ +#define VAL_GPIO_OTHER_MODER 0x00000004 /* Input Pin0, Output Pin1 */ +#define VAL_GPIO_OTHER_OTYPER 0x00000000 /* Push-Pull Pin1 */ +#define VAL_GPIO_OTHER_OSPEEDR 0x00000000 +#define VAL_GPIO_OTHER_PUPDR 0x00000009 /* Pull-up Pin0, Pull-down Pin1 */ diff --git a/gnuk/chopstx/board/board-fst-01-00.h b/gnuk/chopstx/board/board-fst-01-00.h new file mode 100644 index 0000000000..e317453b48 --- /dev/null +++ b/gnuk/chopstx/board/board-fst-01-00.h @@ -0,0 +1,33 @@ +#define BOARD_NAME "FST-01-00" +#define BOARD_ID 0x613870a9 + +#define MCU_STM32F1 1 +#define STM32F10X_MD /* Medium-density device */ + +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 9 +#define STM32_HSECLK 8000000 + +#define GPIO_LED_BASE GPIOA_BASE +#define GPIO_LED_SET_TO_EMIT 8 +#define GPIO_USB_BASE GPIOA_BASE +#define GPIO_USB_SET_TO_ENABLE 10 +#undef GPIO_OTHER_BASE + +/* + * Port A setup. + * PA0 - input with pull-up. AN0 + * PA1 - input with pull-up. AN1 + * PA8 - Push pull output 50MHz (LED 1:ON 0:OFF) + * PA10 - Push pull output 50MHz (USB 1:ON 0:OFF) + * PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM) + * PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP) + * ------------------------ Default + * PAx - input with pull-up + */ +#define VAL_GPIO_LED_ODR 0xFFFFE7FF +#define VAL_GPIO_LED_CRL 0x88888888 /* PA7...PA0 */ +#define VAL_GPIO_LED_CRH 0x88811383 /* PA15...PA8 */ + +#define RCC_ENR_IOP_EN RCC_APB2ENR_IOPAEN +#define RCC_RSTR_IOP_RST RCC_APB2RSTR_IOPARST diff --git a/gnuk/chopstx/board/board-fst-01.h b/gnuk/chopstx/board/board-fst-01.h new file mode 100644 index 0000000000..3b89c66fb5 --- /dev/null +++ b/gnuk/chopstx/board/board-fst-01.h @@ -0,0 +1,87 @@ +#define BOARD_NAME "FST-01" +#define BOARD_ID 0x696886af +/* echo -n "FST-01" | sha256sum | sed -e 's/^.*\(........\) -$/\1/' */ + +#define MCU_STM32F1 1 +#define STM32F10X_MD /* Medium-density device */ + +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 6 +#define STM32_HSECLK 12000000 + +#define GPIO_LED_BASE GPIOB_BASE +#define GPIO_LED_SET_TO_EMIT 0 +#define GPIO_USB_BASE GPIOA_BASE +#define GPIO_USB_SET_TO_ENABLE 10 +#undef GPIO_OTHER_BASE + +/* + * Port A setup. + * PA0 - input with pull-up (TIM2_CH1): AN0 for NeuG + * PA1 - input with pull-down (TIM2_CH2) + * PA2 - input with pull-up (TIM2_CH3) connected to CIR module + * PA3 - input with pull-up: external pin available to user + * 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) + * PA10 - Push pull output (USB 1:ON 0:OFF) + * PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM) + * PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP) + * ------------------------ Default + * PA8 - input with pull-up. + * PA9 - input with pull-up. + * PA13 - input with pull-up. + * PA14 - input with pull-up. + * PA15 - input with pull-up. + */ +#define VAL_GPIO_USB_ODR 0xFFFFE7FD +#define VAL_GPIO_USB_CRL 0xBBB38888 /* PA7...PA0 */ +#define VAL_GPIO_USB_CRH 0x88811388 /* PA15...PA8 */ + +/* + * Port B setup. + * PB0 - Push pull output (LED 1:ON 0:OFF) + * PB1 - input with pull-up: AN9 for NeuG + * ------------------------ Default + * PBx - input with pull-up. + */ +#define VAL_GPIO_LED_ODR 0xFFFFFFFF +#define VAL_GPIO_LED_CRL 0x88888883 /* PA7...PA0 */ +#define VAL_GPIO_LED_CRH 0x88888888 /* PA15...PA8 */ + +#define RCC_ENR_IOP_EN (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN) +#define RCC_RSTR_IOP_RST (RCC_APB2RSTR_IOPARST | RCC_APB2RSTR_IOPBRST) + +/* + * Board specific information other than clock and GPIO initial + * setting should not be in board-*.h, but each driver should include + * information by itself. + * + * Please see NeuG's ADC driver how board specific handling is done. + * + * Given the situation of Chopstx's boards support, which is not that + * huge, this works well. If scalability and flexibility will matter, + * we will need something like device tree in which boot process can + * pass information to application program. + * + * Following constants are here, because experimental CIR driver is + * written before this design decision of Chopstx. + * + * Those will be removed soon, once such an driver will be improved + * in new style. + */ +#if defined(PINPAD_CIR_SUPPORT) +/* For pin-cir settings of Gnuk */ +#define TIMx TIM2 +#define INTR_REQ_TIM TIM2_IRQ +#define AFIO_EXTICR_INDEX 0 +#define AFIO_EXTICR1_EXTIx_Py AFIO_EXTICR1_EXTI2_PA +#define EXTI_PR EXTI_PR_PR2 +#define EXTI_IMR EXTI_IMR_MR2 +#define EXTI_FTSR_TR EXTI_FTSR_TR2 +#define INTR_REQ_EXTI EXTI2_IRQ +#define ENABLE_RCC_APB1 +#define RCC_APBnENR_TIMxEN RCC_APB1ENR_TIM2EN +#define RCC_APBnRSTR_TIMxRST RCC_APB1RSTR_TIM2RST +#endif diff --git a/gnuk/chopstx/board/board-fst-01g.h b/gnuk/chopstx/board/board-fst-01g.h new file mode 100644 index 0000000000..63a8cc7356 --- /dev/null +++ b/gnuk/chopstx/board/board-fst-01g.h @@ -0,0 +1,86 @@ +#define BOARD_NAME "FST-01G" +#define BOARD_ID 0x8801277f +/* echo -n "FST-01G" | sha256sum | sed -e 's/^.*\(........\) -$/\1/' */ + +#define MCU_STM32F1 1 +#define STM32F10X_MD /* Medium-density device */ + +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 6 +#define STM32_HSECLK 12000000 + +#define GPIO_LED_BASE GPIOB_BASE +#define GPIO_LED_SET_TO_EMIT 0 +#define GPIO_USB_BASE GPIOA_BASE +#undef GPIO_OTHER_BASE + +/* + * Port A setup. + * PA0 - input with pull-up (TIM2_CH1): AN0 for NeuG + * PA1 - input with pull-down (TIM2_CH2) + * PA2 - input with pull-up (TIM2_CH3) connected to CIR module + * PA3 - input with pull-up: external pin available to user + * 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) + * PA10 - Push pull output 1 default + * (so that binary for FST-01G also works on FST-01) + * PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM) + * PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP) + * ------------------------ Default + * PA8 - input with pull-up. + * PA9 - input with pull-up. + * PA13 - input with pull-up. + * PA14 - input with pull-up. + * PA15 - input with pull-up. + */ +#define VAL_GPIO_USB_ODR 0xFFFFE7FD +#define VAL_GPIO_USB_CRL 0xBBB38888 /* PA7...PA0 */ +#define VAL_GPIO_USB_CRH 0x88811388 /* PA15...PA8 */ + +/* + * Port B setup. + * PB0 - Push pull output (LED 1:ON 0:OFF) + * PB1 - input with pull-up: AN9 for NeuG + * ------------------------ Default + * PBx - input with pull-up. + */ +#define VAL_GPIO_LED_ODR 0xFFFFFFFF +#define VAL_GPIO_LED_CRL 0x88888883 /* PA7...PA0 */ +#define VAL_GPIO_LED_CRH 0x88888888 /* PA15...PA8 */ + +#define RCC_ENR_IOP_EN (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN) +#define RCC_RSTR_IOP_RST (RCC_APB2RSTR_IOPARST | RCC_APB2RSTR_IOPBRST) + +/* + * Board specific information other than clock and GPIO initial + * setting should not be in board-*.h, but each driver should include + * information by itself. + * + * Please see NeuG's ADC driver how board specific handling is done. + * + * Given the situation of Chopstx's boards support, which is not that + * huge, this works well. If scalability and flexibility will matter, + * we will need something like device tree in which boot process can + * pass information to application program. + * + * Following constants are here, because experimental CIR driver is + * written before this design decision of Chopstx. + * + * Those will be removed soon, once such an driver will be improved + * in new style. + */ +#if defined(PINPAD_CIR_SUPPORT) +#define TIMx TIM2 +#define INTR_REQ_TIM TIM2_IRQ +#define AFIO_EXTICR_INDEX 0 +#define AFIO_EXTICR1_EXTIx_Py AFIO_EXTICR1_EXTI2_PA +#define EXTI_PR EXTI_PR_PR2 +#define EXTI_IMR EXTI_IMR_MR2 +#define EXTI_FTSR_TR EXTI_FTSR_TR2 +#define INTR_REQ_EXTI EXTI2_IRQ +#define ENABLE_RCC_APB1 +#define RCC_APBnENR_TIMxEN RCC_APB1ENR_TIM2EN +#define RCC_APBnRSTR_TIMxRST RCC_APB1RSTR_TIM2RST +#endif diff --git a/gnuk/chopstx/board/board-fst-01sz.h b/gnuk/chopstx/board/board-fst-01sz.h new file mode 100644 index 0000000000..949ceec4ee --- /dev/null +++ b/gnuk/chopstx/board/board-fst-01sz.h @@ -0,0 +1,43 @@ +#define BOARD_NAME "FST-01SZ" +#define BOARD_ID 0x7e6fb084 +/* echo -n "FST-01SZ" | sha256sum | sed -e 's/^.*\(........\) -$/\1/' */ + +#define MCU_STM32F1_GD32F1 1 +#define STM32_USBPRE STM32_USBPRE_DIV2 +#define STM32_ADCPRE STM32_ADCPRE_DIV8 + +#define MCU_STM32F1 1 +#define STM32F10X_MD /* Medium-density device */ + +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 8 +#define STM32_HSECLK 12000000 + +#define GPIO_LED_BASE GPIOA_BASE +#define GPIO_LED_SET_TO_EMIT 8 +#define GPIO_USB_BASE GPIOA_BASE +#undef GPIO_OTHER_BASE + +/* + * Port A setup. + * PA0 - input with pull-up: AN0 for NeuG + * PA1 - input with pull-up: AN1 for NeuG + * PA3 - input with pull-up: Hall effect sensor output + * PA8 - Push pull output 10MHz 0 default (LED 1:ON 0:OFF) + * PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM) + * PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP) + * ------------------------ Default + * PAx - input with pull-up. + */ +#define VAL_GPIO_LED_ODR 0xFFFFE6FF +#define VAL_GPIO_LED_CRL 0x88888888 /* PA7...PA0 */ +#define VAL_GPIO_LED_CRH 0x88811881 /* PA15...PA8 */ + +#define RCC_ENR_IOP_EN RCC_APB2ENR_IOPAEN +#define RCC_RSTR_IOP_RST RCC_APB2RSTR_IOPARST + +/* + * Board specific information other than clock and GPIO initial + * setting should not be in board-*.h, but each driver should include + * such specific information by itself. + */ diff --git a/gnuk/chopstx/board/board-gnu-linux.h b/gnuk/chopstx/board/board-gnu-linux.h new file mode 100644 index 0000000000..76eca42c36 --- /dev/null +++ b/gnuk/chopstx/board/board-gnu-linux.h @@ -0,0 +1,4 @@ +#define BOARD_NAME "GNU/Linux" +/* Emulation on GNU/Linux */ +/* echo -n "GNU/Linux" | shasum -a 256 | sed -e 's/^.*\(........\) -$/\1/' */ +#define BOARD_ID 0x7ec86145 diff --git a/gnuk/chopstx/board/board-maple-mini.h b/gnuk/chopstx/board/board-maple-mini.h new file mode 100644 index 0000000000..ac3dc911c4 --- /dev/null +++ b/gnuk/chopstx/board/board-maple-mini.h @@ -0,0 +1,42 @@ +#define BOARD_NAME "Maple Mini" +#define BOARD_ID 0x7a445272 + +#define MCU_STM32F1 1 +#define STM32F10X_MD /* Medium-density device */ + +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 9 +#define STM32_HSECLK 8000000 + +#define GPIO_LED_BASE GPIOB_BASE +#define GPIO_LED_SET_TO_EMIT 1 +#define GPIO_USB_BASE GPIOB_BASE +#define GPIO_USB_CLEAR_TO_ENABLE 9 +#define GPIO_OTHER_BASE GPIOA_BASE + +/* + * Port A setup. + * PA0 - input with pull-up. AN0 + * PA1 - input with pull-up. AN1 + * PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM) + * PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP) + * ------------------------ Default + * PAx - input with pull-up + */ +#define VAL_GPIO_OTHER_ODR 0xFFFFE7FF +#define VAL_GPIO_OTHER_CRL 0x88888888 /* PA7...PA0 */ +#define VAL_GPIO_OTHER_CRH 0x88811888 /* PA15...PA8 */ + +/* + * Port B setup. + * PB1 - Push pull output 50MHz (LED 1:ON 0:OFF) + * PB9 - Push pull output 50MHz (USB 1:ON 0:OFF) + * ------------------------ Default + * PBx - input with pull-up + */ +#define VAL_GPIO_LED_ODR 0xFFFFFFFF +#define VAL_GPIO_LED_CRL 0x88888838 /* PB7...PB0 */ +#define VAL_GPIO_LED_CRH 0x88888838 /* PB15...PB8 */ + +#define RCC_ENR_IOP_EN (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN) +#define RCC_RSTR_IOP_RST (RCC_APB2RSTR_IOPARST | RCC_APB2RSTR_IOPBRST) diff --git a/gnuk/chopstx/board/board-nitrokey-start.h b/gnuk/chopstx/board/board-nitrokey-start.h new file mode 100644 index 0000000000..a033233de2 --- /dev/null +++ b/gnuk/chopstx/board/board-nitrokey-start.h @@ -0,0 +1,60 @@ +#define BOARD_NAME "NITROKEY-START" +#define BOARD_ID 0xad1e7ebd + +#define MCU_STM32F1 1 +#define STM32F10X_MD /* Medium-density device */ + +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 6 +#define STM32_HSECLK 12000000 + +#define GPIO_LED_BASE GPIOB_BASE +#define GPIO_LED_SET_TO_EMIT 0 +#define GPIO_USB_BASE GPIOA_BASE +#define GPIO_USB_SET_TO_ENABLE 15 +#undef GPIO_OTHER_BASE + +/* + * Port A setup. + * PA0 - input with pull-up: AN0 for NeuG + * PA1 - input with pull-up: AN1 for NeuG + * PA2 - floating input + * PA3 - floating input + * PA4 - floating input + * PA5 - floating input + * PA6 - floating input + * PA7 - Push pull output (Red LED1 1:ON 0:OFF) + * PA8 - floating input (smartcard, SCDSA) + * PA9 - floating input + * PA10 - floating input + * PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM) + * PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP) + * PA15 - Push pull output (USB_EN 1:ON 0:OFF) + * ------------------------ Default + * PA8 - input with pull-up. + * PA9 - floating input. + * PA10 - floating input. + * PA13 - input with pull-up. + * PA14 - input with pull-up. + * PA15 - Push pull output (USB 1:ON 0:OFF) + */ +#define VAL_GPIO_USB_ODR 0xFFFFE77F +#define VAL_GPIO_USB_CRL 0x34444488 /* PA7...PA0 */ +#define VAL_GPIO_USB_CRH 0x38811444 /* PA15...PA8 */ + +/* + * Port B setup. + * PB0 - Push pull output (Green LED2 1:ON 0:OFF) + * ------------------------ Default + * PBx - input with pull-up. + */ +#define VAL_GPIO_LED_ODR 0xFFFFFFFF +#define VAL_GPIO_LED_CRL 0x88888883 /* PA7...PA0 */ +#define VAL_GPIO_LED_CRH 0x88888888 /* PA15...PA8 */ + +#define RCC_ENR_IOP_EN \ + (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN) +#define RCC_RSTR_IOP_RST \ + (RCC_APB2RSTR_IOPARST | RCC_APB2RSTR_IOPBRST | RCC_APB2RSTR_AFIORST) + +#define AFIO_MAPR_SOMETHING AFIO_MAPR_SWJ_CFG_JTAGDISABLE diff --git a/gnuk/chopstx/board/board-olimex-stm32-h103.h b/gnuk/chopstx/board/board-olimex-stm32-h103.h new file mode 100644 index 0000000000..348860df8a --- /dev/null +++ b/gnuk/chopstx/board/board-olimex-stm32-h103.h @@ -0,0 +1,33 @@ +#define BOARD_NAME "Olimex STM32-H103" +#define BOARD_ID 0xf92bb594 + +#define MCU_STM32F1 1 +#define STM32F10X_MD /* Medium-density device */ + +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 9 +#define STM32_HSECLK 8000000 + +#define GPIO_LED_BASE GPIOC_BASE +#define GPIO_LED_CLEAR_TO_EMIT 12 +#define GPIO_USB_BASE GPIOC_BASE +#define GPIO_USB_CLEAR_TO_ENABLE 11 +#undef GPIO_OTHER_BASE + +/* + * Port C setup. + * PC0 - input with pull-up. AN10 for NeuG + * PC1 - input with pull-up. AN11 for NeuG + * PC6 - input without pull-up/down + * PC7 - input without pull-up/down + * PC11 - Open-drain output 50MHz (USB disconnect). + * PC12 - Push Pull output 50MHz (LED). + * ------------------------ Default + * PCx - input with pull-up + */ +#define VAL_GPIO_LED_ODR 0xFFFFFFFF +#define VAL_GPIO_LED_CRL 0x44888888 /* PC7...PC0 */ +#define VAL_GPIO_LED_CRH 0x88837888 /* PC15...PC8 */ + +#define RCC_ENR_IOP_EN RCC_APB2ENR_IOPCEN +#define RCC_RSTR_IOP_RST RCC_APB2RSTR_IOPCRST diff --git a/gnuk/chopstx/board/board-st-dongle.h b/gnuk/chopstx/board/board-st-dongle.h new file mode 100644 index 0000000000..ef9b4f323a --- /dev/null +++ b/gnuk/chopstx/board/board-st-dongle.h @@ -0,0 +1,34 @@ +#define BOARD_NAME "ST Dongle" +/* echo -n "ST Dongle" | shasum -a 256 | sed -e 's/^.*\(........\) -$/\1/' */ +#define BOARD_ID 0x2cd4e471 + +#define MCU_STM32F1 1 +#define STM32F10X_MD /* Medium-density device */ + +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 9 +#define STM32_HSECLK 8000000 + +#define GPIO_LED_BASE GPIOA_BASE +#define GPIO_LED_SET_TO_EMIT 9 +#define GPIO_USB_BASE GPIOA_BASE +#define GPIO_USB_SET_TO_ENABLE 15 +#undef GPIO_OTHER_BASE + +/* + * Port A setup. + * PA0 - input with pull-up. AN0 + * PA1 - input with pull-up. AN1 + * PA9 - Push pull output 50MHz (LED 1:ON 0:OFF) + * PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM) + * PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP) + * PA15 - Push pull output 50MHz (USB 1:ON 0:OFF) + * ------------------------ Default + * PAx - input with pull-up + */ +#define VAL_GPIO_LED_ODR 0xFFFFE7FF +#define VAL_GPIO_LED_CRL 0x88888888 /* PA7...PA0 */ +#define VAL_GPIO_LED_CRH 0x38811838 /* PA15...PA8 */ + +#define RCC_ENR_IOP_EN RCC_APB2ENR_IOPAEN +#define RCC_RSTR_IOP_RST RCC_APB2RSTR_IOPARST diff --git a/gnuk/chopstx/board/board-st-nucleo-f103.h b/gnuk/chopstx/board/board-st-nucleo-f103.h new file mode 100644 index 0000000000..7560725ea2 --- /dev/null +++ b/gnuk/chopstx/board/board-st-nucleo-f103.h @@ -0,0 +1,74 @@ +#define BOARD_NAME "ST Nucleo F103" +#define BOARD_ID 0x9b87c16d + +/* + * Please add X3 and USB cable to ST Nucleo F103. + * + * Solder X3 XTAL of 8MHz (and put C33 and C34 of 22pF). + * Solder the bridges for R35 and R37, since it's 0 ohm. + * + * (Optional) Remove SB54 and SB55. + * + * At CN10, connect USB cable + * Vbus RED --> 10 NC ----------> CN7 (6 E5V) + * D+ GREEN --> 12 PA11 ---[1K5]--> CN6 (4 3V3) + * D- WHITE --> 14 PA12 + * GND BLACK --> 20 GND + */ + +#define MCU_STM32F1 1 +#define STM32F10X_MD /* Medium-density device */ + +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 9 +#define STM32_HSECLK 8000000 + +#define GPIO_LED_BASE GPIOA_BASE +#define GPIO_LED_SET_TO_EMIT 5 +#undef GPIO_USB_BASE /* No external DISCONNECT/RENUM circuit. */ +#define GPIO_OTHER_BASE GPIOB_BASE + +/* + * Port A setup. + * PA0 - Input with pull-up USART2-CTS + * PA1 - Alternate function push pull output 2MHz USART2-RTS + * PA2 - Alternate function push pull output 2MHz USART2-TX + * PA3 - Input with pull-up USART2-RX + * PA4 - Alternate function push pull output 2MHz USART2-CK + * PA5 - Push pull output 2MHz (LED 1:ON 0:OFF) + * PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM) + * PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP) + * ------------------------ Default + * PAx - input with pull-up + */ +#define VAL_GPIO_LED_ODR 0xFFFFE7FF +#define VAL_GPIO_LED_CRL 0x882A8AA8 /* PA7...PA0 */ +#define VAL_GPIO_LED_CRH 0x88811888 /* PA15...PA8 */ + +/* + * Port B setup. + * PB0 - input with pull-up: AN8 for NeuG + * PB1 - input with pull-up: AN9 for NeuG + * --- + * --- + * PB4 - Input with pull-up: Card insertion detect: 0 when detected + * --- + * PB6 - Output push pull 2MHz: Vcc for card: default 0 + * --- + * PB8 - Output push pull 2MHz: Vpp for card: default 0 + * PB9 - Output push pull 2MHz: RST for card: default 0 + * PB10 - Alternate function open-drain output 50MHz USART3-TX + * PB11 - Input with pull-up USART3-RX + * PB12 - Alternate function push pull output 50MHz USART3-CK + * PB13 - Input with pull-up USART3-CTS + * PB14 - Alternate function push pull output 50MHz USART3-RTS + * --- + * ------------------------ Default + * PBx - input with pull-up. + */ +#define VAL_GPIO_OTHER_ODR 0xFFFFFCBF +#define VAL_GPIO_OTHER_CRL 0x82888888 /* PB7...PB0 */ +#define VAL_GPIO_OTHER_CRH 0x8B8B8F22 /* PB15...PB8 */ + +#define RCC_ENR_IOP_EN (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN) +#define RCC_RSTR_IOP_RST (RCC_APB2RSTR_IOPARST | RCC_APB2RSTR_IOPBRST) diff --git a/gnuk/chopstx/board/board-stbee-mini.h b/gnuk/chopstx/board/board-stbee-mini.h new file mode 100644 index 0000000000..d9ab407b0e --- /dev/null +++ b/gnuk/chopstx/board/board-stbee-mini.h @@ -0,0 +1,114 @@ +#define BOARD_NAME "STBee Mini" +#define BOARD_ID 0x1f341961 + +#define MCU_STM32F1 1 +#define STM32F10X_MD /* Medium-density device */ + +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 6 +#define STM32_HSECLK 12000000 + +#define GPIO_LED_BASE GPIOA_BASE +#define GPIO_LED_CLEAR_TO_EMIT 13 +#define GPIO_USB_BASE GPIOA_BASE +#define GPIO_USB_SET_TO_ENABLE 14 + +#if defined(PINPAD_CIR_SUPPORT) || defined(PINPAD_DIAL_SUPPORT) +#define HAVE_7SEGLED 1 +/* + * Timer assignment for CIR + */ +#define TIMx TIM3 +#define INTR_REQ_TIM TIM3_IRQ +#define AFIO_EXTICR_INDEX 0 +#endif + +#if defined(PINPAD_CIR_SUPPORT) +#define AFIO_EXTICR1_EXTIx_Py AFIO_EXTICR1_EXTI0_PB +#define EXTI_PR EXTI_PR_PR0 +#define EXTI_IMR EXTI_IMR_MR0 +#define EXTI_FTSR_TR EXTI_FTSR_TR0 +#define INTR_REQ_EXTI EXTI0_IRQ +#define RCC_APBnENR_TIMxEN RCC_APB1ENR_TIM3EN +#define RCC_APBnRSTR_TIMxRST RCC_APB1RSTR_TIM3RST +#elif defined(PINPAD_DIAL_SUPPORT) +#define AFIO_EXTICR1_EXTIx_Py AFIO_EXTICR1_EXTI2_PB +#define EXTI_PR EXTI_PR_PR2 +#define EXTI_IMR EXTI_IMR_MR2 +#define EXTI_FTSR_TR EXTI_FTSR_TR2 +#define INTR_REQ_EXTI EXTI2_IRQ +#define RCC_APBnENR_TIMxEN RCC_APB1ENR_TIM4EN +#define RCC_APBnRSTR_TIMxRST RCC_APB1RSTR_TIM4RST +#endif +#define ENABLE_RCC_APB1 + +#if defined(PINPAD_CIR_SUPPORT) || defined(PINPAD_DIAL_SUPPORT) +/* + * Port A setup. + * PA1 - Digital input with PullUp. AN1 for NeuG + * PA2 - Digital input with PullUp. AN2 for NeuG + * PA6 - (TIM3_CH1) input with pull-up + * PA7 - (TIM3_CH2) input with pull-down + * PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM) + * PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP) + * PA13 - Open Drain output (LED1 0:ON 1:OFF) + * PA14 - Push pull output (USB ENABLE 0:DISABLE 1:ENABLE) + * PA15 - Open Drain output (LED2 0:ON 1:OFF) + */ +#define VAL_GPIO_LED_ODR 0xFFFFE77F +#define VAL_GPIO_LED_CRL 0x88888888 /* PA7...PA0 */ +#define VAL_GPIO_LED_CRH 0x63611888 /* PA15...PA8 */ + +#define GPIO_OTHER_BASE GPIOB_BASE +/* + * Port B setup. + * PB0 - Push pull output (LED 1:ON 0:OFF) + * ------------------------ Default + * PBx - input with pull-up. + */ +#define VAL_GPIO_OTHER_ODR 0xFFFFFFFF +#define VAL_GPIO_OTHER_CRL 0x88888888 /* PB7...PB0 */ +#define VAL_GPIO_OTHER_CRH 0x66666666 /* PB15...PB8 */ + +/* Port B setup. */ +#define GPIOB_CIR 0 +#define GPIOB_BUTTON 2 +#define GPIOB_ROT_A 6 +#define GPIOB_ROT_B 7 + +#define GPIOB_7SEG_DP 15 +#define GPIOB_7SEG_A 14 +#define GPIOB_7SEG_B 13 +#define GPIOB_7SEG_C 12 +#define GPIOB_7SEG_D 11 +#define GPIOB_7SEG_E 10 +#define GPIOB_7SEG_F 9 +#define GPIOB_7SEG_G 8 + +#define RCC_ENR_IOP_EN \ + (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN) +#define RCC_RSTR_IOP_RST \ + (RCC_APB2RSTR_IOPARST | RCC_APB2RSTR_IOPBRST | RCC_APB2RSTR_AFIORST) +#else +/* + * Port A setup. + * PA1 - Digital input with PullUp. AN1 for NeuG + * PA2 - Digital input with PullUp. AN2 for NeuG + * PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM) + * PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP) + * PA13 - Open Drain output (LED1 0:ON 1:OFF) + * PA14 - Push pull output (USB ENABLE 0:DISABLE 1:ENABLE) + * PA15 - Open Drain output (LED2 0:ON 1:OFF) + */ +#define VAL_GPIO_LED_ODR 0xFFFFE7FF +#define VAL_GPIO_LED_CRL 0x88888888 /* PA7...PA0 */ +#define VAL_GPIO_LED_CRH 0x63611888 /* PA15...PA8 */ + +#undef GPIO_OTHER_BASE + +#define RCC_ENR_IOP_EN (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN) +#define RCC_RSTR_IOP_RST (RCC_APB2RSTR_IOPARST | RCC_APB2RSTR_AFIORST) +#endif + + +#define AFIO_MAPR_SOMETHING AFIO_MAPR_SWJ_CFG_DISABLE diff --git a/gnuk/chopstx/board/board-stbee.h b/gnuk/chopstx/board/board-stbee.h new file mode 100644 index 0000000000..360ccbf27f --- /dev/null +++ b/gnuk/chopstx/board/board-stbee.h @@ -0,0 +1,37 @@ +#define BOARD_NAME "STBee" +#define BOARD_ID 0x945c37e8 + +#define MCU_STM32F1 1 +/* High-density device */ + +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 6 +#define STM32_HSECLK 12000000 + +#define GPIO_LED_BASE GPIOD_BASE +#define GPIO_LED_CLEAR_TO_EMIT 4 +#define GPIO_USB_BASE GPIOD_BASE +#define GPIO_USB_CLEAR_TO_ENABLE 3 +#define GPIO_OTHER_BASE GPIOA_BASE + +/* + * Port A setup. + * PA0 - Normal input. + * PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM) + * PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP) + */ +#define VAL_GPIO_OTHER_ODR 0xFFFFE7FF +#define VAL_GPIO_OTHER_CRL 0x88888884 /* PA7...PA0 */ +#define VAL_GPIO_OTHER_CRH 0x88811888 /* PA15...PA8 */ + +#define RCC_ENR_IOP_EN (RCC_APB2ENR_IOPAEN|RCC_APB2ENR_IOPDEN) +#define RCC_RSTR_IOP_RST (RCC_APB2RSTR_IOPARST|RCC_APB2RSTR_IOPDRST) + +/* + * Port D setup. + * PD3 - Push pull output (USB_DISC 1:USB-DISABLE 0:USB-ENABLE) 2MHz + * PD4 - Open Drain output 2MHz (LED1). + */ +#define VAL_GPIO_LED_ODR 0xFFFFFFFF +#define VAL_GPIO_LED_CRL 0x88862888 /* PD7...PD0 */ +#define VAL_GPIO_LED_CRH 0x88888888 /* PD15...PD8 */ diff --git a/gnuk/chopstx/board/board-stm32-primer2.h b/gnuk/chopstx/board/board-stm32-primer2.h new file mode 100644 index 0000000000..f392094626 --- /dev/null +++ b/gnuk/chopstx/board/board-stm32-primer2.h @@ -0,0 +1,59 @@ +#define BOARD_NAME "STM32 Primer2" +#define BOARD_ID 0x21e5798d + +#define MCU_STM32F1 1 +/* High-density device */ + +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 6 +#define STM32_HSECLK 12000000 + +#define GPIO_LED_BASE GPIOE_BASE +#define GPIO_LED_SET_TO_EMIT 0 +#define GPIO_USB_BASE GPIOD_BASE +#define GPIO_USB_CLEAR_TO_ENABLE 3 +#define GPIO_OTHER_BASE GPIOA_BASE + +/* + * Port A setup. + * PA0 - input with pull-up. AN0 + * PA1 - input with pull-up. AN1 + * PA8 - Input with pull-down (PBUTTON). + * PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM) + * PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP) + * ------------------------ Default + * PAx - input with pull-up + */ +#define VAL_GPIO_OTHER_ODR 0xFFFFE6FF +#define VAL_GPIO_OTHER_CRL 0x88888888 /* PA7...PA0 */ +#define VAL_GPIO_OTHER_CRH 0x88811888 /* PA15...PA8 */ + +/* + * Port D setup. + * PD3 - Push pull output 50MHz (USB 1:ON 0:OFF) + * ------------------------ Default + * PDx - input with pull-up + */ +#define VAL_GPIO_USB_ODR 0xFFFFFFFF +#define VAL_GPIO_USB_CRL 0x88883888 /* PD7...PD0 */ +#define VAL_GPIO_USB_CRH 0x88888888 /* PD15...PD8 */ + +/* + * Port E setup. + * PE0 - Push pull output (LED 1:ON 0:OFF) + * PE1 - Push pull output (LED 1:ON 0:OFF) + * PE3 - Input with pull-down (JOYSTICK L). + * PE4 - Input with pull-down (JOYSTICK R). + * PE5 - Input with pull-down (JOYSTICK U). + * PE6 - Input with pull-down (JOYSTICK D). + * ------------------------ Default + * PEx - input with pull-up + */ +#define VAL_GPIO_LED_ODR 0xFFFFFF87 +#define VAL_GPIO_LED_CRL 0x88888833 /* PE7...PE0 */ +#define VAL_GPIO_LED_CRH 0x88888888 /* PE15...PE8 */ + +#define RCC_ENR_IOP_EN \ + (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPDEN | RCC_APB2ENR_IOPEEN) +#define RCC_RSTR_IOP_RST \ + (RCC_APB2RSTR_IOPARST | RCC_APB2RSTR_IOPDRST | RCC_APB2RSTR_IOPERST) diff --git a/gnuk/chopstx/board/board-stm32f0-discovery.h b/gnuk/chopstx/board/board-stm32f0-discovery.h new file mode 100644 index 0000000000..38ae214d82 --- /dev/null +++ b/gnuk/chopstx/board/board-stm32f0-discovery.h @@ -0,0 +1,48 @@ +#define BOARD_NAME "STM32F0 Discovery" +#define BOARD_ID 0xde4b4bc1 + +/* + * Running at 48MHz with HSI as clock source. + * + */ +#define MCU_STM32F0 1 +/* __ARM_ARCH_6M__ */ + + +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 12 +#define STM32_HSICLK 8000000 + +#define GPIO_LED_BASE GPIOC_BASE +#define GPIO_LED_SET_TO_EMIT 8 +#define GPIO_OTHER_BASE GPIOA_BASE /* USER BUTTON */ + +/* + * Port C setup. + * PC9 - LED3 (LED 1:ON 0:OFF) + * PC8 - LED4 (LED 1:ON 0:OFF) + */ +#define VAL_GPIO_LED_MODER 0x00050000 /* Output Pin9 and Pin8 */ +#define VAL_GPIO_LED_OTYPER 0x00000000 /* Push-Pull */ +#define VAL_GPIO_LED_OSPEEDR 0x000f0000 /* High speed: Pin9 and Pin8 */ +#define VAL_GPIO_LED_PUPDR 0x00000000 /* No pull-up/pull-down */ + + +#if 0 +#define RCC_ENR_IOP_EN (RCC_AHBENR_IOPAEN | RCC_AHBENR_IOPCEN) +#define RCC_RSTR_IOP_RST (RCC_AHBRSTR_IOPARST | RCC_AHBRSTR_IOPCRST) +#else +#define RCC_ENR_IOP_EN RCC_AHBENR_IOPCEN +#define RCC_RSTR_IOP_RST RCC_AHBRSTR_IOPCRST +#endif + +/* ??? NeuG settings for ADC2 is default (PA0: Analog IN0, PA1: Analog IN1). */ + +/* + * Port A setup. + * PA0 - USER Button + */ +#define VAL_GPIO_OTHER_MODER 0x00000000 /* Input Pin0 */ +#define VAL_GPIO_OTHER_OTYPER 0x00000000 /* Push-Pull */ +#define VAL_GPIO_OTHER_OSPEEDR 0x00000000 +#define VAL_GPIO_OTHER_PUPDR 0x00000000 /* No pull-up/pull-down */ diff --git a/gnuk/chopstx/board/board-stm8s-discovery.h b/gnuk/chopstx/board/board-stm8s-discovery.h new file mode 100644 index 0000000000..3192ad7273 --- /dev/null +++ b/gnuk/chopstx/board/board-stm8s-discovery.h @@ -0,0 +1,61 @@ +#define BOARD_NAME "STM8S Discovery" +#define BOARD_ID 0x2f0976bb + +#define MCU_STM32F1 1 +#define STM32F10X_MD /* Medium-density device */ + +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 9 +#define STM32_HSECLK 8000000 + +#define GPIO_LED_BASE GPIOA_BASE +#define GPIO_LED_SET_TO_EMIT 8 +#undef GPIO_USB_BASE /* No external DISCONNECT/RENUM circuit. */ +#define GPIO_OTHER_BASE GPIOB_BASE + +/* + * Port A setup. + * PA0 - input with pull-up. AN0 + * PA1 - input with pull-up. AN1 + * PA8 - Push pull output 10MHz (LED 1:ON 0:OFF) + * PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM) + * PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP) + * ------------------------ Default + * PAx - input with pull-up + */ +#define VAL_GPIO_LED_ODR 0xFFFFE7FF +#define VAL_GPIO_LED_CRL 0x88888888 /* PA7...PA0 */ +#define VAL_GPIO_LED_CRH 0x88811881 /* PA15...PA8 */ + +#define RCC_ENR_IOP_EN \ + (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN) +#define RCC_RSTR_IOP_RST \ + (RCC_APB2RSTR_IOPARST | RCC_APB2RSTR_IOPBRST | RCC_APB2RSTR_AFIORST) + +/* + * Port B setup. + * PB4 - (TIM3_CH1) input with pull-up + * PB5 - (TIM3_CH2) input with pull-up, connected to CIR module + * Everything input with pull-up except: + * PB0 - (TIM3_CH3) input with pull-down + */ +#define VAL_GPIO_OTHER_ODR 0xFFFFFFFE +#define VAL_GPIO_OTHER_CRL 0x88888888 /* PB7...PB0 */ +#define VAL_GPIO_OTHER_CRH 0x88888888 /* PB15...PB8 */ + + +#if defined(PINPAD_CIR_SUPPORT) +#define TIMx TIM3 +#define INTR_REQ_TIM TIM3_IRQ +#define AFIO_EXTICR_INDEX 1 +#define AFIO_EXTICR1_EXTIx_Py AFIO_EXTICR2_EXTI5_PB +#define EXTI_PR EXTI_PR_PR5 +#define EXTI_IMR EXTI_IMR_MR5 +#define EXTI_FTSR_TR EXTI_FTSR_TR5 +#define INTR_REQ_EXTI EXTI9_5_IRQ +#define ENABLE_RCC_APB1 +#define RCC_APBnENR_TIMxEN RCC_APB1ENR_TIM3EN +#define RCC_APBnRSTR_TIMxRST RCC_APB1RSTR_TIM3RST +#define AFIO_MAPR_SOMETHING AFIO_MAPR_TIM3_REMAP_PARTIALREMAP + /* Remap (PB4, PB5) -> (TIM3_CH1, TIM3_CH2) */ +#endif diff --git a/gnuk/chopstx/chopstx-cortex-m.c b/gnuk/chopstx/chopstx-cortex-m.c new file mode 100644 index 0000000000..f7e49fc996 --- /dev/null +++ b/gnuk/chopstx/chopstx-cortex-m.c @@ -0,0 +1,699 @@ +/* + * chopstx-cortex-m.c - Threads and only threads: Arch specific code + * for Cortex-M0/M3 + * + * Copyright (C) 2013, 2014, 2015, 2016, 2017, 2018 + * Flying Stone Technology + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx 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. + * + * Chopstx 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 . + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +/* Data Memory Barrier. */ +static void +chx_dmb (void) +{ + asm volatile ("dmb" : : : "memory"); +} + +/* Saved registers on the stack. */ +struct chx_stack_regs { + uint32_t reg[8]; /* r0, r1, r2, r3, r12, lr, pc, xpsr */ +}; + +/* + * Constants for ARM. + */ +#define REG_SP 8 + +#define REG_R0 0 +#define REG_LR 5 +#define REG_PC 6 +#define REG_XPSR 7 + +#define INITIAL_XPSR 0x01000000 /* T=1 */ + +/* + * Exception priority: lower has higher precedence. + * + * Cortex-M3 + * ===================================== + * Prio 0x30: svc + * --------------------- + * Prio 0x40: thread temporarily inhibiting schedule for critical region + * ... + * Prio 0xb0: systick, external interrupt + * Prio 0xc0: pendsv + * ===================================== + * + * Cortex-M0 + * ===================================== + * Prio 0x00: thread temporarily inhibiting schedule for critical region + * ... + * Prio 0x40: systick, external interrupt + * Prio 0x80: pendsv + * Prio 0x80: svc + * ===================================== + */ + +#define CPU_EXCEPTION_PRIORITY_CLEAR 0 + +#if defined(__ARM_ARCH_6M__) +#define CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED 0x00 +/* ... */ +#define CPU_EXCEPTION_PRIORITY_SYSTICK CPU_EXCEPTION_PRIORITY_INTERRUPT +#define CPU_EXCEPTION_PRIORITY_INTERRUPT 0x40 +#define CPU_EXCEPTION_PRIORITY_PENDSV 0x80 +#define CPU_EXCEPTION_PRIORITY_SVC 0x80 /* No use in this arch */ +#elif defined(__ARM_ARCH_7M__) +#define CPU_EXCEPTION_PRIORITY_SVC 0x30 + +#define CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED 0x40 +/* ... */ +#define CPU_EXCEPTION_PRIORITY_SYSTICK CPU_EXCEPTION_PRIORITY_INTERRUPT +#define CPU_EXCEPTION_PRIORITY_INTERRUPT 0xb0 +#define CPU_EXCEPTION_PRIORITY_PENDSV 0xc0 +#else +#error "no support for this arch" +#endif + +/* + * Lower layer architecture specific functions. + * + * system tick and interrupt + */ + +/* + * System tick + */ +/* SysTick registers. */ +static volatile uint32_t *const SYST_CSR = (uint32_t *)0xE000E010; +static volatile uint32_t *const SYST_RVR = (uint32_t *)0xE000E014; +static volatile uint32_t *const SYST_CVR = (uint32_t *)0xE000E018; + +static void +chx_systick_reset (void) +{ + *SYST_RVR = 0; + *SYST_CVR = 0; + *SYST_CSR = 7; +} + +static void +chx_systick_reload (uint32_t ticks) +{ + *SYST_RVR = ticks; + *SYST_CVR = 0; /* write (any) to clear the counter to reload. */ + *SYST_RVR = 0; +} + +static uint32_t +chx_systick_get (void) +{ + return *SYST_CVR; +} + +static uint32_t usec_to_ticks (uint32_t usec) +{ + return usec * MHZ; +} + +/* + * Interrupt Handling + */ + +/* NVIC: Nested Vectored Interrupt Controller. */ +struct NVIC { + volatile uint32_t ISER[8]; + uint32_t unused1[24]; + volatile uint32_t ICER[8]; + uint32_t unused2[24]; + volatile uint32_t ISPR[8]; + uint32_t unused3[24]; + volatile uint32_t ICPR[8]; + uint32_t unused4[24]; + volatile uint32_t IABR[8]; + uint32_t unused5[56]; + volatile uint32_t IPR[60]; +}; + +static struct NVIC *const NVIC = (struct NVIC *)0xE000E100; +#define NVIC_ISER(n) (NVIC->ISER[n >> 5]) +#define NVIC_ICER(n) (NVIC->ICER[n >> 5]) +#define NVIC_ICPR(n) (NVIC->ICPR[n >> 5]) +#define NVIC_IPR(n) (NVIC->IPR[n >> 2]) + + +static void +chx_enable_intr (uint8_t irq_num) +{ + NVIC_ISER (irq_num) = 1 << (irq_num & 0x1f); +} + +static void +chx_clr_intr (uint8_t irq_num) +{ /* Clear pending interrupt. */ + NVIC_ICPR (irq_num) = 1 << (irq_num & 0x1f); +} + +static void +chx_disable_intr (uint8_t irq_num) +{ + NVIC_ICER (irq_num) = 1 << (irq_num & 0x1f); +} + +static void +chx_set_intr_prio (uint8_t n) +{ + unsigned int sh = (n & 3) << 3; + + NVIC_IPR (n) = (NVIC_IPR(n) & ~(0xFF << sh)) + | (CPU_EXCEPTION_PRIORITY_INTERRUPT << sh); +} + +static volatile uint32_t *const ICSR = (uint32_t *)0xE000ED04; + +/* Priority control. */ +static uint32_t *const AIRCR = (uint32_t *)0xE000ED0C; +static uint32_t *const SHPR2 = (uint32_t *)0xE000ED1C; +static uint32_t *const SHPR3 = (uint32_t *)0xE000ED20; + +static void +chx_prio_init (void) +{ + *AIRCR = 0x05FA0000 | ( 5 << 8); /* PRIGROUP = 5, 2-bit:2-bit. */ + *SHPR2 = (CPU_EXCEPTION_PRIORITY_SVC << 24); + *SHPR3 = ((CPU_EXCEPTION_PRIORITY_SYSTICK << 24) + | (CPU_EXCEPTION_PRIORITY_PENDSV << 16)); +} + + +static void +chx_cpu_sched_lock (void) +{ + if (running->prio < CHOPSTX_PRIO_INHIBIT_PREEMPTION) + { +#if defined(__ARM_ARCH_6M__) + asm volatile ("cpsid i" : : : "memory"); +#else + register uint32_t tmp = CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED; + asm volatile ("msr BASEPRI, %0" : : "r" (tmp) : "memory"); +#endif + } +} + +static void +chx_cpu_sched_unlock (void) +{ + if (running->prio < CHOPSTX_PRIO_INHIBIT_PREEMPTION) + { +#if defined(__ARM_ARCH_6M__) + asm volatile ("cpsie i" : : : "memory"); +#else + register uint32_t tmp = CPU_EXCEPTION_PRIORITY_CLEAR; + asm volatile ("msr BASEPRI, %0" : : "r" (tmp) : "memory"); +#endif + } +} + + +void +chx_handle_intr (void) +{ + struct chx_pq *p; + register uint32_t irq_num; + + asm volatile ("mrs %0, IPSR\n\t" + "sub %0, #16" /* Exception # - 16 = interrupt number. */ + : "=r" (irq_num) : /* no input */ : "memory"); + + chx_disable_intr (irq_num); + chx_spin_lock (&q_intr.lock); + for (p = q_intr.q.next; p != (struct chx_pq *)&q_intr.q; p = p->next) + if (p->v == irq_num) + { /* should be one at most. */ + struct chx_px *px = (struct chx_px *)p; + + ll_dequeue (p); + chx_wakeup (p); + chx_request_preemption (px->master->prio); + break; + } + chx_spin_unlock (&q_intr.lock); +} + +static void +chx_init_arch (struct chx_thread *tp) +{ + memset (&tp->tc, 0, sizeof (tp->tc)); +} + +static void +chx_request_preemption (uint16_t prio) +{ + if (running == NULL || (uint16_t)running->prio < prio) + { + *ICSR = (1 << 28); + asm volatile ("" : : : "memory"); + } +} + + +/* + * chx_sched: switch to another thread. + * + * There are two cases: + * YIELD=0 (SLEEP): Current RUNNING thread is already connected to + * something (mutex, cond, intr, etc.) + * YIELD=1 (YIELD): Current RUNNING thread is active, + * it is needed to be enqueued to READY queue. + * + * For Cortex-M0, this should be AAPCS-compliant function entry, so we + * put "noinline" attribute. + * + * AAPCS: ARM Architecture Procedure Call Standard + * + * Returns: + * >= 1 on wakeup by others, value means ticks remained for sleep. + * 0 on normal wakeup (timer expiration, lock acquirement). + * -1 on cancellation. + */ +static uintptr_t __attribute__ ((naked, noinline)) +chx_sched (uint32_t yield) +{ + register struct chx_thread *tp asm ("r0"); + +#if defined(__ARM_ARCH_7M__) + asm volatile ( + "svc #0" + : "=r" (tp) : "0" (yield): "memory"); +#else + register uint32_t arg_yield asm ("r1"); + + /* Build stack data as if it were an exception entry. */ + /* + * r0: TP scratch + * r1: 0 scratch + * r2: 0 scratch + * r3: 0 scratch + * r12: 0 scratch + * lr as-is + * pc: return address (= .L_CONTEXT_SWITCH_FINISH) + * psr: INITIAL_XPSR scratch + */ + asm ("mov r1, lr\n\t" + "ldr r2, =.L_CONTEXT_SWITCH_FINISH\n\t" + "mov r3, #128\n\t" + "lsl r3, #17\n\t" + "push {r1, r2, r3}\n\t" + "mov r1, #0\n\t" + "mov r2, r1\n\t" + "mov r3, r1\n\t" + "push {r1, r2, r3}\n\t" + "mov r1, r0\n\t" + "ldr r2, =running\n\t" + "ldr r0, [r2]\n\t" + "push {r0, r3}" + : "=r" (tp), "=r" (arg_yield) + : "0" (yield) + : "r2", "r3", "memory"); + + /* Save registers onto CHX_THREAD struct. */ + asm ("add r0, #20\n\t" + "stm r0!, {r4, r5, r6, r7}\n\t" + "mov r2, r8\n\t" + "mov r3, r9\n\t" + "mov r4, r10\n\t" + "mov r5, r11\n\t" + "mov r6, sp\n\t" + "stm r0!, {r2, r3, r4, r5, r6}\n\t" + "sub r0, #56" + : /* no output */ + : "r" (tp) + : "r2", "r3", "r4", "r5", "r6", "r7", "memory"); + + if (arg_yield) + { + if (tp->flag_sched_rr) + chx_timer_dequeue (tp); + chx_ready_enqueue (tp); + } + + tp = chx_ready_pop (); + if (tp && tp->flag_sched_rr) + { + chx_spin_lock (&q_timer.lock); + tp = chx_timer_insert (tp, PREEMPTION_USEC); + chx_spin_unlock (&q_timer.lock); + } + + asm volatile (/* Now, r0 points to the thread to be switched. */ + /* Put it to *running. */ + "ldr r1, =running\n\t" + /* Update running. */ + "str r0, [r1]\n\t" + "cmp r0, #0\n\t" + "bne 0f\n\t" + + /* Spawn an IDLE thread. */ + "ldr r1, =__main_stack_end__\n\t" + "mov sp, r1\n\t" + "ldr r0, =chx_idle\n\t" /* PC = idle */ + /**/ + /* Unmask interrupts. */ + "cpsie i\n\t" + "bx r0\n" + + /* Normal context switch */ + "0:\n\t" + "add r0, #20\n\t" + "ldm r0!, {r4, r5, r6, r7}\n\t" + "ldm r0!, {r1, r2, r3}\n\t" + "mov r8, r1\n\t" + "mov r9, r2\n\t" + "mov r10, r3\n\t" + "ldm r0!, {r1, r2}\n\t" + "mov r11, r1\n\t" + "mov sp, r2\n\t" + "sub r0, #45\n\t" + "ldrb r1, [r0]\n\t" /* ->PRIO field. */ + "cmp r1, #247\n\t" + "bhi 1f\n\t" /* Leave interrupt disabled if >= 248 */ + /**/ + /* Unmask interrupts. */ + "cpsie i\n" + /**/ + "1:\n\t" + /* + 0: r0 + 4: r1 + 8: r2 + 12: r3 + 16: r12 + 20: lr + 24: pc + 28: psr + 32: possibly exists for alignment + [28 or 32] <-- pc + */ + "ldr r0, [sp, #28]\n\t" + "lsl r1, r0, #23\n\t" + "bcc 2f\n\t" + /**/ + "ldr r2, [sp, #24]\n\t" + "mov r1, #1\n\t" + "orr r2, r1\n\t" /* Ensure Thumb-mode */ + "str r2, [sp, #32]\n\t" + "msr APSR_nzcvq, r0\n\t" + /**/ + "ldr r0, [sp, #20]\n\t" + "mov lr, r0\n\t" + "ldr r0, [sp, #16]\n\t" + "mov r12, r0\n\t" + "pop {r0, r1, r2, r3}\n\t" + "add sp, #16\n\t" + "pop {pc}\n" + "2:\n\t" + "ldr r2, [sp, #24]\n\t" + "mov r1, #1\n\t" + "orr r2, r1\n\t" /* Ensure Thumb-mode */ + "str r2, [sp, #28]\n\t" + "msr APSR_nzcvq, r0\n\t" + /**/ + "ldr r0, [sp, #20]\n\t" + "mov lr, r0\n\t" + "ldr r0, [sp, #16]\n\t" + "mov r12, r0\n\t" + "pop {r0, r1, r2, r3}\n\t" + "add sp, #12\n\t" + "pop {pc}\n\t" + ".L_CONTEXT_SWITCH_FINISH:" + : "=r" (tp) /* Return value in R0 */ + : "0" (tp) + : "memory"); +#endif + + asm volatile ("bx lr" + : "=r" (tp) + : "0" (tp->v) + : "memory"); + return (uintptr_t)tp; +} + +extern void cause_link_time_error_unexpected_size_of_struct_chx_thread (void); + +static struct chx_thread * +chopstx_create_arch (uintptr_t stack_addr, size_t stack_size, + voidfunc thread_entry, void *arg) +{ + struct chx_thread *tp; + void *stack; + struct chx_stack_regs *p; + + if (CHOPSTX_THREAD_SIZE != sizeof(struct chx_thread)) + cause_link_time_error_unexpected_size_of_struct_chx_thread (); + + if (stack_size < sizeof (struct chx_thread) + 8 * sizeof (uint32_t)) + chx_fatal (CHOPSTX_ERR_THREAD_CREATE); + + stack = (void *)(stack_addr + stack_size - sizeof (struct chx_thread) + - sizeof (struct chx_stack_regs)); + memset (stack, 0, sizeof (struct chx_stack_regs)); + tp = (struct chx_thread *)(stack + sizeof (struct chx_stack_regs)); + p = (struct chx_stack_regs *)stack; + p->reg[REG_R0] = (uint32_t)arg; + p->reg[REG_LR] = (uint32_t)chopstx_exit; + p->reg[REG_PC] = (uint32_t)thread_entry; + p->reg[REG_XPSR] = INITIAL_XPSR; + + memset (&tp->tc, 0, sizeof (tp->tc)); + tp->tc.reg[REG_SP] = (uint32_t)stack; + + return tp; +} + +/* + * Lower layer architecture specific exception handling entries. + * + */ + +void __attribute__ ((naked)) +preempt (void) +{ + register struct chx_thread *tp asm ("r0"); + register struct chx_thread *cur asm ("r1"); + + asm volatile ( +#if defined(__ARM_ARCH_6M__) + "cpsid i\n\t" +#else + "msr BASEPRI, r0\n\t" +#endif + "ldr r2, =running\n\t" + "ldr r0, [r2]\n\t" + "mov r1, r0" + : "=r" (tp), "=r" (cur) + : "0" (CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED) + : "r2"); + + if (!cur) + /* It's idle thread. It's ok to clobber registers. */ + ; + else + { + /* Save registers onto CHX_THREAD struct. */ + asm volatile ( + "add %0, #20\n\t" + "stm %0!, {r4, r5, r6, r7}\n\t" + "mov r2, r8\n\t" + "mov r3, r9\n\t" + "mov r4, r10\n\t" + "mov r5, r11\n\t" + "mrs r6, PSP\n\t" /* r13(=SP) in user space. */ + "stm %0!, {r2, r3, r4, r5, r6}" + : "=r" (cur) + : "0" (cur) + /* + * Memory clobber constraint here is not accurate, but this + * works. R7 keeps its value, but having "r7" here prevents + * use of R7 before this asm statement. + */ + : "r2", "r3", "r4", "r5", "r6", "r7", "memory"); + + if (tp) + { + if (tp->flag_sched_rr) + { + if (tp->state == THREAD_RUNNING) + { + chx_timer_dequeue (tp); + chx_ready_enqueue (tp); + } + /* + * It may be THREAD_READY after chx_timer_expired. + * Then, do nothing. + */ + } + else + chx_ready_push (tp); + running = NULL; + } + } + + /* Registers on stack (PSP): r0, r1, r2, r3, r12, lr, pc, xpsr */ + + tp = chx_ready_pop (); + if (tp && tp->flag_sched_rr) + { + chx_spin_lock (&q_timer.lock); + tp = chx_timer_insert (tp, PREEMPTION_USEC); + chx_spin_unlock (&q_timer.lock); + } + + asm volatile ( + ".L_CONTEXT_SWITCH:\n\t" + /* Now, r0 points to the thread to be switched. */ + /* Put it to *running. */ + "ldr r1, =running\n\t" + /* Update running. */ + "str r0, [r1]\n\t" +#if defined(__ARM_ARCH_6M__) + "cmp r0, #0\n\t" + "beq 1f\n\t" +#else + "cbz r0, 1f\n\t" +#endif + /**/ + "add r0, #20\n\t" + "ldm r0!, {r4, r5, r6, r7}\n\t" +#if defined(__ARM_ARCH_6M__) + "ldm r0!, {r1, r2, r3}\n\t" + "mov r8, r1\n\t" + "mov r9, r2\n\t" + "mov r10, r3\n\t" + "ldm r0!, {r1, r2}\n\t" + "mov r11, r1\n\t" + "msr PSP, r2\n\t" +#else + "ldr r8, [r0], #4\n\t" + "ldr r9, [r0], #4\n\t" + "ldr r10, [r0], #4\n\t" + "ldr r11, [r0], #4\n\t" + "ldr r1, [r0], #4\n\t" + "msr PSP, r1\n\t" +#endif + "sub r0, #45\n\t" + "ldrb r1, [r0]\n\t" /* ->PRIO field. */ + "mov r0, #0\n\t" + "cmp r1, #247\n\t" + "bhi 0f\n\t" /* Leave interrupt disabled if >= 248 */ + /**/ + /* Unmask interrupts. */ +#if defined(__ARM_ARCH_6M__) + "cpsie i\n" +#else + "msr BASEPRI, r0\n" +#endif + /**/ + "0:\n\t" + "sub r0, #3\n\t" /* EXC_RETURN to a thread with PSP */ + "bx r0\n" + "1:\n\t" + /* Spawn an IDLE thread. */ + "ldr r0, =__main_stack_end__-32\n\t" + "msr PSP, r0\n\t" + "mov r1, #0\n\t" + "mov r2, #0\n\t" + "mov r3, #0\n\t" + "stm r0!, {r1, r2, r3}\n\t" + "stm r0!, {r1, r2, r3}\n\t" + "ldr r1, =chx_idle\n\t" /* PC = idle */ + "mov r2, #0x010\n\t" + "lsl r2, r2, #20\n\t" /* xPSR = T-flag set (Thumb) */ + "stm r0!, {r1, r2}\n\t" + /**/ + /* Unmask interrupts. */ + "mov r0, #0\n\t" +#if defined(__ARM_ARCH_6M__) + "cpsie i\n\t" +#else + "msr BASEPRI, r0\n" +#endif + /**/ + "sub r0, #3\n\t" /* EXC_RETURN to a thread with PSP */ + "bx r0" + : /* no output */ : "r" (tp) : "memory"); +} + +#if defined(__ARM_ARCH_7M__) +/* + * System call: switch to another thread. + * There are two cases: + * ORIG_R0=0 (SLEEP): Current RUNNING thread is already connected to + * something (mutex, cond, intr, etc.) + * ORIG_R0=1 (YIELD): Current RUNNING thread is active, + * it is needed to be enqueued to READY queue. + */ +void __attribute__ ((naked)) +svc (void) +{ + register struct chx_thread *tp asm ("r0"); + register uint32_t orig_r0 asm ("r1"); + + asm ("ldr r1, =running\n\t" + "ldr r0, [r1]\n\t" + "add r1, r0, #20\n\t" + /* Save registers onto CHX_THREAD struct. */ + "stm r1!, {r4, r5, r6, r7}\n\t" + "mov r2, r8\n\t" + "mov r3, r9\n\t" + "mov r4, r10\n\t" + "mov r5, r11\n\t" + "mrs r6, PSP\n\t" /* r13(=SP) in user space. */ + "stm r1!, {r2, r3, r4, r5, r6}\n\t" + "ldr r1, [r6]\n\t" + "str r0, [r6]" + : "=r" (tp), "=r" (orig_r0) + : /* no input */ + : "r2", "r3", "r4", "r5", "r6", "memory"); + + if (orig_r0) /* yield */ + { + if (tp->flag_sched_rr) + chx_timer_dequeue (tp); + chx_ready_enqueue (tp); + running = NULL; + } + + tp = chx_ready_pop (); + if (tp && tp->flag_sched_rr) + { + chx_spin_lock (&q_timer.lock); + chx_timer_insert (tp, PREEMPTION_USEC); + chx_spin_unlock (&q_timer.lock); + } + + asm volatile ( + "b .L_CONTEXT_SWITCH" + : /* no output */ : "r" (tp) : "memory"); +} +#endif diff --git a/gnuk/chopstx/chopstx-cortex-m.h b/gnuk/chopstx/chopstx-cortex-m.h new file mode 100644 index 0000000000..36515f6015 --- /dev/null +++ b/gnuk/chopstx/chopstx-cortex-m.h @@ -0,0 +1,12 @@ +/* + * The thread context: specific to ARM Cortex-M0/M3. + * + * In this structure, it's only partial information; Other part of the + * context is on the stack. + * + */ +struct tcontext { + uint32_t reg[9]; /* r4, r5, r6, r7, r8, r9, r10, r11, r13(sp) */ +}; + +typedef struct tcontext tcontext_t; diff --git a/gnuk/chopstx/chopstx-gnu-linux.c b/gnuk/chopstx/chopstx-gnu-linux.c new file mode 100644 index 0000000000..9c63b97d6d --- /dev/null +++ b/gnuk/chopstx/chopstx-gnu-linux.c @@ -0,0 +1,351 @@ +/* + * chopstx-gnu-linux.c - Threads and only threads: Arch specific code + * for GNU/Linux emulation + * + * Copyright (C) 2017, 2018 Flying Stone Technology + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx 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. + * + * Chopstx 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 . + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +#include +#include +#include +#include + +/* Data Memory Barrier. */ +static void +chx_dmb (void) +{ +} + + +static sigset_t ss_cur; + +static void +chx_systick_reset (void) +{ + const struct itimerval it = { {0, 0}, {0, 0} }; + + setitimer (ITIMER_REAL, &it, 0); +} + +static void +chx_systick_reload (uint32_t ticks) +{ + struct itimerval it; + + it.it_value.tv_sec = 0; + it.it_value.tv_usec = (ticks / MHZ); + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + + setitimer (ITIMER_REAL, &it, 0); +} + +static uint32_t +chx_systick_get (void) +{ + struct itimerval it; + getitimer (ITIMER_REAL, &it); + return it.it_value.tv_usec * 72; +} + +static uint32_t +usec_to_ticks (uint32_t usec) +{ + return usec * MHZ; +} + + +static void +chx_enable_intr (uint8_t irq_num) +{ + sigdelset (&ss_cur, irq_num); +} + +static void +chx_clr_intr (uint8_t irq_num) +{ /* Clear pending interrupt. */ + (void)irq_num; +} + +static void +chx_disable_intr (uint8_t irq_num) +{ + sigaddset (&ss_cur, irq_num); +} + +static void +chx_set_intr_prio (uint8_t n) +{ + (void)n; +} + +static void +chx_prio_init (void) +{ +} + +static void +chx_cpu_sched_lock (void) +{ + sigset_t ss; + + sigfillset (&ss); + pthread_sigmask (SIG_BLOCK, &ss, &ss_cur); +} + +static void +chx_cpu_sched_unlock (void) +{ + pthread_sigmask (SIG_SETMASK, &ss_cur, NULL); +} + +static void +idle (void) +{ + for (;;) + pause (); +} + +void +chx_handle_intr (uint32_t irq_num) +{ + struct chx_pq *p; + + chx_disable_intr (irq_num); + chx_spin_lock (&q_intr.lock); + for (p = q_intr.q.next; p != (struct chx_pq *)&q_intr.q; p = p->next) + if (p->v == irq_num) + { /* should be one at most. */ + struct chx_px *px = (struct chx_px *)p; + + ll_dequeue (p); + chx_wakeup (p); + chx_spin_unlock (&q_intr.lock); + chx_request_preemption (px->master->prio); + return; + } + chx_spin_unlock (&q_intr.lock); +} + + +static ucontext_t idle_tc; +static char idle_stack[4096]; + +struct chx_thread main_thread; + +void +chx_sigmask (ucontext_t *uc) +{ + /* Modify oldmask to SS_CUR, so that the signal mask will + * be set to SS_CUR. + * + * In user-level, sigset_t is big, but only the first word + * is used by the kernel. + */ + memcpy (&uc->uc_sigmask, &ss_cur, sizeof (uint64_t)); +} + +static void +sigalrm_handler (int sig, siginfo_t *siginfo, void *arg) +{ + extern void chx_timer_expired (void); + ucontext_t *uc = arg; + (void)sig; + (void)siginfo; + chx_timer_expired (); + chx_sigmask (uc); +} + +static void +chx_init_arch (struct chx_thread *tp) +{ + struct sigaction sa; + + sigemptyset (&ss_cur); + + sa.sa_sigaction = sigalrm_handler; + sigfillset (&sa.sa_mask); + sa.sa_flags = SA_SIGINFO|SA_RESTART; + sigaction (SIGALRM, &sa, NULL); + + getcontext (&idle_tc); + idle_tc.uc_stack.ss_sp = idle_stack; + idle_tc.uc_stack.ss_size = sizeof (idle_stack); + idle_tc.uc_link = NULL; + makecontext (&idle_tc, idle, 0); + + getcontext (&tp->tc); +} + +static void +chx_request_preemption (uint16_t prio) +{ + struct chx_thread *tp, *tp_prev; + ucontext_t *tcp; + + if (running && (uint16_t)running->prio >= prio) + return; + + /* Change the context to another thread with higher priority. */ + tp = tp_prev = running; + if (tp) + { + if (tp->flag_sched_rr) + { + if (tp->state == THREAD_RUNNING) + { + chx_timer_dequeue (tp); + chx_ready_enqueue (tp); + } + } + else + chx_ready_push (tp); + running = NULL; + } + + tp = running = chx_ready_pop (); + if (tp) + { + tcp = &tp->tc; + if (tp->flag_sched_rr) + { + chx_spin_lock (&q_timer.lock); + tp = chx_timer_insert (tp, PREEMPTION_USEC); + chx_spin_unlock (&q_timer.lock); + } + } + else + tcp = &idle_tc; + + if (tp_prev) + { + /* + * The swapcontext implementation may reset sigmask in the + * middle of its execution, unfortunately. It is best if + * sigmask restore is done at the end of the routine, but we + * can't assume that. + * + * Thus, there might be a race condition with regards to the + * user context TCP, if signal mask is cleared and signal comes + * in. To avoid this situation, we block signals. + * + * We don't need to fill the mask here. It keeps the condition + * of blocking signals before&after swapcontext call. It is + * done by the signal mask for sigaction, the initial creation + * of the thread, and the condition of chx_sched function which + * mandates holding cpu_sched_lock. + */ + swapcontext (&tp_prev->tc, tcp); + } + else if (tp) + { + setcontext (tcp); + } +} + +/* + * chx_sched: switch to another thread. + * + * There are two cases: + * YIELD=0 (SLEEP): Current RUNNING thread is already connected to + * something (mutex, cond, intr, etc.) + * YIELD=1 (YIELD): Current RUNNING thread is active, + * it is needed to be enqueued to READY queue. + * + * Returns: + * 1 on wakeup by others. + * 0 on normal wakeup. + * -1 on cancellation. + */ +static uintptr_t +chx_sched (uint32_t yield) +{ + struct chx_thread *tp, *tp_prev; + uintptr_t v; + ucontext_t *tcp; + + tp = tp_prev = running; + if (yield) + { + if (tp->flag_sched_rr) + chx_timer_dequeue (tp); + chx_ready_enqueue (tp); + } + + running = tp = chx_ready_pop (); + if (tp) + { + v = tp->v; + if (tp->flag_sched_rr) + { + chx_spin_lock (&q_timer.lock); + tp = chx_timer_insert (tp, PREEMPTION_USEC); + chx_spin_unlock (&q_timer.lock); + } + tcp = &tp->tc; + } + else + { + v = 0; + tcp = &idle_tc; + } + + swapcontext (&tp_prev->tc, tcp); + chx_cpu_sched_unlock (); + return v; +} + +static void __attribute__((__noreturn__)) +chx_thread_start (voidfunc thread_entry, void *arg) +{ + chx_cpu_sched_unlock (); + thread_entry (arg); + chopstx_exit (0); +} + +static struct chx_thread * +chopstx_create_arch (uintptr_t stack_addr, size_t stack_size, + voidfunc thread_entry, void *arg) +{ + struct chx_thread *tp; + tp = malloc (sizeof (struct chx_thread)); + if (!tp) + chx_fatal (CHOPSTX_ERR_THREAD_CREATE); + + /* + * Calling getcontext with sched_lock held, the context is with + * signal blocked. The sigmask will be cleared in chx_thread_start. + */ + chx_cpu_sched_lock (); + getcontext (&tp->tc); + tp->tc.uc_stack.ss_sp = (void *)stack_addr; + tp->tc.uc_stack.ss_size = stack_size; + tp->tc.uc_link = NULL; + + makecontext (&tp->tc, (void (*)(void))chx_thread_start, + 4, thread_entry, arg); + chx_cpu_sched_unlock (); + return tp; +} diff --git a/gnuk/chopstx/chopstx-gnu-linux.h b/gnuk/chopstx/chopstx-gnu-linux.h new file mode 100644 index 0000000000..aaa93d4d09 --- /dev/null +++ b/gnuk/chopstx/chopstx-gnu-linux.h @@ -0,0 +1,10 @@ +#include +/* + * The thread context: specific to GNU/Linux. + * + * We use the type ucontext_t, which includes all registers; + * Note that signal mask is also included in ucontext_t. + * + */ + +typedef ucontext_t tcontext_t; diff --git a/gnuk/chopstx/chopstx.c b/gnuk/chopstx/chopstx.c new file mode 100644 index 0000000000..4d672c77fb --- /dev/null +++ b/gnuk/chopstx/chopstx.c @@ -0,0 +1,1559 @@ +/* + * chopstx.c - Threads and only threads. + * + * Copyright (C) 2013, 2014, 2015, 2016, 2017, 2018, 2019 + * Flying Stone Technology + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx 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. + * + * Chopstx 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 . + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +#include +#include +#include +#include +#include + +/* + * Thread priority: greater (as integer) has higher precedence. + */ +#if !defined(CHX_PRIO_MAIN_INIT) +#define CHX_PRIO_MAIN_INIT 1 +#endif +#if !defined(CHX_FLAGS_MAIN) +#define CHX_FLAGS_MAIN 0 +#endif + +/* Constant for round robin scheduling. */ +#if !defined(PREEMPTION_USEC) +#define PREEMPTION_USEC 1000 /* 1ms */ +#endif + +#define MAX_PRIO (255+1) + +#ifndef MHZ +#define MHZ 72 +#endif + +typedef void *(voidfunc) (void *); + +void __attribute__((weak)) chx_fatal (uint32_t err_code); + +/** + * chx_fatal - Fatal error point. + * @err_code: Error code + * + * When it detects a coding error, this function will be called to + * stop further execution of code. It never returns. + */ +void +chx_fatal (uint32_t err_code) +{ + (void)err_code; + for (;;); +} + +/* Include the definition of thread context structure. */ +#ifdef GNU_LINUX_EMULATION +#include "chopstx-gnu-linux.h" +#else +#include "chopstx-cortex-m.h" +#endif + +/* ALLOW_SLEEP for the idle thread. */ +int chx_allow_sleep; +static struct chx_spinlock chx_enable_sleep_lock; + +/* RUNNING: the current thread. */ +struct chx_thread *running; + +struct chx_queue { + struct chx_qh q; + struct chx_spinlock lock; +}; + +/* READY: priority queue. */ +static struct chx_queue q_ready; + +/* Queue of threads waiting for timer. */ +static struct chx_queue q_timer; + +/* Queue of threads which wait for the exit of some thread. */ +static struct chx_queue q_join; + +/* Queue of threads which wait for some interrupts. */ +static struct chx_queue q_intr; + +/* Forward declaration(s). */ +static void chx_request_preemption (uint16_t prio); +static int chx_wakeup (struct chx_pq *p); +static struct chx_thread * chx_timer_insert (struct chx_thread *tp, uint32_t usec); +static uint32_t chx_timer_dequeue (struct chx_thread *tp); + + + +/**************/ +static void chx_spin_init (struct chx_spinlock *lk) +{ + (void)lk; +} + +static void chx_spin_lock (struct chx_spinlock *lk) +{ + (void)lk; +} + +static void chx_spin_unlock (struct chx_spinlock *lk) +{ + (void)lk; +} + +/**************/ +struct chx_pq { + struct chx_pq *next, *prev; + uint32_t : 4; + uint32_t : 5; + uint32_t : 6; + uint32_t flag_is_proxy : 1; + uint32_t : 8; + uint32_t prio : 8; + struct chx_qh *parent; + uintptr_t v; +}; + +struct chx_px { /* inherits PQ */ + struct chx_pq *next, *prev; + uint32_t : 4; + uint32_t : 5; + uint32_t : 6; + uint32_t flag_is_proxy : 1; + uint32_t : 8; + uint32_t prio : 8; + struct chx_qh *parent; + uintptr_t v; + struct chx_thread *master; + uint32_t *counter_p; + uint16_t *ready_p; + struct chx_spinlock lock; /* spinlock to update the COUNTER */ +}; + +struct chx_thread { /* inherits PQ */ + struct chx_pq *next, *prev; + uint32_t state : 4; + uint32_t flag_detached : 1; + uint32_t flag_got_cancel : 1; + uint32_t flag_join_req : 1; + uint32_t flag_sched_rr : 1; + uint32_t flag_cancelable : 1; + uint32_t : 6; + uint32_t flag_is_proxy : 1; + uint32_t prio_orig : 8; + uint32_t prio : 8; + struct chx_qh *parent; + uintptr_t v; + tcontext_t tc; + struct chx_mtx *mutex_list; + struct chx_cleanup *clp; +}; + + +/* + * Double linked list handling. + */ + +static int +ll_empty (struct chx_qh *q) +{ + return q == (struct chx_qh *)q->next; +} + +static struct chx_pq * +ll_dequeue (struct chx_pq *pq) +{ + pq->next->prev = pq->prev; + pq->prev->next = pq->next; + pq->prev = pq->next = pq; + return pq; +} + +static void +ll_insert (struct chx_pq *pq0, struct chx_qh *q) +{ + struct chx_pq *pq = (struct chx_pq *)q; + + pq0->next = (struct chx_pq *)pq; + pq0->prev = pq->prev; + pq->prev->next = (struct chx_pq *)pq0; + pq->prev = pq0; +} + + +static struct chx_pq * +ll_pop (struct chx_qh *q) +{ + if (q == (struct chx_qh *)q->next) + return NULL; + + return ll_dequeue (q->next); +} + +static void +ll_prio_push (struct chx_pq *pq0, struct chx_qh *q0) +{ + struct chx_pq *p; + + for (p = q0->next; p != (struct chx_pq *)q0; p = p->next) + if (p->prio <= pq0->prio) + break; + + pq0->parent = q0; + ll_insert (pq0, (struct chx_qh *)p); +} + +static void +ll_prio_enqueue (struct chx_pq *pq0, struct chx_qh *q0) +{ + struct chx_pq *p; + + for (p = q0->next; p != (struct chx_pq *)q0; p = p->next) + if (p->prio < pq0->prio) + break; + + pq0->parent = q0; + ll_insert (pq0, (struct chx_qh *)p); +} + + +/* + * Thread status. + */ +enum { + THREAD_RUNNING=0, + THREAD_READY, + THREAD_WAIT_MTX, + THREAD_WAIT_CND, + THREAD_WAIT_TIME, + THREAD_WAIT_EXIT, + THREAD_WAIT_POLL, + /**/ + THREAD_EXITED=0x0E, + THREAD_FINISHED=0x0F +}; + + +static struct chx_thread * +chx_ready_pop (void) +{ + struct chx_thread *tp; + + chx_spin_lock (&q_ready.lock); + tp = (struct chx_thread *)ll_pop (&q_ready.q); + if (tp) + tp->state = THREAD_RUNNING; + chx_spin_unlock (&q_ready.lock); + + return tp; +} + + +static void +chx_ready_push (struct chx_thread *tp) +{ + chx_spin_lock (&q_ready.lock); + tp->state = THREAD_READY; + ll_prio_push ((struct chx_pq *)tp, &q_ready.q); + chx_spin_unlock (&q_ready.lock); +} + + +static void +chx_ready_enqueue (struct chx_thread *tp) +{ + chx_spin_lock (&q_ready.lock); + tp->state = THREAD_READY; + ll_prio_enqueue ((struct chx_pq *)tp, &q_ready.q); + chx_spin_unlock (&q_ready.lock); +} + +/* + * Here comes architecture specific code. + */ + +#ifdef GNU_LINUX_EMULATION +#include "chopstx-gnu-linux.c" +#else +#include "chopstx-cortex-m.c" +#endif + +static void +chx_set_timer (struct chx_thread *tp, uint32_t ticks) +{ + if (tp == (struct chx_thread *)&q_timer.q) + chx_systick_reload (ticks); + else + tp->v = ticks; +} + +static struct chx_thread * +chx_timer_insert (struct chx_thread *tp, uint32_t usec) +{ + struct chx_pq *p; + uint32_t ticks = usec_to_ticks (usec); + uint32_t next_ticks = chx_systick_get (); + + for (p = q_timer.q.next; p != (struct chx_pq *)&q_timer.q; p = p->next) + { + if (ticks < next_ticks) + { + tp->parent = &q_timer.q; + ll_insert ((struct chx_pq *)tp, (struct chx_qh *)p); + chx_set_timer ((struct chx_thread *)tp->prev, ticks); + chx_set_timer (tp, (next_ticks - ticks)); + break; + } + else + { + ticks -= next_ticks; + next_ticks = p->v; + } + } + + if (p == (struct chx_pq *)&q_timer.q) + { + tp->parent = &q_timer.q; + ll_insert ((struct chx_pq *)tp, (struct chx_qh *)p); + chx_set_timer ((struct chx_thread *)tp->prev, ticks); + chx_set_timer (tp, 1); /* Non-zero for the last entry. */ + } + + return tp; +} + + +static uint32_t +chx_timer_dequeue (struct chx_thread *tp) +{ + struct chx_thread *tp_prev; + uint32_t ticks_remained; + + chx_spin_lock (&q_timer.lock); + ticks_remained = chx_systick_get (); + tp_prev = (struct chx_thread *)tp->prev; + if (tp_prev == (struct chx_thread *)&q_timer.q) + { + if (tp->next == (struct chx_pq *)&q_timer.q) + chx_systick_reload (0); /* Cancel timer. */ + else + chx_systick_reload (ticks_remained + tp->v); /* Update timer. */ + } + else + { + struct chx_pq *p; + + for (p = q_timer.q.next; p != (struct chx_pq *)tp; p = p->next) + ticks_remained += p->v; + + tp_prev->v += tp->v; + } + ll_dequeue ((struct chx_pq *)tp); + tp->v = 0; + chx_spin_unlock (&q_timer.lock); + return ticks_remained; +} + + +void +chx_timer_expired (void) +{ + struct chx_thread *tp; + uint16_t prio = 0; /* Use uint16_t here. */ + + chx_spin_lock (&q_timer.lock); + if ((tp = (struct chx_thread *)ll_pop (&q_timer.q))) + { + uint32_t next_tick = tp->v; + + tp->v = (uintptr_t)0; + chx_ready_enqueue (tp); + if (tp == running) /* tp->flag_sched_rr == 1 */ + prio = MAX_PRIO; + else + if ((uint16_t)tp->prio > prio) + prio = (uint16_t)tp->prio; + + if (!ll_empty (&q_timer.q)) + { + struct chx_thread *tp_next; + + for (tp = (struct chx_thread *)q_timer.q.next; + tp != (struct chx_thread *)&q_timer.q && next_tick == 0; + tp = tp_next) + { + next_tick = tp->v; + tp->v = (uintptr_t)0; + tp_next = (struct chx_thread *)tp->next; + ll_dequeue ((struct chx_pq *)tp); + chx_ready_enqueue (tp); + if (tp == running) + prio = MAX_PRIO; + else + if ((uint16_t)tp->prio > prio) + prio = (uint16_t)tp->prio; + } + + if (!ll_empty (&q_timer.q)) + chx_set_timer ((struct chx_thread *)&q_timer.q, next_tick); + } + } + + chx_spin_unlock (&q_timer.lock); + chx_request_preemption (prio); +} + + +void +chx_systick_init (void) +{ + chx_systick_reset (); + + if ((CHX_FLAGS_MAIN & CHOPSTX_SCHED_RR)) + { + chx_cpu_sched_lock (); + chx_spin_lock (&q_timer.lock); + chx_timer_insert (running, PREEMPTION_USEC); + chx_spin_unlock (&q_timer.lock); + chx_cpu_sched_unlock (); + } +} + +chopstx_t chopstx_main; + +void +chx_init (struct chx_thread *tp) +{ + chx_prio_init (); + chx_init_arch (tp); + chx_spin_init (&chx_enable_sleep_lock); + + q_ready.q.next = q_ready.q.prev = (struct chx_pq *)&q_ready.q; + chx_spin_init (&q_ready.lock); + q_timer.q.next = q_timer.q.prev = (struct chx_pq *)&q_timer.q; + chx_spin_init (&q_timer.lock); + q_join.q.next = q_join.q.prev = (struct chx_pq *)&q_join.q; + chx_spin_init (&q_join.lock); + q_intr.q.next = q_intr.q.prev = (struct chx_pq *)&q_intr.q; + chx_spin_init (&q_intr.lock); + tp->next = tp->prev = (struct chx_pq *)tp; + tp->mutex_list = NULL; + tp->clp = NULL; + tp->state = THREAD_RUNNING; + tp->flag_got_cancel = tp->flag_join_req = 0; + tp->flag_cancelable = 1; + tp->flag_sched_rr = (CHX_FLAGS_MAIN & CHOPSTX_SCHED_RR)? 1 : 0; + tp->flag_detached = (CHX_FLAGS_MAIN & CHOPSTX_DETACHED)? 1 : 0; + tp->flag_is_proxy = 0; + tp->prio_orig = CHX_PRIO_MAIN_INIT; + tp->prio = 0; + tp->parent = NULL; + tp->v = 0; + running = tp; + + if (CHX_PRIO_MAIN_INIT >= CHOPSTX_PRIO_INHIBIT_PREEMPTION) + chx_cpu_sched_lock (); + + tp->prio = CHX_PRIO_MAIN_INIT; + + chopstx_main = (chopstx_t)tp; +} + +#define CHX_SLEEP 0 +#define CHX_YIELD 1 + + +/* + * Wakeup the thread TP. Called with schedule lock held. + */ +static int +chx_wakeup (struct chx_pq *pq) +{ + int yield = 0; + struct chx_thread *tp; + + if (pq->flag_is_proxy) + { + struct chx_px *px = (struct chx_px *)pq; + + chx_spin_lock (&px->lock); + (*px->counter_p)++; + *px->ready_p = 1; + tp = px->master; + if (tp->state == THREAD_WAIT_POLL) + { + if (tp->parent == &q_timer.q) + tp->v = (uintptr_t)chx_timer_dequeue (tp); + else + tp->v = (uintptr_t)1; + + chx_ready_enqueue (tp); + if (!running || tp->prio > running->prio) + yield = 1; + } + chx_spin_unlock (&px->lock); + } + else + { + tp = (struct chx_thread *)pq; + tp->v = (uintptr_t)1; + chx_ready_enqueue (tp); + if (!running || tp->prio > running->prio) + yield = 1; + } + + return yield; +} + + +/* The RETVAL is saved into ->v. */ +static void __attribute__((noreturn)) +chx_exit (void *retval) +{ + struct chx_pq *p; + + chx_cpu_sched_lock (); + if (running->flag_join_req) + { /* wake up a thread which requests to join */ + chx_spin_lock (&q_join.lock); + for (p = q_join.q.next; p != (struct chx_pq *)&q_join.q; p = p->next) + if (p->v == (uintptr_t)running) + { /* should be one at most. */ + ll_dequeue (p); + chx_wakeup (p); + break; + } + chx_spin_unlock (&q_join.lock); + } + + if (running->flag_sched_rr) + chx_timer_dequeue (running); + if (running->flag_detached) + running->state = THREAD_FINISHED; + else + running->state = THREAD_EXITED; + running->v = (uintptr_t)retval; + chx_sched (CHX_SLEEP); + /* never comes here. */ + for (;;); +} + + +/* + * Lower layer mutex unlocking. Called with schedule lock held. + * Return PRIO of the thread which is waken up. + */ +static chopstx_prio_t +chx_mutex_unlock (chopstx_mutex_t *mutex) +{ + struct chx_thread *tp; + + mutex->owner = NULL; + running->mutex_list = mutex->list; + mutex->list = NULL; + + tp = (struct chx_thread *)ll_pop (&mutex->q); + if (!tp) + return 0; + else + { + uint16_t newprio = running->prio_orig; + chopstx_mutex_t *m; + + tp->v = (uintptr_t)0; + chx_ready_enqueue (tp); + + /* Examine mutexes we hold, and determine new priority for running. */ + for (m = running->mutex_list; m; m = m->list) + if (!ll_empty (&m->q) + && ((struct chx_thread *)(m->q.next))->prio > newprio) + newprio = ((struct chx_thread *)m->q.next)->prio; + /* Then, assign it. */ + running->prio = newprio; + + return tp->prio; + } +} + +#define CHOPSTX_PRIO_MASK ((1 << CHOPSTX_PRIO_BITS) - 1) + +/** + * chopstx_create - Create a thread + * @flags_and_prio: Flags and priority + * @stack_addr: Stack address + * @stack_size: Size of stack + * @thread_entry: Entry function of new thread + * @arg: Argument to the thread entry function + * + * Create a thread. Returns thread ID. + */ +chopstx_t +chopstx_create (uint32_t flags_and_prio, + uintptr_t stack_addr, size_t stack_size, + voidfunc thread_entry, void *arg) +{ + struct chx_thread *tp; + chopstx_prio_t prio = (flags_and_prio & CHOPSTX_PRIO_MASK); + + tp = chopstx_create_arch (stack_addr, stack_size, thread_entry, + arg); + tp->next = tp->prev = (struct chx_pq *)tp; + tp->mutex_list = NULL; + tp->clp = NULL; + tp->state = THREAD_EXITED; + tp->flag_got_cancel = tp->flag_join_req = 0; + tp->flag_cancelable = 1; + tp->flag_sched_rr = (flags_and_prio & CHOPSTX_SCHED_RR)? 1 : 0; + tp->flag_detached = (flags_and_prio & CHOPSTX_DETACHED)? 1 : 0; + tp->flag_is_proxy = 0; + tp->prio_orig = tp->prio = prio; + tp->parent = NULL; + tp->v = 0; + + chx_cpu_sched_lock (); + chx_ready_enqueue (tp); + if (tp->prio > running->prio) + chx_sched (CHX_YIELD); + else + chx_cpu_sched_unlock (); + + return (chopstx_t)tp; +} + +/* + * Internal timer uses SYSTICK and it has rather smaller upper limit. + * Thus, we can't let the thread sleep too long, but let it loops. + * + * The caller should make a loop with chx_snooze. + */ +#define MAX_USEC_FOR_TIMER (16777215/MHZ) /* SYSTICK is 24-bit. */ + +/* + * Sleep for some event (MAX_USEC_FOR_TIMER at max). + * + * Returns: + * -1 on cancellation of the thread. + * 0 on timeout. + * 1 when no sleep is needed any more, or some event occurs. + */ +static int +chx_snooze (uint32_t state, uint32_t *usec_p) +{ + uint32_t usec = *usec_p; + uint32_t usec0; + int r; + + if (usec == 0) + { + chx_cpu_sched_unlock (); + return 1; + } + + usec0 = (usec > MAX_USEC_FOR_TIMER) ? MAX_USEC_FOR_TIMER: usec; + if (running->flag_sched_rr) + chx_timer_dequeue (running); + + chx_spin_lock (&q_timer.lock); + running->state = state; + chx_timer_insert (running, usec0); + chx_spin_unlock (&q_timer.lock); + r = chx_sched (CHX_SLEEP); + if (r == 0) + *usec_p -= usec0; + else if (r > 0) + { + *usec_p -= (usec0 - r / MHZ); + r = 1; + } + + return r; +} + + +static void +chopstx_usec_wait_var (uint32_t *var) +{ + int r = 0; + + do + { + chopstx_testcancel (); + chx_cpu_sched_lock (); + r = chx_snooze (THREAD_WAIT_TIME, var); + } + while (r == 0); +} + + +/** + * chopstx_usec_wait - Sleep for micro seconds + * @usec: number of micro seconds + * + * Sleep for @usec. + */ +void +chopstx_usec_wait (uint32_t usec) +{ + chopstx_usec_wait_var (&usec); +} + + +/** + * chopstx_mutex_init - Initialize the mutex + * @mutex: Mutex + * + * Initialize @mutex. + */ +void +chopstx_mutex_init (chopstx_mutex_t *mutex) +{ + chx_spin_init (&mutex->lock); + mutex->q.next = mutex->q.prev = (struct chx_pq *)&mutex->q; + mutex->list = NULL; + mutex->owner = NULL; +} + + +/* + * Re-queue TP after priority change. + * Returns a thread which can wake up this thread TP. + */ +static struct chx_thread * +requeue (struct chx_thread *tp) +{ + if (tp->state == THREAD_READY) + { + chx_spin_lock (&q_ready.lock); + ll_prio_enqueue (ll_dequeue ((struct chx_pq *)tp), tp->parent); + chx_spin_unlock (&q_ready.lock); + } + else if (tp->state == THREAD_WAIT_MTX) + { + struct chx_mtx *mutex = (struct chx_mtx *)tp->parent; + + chx_spin_lock (&mutex->lock); + ll_prio_enqueue (ll_dequeue ((struct chx_pq *)tp), tp->parent); + chx_spin_unlock (&mutex->lock); + return mutex->owner; + } + else if (tp->state == THREAD_WAIT_CND) + { + struct chx_cond *cond = (struct chx_cond *)tp->parent; + + chx_spin_lock (&cond->lock); + ll_prio_enqueue (ll_dequeue ((struct chx_pq *)tp), tp->parent); + chx_spin_unlock (&cond->lock); + /* We don't know who can wake up this thread. */ + } + else if (tp->state == THREAD_WAIT_EXIT) + /* Requeue is not needed as waiting for the thread is only by one. */ + return (struct chx_thread *)tp->v; + + return NULL; +} + +/** + * chopstx_mutex_lock - Lock the mutex + * @mutex: Mutex + * + * Lock @mutex. + */ +void +chopstx_mutex_lock (chopstx_mutex_t *mutex) +{ + struct chx_thread *tp = running; + + while (1) + { + chopstx_mutex_t *m = mutex; + struct chx_thread *tp0; + + chx_cpu_sched_lock (); + chx_spin_lock (&m->lock); + if (m->owner == NULL) + { + /* The mutex is acquired. */ + m->owner = tp; + m->list = tp->mutex_list; + tp->mutex_list = m; + chx_spin_unlock (&m->lock); + chx_cpu_sched_unlock (); + break; + } + + /* Priority inheritance. */ + tp0 = m->owner; + while (tp0 && tp0->prio < tp->prio) + { + tp0->prio = tp->prio; + if (tp0->state == THREAD_WAIT_TIME + || tp0->state == THREAD_WAIT_POLL) + { + if (tp0->parent == &q_timer.q) + tp0->v = (uintptr_t)chx_timer_dequeue (tp0); + else + tp0->v = (uintptr_t)1; + + chx_ready_enqueue (tp0); + tp0 = NULL; + } + else + tp0 = requeue (tp0); + } + + if (tp->flag_sched_rr) + chx_timer_dequeue (tp); + ll_prio_enqueue ((struct chx_pq *)tp, &mutex->q); + tp->state = THREAD_WAIT_MTX; + chx_spin_unlock (&mutex->lock); + chx_sched (CHX_SLEEP); + } +} + + +/** + * chopstx_mutex_unlock - Unlock the mutex + * @mutex: Mutex + * + * Unlock @mutex. + */ +void +chopstx_mutex_unlock (chopstx_mutex_t *mutex) +{ + chopstx_prio_t prio; + + chx_cpu_sched_lock (); + chx_spin_lock (&mutex->lock); + prio = chx_mutex_unlock (mutex); + chx_spin_unlock (&mutex->lock); + if (prio > running->prio) + chx_sched (CHX_YIELD); + else + chx_cpu_sched_unlock (); +} + + +/** + * chopstx_cond_init - Initialize the condition variable + * @cond: Condition variable + * + * Initialize @cond. + */ +void +chopstx_cond_init (chopstx_cond_t *cond) +{ + chx_spin_init (&cond->lock); + cond->q.next = cond->q.prev = (struct chx_pq *)&cond->q; +} + + +/** + * chopstx_cond_wait - Wait on the condition variable + * @cond: Condition variable + * @mutex: Associated mutex + * + * Wait for @cond with @mutex. + */ +void +chopstx_cond_wait (chopstx_cond_t *cond, chopstx_mutex_t *mutex) +{ + struct chx_thread *tp = running; + int r; + + chopstx_testcancel (); + chx_cpu_sched_lock (); + + if (mutex) + { + chx_spin_lock (&mutex->lock); + chx_mutex_unlock (mutex); + chx_spin_unlock (&mutex->lock); + } + + if (tp->flag_sched_rr) + chx_timer_dequeue (tp); + chx_spin_lock (&cond->lock); + ll_prio_enqueue ((struct chx_pq *)tp, &cond->q); + tp->state = THREAD_WAIT_CND; + chx_spin_unlock (&cond->lock); + r = chx_sched (CHX_SLEEP); + + if (mutex) + chopstx_mutex_lock (mutex); + + if (r < 0) + chopstx_exit (CHOPSTX_CANCELED); +} + + +/** + * chopstx_cond_signal - Wake up a thread waiting on the condition variable + * @cond: Condition variable + * + * Wake up a thread waiting on @cond. + */ +void +chopstx_cond_signal (chopstx_cond_t *cond) +{ + struct chx_pq *p; + int yield = 0; + + chx_cpu_sched_lock (); + chx_spin_lock (&cond->lock); + p = ll_pop (&cond->q); + if (p) + yield = chx_wakeup (p); + chx_spin_unlock (&cond->lock); + if (yield) + chx_sched (CHX_YIELD); + else + chx_cpu_sched_unlock (); +} + + +/** + * chopstx_cond_broadcast - Wake up all waiting on the condition variable + * @cond: Condition Variable + * + * Wake up all threads waiting on @cond. + */ +void +chopstx_cond_broadcast (chopstx_cond_t *cond) +{ + struct chx_pq *p; + int yield = 0; + + chx_cpu_sched_lock (); + chx_spin_lock (&cond->lock); + while ((p = ll_pop (&cond->q))) + yield |= chx_wakeup (p); + chx_spin_unlock (&cond->lock); + if (yield) + chx_sched (CHX_YIELD); + else + chx_cpu_sched_unlock (); +} + + +static void +chx_cond_hook (struct chx_px *px, struct chx_poll_head *pd) +{ + struct chx_poll_cond *pc = (struct chx_poll_cond *)pd; + + chopstx_testcancel (); + + if (pc->mutex) + chopstx_mutex_lock (pc->mutex); + + if ((*pc->check) (pc->arg) != 0) + { + chx_spin_lock (&px->lock); + (*px->counter_p)++; + *px->ready_p = 1; + chx_spin_unlock (&px->lock); + } + else + { /* Condition doesn't met. + * Register the proxy to wait for the condition. + */ + pc->ready = 0; + chx_cpu_sched_lock (); + chx_spin_lock (&pc->cond->lock); + ll_prio_enqueue ((struct chx_pq *)px, &pc->cond->q); + chx_spin_unlock (&pc->cond->lock); + chx_cpu_sched_unlock (); + } + + if (pc->mutex) + chopstx_mutex_unlock (pc->mutex); +} + + +/** + * chopstx_claim_irq - Claim interrupt request to handle + * @intr: Pointer to INTR structure + * @irq_num: IRQ Number (hardware specific) + * + * Claim interrupt @intr with @irq_num + */ +void +chopstx_claim_irq (chopstx_intr_t *intr, uint8_t irq_num) +{ + intr->type = CHOPSTX_POLL_INTR; + intr->ready = 0; + intr->irq_num = irq_num; + + chx_cpu_sched_lock (); + chx_spin_lock (&q_intr.lock); + chx_disable_intr (irq_num); + chx_clr_intr (irq_num); + chx_set_intr_prio (irq_num); + chx_spin_unlock (&q_intr.lock); + chx_cpu_sched_unlock (); +} + + +static void +chx_intr_hook (struct chx_px *px, struct chx_poll_head *pd) +{ + struct chx_intr *intr = (struct chx_intr *)pd; + + chopstx_testcancel (); + chx_cpu_sched_lock (); + px->v = intr->irq_num; + if (intr->ready) + { + chx_spin_lock (&px->lock); + (*px->counter_p)++; + chx_spin_unlock (&px->lock); + } + else + { + chx_spin_lock (&q_intr.lock); + ll_prio_enqueue ((struct chx_pq *)px, &q_intr.q); + chx_enable_intr (intr->irq_num); + chx_spin_unlock (&q_intr.lock); + } + chx_cpu_sched_unlock (); +} + + +/** + * chopstx_intr_wait - Wait for interrupt request from hardware + * @intr: Pointer to INTR structure + * + * Wait for the interrupt @intr to be occured. + * + */ +void +chopstx_intr_wait (chopstx_intr_t *intr) +{ + chopstx_poll (NULL, 1, (struct chx_poll_head **)&intr); +} + + +/** + * chopstx_intr_done - Finish an IRQ handling + * @intr: Pointer to INTR structure + * + * Finish for the interrupt @intr occurred. + * + */ +void +chopstx_intr_done (chopstx_intr_t *intr) +{ + chx_dmb (); + + if (intr->ready) + { + chx_clr_intr (intr->irq_num); + intr->ready = 0; + } +} + + +/** + * chopstx_cleanup_push - Register a clean-up + * @clp: Pointer to clean-up structure + * + * Register a clean-up structure. + */ +void +chopstx_cleanup_push (struct chx_cleanup *clp) +{ + clp->next = running->clp; + running->clp = clp; +} + +/** + * chopstx_cleanup_pop - Release a clean-up + * @execute: Execute the clen-up function on release + * + * Unregister a clean-up structure. When @execute is non-zero, the + * clean-up will be executed. + */ +void +chopstx_cleanup_pop (int execute) +{ + struct chx_cleanup *clp = running->clp; + + if (clp) + { + running->clp = clp->next; + if (execute) + clp->routine (clp->arg); + } +} + + +/** + * chopstx_exit - Terminate the execution of running thread + * @retval: Return value (to be caught by a joining thread) + * + * Calling this function terminates the execution of running thread, + * after calling clean up functions. If the calling thread still + * holds mutexes, they will be released. This function never + * returns. + */ +void +chopstx_exit (void *retval) +{ + struct chx_mtx *m, *m_next; + struct chx_cleanup *clp = running->clp; + + running->clp = NULL; + while (clp) + { + clp->routine (clp->arg); + clp = clp->next; + } + + /* Release all mutexes this thread still holds. */ + for (m = running->mutex_list; m; m = m_next) + { + m_next = m->list; + + chx_cpu_sched_lock (); + chx_spin_lock (&m->lock); + chx_mutex_unlock (m); + chx_spin_unlock (&m->lock); + chx_cpu_sched_unlock (); + } + + chx_exit (retval); +} + + +/** + * chopstx_join - join with a terminated thread + * @thd: Thread to wait + * @ret: Pointer to void * to store return value + * + * Waits for the thread of @thd to terminate. + * Returns 0 on success, 1 when waiting is interrupted. + */ +int +chopstx_join (chopstx_t thd, void **ret) +{ + struct chx_thread *tp = (struct chx_thread *)thd; + int r = 0; + + /* + * We don't offer deadlock detection. It's users' responsibility. + */ + + chopstx_testcancel (); + + chx_cpu_sched_lock (); + if (tp->flag_detached) + { + chx_cpu_sched_unlock (); + chx_fatal (CHOPSTX_ERR_JOIN); + } + + if (tp->state != THREAD_EXITED) + { + struct chx_thread *tp0 = tp; + + if (running->flag_sched_rr) + chx_timer_dequeue (running); + chx_spin_lock (&q_join.lock); + ll_prio_enqueue ((struct chx_pq *)running, &q_join.q); + running->v = (uintptr_t)tp; + running->state = THREAD_WAIT_EXIT; + tp->flag_join_req = 1; + + /* Priority inheritance. */ + tp0 = tp; + while (tp0 && tp0->prio < running->prio) + { + tp0->prio = running->prio; + tp0 = requeue (tp0); + } + chx_spin_unlock (&q_join.lock); + r = chx_sched (CHX_SLEEP); + } + else + chx_cpu_sched_unlock (); + + if (r < 0) + chopstx_exit (CHOPSTX_CANCELED); + + if (r == 0) + { + tp->state = THREAD_FINISHED; + if (ret) + *ret = (void *)tp->v; + } + + return r; +} + + +static void +chx_join_hook (struct chx_px *px, struct chx_poll_head *pd) +{ + struct chx_poll_join *pj = (struct chx_poll_join *)pd; + struct chx_thread *tp = (struct chx_thread *)pj->thd; + + chopstx_testcancel (); + chx_cpu_sched_lock (); + + if (tp->flag_detached) + { + chx_cpu_sched_unlock (); + chx_fatal (CHOPSTX_ERR_JOIN); + } + + if (tp->state == THREAD_EXITED) + { + chx_spin_lock (&px->lock); + (*px->counter_p)++; + *px->ready_p = 1; + chx_spin_unlock (&px->lock); + } + else + { /* Not yet exited. + * Register the proxy to wait for TP's exit. + */ + pj->ready = 0; + px->v = (uintptr_t)tp; + chx_spin_lock (&q_join.lock); + ll_prio_enqueue ((struct chx_pq *)px, &q_join.q); + chx_spin_unlock (&q_join.lock); + tp->flag_join_req = 1; + } + chx_cpu_sched_unlock (); +} + + +/** + * chopstx_cancel - request a cancellation to a thread + * @thd: Thread to be canceled + * + * This function requests a cancellation of a thread @thd. + * No return value. + */ +void +chopstx_cancel (chopstx_t thd) +{ + struct chx_thread *tp = (struct chx_thread *)thd; + + chx_cpu_sched_lock (); + tp->flag_got_cancel = 1; + if (!tp->flag_cancelable) + { + chx_cpu_sched_unlock (); + return; + } + + /* Cancellation points: cond_wait, usec_wait, join, and poll. */ + if (tp->state == THREAD_WAIT_CND) + { + struct chx_cond *cond = (struct chx_cond *)tp->parent; + + chx_spin_lock (&cond->lock); + ll_dequeue ((struct chx_pq *)tp); + chx_spin_unlock (&cond->lock); + } + else if (tp->state == THREAD_WAIT_TIME) + chx_timer_dequeue (tp); + else if (tp->state == THREAD_WAIT_EXIT) + { + chx_spin_lock (&q_join.lock); + ll_dequeue ((struct chx_pq *)tp); + chx_spin_unlock (&q_join.lock); + } + else if (tp->state == THREAD_WAIT_POLL) + { + if (tp->parent == &q_timer.q) + chx_timer_dequeue (tp); + } + else + { + chx_cpu_sched_unlock (); + return; + } + + tp->v = (uintptr_t)-1; + chx_ready_enqueue (tp); + if (tp->prio > running->prio) + chx_sched (CHX_YIELD); + else + chx_cpu_sched_unlock (); +} + + +/** + * chopstx_testcancel - catch pending cancellation request + * + * Calling chopstx_testcancel creates a cancellation point. + * No return value. If the thread is canceled, this function + * does not return. + */ +void +chopstx_testcancel (void) +{ + if (running->flag_cancelable && running->flag_got_cancel) + chopstx_exit (CHOPSTX_CANCELED); +} + + +/** + * chopstx_setcancelstate - set cancelability state + * @cancel_disable: 0 to enable cancelation, otherwise disabled. + * + * Calling chopstx_setcancelstate sets cancelability state. + * + * Returns old state which is 0 when it was enabled. + */ +int +chopstx_setcancelstate (int cancel_disable) +{ + int old_state = !running->flag_cancelable; + + running->flag_cancelable = (cancel_disable == 0); + chopstx_testcancel (); + return old_state; +} + +static void +chx_proxy_init (struct chx_px *px, uint32_t *cp) +{ + px->next = px->prev = (struct chx_pq *)px; + px->flag_is_proxy = 1; + px->prio = running->prio; + px->parent = NULL; + px->v = 0; + px->master = running; + px->counter_p = cp; + px->ready_p = NULL; + chx_spin_init (&px->lock); +} + + +/** + * chopstx_poll - wait for condition variable, thread's exit, or IRQ + * @usec_p: Pointer to usec for timeout. Forever if NULL. It is + * updated on return + * @n: Number of poll descriptors + * @pd_array: Pointer to an array of poll descriptor pointer which + * should be one of: + * chopstx_poll_cond_t, chopstx_poll_join_t, or chopstx_intr_t. + * + * Returns number of active descriptors. + */ +int +chopstx_poll (uint32_t *usec_p, int n, struct chx_poll_head *const pd_array[]) +{ + uint32_t counter = 0; + int i; + struct chx_px px[n]; + struct chx_poll_head *pd; + int r = 0; + + chx_dmb (); + chopstx_testcancel (); + + for (i = 0; i < n; i++) + chx_proxy_init (&px[i], &counter); + + for (i = 0; i < n; i++) + { + pd = pd_array[i]; + px[i].ready_p = &pd->ready; + if (pd->type == CHOPSTX_POLL_COND) + chx_cond_hook (&px[i], pd); + else if (pd->type == CHOPSTX_POLL_INTR) + chx_intr_hook (&px[i], pd); + else + chx_join_hook (&px[i], pd); + } + + chx_cpu_sched_lock (); + chx_spin_lock (&px->lock); + if (counter) + { + chx_spin_unlock (&px->lock); + chx_cpu_sched_unlock (); + } + else if (usec_p == NULL) + { + if (running->flag_sched_rr) + chx_timer_dequeue (running); + + running->state = THREAD_WAIT_POLL; + chx_spin_unlock (&px->lock); + r = chx_sched (CHX_SLEEP); + } + else + { + chx_spin_unlock (&px->lock); + chx_cpu_sched_unlock (); + do + { + chopstx_testcancel (); + chx_cpu_sched_lock (); + if (counter) + { + chx_cpu_sched_unlock (); + break; + } + r = chx_snooze (THREAD_WAIT_POLL, usec_p); + } + while (r == 0); + } + + chx_dmb (); + for (i = 0; i < n; i++) + { + pd = pd_array[i]; + chx_cpu_sched_lock (); + chx_spin_lock (&px[i].lock); + if (pd->type == CHOPSTX_POLL_COND) + { + struct chx_poll_cond *pc = (struct chx_poll_cond *)pd; + + if (pc->ready == 0) + { + chx_spin_lock (&pc->cond->lock); + ll_dequeue ((struct chx_pq *)&px[i]); + chx_spin_unlock (&pc->cond->lock); + } + } + else if (pd->type == CHOPSTX_POLL_INTR) + { + struct chx_intr *intr = (struct chx_intr *)pd; + + if (intr->ready == 0) + { + chx_spin_lock (&q_intr.lock); + ll_dequeue ((struct chx_pq *)&px[i]); + chx_spin_unlock (&q_intr.lock); + chx_disable_intr (intr->irq_num); + } + } + else + { + struct chx_poll_join *pj = (struct chx_poll_join *)pd; + + if (pj->ready == 0) + { + chx_spin_lock (&q_join.lock); + ll_dequeue ((struct chx_pq *)&px[i]); + chx_spin_unlock (&q_join.lock); + } + } + chx_spin_unlock (&px[i].lock); + chx_cpu_sched_unlock (); + } + + if (r < 0) + chopstx_exit (CHOPSTX_CANCELED); + + return counter; +} + + +/** + * chopstx_setpriority - change the schedule priority of running thread + * @prio: priority + * + * Change the schedule priority with @prio. + * Returns the old priority. + * + * In general, it is not recommended to use this function because + * dynamically changing schedule priorities complicates the system. + * Only a possible valid usage of this function is in the main thread + * which starts its execution with priority of CHX_PRIO_MAIN_INIT, and + * let it change its priority after initialization of other threads. + */ +chopstx_prio_t +chopstx_setpriority (chopstx_prio_t prio_new) +{ + struct chx_thread *tp = running; + chopstx_prio_t prio_orig, prio_cur; + + chx_cpu_sched_lock (); + prio_orig = tp->prio_orig; + prio_cur = tp->prio; + + tp->prio_orig = prio_new; + if (prio_cur == prio_orig) + /* No priority inheritance is active. */ + tp->prio = prio_new; + else + /* Priority inheritance is active. */ + /* In this case, only when new priority is greater, change the + priority of this thread. */ + if (prio_new > prio_cur) + tp->prio = prio_new; + + if (tp->prio < prio_cur) + chx_sched (CHX_YIELD); + else if (tp->prio < CHOPSTX_PRIO_INHIBIT_PREEMPTION) + chx_cpu_sched_unlock (); + + return prio_orig; +} + + +/** + * chopstx_conf_idle - Configure IDLE thread + * @enable_sleep: Enable sleep on idle or not + * + * If @enable_sleep is > 0, allow sleep for the idle thread. + * + * Behavior of @enable_sleep >= 1 depends on MCU. + * + * For STM32F0, 1 for Sleep (CPU clock OFF only), 2 for Stop (Wakeup + * by EXTI, voltage regulator on), 3 for Stop (Wakeup by EXTI, voltage + * regulator low-power), 4 for Standby (Wakeup by RESET, voltage + * regulator off). + * + * For STM32F103, 1 for normal sleep, and 2 for sleep with lower 8MHz + * clock. + * + * Return previous value of @enable_sleep. + */ +extern void chx_sleep_mode (int enable_sleep); + +int +chopstx_conf_idle (int enable_sleep) +{ + int r; + + chx_spin_lock (&chx_enable_sleep_lock); + r = chx_allow_sleep; + chx_sleep_mode (enable_sleep); + chx_allow_sleep = enable_sleep; + chx_spin_unlock (&chx_enable_sleep_lock); + + return r; +} diff --git a/gnuk/chopstx/chopstx.h b/gnuk/chopstx/chopstx.h new file mode 100644 index 0000000000..a36a74b880 --- /dev/null +++ b/gnuk/chopstx/chopstx.h @@ -0,0 +1,167 @@ +/* + * chopstx.h - Threads and only threads. + * + * Copyright (C) 2013, 2016, 2017, 2018 Flying Stone Technology + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx 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. + * + * Chopstx 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 . + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +struct chx_qh { + struct chx_pq *next, *prev; +}; + +typedef uintptr_t chopstx_t; +typedef uint8_t chopstx_prio_t; + +extern chopstx_t chopstx_main; + + +/* NOTE: This signature is different to PTHREAD's one. */ +chopstx_t +chopstx_create (uint32_t flags_and_prio, + uintptr_t stack_addr, size_t stack_size, + void *(thread_entry) (void *), void *); +#define CHOPSTX_PRIO_BITS 8 +#define CHOPSTX_DETACHED 0x10000 +#define CHOPSTX_SCHED_RR 0x20000 + +#define CHOPSTX_PRIO_INHIBIT_PREEMPTION 248 + +void chopstx_usec_wait (uint32_t usec); + +struct chx_spinlock { + /* nothing for uniprocessor. */ +}; + +typedef struct chx_mtx { + struct chx_qh q; + struct chx_spinlock lock; + struct chx_thread *owner; + struct chx_mtx *list; +} chopstx_mutex_t; + +/* NOTE: This signature is different to PTHREAD's one. */ +void chopstx_mutex_init (chopstx_mutex_t *mutex); + +void chopstx_mutex_lock (chopstx_mutex_t *mutex); + +void chopstx_mutex_unlock (chopstx_mutex_t *mutex); + +typedef struct chx_cond { + struct chx_qh q; + struct chx_spinlock lock; +} chopstx_cond_t; + +/* NOTE: This signature is different to PTHREAD's one. */ +void chopstx_cond_init (chopstx_cond_t *cond); + +void chopstx_cond_wait (chopstx_cond_t *cond, chopstx_mutex_t *mutex); +void chopstx_cond_signal (chopstx_cond_t *cond); +void chopstx_cond_broadcast (chopstx_cond_t *cond); + +/* + * Library provides default implementation as weak reference. + * User can replace it. + */ +void chx_fatal (uint32_t err_code) __attribute__((__noreturn__)); + +int chopstx_join (chopstx_t, void **); +void chopstx_exit (void *retval) __attribute__((__noreturn__)); + + +enum { + CHOPSTX_ERR_NONE = 0, + CHOPSTX_ERR_THREAD_CREATE, + CHOPSTX_ERR_JOIN, +}; + +#define CHOPSTX_CANCELED ((void *) -1) + +void chopstx_cancel (chopstx_t thd); +void chopstx_testcancel (void); + +/* NOTE: This signature is different to PTHREAD's one. */ +int chopstx_setcancelstate (int); + +typedef struct chx_cleanup { + struct chx_cleanup *next; + void (*routine) (void *); + void *arg; +} chopstx_cleanup_t; + +/* NOTE: This signature is different to PTHREAD's one. */ +void chopstx_cleanup_push (chopstx_cleanup_t *clp); +void chopstx_cleanup_pop (int execute); + +chopstx_prio_t chopstx_setpriority (chopstx_prio_t); + +enum { + CHOPSTX_POLL_COND = 0, + CHOPSTX_POLL_INTR, + CHOPSTX_POLL_JOIN, +}; + +struct chx_poll_head { + uint16_t type; + uint16_t ready; +}; + +struct chx_poll_cond { + uint16_t type; + uint16_t ready; + /**/ + chopstx_cond_t *cond; + chopstx_mutex_t *mutex; + int (*check) (void *); + void *arg; +}; +typedef struct chx_poll_cond chopstx_poll_cond_t; + +struct chx_poll_join { + uint16_t type; + uint16_t ready; + /**/ + chopstx_t thd; +}; +typedef struct chx_poll_join chopstx_poll_join_t; + +struct chx_intr { + uint16_t type; + uint16_t ready; + /**/ + uint8_t irq_num; +}; +typedef struct chx_intr chopstx_intr_t; + +void chopstx_claim_irq (chopstx_intr_t *intr, uint8_t irq_num); + +void chopstx_intr_wait (chopstx_intr_t *intr); +void chopstx_intr_done (chopstx_intr_t *intr); + + +int chopstx_poll (uint32_t *usec_p, int n, + struct chx_poll_head *const pd_array[]); + +int chopstx_conf_idle (int enable_sleep); + +#define CHOPSTX_THREAD_SIZE 64 diff --git a/gnuk/chopstx/chopstx.png b/gnuk/chopstx/chopstx.png new file mode 100644 index 0000000000..5335dccfde Binary files /dev/null and b/gnuk/chopstx/chopstx.png differ diff --git a/gnuk/chopstx/chopstx.svg b/gnuk/chopstx/chopstx.svg new file mode 100644 index 0000000000..61ab6ae853 --- /dev/null +++ b/gnuk/chopstx/chopstx.svg @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gnuk/chopstx/contrib/ackbtn-stm32f103.c b/gnuk/chopstx/contrib/ackbtn-stm32f103.c new file mode 100644 index 0000000000..d06a5e286b --- /dev/null +++ b/gnuk/chopstx/contrib/ackbtn-stm32f103.c @@ -0,0 +1,115 @@ +/* + * ackbtn-stm32f103.c - Acknowledge button support for STM32F103 + * + * Copyright (C) 2018 g10 Code GmbH + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx 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. + * + * Chopstx 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 . + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +#include +#include +#include +#include + +#include "board.h" +#include "sys.h" + +/* + * All EXTI registers (EXTI_IMR, EXTI_EMR, EXTI_PR , EXTI_RTSR, and + * EXTI_FTSR) have same structure, where each bit of X is used for + * line X, from 0 up to 19. + * + * We use 31-bit of PIN_CONFIG to represent if it's for rising edge or + * falling edge. + */ +static uint32_t pin_config; +#define PINCFG_EDGE 0x80000000 +#define PINCFG_EDGE_RISING PINCFG_EDGE + +void +ackbtn_init (chopstx_intr_t *intr) +{ + uint8_t irq_num; + uint32_t afio_exticr_index; + uint32_t afio_exticr_extiX_pY; + + switch (SYS_BOARD_ID) + { + case BOARD_ID_FST_01: + case BOARD_ID_FST_01G: + /* PA2 can be connected to a hall sensor or a switch */ + afio_exticr_index = 0; + afio_exticr_extiX_pY = AFIO_EXTICR1_EXTI2_PA; + irq_num = EXTI2_IRQ; + pin_config = 0x0004; /* EXTI_PR_PR2 == EXTI_IMR_MR2 == EXTI_RTSR_TR2 */ + pin_config |= PINCFG_EDGE_RISING; + break; + + case BOARD_ID_FST_01SZ: + default: + /* PA3 is connected to a hall sensor DRV5032FA */ + afio_exticr_index = 0; + afio_exticr_extiX_pY = AFIO_EXTICR1_EXTI3_PA; + irq_num = EXTI3_IRQ; + pin_config = 0x0008; /* EXTI_PR_PR3 == EXTI_IMR_MR3 == EXTI_RTSR_TR3 */ + pin_config |= PINCFG_EDGE_RISING; + break; + } + + /* Configure EXTI line */ + if (afio_exticr_extiX_pY) + AFIO->EXTICR[afio_exticr_index] |= afio_exticr_extiX_pY; + + /* Interrupt is masked, now */ + EXTI->IMR &= ~(pin_config & ~PINCFG_EDGE); + + chopstx_claim_irq (intr, irq_num); +} + +void +ackbtn_enable (void) +{ + /* Clear pending interrupt */ + EXTI->PR |= (pin_config & ~PINCFG_EDGE); + /* Enable interrupt, clearing the mask */ + EXTI->IMR |= (pin_config & ~PINCFG_EDGE); + + /* Configure which edge is detected */ + if ((pin_config & PINCFG_EDGE)) + EXTI->RTSR |= (pin_config & ~PINCFG_EDGE); + else + EXTI->FTSR |= (pin_config & ~PINCFG_EDGE); +} + +void +ackbtn_disable (void) +{ + /* Disable interrupt having the mask */ + EXTI->IMR &= ~(pin_config & ~PINCFG_EDGE); + /* Clear pending interrupt */ + EXTI->PR |= (pin_config & ~PINCFG_EDGE); + + /* Disable edge detection */ + EXTI->RTSR &= ~(pin_config & ~PINCFG_EDGE); + EXTI->FTSR &= ~(pin_config & ~PINCFG_EDGE); +} diff --git a/gnuk/chopstx/contrib/ackbtn.h b/gnuk/chopstx/contrib/ackbtn.h new file mode 100644 index 0000000000..da0e29c28f --- /dev/null +++ b/gnuk/chopstx/contrib/ackbtn.h @@ -0,0 +1,3 @@ +void ackbtn_init (chopstx_intr_t *intr); +void ackbtn_enable (void); +void ackbtn_disable (void); diff --git a/gnuk/chopstx/contrib/adc-gnu-linux.c b/gnuk/chopstx/contrib/adc-gnu-linux.c new file mode 100644 index 0000000000..e71d7f9584 --- /dev/null +++ b/gnuk/chopstx/contrib/adc-gnu-linux.c @@ -0,0 +1,77 @@ +/* + * adc-gnu-linux.c - ADC driver for GNU/Linux emulation. + * This ADC driver just fills pseudo random values. + * It's completely useless other than for NeuG. + * + * Copyright (C) 2017 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx 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. + * + * Chopstx 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 . + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +#include +#include +#include +#include "adc.h" + +#define ADC_RANDOM_SEED 0x01034649 /* "Hello, father!" in Japanese */ + +/* + * Do calibration for ADC. + */ +int +adc_init (void) +{ + srandom (ADC_RANDOM_SEED); + return 0; +} + +void +adc_start (void) +{ +} + +uint32_t adc_buf[64]; + +void +adc_start_conversion (int offset, int count) +{ + while (count--) + adc_buf[offset++] = random (); +} + + +void +adc_stop (void) +{ +} + + +/* + * Return 0 on success. + * Return 1 on error. + */ +int +adc_wait_completion (void) +{ + return 0; +} diff --git a/gnuk/chopstx/contrib/adc-mkl27z.c b/gnuk/chopstx/contrib/adc-mkl27z.c new file mode 100644 index 0000000000..37b81d07f5 --- /dev/null +++ b/gnuk/chopstx/contrib/adc-mkl27z.c @@ -0,0 +1,322 @@ +/* + * adc-mkl27z.c - ADC driver for MKL27Z + * In this ADC driver, there are NeuG specific parts. + * It only records lower 8-bit of 16-bit data. + * You need to modify to use this as generic ADC driver. + * + * Copyright (C) 2016 Flying Stone Technology + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx 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. + * + * Chopstx 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 . + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +#include +#include +#include +#include + +struct DMAMUX { + volatile uint32_t CHCFG0; + volatile uint32_t CHCFG1; + volatile uint32_t CHCFG2; + volatile uint32_t CHCFG3; +}; +static struct DMAMUX *const DMAMUX = (struct DMAMUX *)0x40021000; + +#define INTR_REQ_DMA0 0 + +struct DMA { + volatile uint32_t SAR; + volatile uint32_t DAR; + volatile uint32_t DSR_BCR; + volatile uint32_t DCR; +}; +static struct DMA *const DMA0 = (struct DMA *)0x40008100; +static struct DMA *const DMA1 = (struct DMA *)0x40008110; + + +/* We don't use ADC interrupt. Just for reference. */ +#define INTR_REQ_ADC 15 + +struct ADC { + volatile uint32_t SC1[2];/* Status and Control Registers 1 */ + volatile uint32_t CFG1; /* Configuration Register 1 */ + volatile uint32_t CFG2; /* Configuration Register 2 */ + volatile uint32_t R[2]; /* Data Result Register */ + + /* Compare Value Registers 1, 2 */ + volatile uint32_t CV1; + volatile uint32_t CV2; + + volatile uint32_t SC2; /* Status and Control Register 2 */ + volatile uint32_t SC3; /* Status and Control Register 3 */ + + volatile uint32_t OFS; /* Offset Correction Register */ + volatile uint32_t PG; /* Plus-Side Gain Register */ + volatile uint32_t MG; /* Minus-Side Gain Register */ + + /* Plus-Side General Calibration Value Registers */ + volatile uint32_t CLPD; + volatile uint32_t CLPS; + volatile uint32_t CLP4; + volatile uint32_t CLP3; + volatile uint32_t CLP2; + volatile uint32_t CLP1; + volatile uint32_t CLP0; + uint32_t rsvd0; + /* Minus-Side General Calibration Value Registers */ + volatile uint32_t CLMD; + volatile uint32_t CLMS; + volatile uint32_t CLM4; + volatile uint32_t CLM3; + volatile uint32_t CLM2; + volatile uint32_t CLM1; + volatile uint32_t CLM0; +}; +static struct ADC *const ADC0 = (struct ADC *)0x4003B000; + +/* SC1 */ +#define ADC_SC1_DIFF (1 << 5) +#define ADC_SC1_AIEN (1 << 6) +#define ADC_SC1_COCO (1 << 7) +#define ADC_SC1_TEMPSENSOR 26 +#define ADC_SC1_BANDGAP 27 +#define ADC_SC1_ADCSTOP 31 + +/* CFG1 */ +#define ADC_CLOCK_SOURCE_ASYNCH (3 << 0) +#define ADC_MODE_16BIT (3 << 2) +#define ADC_ADLSMP_SHORT (0 << 4) +#define ADC_ADLSMP_LONG (1 << 4) +#define ADC_ADIV_1 (0 << 5) +#define ADC_ADIV_8 (3 << 5) +#define ADC_ADLPC_NORMAL (0 << 7) +#define ADC_ADLPC_LOWPOWER (1 << 7) +/**/ +#define ADC_CLOCK_SOURCE ADC_CLOCK_SOURCE_ASYNCH +#define ADC_MODE ADC_MODE_16BIT +#define ADC_ADLSMP ADC_ADLSMP_SHORT +#define ADC_ADIV ADC_ADIV_1 +#define ADC_ADLPC ADC_ADLPC_LOWPOWER + +/* CFG2 */ +#define ADC_ADLSTS_DEFAULT 0 /* 24 cycles if CFG1.ADLSMP=1, 4 if not. */ +#define ADC_ADHSC_NORMAL (0 << 2) +#define ADC_ADHSC_HIGHSPEED (1 << 2) +#define ADC_ADACK_DISABLE (0 << 3) +#define ADC_ADACK_ENABLE (1 << 3) +#define ADC_MUXSEL_A (0 << 4) +#define ADC_MUXSEL_B (1 << 4) +/**/ +#define ADC_ADLSTS ADC_ADLSTS_DEFAULT +#define ADC_ADHSC ADC_ADHSC_NORMAL +#define ADC_ADACKEN ADC_ADACK_ENABLE +#define ADC_MUXSEL ADC_MUXSEL_A + +/* SC2 */ +#define ADC_SC2_REFSEL_DEFAULT 1 /* Internal Voltage Reference??? */ +#define ADC_SC2_DMAEN (1 << 2) +#define ADC_SC2_ACREN (1 << 3) +#define ADC_SC2_ACFGT (1 << 4) +#define ADC_SC2_ACFE (1 << 5) +#define ADC_SC2_ADTRG (1 << 6) /* For hardware trigger */ + +/* SC3 */ +#define ADC_SC3_AVGS11 0x03 +#define ADC_SC3_AVGE (1 << 2) +#define ADC_SC3_ADCO (1 << 3) +#define ADC_SC3_CALF (1 << 6) +#define ADC_SC3_CAL (1 << 7) + +#define ADC_DMA_SLOT_NUM 40 + +/* + * Buffer to save ADC data. + */ +uint32_t adc_buf[64]; + +static const uint32_t adc0_sc1_setting = ADC_SC1_TEMPSENSOR; + +static chopstx_intr_t adc_intr; + +struct adc_internal { + uint32_t buf[64]; + uint8_t *p; + int phase : 8; + int count : 8; +}; +struct adc_internal adc; + +/* + * Initialize ADC module, do calibration. + * + * This is called by MAIN, only once, hopefully before creating any + * other threads (to be accurate). + * + * We configure ADC0 to kick DMA0, configure DMA0 to kick DMA1. + * DMA0 records output of ADC0 to the ADC.BUF. + * DMA1 kicks ADC0 again to get another value. + * + * ADC0 --[finish conversion]--> DMA0 --[Link channel 1]--> DMA1 + */ +int +adc_init (void) +{ + uint32_t v; + + /* Enable ADC0 and DMAMUX clock. */ + SIM->SCGC6 |= (1 << 27) | (1 << 1); + /* Enable DMA clock. */ + SIM->SCGC7 |= (1 << 8); + + /* ADC0 setting for calibration. */ + ADC0->CFG1 = ADC_CLOCK_SOURCE | ADC_MODE | ADC_ADLSMP | ADC_ADIV | ADC_ADLPC; + ADC0->CFG2 = ADC_ADLSTS | ADC_ADHSC | ADC_ADACKEN | ADC_MUXSEL; + ADC0->SC2 = ADC_SC2_REFSEL_DEFAULT; + ADC0->SC3 = ADC_SC3_CAL | ADC_SC3_CALF | ADC_SC3_AVGE | ADC_SC3_AVGS11; + + /* Wait ADC completion */ + while ((ADC0->SC1[0] & ADC_SC1_COCO) == 0) + if ((ADC0->SC3 & ADC_SC3_CALF) != 0) + /* Calibration failure */ + return -1; + + if ((ADC0->SC3 & ADC_SC3_CALF) != 0) + /* Calibration failure */ + return -1; + + /* Configure PG by the calibration values. */ + v = ADC0->CLP0 + ADC0->CLP1 + ADC0->CLP2 + ADC0->CLP3 + ADC0->CLP4 + ADC0->CLPS; + ADC0->PG = 0x8000 | (v >> 1); + + /* Configure MG by the calibration values. */ + v = ADC0->CLM0 + ADC0->CLM1 + ADC0->CLM2 + ADC0->CLM3 + ADC0->CLM4 + ADC0->CLMS; + ADC0->MG = 0x8000 | (v >> 1); + + ADC0->SC1[0] = ADC_SC1_ADCSTOP; + + /* DMAMUX setting. */ + DMAMUX->CHCFG0 = (1 << 7) | ADC_DMA_SLOT_NUM; + + /* DMA0 initial setting. */ + DMA0->SAR = (uint32_t)&ADC0->R[0]; + + /* DMA1 initial setting. */ + DMA1->SAR = (uint32_t)&adc0_sc1_setting; + DMA1->DAR = (uint32_t)&ADC0->SC1[0]; + + chopstx_claim_irq (&adc_intr, INTR_REQ_DMA0); + return 0; +} + +/* + * Start using ADC. + */ +void +adc_start (void) +{ + ADC0->CFG1 = ADC_CLOCK_SOURCE | ADC_MODE | ADC_ADLSMP | ADC_ADIV | ADC_ADLPC; + ADC0->CFG2 = ADC_ADLSTS | ADC_ADHSC | ADC_ADACKEN | ADC_MUXSEL; + ADC0->SC2 = ADC_SC2_REFSEL_DEFAULT | ADC_SC2_DMAEN; + ADC0->SC3 = 0; +} + +/* + * Kick getting data for COUNT times. + * Data will be saved in ADC_BUF starting at OFFSET. + */ +static void +adc_start_conversion_internal (int count) +{ + /* DMA0 setting. */ + DMA0->DAR = (uint32_t)&adc.buf[0]; + DMA0->DSR_BCR = 4 * count; + DMA0->DCR = (1 << 31) | (1 << 30) | (1 << 29) | (0 << 20) | (1 << 19) + | (0 << 17) | (1 << 7) | (2 << 4) | (1 << 2); + + /* Kick DMA1. */ + DMA1->DSR_BCR = 4 * count; + DMA1->DCR = (1 << 30) | (1 << 29) | (0 << 19) | (0 << 17) | (1 << 16) | (1 << 7); +} + + +/* + * Kick getting data for COUNT times. + * Data will be saved in ADC_BUF starting at OFFSET. + */ +void +adc_start_conversion (int offset, int count) +{ + adc.p = (uint8_t *)&adc_buf[offset]; + adc.phase = 0; + adc.count = count; + adc_start_conversion_internal (count); +} + + +static void +adc_stop_conversion (void) +{ + ADC0->SC1[0] = ADC_SC1_ADCSTOP; +} + +/* + * Stop using ADC. + */ +void +adc_stop (void) +{ + SIM->SCGC6 &= ~(1 << 27); +} + +/* + * Return 0 on success. + * Return 1 on error. + */ +int +adc_wait_completion (void) +{ + int i; + + while (1) + { + /* Wait DMA completion */ + chopstx_intr_wait (&adc_intr); + + DMA0->DSR_BCR = (1 << 24); + DMA1->DSR_BCR = (1 << 24); + + adc_stop_conversion (); + + chopstx_intr_done (&adc_intr); + + for (i = 0; i < adc.count; i++) + *adc.p++ = (uint8_t)adc.buf[i]; + + if (++adc.phase >= 4) + break; + + adc_start_conversion_internal (adc.count); + } + + return 0; +} diff --git a/gnuk/chopstx/contrib/adc-stm32f103.c b/gnuk/chopstx/contrib/adc-stm32f103.c new file mode 100644 index 0000000000..750061eab3 --- /dev/null +++ b/gnuk/chopstx/contrib/adc-stm32f103.c @@ -0,0 +1,347 @@ +/* + * adc_stm32f103.c - ADC driver for STM32F103 + * In this ADC driver, there are NeuG specific parts. + * You need to modify to use this as generic ADC driver. + * + * Copyright (C) 2011, 2012, 2013, 2015, 2016, 2017, 2018 + * Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx 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. + * + * Chopstx 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 . + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +#include +#include +#include +#include +#include "adc.h" +#include "board.h" +#include "sys.h" + +#define STM32_ADC_ADC1_DMA_PRIORITY 2 + +#define ADC_SMPR1_SMP_VREF(n) ((n) << 21) +#define ADC_SMPR1_SMP_SENSOR(n) ((n) << 18) + +#define ADC_SMPR1_SMP_AN10(n) ((n) << 0) +#define ADC_SMPR1_SMP_AN11(n) ((n) << 3) + +#define ADC_SMPR2_SMP_AN0(n) ((n) << 0) +#define ADC_SMPR2_SMP_AN1(n) ((n) << 3) +#define ADC_SMPR2_SMP_AN2(n) ((n) << 6) +#define ADC_SMPR2_SMP_AN8(n) ((n) << 24) +#define ADC_SMPR2_SMP_AN9(n) ((n) << 27) + +#define ADC_SQR1_NUM_CH(n) (((n) - 1) << 20) + +#define ADC_SQR3_SQ1_N(n) ((n) << 0) +#define ADC_SQR3_SQ2_N(n) ((n) << 5) +#define ADC_SQR3_SQ3_N(n) ((n) << 10) +#define ADC_SQR3_SQ4_N(n) ((n) << 15) + +#define ADC_SAMPLE_1P5 0 + +#define ADC_CHANNEL_IN0 0 +#define ADC_CHANNEL_IN1 1 +#define ADC_CHANNEL_IN2 2 +#define ADC_CHANNEL_IN8 8 +#define ADC_CHANNEL_IN9 9 +#define ADC_CHANNEL_IN10 10 +#define ADC_CHANNEL_IN11 11 +#define ADC_CHANNEL_SENSOR 16 +#define ADC_CHANNEL_VREFINT 17 + +#define DELIBARATELY_DO_IT_WRONG_VREF_SAMPLE_TIME +#ifndef MCU_STM32F1_GD32F1 +#define DELIBARATELY_DO_IT_WRONG_START_STOP +#endif + +#ifdef DELIBARATELY_DO_IT_WRONG_VREF_SAMPLE_TIME +#define ADC_SAMPLE_VREF ADC_SAMPLE_1P5 +#define ADC_SAMPLE_SENSOR ADC_SAMPLE_1P5 +#else +#define ADC_SAMPLE_VREF ADC_SAMPLE_239P5 +#define ADC_SAMPLE_SENSOR ADC_SAMPLE_239P5 +#endif + +#define NEUG_DMA_CHANNEL STM32_DMA1_STREAM1 +#define NEUG_DMA_MODE \ + ( STM32_DMA_CR_PL (STM32_ADC_ADC1_DMA_PRIORITY) \ + | STM32_DMA_CR_MSIZE_WORD | STM32_DMA_CR_PSIZE_WORD \ + | STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE \ + | STM32_DMA_CR_TEIE ) + +#define NEUG_ADC_SETTING1_SMPR1 ADC_SMPR1_SMP_VREF(ADC_SAMPLE_VREF) \ + | ADC_SMPR1_SMP_SENSOR(ADC_SAMPLE_SENSOR) +#define NEUG_ADC_SETTING1_SMPR2 0 +#define NEUG_ADC_SETTING1_SQR3 ADC_SQR3_SQ1_N(ADC_CHANNEL_VREFINT) \ + | ADC_SQR3_SQ2_N(ADC_CHANNEL_SENSOR) \ + | ADC_SQR3_SQ3_N(ADC_CHANNEL_SENSOR) \ + | ADC_SQR3_SQ4_N(ADC_CHANNEL_VREFINT) +#define NEUG_ADC_SETTING1_NUM_CHANNELS 4 + +/* + * ADC finish interrupt + */ +#define INTR_REQ_DMA1_Channel1 11 + +static chopstx_intr_t adc_intr; + +/* + * Do calibration for both of ADCs. + */ +int +adc_init (void) +{ + RCC->APB2ENR |= (RCC_APB2ENR_ADC1EN | RCC_APB2ENR_ADC2EN); + RCC->APB2RSTR = (RCC_APB2RSTR_ADC1RST | RCC_APB2RSTR_ADC2RST); + RCC->APB2RSTR = 0; + + ADC1->CR1 = 0; + ADC1->CR2 = ADC_CR2_ADON; + chopstx_usec_wait (1000); + + ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_RSTCAL; + while ((ADC1->CR2 & ADC_CR2_RSTCAL) != 0) + ; + ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_CAL; + while ((ADC1->CR2 & ADC_CR2_CAL) != 0) + ; + ADC1->CR2 = 0; + + ADC2->CR1 = 0; + ADC2->CR2 = ADC_CR2_ADON; + chopstx_usec_wait (1000); + + ADC2->CR2 = ADC_CR2_ADON | ADC_CR2_RSTCAL; + while ((ADC2->CR2 & ADC_CR2_RSTCAL) != 0) + ; + ADC2->CR2 = ADC_CR2_ADON | ADC_CR2_CAL; + while ((ADC2->CR2 & ADC_CR2_CAL) != 0) + ; + ADC2->CR2 = 0; + RCC->APB2ENR &= ~(RCC_APB2ENR_ADC1EN | RCC_APB2ENR_ADC2EN); + + chopstx_claim_irq (&adc_intr, INTR_REQ_DMA1_Channel1); + return 0; +} + +static void +get_adc_config (uint32_t config[4]) +{ + config[2] = ADC_SQR1_NUM_CH(2); + switch (SYS_BOARD_ID) + { + case BOARD_ID_FST_01G: + case BOARD_ID_FST_01: + config[0] = 0; + config[1] = ADC_SMPR2_SMP_AN0(ADC_SAMPLE_1P5) + | ADC_SMPR2_SMP_AN9(ADC_SAMPLE_1P5); + config[3] = ADC_SQR3_SQ1_N(ADC_CHANNEL_IN0) + | ADC_SQR3_SQ2_N(ADC_CHANNEL_IN9); + break; + + case BOARD_ID_OLIMEX_STM32_H103: + case BOARD_ID_STBEE: + config[0] = ADC_SMPR1_SMP_AN10(ADC_SAMPLE_1P5) + | ADC_SMPR1_SMP_AN11(ADC_SAMPLE_1P5); + config[1] = 0; + config[3] = ADC_SQR3_SQ1_N(ADC_CHANNEL_IN10) + | ADC_SQR3_SQ2_N(ADC_CHANNEL_IN11); + break; + + case BOARD_ID_STBEE_MINI: + config[0] = 0; + config[1] = ADC_SMPR2_SMP_AN1(ADC_SAMPLE_1P5) + | ADC_SMPR2_SMP_AN2(ADC_SAMPLE_1P5); + config[3] = ADC_SQR3_SQ1_N(ADC_CHANNEL_IN1) + | ADC_SQR3_SQ2_N(ADC_CHANNEL_IN2); + break; + + case BOARD_ID_ST_NUCLEO_F103: + config[0] = 0; + config[1] = ADC_SMPR2_SMP_AN8(ADC_SAMPLE_1P5) + | ADC_SMPR2_SMP_AN9(ADC_SAMPLE_1P5); + config[3] = ADC_SQR3_SQ1_N(ADC_CHANNEL_IN8) + | ADC_SQR3_SQ2_N(ADC_CHANNEL_IN9); + break; + + case BOARD_ID_CQ_STARM: + case BOARD_ID_FST_01_00: + case BOARD_ID_MAPLE_MINI: + case BOARD_ID_STM32_PRIMER2: + case BOARD_ID_STM8S_DISCOVERY: + case BOARD_ID_ST_DONGLE: + case BOARD_ID_NITROKEY_START: + case BOARD_ID_FST_01SZ: + default: + config[0] = 0; + config[1] = ADC_SMPR2_SMP_AN0(ADC_SAMPLE_1P5) + | ADC_SMPR2_SMP_AN1(ADC_SAMPLE_1P5); + config[3] = ADC_SQR3_SQ1_N(ADC_CHANNEL_IN0) + | ADC_SQR3_SQ2_N(ADC_CHANNEL_IN1); + break; + } +} + + +void +adc_start (void) +{ + uint32_t config[4]; + + get_adc_config (config); + + /* Use DMA channel 1. */ + RCC->AHBENR |= RCC_AHBENR_DMA1EN; + DMA1_Channel1->CCR = STM32_DMA_CCR_RESET_VALUE; + DMA1->IFCR = 0xffffffff; + + RCC->APB2ENR |= (RCC_APB2ENR_ADC1EN | RCC_APB2ENR_ADC2EN); + + ADC1->SMPR1 = NEUG_ADC_SETTING1_SMPR1; + ADC1->SMPR2 = NEUG_ADC_SETTING1_SMPR2; + ADC1->SQR1 = ADC_SQR1_NUM_CH(NEUG_ADC_SETTING1_NUM_CHANNELS); + ADC1->SQR2 = 0; + ADC1->SQR3 = NEUG_ADC_SETTING1_SQR3; + ADC1->CR1 = (ADC_CR1_DUALMOD_2 | ADC_CR1_DUALMOD_1 | ADC_CR1_DUALMOD_0 + | ADC_CR1_SCAN); + ADC1->CR2 = (ADC_CR2_TSVREFE | ADC_CR2_EXTTRIG | ADC_CR2_SWSTART + | ADC_CR2_EXTSEL | ADC_CR2_DMA | ADC_CR2_CONT | ADC_CR2_ADON); + chopstx_usec_wait (1000); + + ADC2->SMPR1 = config[0]; + ADC2->SMPR2 = config[1]; + ADC2->SQR1 = config[2]; + ADC2->SQR2 = 0; + ADC2->SQR3 = config[3]; + ADC2->CR1 = (ADC_CR1_DUALMOD_2 | ADC_CR1_DUALMOD_1 | ADC_CR1_DUALMOD_0 + | ADC_CR1_SCAN); + ADC2->CR2 = ADC_CR2_EXTTRIG | ADC_CR2_CONT | ADC_CR2_ADON; + chopstx_usec_wait (1000); + +#ifdef DELIBARATELY_DO_IT_WRONG_START_STOP + /* + * We could just let ADC run continuously always and only enable DMA + * to receive stable data from ADC. But our purpose is not to get + * correct data but noise. In fact, we can get more noise when we + * start/stop ADC each time. + */ + ADC2->CR2 = 0; + ADC1->CR2 = 0; +#else + /* Start conversion. */ + ADC2->CR2 = ADC_CR2_EXTTRIG | ADC_CR2_CONT | ADC_CR2_ADON; + ADC1->CR2 = (ADC_CR2_TSVREFE | ADC_CR2_EXTTRIG | ADC_CR2_SWSTART + | ADC_CR2_EXTSEL | ADC_CR2_DMA | ADC_CR2_CONT | ADC_CR2_ADON); +#endif +} + +uint32_t adc_buf[64]; + +void +adc_start_conversion (int offset, int count) +{ + DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR; /* SetPeripheral */ + DMA1_Channel1->CMAR = (uint32_t)&adc_buf[offset]; /* SetMemory0 */ + DMA1_Channel1->CNDTR = count; /* Counter */ + DMA1_Channel1->CCR = NEUG_DMA_MODE | DMA_CCR1_EN; /* Mode */ + +#ifdef DELIBARATELY_DO_IT_WRONG_START_STOP + /* Power on */ + ADC2->CR2 = ADC_CR2_EXTTRIG | ADC_CR2_CONT | ADC_CR2_ADON; + ADC1->CR2 = (ADC_CR2_TSVREFE | ADC_CR2_EXTTRIG | ADC_CR2_SWSTART + | ADC_CR2_EXTSEL | ADC_CR2_DMA | ADC_CR2_CONT | ADC_CR2_ADON); + /* + * Start conversion. tSTAB is 1uS, but we don't follow the spec, to + * get more noise. + */ + ADC2->CR2 = ADC_CR2_EXTTRIG | ADC_CR2_CONT | ADC_CR2_ADON; + ADC1->CR2 = (ADC_CR2_TSVREFE | ADC_CR2_EXTTRIG | ADC_CR2_SWSTART + | ADC_CR2_EXTSEL | ADC_CR2_DMA | ADC_CR2_CONT | ADC_CR2_ADON); +#endif +} + + +static void adc_stop_conversion (void) +{ + DMA1_Channel1->CCR &= ~DMA_CCR1_EN; + +#ifdef DELIBARATELY_DO_IT_WRONG_START_STOP + ADC2->CR2 = 0; + ADC1->CR2 = 0; +#endif +} + +void +adc_stop (void) +{ + ADC1->CR1 = 0; + ADC1->CR2 = 0; + + ADC2->CR1 = 0; + ADC2->CR2 = 0; + + RCC->AHBENR &= ~RCC_AHBENR_DMA1EN; + RCC->APB2ENR &= ~(RCC_APB2ENR_ADC1EN | RCC_APB2ENR_ADC2EN); +} + + +static uint32_t adc_err; + +/* + * Return 0 on success. + * Return 1 on error. + */ +int +adc_wait_completion (void) +{ + uint32_t flags; + + while (1) + { + chopstx_intr_wait (&adc_intr); + flags = DMA1->ISR & STM32_DMA_ISR_MASK; /* Channel 1 interrupt cause. */ + /* + * Clear interrupt cause of channel 1. + * + * Note that CGIFx=0, as CGIFx=1 clears all of GIF, HTIF, TCIF + * and TEIF. + */ + DMA1->IFCR = (flags & ~1); + chopstx_intr_done (&adc_intr); + + if ((flags & STM32_DMA_ISR_TEIF) != 0) /* DMA errors */ + { + /* Should never happened. If any, it's coding error. */ + /* Access an unmapped address space or alignment violation. */ + adc_err++; + adc_stop_conversion (); + return 1; + } + else if ((flags & STM32_DMA_ISR_TCIF) != 0) /* Transfer complete */ + { + adc_stop_conversion (); + return 0; + } + } +} diff --git a/gnuk/chopstx/contrib/usart-stm32f103.c b/gnuk/chopstx/contrib/usart-stm32f103.c new file mode 100644 index 0000000000..437754a953 --- /dev/null +++ b/gnuk/chopstx/contrib/usart-stm32f103.c @@ -0,0 +1,656 @@ +/* + * usart-stm32.c - USART driver for STM32F103 (USART2 and USART3) + * + * Copyright (C) 2017, 2019 g10 Code GmbH + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx 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. + * + * Chopstx 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 . + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +#include +#include +#include +#include +#include + +struct USART { + volatile uint32_t SR; + volatile uint32_t DR; + volatile uint32_t BRR; + volatile uint32_t CR1; + volatile uint32_t CR2; + volatile uint32_t CR3; + volatile uint32_t GTPR; +}; + +#define USART2_BASE (APB1PERIPH_BASE + 0x4400) +#define USART3_BASE (APB1PERIPH_BASE + 0x4800) +static struct USART *const USART2 = (struct USART *)USART2_BASE; +static struct USART *const USART3 = (struct USART *)USART3_BASE; + +#define USART_SR_CTS (1 << 9) +#define USART_SR_LBD (1 << 8) +#define USART_SR_TXE (1 << 7) +#define USART_SR_TC (1 << 6) +#define USART_SR_RXNE (1 << 5) +#define USART_SR_IDLE (1 << 4) +#define USART_SR_ORE (1 << 3) +#define USART_SR_NE (1 << 2) +#define USART_SR_FE (1 << 1) +#define USART_SR_PE (1 << 0) + + +#define USART_CR1_UE (1 << 13) +#define USART_CR1_M (1 << 12) +#define USART_CR1_WAKE (1 << 11) +#define USART_CR1_PCE (1 << 10) +#define USART_CR1_PS (1 << 9) +#define USART_CR1_PEIE (1 << 8) +#define USART_CR1_TXEIE (1 << 7) +#define USART_CR1_TCIE (1 << 6) +#define USART_CR1_RXNEIE (1 << 5) +#define USART_CR1_IDLEIE (1 << 4) +#define USART_CR1_TE (1 << 3) +#define USART_CR1_RE (1 << 2) +#define USART_CR1_RWU (1 << 1) +#define USART_CR1_SBK (1 << 0) + + +static struct USART * +get_usart_dev (uint8_t dev_no) +{ + if (dev_no == 2) + return USART2; + else if (dev_no == 3) + return USART3; + + return NULL; +} + +/* We assume 36MHz f_PCLK */ +struct brr_setting { + uint8_t baud_spec; + uint16_t brr_value; +}; +#define NUM_BAUD (int)(sizeof (brr_table) / sizeof (struct brr_setting)) + +static const struct brr_setting brr_table[] = { + { B600, (3750 << 4)}, + { B1200, (1875 << 4)}, + { B2400, ( 937 << 4)|8}, + { B9600, ( 234 << 4)|6}, + { B19200, ( 117 << 4)|3}, + { B57600, ( 39 << 4)|1}, + { B115200, ( 19 << 4)|8}, + { B230400, ( 9 << 4)|12}, + { B460800, ( 4 << 4)|14}, + { B921600, ( 2 << 4)|7}, + { BSCARD, ( 232 << 4)|8}, +}; + +static void *usart_main (void *arg); + +static struct usart_stat usart2_stat; +static struct usart_stat usart3_stat; + +void +usart_config_clken (uint8_t dev_no, int on) +{ + struct USART *USARTx = get_usart_dev (dev_no); + + if (on) + USARTx->CR2 |= (1 << 11); + else + USARTx->CR2 &= ~(1 << 11); +} + + +int +usart_config (uint8_t dev_no, uint32_t config_bits) +{ + struct USART *USARTx = get_usart_dev (dev_no); + uint8_t baud_spec = (config_bits & MASK_BAUD); + int i; + uint32_t cr1_config = (USART_CR1_UE | USART_CR1_RXNEIE + | USART_CR1_TE | USART_CR1_RE); + /* TXEIE will be enabled when putting char */ + /* No CTSIE, PEIE, TCIE, IDLEIE, LBDIE */ + if (USARTx == NULL) + return -1; + + /* Disable USART before configure. */ + USARTx->CR1 &= ~USART_CR1_UE; + + if (((config_bits & MASK_CS) == CS7 && (config_bits & PARENB)) + || ((config_bits & MASK_CS) == CS8 && (config_bits & PARENB) == 0)) + cr1_config &= ~USART_CR1_M; + else if ((config_bits & MASK_CS) == CS8) + cr1_config |= USART_CR1_M; + else + return -1; + + if ((config_bits & PARENB) == 0) + cr1_config &= ~(USART_CR1_PCE | USART_CR1_PEIE); + else + cr1_config |= (USART_CR1_PCE | USART_CR1_PEIE); + + if ((config_bits & PARODD) == 0) + cr1_config &= ~USART_CR1_PS; + else + cr1_config |= USART_CR1_PS; + + if ((config_bits & MASK_STOP) == STOP0B5) + USARTx->CR2 = (0x1 << 12); + else if ((config_bits & MASK_STOP) == STOP1B) + USARTx->CR2 = (0x0 << 12); + else if ((config_bits & MASK_STOP) == STOP1B5) + USARTx->CR2 = (0x3 << 12); + else /* if ((config_bits & MASK_STOP) == STOP2B) */ + USARTx->CR2 = (0x2 << 12); + + for (i = 0; i < NUM_BAUD; i++) + if (brr_table[i].baud_spec == baud_spec) + break; + + if (i >= NUM_BAUD) + return -1; + + USARTx->BRR = brr_table[i].brr_value; + + if ((config_bits & MASK_FLOW)) + USARTx->CR3 = (1 << 9) | (1 << 8); + else + USARTx->CR3 = 0; + + USARTx->CR1 = cr1_config; + + /* SCEN (smartcard enable) should be set _after_ CR1. */ + if ((config_bits & MASK_MODE)) + { + if ((config_bits & MASK_MODE) == MODE_SMARTCARD) + { + USARTx->GTPR = (16 << 8) | 5; + USARTx->CR3 |= ((1 << 5) | (1 << 4)); + } + else if ((config_bits & MASK_MODE) == MODE_IRDA) + USARTx->CR3 |= (1 << 1); + else if ((config_bits & MASK_MODE) == MODE_IRDA_LP) + USARTx->CR3 |= (1 << 2) | (1 << 1); + } + + return 0; +} + +static int (*ss_notify_callback) (uint8_t dev_no, uint16_t notify_bits); + +void +usart_init (uint16_t prio, uintptr_t stack_addr, size_t stack_size, + int (*cb) (uint8_t dev_no, uint16_t notify_bits)) +{ + ss_notify_callback = cb; + usart2_stat.dev_no = 2; + usart3_stat.dev_no = 3; + + /* Enable USART2 and USART3 clocks, and strobe reset. */ + RCC->APB1ENR |= ((1 << 18) | (1 << 17)); + RCC->APB1RSTR = ((1 << 18) | (1 << 17)); + RCC->APB1RSTR = 0; + + chopstx_create (prio, stack_addr, stack_size, usart_main, NULL); +} + +/* + * Ring buffer + */ +#define MAX_RB_BUF 1024 + +struct rb { + uint8_t *buf; + chopstx_mutex_t m; + chopstx_cond_t data_available; + chopstx_cond_t space_available; + uint32_t head :10; + uint32_t tail :10; + uint32_t size :10; + uint32_t full : 1; + uint32_t empty : 1; +}; + +/* + * Note: size = 1024 can still work, regardless of the limit of 10-bit. + */ +static void +rb_init (struct rb *rb, uint8_t *p, uint16_t size) +{ + rb->buf = p; + rb->size = size; + chopstx_mutex_init (&rb->m); + chopstx_cond_init (&rb->data_available); + chopstx_cond_init (&rb->space_available); + rb->head = rb->tail = 0; + rb->full = 0; + rb->empty = 1; +} + +static void +rb_add (struct rb *rb, uint8_t v) +{ + rb->buf[rb->tail++] = v; + if (rb->tail == rb->size) + rb->tail = 0; + if (rb->tail == rb->head) + rb->full = 1; + rb->empty = 0; +} + +static uint8_t +rb_del (struct rb *rb) +{ + uint32_t v = rb->buf[rb->head++]; + + if (rb->head == rb->size) + rb->head = 0; + if (rb->head == rb->tail) + rb->empty = 1; + rb->full = 0; + + return v; +} + +/* + * Application: consumer + * Hardware: generator + */ +static int +rb_ll_put (struct rb *rb, uint8_t v) +{ + int r; + + chopstx_mutex_lock (&rb->m); + if (rb->full) + r = -1; + else + { + r = 0; + rb_add (rb, v); + chopstx_cond_signal (&rb->data_available); + } + chopstx_mutex_unlock (&rb->m); + return r; +} + +/* + * Application: generator + * Hardware: consumer + */ +static int +rb_ll_get (struct rb *rb) +{ + int r; + + chopstx_mutex_lock (&rb->m); + if (rb->empty) + r = -1; + else + { + r = rb_del (rb); + chopstx_cond_signal (&rb->space_available); + } + chopstx_mutex_unlock (&rb->m); + return r; +} + +static void +rb_ll_flush (struct rb *rb) +{ + chopstx_mutex_lock (&rb->m); + while (!rb->empty) + rb_del (rb); + chopstx_cond_signal (&rb->space_available); + chopstx_mutex_unlock (&rb->m); +} + +/* + * Application: consumer + * Hardware: generator + */ +static int +rb_read (struct rb *rb, uint8_t *buf, uint16_t buflen) +{ + int i = 0; + + chopstx_mutex_lock (&rb->m); + while (rb->empty) + chopstx_cond_wait (&rb->data_available, &rb->m); + + while (i < buflen) + { + buf[i++] = rb_del (rb); + if (rb->empty) + break; + } + chopstx_cond_signal (&rb->space_available); + chopstx_mutex_unlock (&rb->m); + + return i; +} + +/* + * Application: generator + * Hardware: consumer + */ +static void +rb_write (struct rb *rb, uint8_t *buf, uint16_t buflen) +{ + int i = 0; + + chopstx_mutex_lock (&rb->m); + + do + { + while (rb->full) + chopstx_cond_wait (&rb->space_available, &rb->m); + + while (i < buflen) + { + rb_add (rb, buf[i++]); + if (rb->full) + { + chopstx_cond_signal (&rb->data_available); + break; + } + } + } + while (i < buflen); + + if (i) + chopstx_cond_signal (&rb->data_available); + chopstx_mutex_unlock (&rb->m); +} + +static int +rb_empty_check (void *arg) +{ + struct rb *rb = arg; + return rb->empty == 0; +} + +static void +rb_get_prepare_poll (struct rb *rb, chopstx_poll_cond_t *poll_desc) +{ + poll_desc->type = CHOPSTX_POLL_COND; + poll_desc->ready = 0; + poll_desc->cond = &rb->data_available; + poll_desc->mutex = &rb->m; + poll_desc->check = rb_empty_check; + poll_desc->arg = rb; +} + +#define INTR_REQ_USART2 38 +#define INTR_REQ_USART3 39 + +static uint8_t buf_usart2_rb_a2h[256]; +static uint8_t buf_usart2_rb_h2a[512]; +static uint8_t buf_usart3_rb_a2h[256]; +static uint8_t buf_usart3_rb_h2a[512]; + +static struct chx_intr usart2_intr; +static struct chx_intr usart3_intr; + +static struct rb usart2_rb_a2h; +static struct rb usart2_rb_h2a; +static struct rb usart3_rb_a2h; +static struct rb usart3_rb_h2a; + +static chopstx_poll_cond_t usart2_app_write_event; +static chopstx_poll_cond_t usart3_app_write_event; + +static struct chx_poll_head *usart_poll[4]; + +/* Global variables so that it can be easier to debug. */ +static int usart2_tx_ready; +static int usart3_tx_ready; + +#define UART_STATE_BITMAP_RX_CARRIER (1 << 0) +#define UART_STATE_BITMAP_TX_CARRIER (1 << 1) +#define UART_STATE_BITMAP_BREAK (1 << 2) +#define UART_STATE_BITMAP_RINGSIGNAL (1 << 3) +#define UART_STATE_BITMAP_FRAMING (1 << 4) +#define UART_STATE_BITMAP_PARITY (1 << 5) +#define UART_STATE_BITMAP_OVERRUN (1 << 6) + +static int +handle_intr (struct USART *USARTx, struct rb *rb2a, struct usart_stat *stat) +{ + int tx_ready = 0; + uint32_t r = USARTx->SR; + int notify_bits = 0; + + if ((r & USART_SR_TXE)) + { + tx_ready = 1; + USARTx->CR1 &= ~USART_CR1_TXEIE; + } + + if ((r & USART_SR_RXNE)) + { + uint32_t data = USARTx->DR; + + /* DR register should be accessed even if data is not used. + * Its read-access has side effect of clearing error flags. + */ + asm volatile ("" : : "r" (data) : "memory"); + + if ((r & USART_SR_NE)) + stat->err_rx_noise++; + else if ((r & USART_SR_FE)) + { + /* NOTE: Noway to distinguish framing error and break */ + + stat->rx_break++; + notify_bits |= UART_STATE_BITMAP_BREAK; + } + else if ((r & USART_SR_PE)) + { + stat->err_rx_parity++; + notify_bits |= UART_STATE_BITMAP_PARITY; + } + else + { + if ((r & USART_SR_ORE)) + { + stat->err_rx_overrun++; + notify_bits |= UART_STATE_BITMAP_OVERRUN; + } + + /* XXX: if CS is 7-bit, mask it, or else parity bit in upper layer */ + if (rb_ll_put (rb2a, (data & 0xff)) < 0) + stat->err_rx_overflow++; + else + stat->rx++; + } + } + else if ((r & USART_SR_ORE)) + { /* Clear ORE */ + uint32_t data = USARTx->DR; + asm volatile ("" : : "r" (data) : "memory"); + stat->err_rx_overrun++; + notify_bits |= UART_STATE_BITMAP_OVERRUN; + } + + if (notify_bits) + { + if ((*ss_notify_callback) (stat->dev_no, notify_bits)) + stat->err_notify_overflow++; + } + + return tx_ready; +} + +static int +handle_tx_ready (struct USART *USARTx, struct rb *rb2h, + struct usart_stat *stat) +{ + int tx_ready = 1; + int c = rb_ll_get (rb2h); + + if (c >= 0) + { + uint32_t r; + + USARTx->DR = (c & 0xff); + stat->tx++; + r = USARTx->SR; + if ((r & USART_SR_TXE) == 0) + { + tx_ready = 0; + USARTx->CR1 |= USART_CR1_TXEIE; + } + } + + return tx_ready; +} + +static void * +usart_main (void *arg) +{ + (void)arg; + + usart2_tx_ready = 1; + usart3_tx_ready = 1; + + chopstx_claim_irq (&usart2_intr, INTR_REQ_USART2); + chopstx_claim_irq (&usart3_intr, INTR_REQ_USART3); + + rb_init (&usart2_rb_a2h, buf_usart2_rb_a2h, sizeof buf_usart2_rb_a2h); + rb_init (&usart2_rb_h2a, buf_usart2_rb_h2a, sizeof buf_usart2_rb_h2a); + rb_init (&usart3_rb_a2h, buf_usart3_rb_a2h, sizeof buf_usart3_rb_a2h); + rb_init (&usart3_rb_h2a, buf_usart3_rb_h2a, sizeof buf_usart3_rb_h2a); + + rb_get_prepare_poll (&usart2_rb_a2h, &usart2_app_write_event); + rb_get_prepare_poll (&usart3_rb_a2h, &usart3_app_write_event); + + while (1) + { + int n = 0; + + usart_poll[n++] = (struct chx_poll_head *)&usart2_intr; + usart_poll[n++] = (struct chx_poll_head *)&usart3_intr; + if (usart2_tx_ready) + usart_poll[n++] = (struct chx_poll_head *)&usart2_app_write_event; + else + usart2_app_write_event.ready = 0; + if (usart3_tx_ready) + usart_poll[n++] = (struct chx_poll_head *)&usart3_app_write_event; + else + usart3_app_write_event.ready = 0; + + chopstx_poll (NULL, n, usart_poll); + + if (usart2_intr.ready) + { + usart2_tx_ready = handle_intr (USART2, &usart2_rb_h2a, &usart2_stat); + chopstx_intr_done (&usart2_intr); + } + + if (usart3_intr.ready) + { + usart3_tx_ready = handle_intr (USART3, &usart3_rb_h2a, &usart3_stat); + chopstx_intr_done (&usart3_intr); + } + + if (usart2_tx_ready && usart2_app_write_event.ready) + usart2_tx_ready = handle_tx_ready (USART2, + &usart2_rb_a2h, &usart2_stat); + + if (usart3_tx_ready && usart3_app_write_event.ready) + usart3_tx_ready = handle_tx_ready (USART3, + &usart3_rb_a2h, &usart3_stat); + } + + return NULL; +} + +int +usart_read (uint8_t dev_no, char *buf, uint16_t buflen) +{ + struct rb *rb; + + if (dev_no == 2) + rb = &usart2_rb_h2a; + else if (dev_no == 3) + rb = &usart3_rb_h2a; + else + return -1; + + if (buf == NULL && buflen == 0) + { + rb_ll_flush (rb); + return 0; + } + else + return rb_read (rb, (uint8_t *)buf, buflen); +} + +int +usart_write (uint8_t dev_no, char *buf, uint16_t buflen) +{ + struct rb *rb; + + if (dev_no == 2) + rb = &usart2_rb_a2h; + else if (dev_no == 3) + rb = &usart3_rb_a2h; + else + return -1; + + if (buf == NULL && buflen == 0) + rb_ll_flush (rb); + else + rb_write (rb, (uint8_t *)buf, buflen); + return 0; +} + +const struct usart_stat * +usart_stat (uint8_t dev_no) +{ + if (dev_no == 2) + return &usart2_stat; + else if (dev_no == 3) + return &usart3_stat; + else + return NULL; +} + +int +usart_send_break (uint8_t dev_no) +{ + struct USART *USARTx = get_usart_dev (dev_no); + if (USARTx == NULL) + return -1; + + if ((USARTx->CR1 & 0x01)) + return 1; /* Busy sending break, which was requested before. */ + + USARTx->CR1 |= 0x01; + return 0; +} diff --git a/gnuk/chopstx/contrib/usart.h b/gnuk/chopstx/contrib/usart.h new file mode 100644 index 0000000000..14b80fabc7 --- /dev/null +++ b/gnuk/chopstx/contrib/usart.h @@ -0,0 +1,75 @@ +#define B0 0 /* POSIX to hang up */ + +/* POSIX supports B75 to B300 */ +#define B600 16 +#define B1200 17 +#define B2400 19 +#define B9600 21 +#define B19200 22 +#define B57600 24 +#define B115200 25 +#define B230400 26 +#define B460800 27 +#define B921600 28 +#define BSCARD 63 +#define MASK_BAUD 0x3f + +/* POSIX supports 5, 6. USB suppots 16 */ +#define CS7 (2 << 6) +#define CS8 (3 << 6) +#define MASK_CS (0x7 << 6) + +#define STOP0B5 (0 << 9) /* USART Hardware only */ +#define STOP1B (1 << 9) /* USB, POSIX */ +#define STOP1B5 (2 << 9) /* USB */ +#define STOP2B (3 << 9) /* USB, POSIX */ +#define MASK_STOP (0x3 << 9) + +#define PARENB (1 << 11) +#define PARODD (2 << 11) +#define MASK_PAR (0x7 << 11) +/* USB 0: none, 1: odd, 2: even, 3: mark, 4: space */ + +#define CRTSCTS (1 << 14) +#define MASK_FLOW (0x1 << 14) + +/* +BAUD_BITS 6 +CS_BITS 3 +STOP_BITS 2 +PAR_BITS 3 +*/ +/* USB: SET_CONTROL_LINE_STATE + DTR RTS */ +/* USB: SERIAL_STATE + DSR DCD RI */ + +/* non-POSIX, non-USB-CDC configs */ +#define MODE_SMARTCARD (1 << 30) +#define MODE_IRDA (2UL << 30) +#define MODE_IRDA_LP (3UL << 30) +#define MASK_MODE (0x3UL << 30) +/* 0: standard, 1: smartcard, 2: IrDA, 3: IrDA-LP */ + +struct usart_stat { + uint8_t dev_no; + + uint32_t tx; + uint32_t rx; + uint32_t rx_break; + uint32_t err_notify_overflow; + uint32_t err_rx_overflow; /* software side */ + uint32_t err_rx_overrun; /* hardware side */ + uint32_t err_rx_noise; + uint32_t err_rx_parity; +}; + + +void usart_init (uint16_t prio, uintptr_t stack_addr, size_t stack_size, + int (*ss_notify_callback) (uint8_t dev_no, uint16_t notify_bits)); +int usart_config (uint8_t dev_no, uint32_t config_bits); +int usart_read (uint8_t dev_no, char *buf, uint16_t buflen); +int usart_write (uint8_t dev_no, char *buf, uint16_t buflen); +const struct usart_stat *usart_stat (uint8_t dev_no); +int usart_send_break (uint8_t dev_no); +void usart_config_clken (uint8_t dev_no, int on); diff --git a/gnuk/chopstx/doc/README b/gnuk/chopstx/doc/README new file mode 100644 index 0000000000..47b0d6e0f6 --- /dev/null +++ b/gnuk/chopstx/doc/README @@ -0,0 +1,13 @@ +The GDOC script is taken from: + + http://git.savannah.gnu.org/cgit/libidn.git/plain/doc/gdoc + +and then modified %highlights_texinfo to use @var for parameters. + +The file 'chopstx-api.texi' is generated by: + + $ ./gdoc -texinfo ../chopstx.c >chopstx-api.texi + +Then, we generate chopstx.info by: + + $ makeinfo -o chopstx.info chopstx.texi diff --git a/gnuk/chopstx/doc/chopstx-api.texi b/gnuk/chopstx/doc/chopstx-api.texi new file mode 100644 index 0000000000..20c36f5e18 --- /dev/null +++ b/gnuk/chopstx/doc/chopstx-api.texi @@ -0,0 +1,231 @@ +@subheading chx_fatal +@anchor{chx_fatal} +@deftypefun {void} {chx_fatal} (uint32_t @var{err_code}) +@var{err_code}: Error code + +When it detects a coding error, this function will be called to +stop further execution of code. It never returns. +@end deftypefun + +@subheading chopstx_create +@anchor{chopstx_create} +@deftypefun {chopstx_t} {chopstx_create} (uint32_t @var{flags_and_prio}, uintptr_t @var{stack_addr}, size_t @var{stack_size}, voidfunc @var{thread_entry}, void * @var{arg}) +@var{flags_and_prio}: Flags and priority + +@var{stack_addr}: Stack address + +@var{stack_size}: Size of stack + +@var{thread_entry}: Entry function of new thread + +@var{arg}: Argument to the thread entry function + +Create a thread. Returns thread ID. +@end deftypefun + +@subheading chopstx_usec_wait +@anchor{chopstx_usec_wait} +@deftypefun {void} {chopstx_usec_wait} (uint32_t @var{usec}) +@var{usec}: number of micro seconds + +Sleep for @var{usec}. +@end deftypefun + +@subheading chopstx_mutex_init +@anchor{chopstx_mutex_init} +@deftypefun {void} {chopstx_mutex_init} (chopstx_mutex_t * @var{mutex}) +@var{mutex}: Mutex + +Initialize @var{mutex}. +@end deftypefun + +@subheading chopstx_mutex_lock +@anchor{chopstx_mutex_lock} +@deftypefun {void} {chopstx_mutex_lock} (chopstx_mutex_t * @var{mutex}) +@var{mutex}: Mutex + +Lock @var{mutex}. +@end deftypefun + +@subheading chopstx_mutex_unlock +@anchor{chopstx_mutex_unlock} +@deftypefun {void} {chopstx_mutex_unlock} (chopstx_mutex_t * @var{mutex}) +@var{mutex}: Mutex + +Unlock @var{mutex}. +@end deftypefun + +@subheading chopstx_cond_init +@anchor{chopstx_cond_init} +@deftypefun {void} {chopstx_cond_init} (chopstx_cond_t * @var{cond}) +@var{cond}: Condition variable + +Initialize @var{cond}. +@end deftypefun + +@subheading chopstx_cond_wait +@anchor{chopstx_cond_wait} +@deftypefun {void} {chopstx_cond_wait} (chopstx_cond_t * @var{cond}, chopstx_mutex_t * @var{mutex}) +@var{cond}: Condition variable + +@var{mutex}: Associated mutex + +Wait for @var{cond} with @var{mutex}. +@end deftypefun + +@subheading chopstx_cond_signal +@anchor{chopstx_cond_signal} +@deftypefun {void} {chopstx_cond_signal} (chopstx_cond_t * @var{cond}) +@var{cond}: Condition variable + +Wake up a thread waiting on @var{cond}. +@end deftypefun + +@subheading chopstx_cond_broadcast +@anchor{chopstx_cond_broadcast} +@deftypefun {void} {chopstx_cond_broadcast} (chopstx_cond_t * @var{cond}) +@var{cond}: Condition Variable + +Wake up all threads waiting on @var{cond}. +@end deftypefun + +@subheading chopstx_claim_irq +@anchor{chopstx_claim_irq} +@deftypefun {void} {chopstx_claim_irq} (chopstx_intr_t * @var{intr}, uint8_t @var{irq_num}) +@var{intr}: Pointer to INTR structure + +@var{irq_num}: IRQ Number (hardware specific) + +Claim interrupt @var{intr} with @var{irq_num} +@end deftypefun + +@subheading chopstx_intr_wait +@anchor{chopstx_intr_wait} +@deftypefun {void} {chopstx_intr_wait} (chopstx_intr_t * @var{intr}) +@var{intr}: Pointer to INTR structure + +Wait for the interrupt @var{intr} to be occured. +@end deftypefun + +@subheading chopstx_intr_done +@anchor{chopstx_intr_done} +@deftypefun {void} {chopstx_intr_done} (chopstx_intr_t * @var{intr}) +@var{intr}: Pointer to INTR structure + +Finish for the interrupt @var{intr} occurred. +@end deftypefun + +@subheading chopstx_cleanup_push +@anchor{chopstx_cleanup_push} +@deftypefun {void} {chopstx_cleanup_push} (struct chx_cleanup * @var{clp}) +@var{clp}: Pointer to clean-up structure + +Register a clean-up structure. +@end deftypefun + +@subheading chopstx_cleanup_pop +@anchor{chopstx_cleanup_pop} +@deftypefun {void} {chopstx_cleanup_pop} (int @var{execute}) +@var{execute}: Execute the clen-up function on release + +Unregister a clean-up structure. When @var{execute} is non-zero, the +clean-up will be executed. +@end deftypefun + +@subheading chopstx_exit +@anchor{chopstx_exit} +@deftypefun {void} {chopstx_exit} (void * @var{retval}) +@var{retval}: Return value (to be caught by a joining thread) + +Calling this function terminates the execution of running thread, +after calling clean up functions. If the calling thread still +holds mutexes, they will be released. This function never +returns. +@end deftypefun + +@subheading chopstx_join +@anchor{chopstx_join} +@deftypefun {int} {chopstx_join} (chopstx_t @var{thd}, void ** @var{ret}) +@var{thd}: Thread to wait + +@var{ret}: Pointer to void * to store return value + +Waits for the thread of @var{thd} to terminate. +Returns 0 on success, 1 when waiting is interrupted. +@end deftypefun + +@subheading chopstx_cancel +@anchor{chopstx_cancel} +@deftypefun {void} {chopstx_cancel} (chopstx_t @var{thd}) +@var{thd}: Thread to be canceled + +This function requests a cancellation of a thread @var{thd}. +No return value. +@end deftypefun + +@subheading chopstx_testcancel +@anchor{chopstx_testcancel} +@deftypefun {void} {chopstx_testcancel} ( @var{void}) + +Calling chopstx_testcancel creates a cancellation point. +No return value. If the thread is canceled, this function +does not return. +@end deftypefun + +@subheading chopstx_setcancelstate +@anchor{chopstx_setcancelstate} +@deftypefun {int} {chopstx_setcancelstate} (int @var{cancel_disable}) +@var{cancel_disable}: 0 to enable cancelation, otherwise disabled. + +Calling chopstx_setcancelstate sets cancelability state. + +Returns old state which is 0 when it was enabled. +@end deftypefun + +@subheading chopstx_poll +@anchor{chopstx_poll} +@deftypefun {int} {chopstx_poll} (uint32_t * @var{usec_p}, int @var{n}, struct chx_poll_head *const [] @var{pd_array}) +@var{usec_p}: Pointer to usec for timeout. Forever if NULL. + +@var{n}: Number of poll descriptors + +@var{pd_array}: Pointer to an array of poll descriptor pointer which +should be one of: +chopstx_poll_cond_t, chopstx_poll_join_t, or chopstx_intr_t. + +Returns number of active descriptors. +@end deftypefun + +@subheading chopstx_setpriority +@anchor{chopstx_setpriority} +@deftypefun {chopstx_prio_t} {chopstx_setpriority} (chopstx_prio_t @var{prio_new}) +Change the schedule priority with @var{prio}. +Returns the old priority. + +In general, it is not recommended to use this function because +dynamically changing schedule priorities complicates the system. +Only a possible valid usage of this function is in the main thread +which starts its execution with priority of CHX_PRIO_MAIN_INIT, and +let it change its priority after initialization of other threads. +@end deftypefun + +@subheading chx_sleep_mode +@anchor{chx_sleep_mode} +@deftypefun {extern void} {chx_sleep_mode} (int @var{enable_sleep}) +@var{enable_sleep}: Enable sleep on idle or not + +If @var{enable_sleep} is > 0, allow sleep for the idle thread. + +Behavior of @var{enable_sleep} >= 1 depends on MCU. + +For STM32F0, 1 for Sleep (CPU clock OFF only), 2 for Stop (Wakeup +by EXTI, voltage regulator on), 3 for Stop (Wakeup by EXTI, voltage +regulator low-power), 4 for Standby (Wakeup by RESET, voltage +regulator off). + +For STM32F103, 1 for normal sleep, and 2 for sleep with lower 8MHz +clock. + +Return previous value of @var{enable_sleep}. +@end deftypefun + diff --git a/gnuk/chopstx/doc/chopstx.texi b/gnuk/chopstx/doc/chopstx.texi new file mode 100644 index 0000000000..805e0bb2eb --- /dev/null +++ b/gnuk/chopstx/doc/chopstx.texi @@ -0,0 +1,196 @@ +\input texinfo @c -*-texinfo-*- +@c %**start of header +@setfilename chopstx.info +@set VERSION 1.14 +@settitle Chopstx Reference Manual +@c Unify some of the indices. +@syncodeindex tp fn +@syncodeindex pg fn +@c %**end of header +@copying +This manual is for Chopstx (version @value{VERSION}). + +@noindent +Copyright @copyright{} 2013, 2015, 2016, 2017, 2018 Flying Stone Technology @* + +@quotation +Permission is granted to copy, distribute and/or modify this document +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. The text of the license can be found in the +section entitled ``Copying''. +@end quotation +@end copying + + +@c ******************************************** + +@dircategory Development +@direntry +* chopstx: (chopstx). Chopstx, the thread library for embedded system. +@end direntry + +@c +@c Titlepage +@c +@setchapternewpage odd +@titlepage +@title Chopstx Reference Manual +@subtitle Version @value{VERSION} +@author NIIBE Yutaka (@email{gniibe@@fsij.org}) + +@page +@vskip 0pt plus 1filll +@insertcopying +@end titlepage + +@ifnothtml +@summarycontents +@contents +@page +@end ifnothtml + +@ifnottex +@node Top +@top Chopstx, The Thread Library +@insertcopying +@end ifnottex + + +@menu +* Introduction:: What is Chopstx. +* Threads and only Threads:: Threads and only Threads. +* Poll or Pole:: Poll or Pole. +* Note (Use of sleep mode):: Use it carefully. +* Compile-time macro:: Macro to be defined. +* API:: API. + +Appendix + +* Copying:: The GNU General Public License says how you + can copy and share the program. + +Indexes + +* Concept Index:: Index of concepts and programs. +* Function and Data Index:: Index of functions, variables and data types. + +@end menu + +@ifhtml +@page +@summarycontents +@contents +@end ifhtml + +@c ******************************************** +@node Introduction +@chapter Introduction + +Chopstx is an RT thread library for ARM Cortex-M0, Cortex-M0plus, +Cortex-M3 and GNU/Linux emulation. Specifically, it is used for +STM32F030, MKL27Z, STM32F103, GD32F103 and as a command on GNU/Linux. + +While most RTOSes come with many features, drivers, and stacks, +Chopstx just offers an RT thread library. + +With Chopstx, interrupt handling is also done by a thread. This +enables coherent code for ease of maintenance. + +While threads are important, we don't need more threads than +necessary. Chopstx provides a feature of poll, so that we can +minimize use of threads. + + +@node Threads and only Threads +@chapter Threads and only Threads + +Chopstx doesn't use the feature of (prioritized) nested vector +interrupt mechanism at all. All interrupts are equally handled by a +single entry of chx_handle_intr which just wakes up corresponding +thread. This is the feature of Chopstx. + +Nested vector interrupt machanism would be useful for interrupt-driven +programming style for specific application targets, or, some other +programing style like the one with spl of Unix. Some engineers +(especially, hardware side) still seem to believe that it is a good +feature to have. But from the view point of programming and +maintenance of software, this is one of the most difficult part with +little benefit, if any. + +With traditional interrupt handling, a demarcation of what should be +done by interrupt handler, bottom half, and thead is crucial for +applications' performance. And because the demarcation should be done +at an early stage of an application development, it has a tendency, +many parts are getting demanding higher priority. Amount of code for +higher priority interrupt hander is getting bigger and bigger, while +losing performance. + +On the other hand, ``Threads (and only Threads)'' programming style +gives us best flexibility and it can make an application more +predictable, deterministic and easy to maintain. + +There are some applications, like square wave generator, which are not +suited to this programming style; Another programming style can +support more performance (frequency). In general, such an example is +best suited by hardware (not software). + + +@node Poll or Pole +@chapter Poll or Pole +Chopstx provides the @code{chopstx_poll} function to wait on multiple events. + +Using @code{chopstx_poll}, we can write an application by event-driven +programming style, with minimum number of threads, avoiding +complicated dependency between threads. + + +@node Note (Use of sleep mode) +@chapter Note (Use of sleep mode) + +Calling the chopstx_conf_idle function (> 0) to allow the idle thread +going to sleep. MCU will be in sleep mode when no threads are +running. By setting relevant bits of system registers, MCU will be +able to be into stop or stand-by mode, which is MCU dependent. + +If you use this sleep feature, please consider and implement your +program carefully. Enabling sleep, it may result a bricked board; A +board with no RESET pin cannot be debugged by JTAG/SWD. + + +@node Compile-time macro +@chapter Compile-time macro + +Compiling Chopstx, a macro MHZ should be supplied. + +For example, when using the makefile rule of chopstx/rules.mk, please +define the make variable DEFS with -DMHZ=72 before inclusion of the rule file. + +@subheading MHZ +@anchor{MHZ} +@defmac {MHZ} +Running CPU clock in MHz. Used for chopstx_usec_wait. +@end defmac + + +@node API +@chapter API + +@include chopstx-api.texi + + +@c ******************************************** + +@include gpl.texi + +@node Concept Index +@unnumbered Concept Index + +@printindex cp + +@node Function and Data Index +@unnumbered Function and Data Index + +@printindex fn + +@bye diff --git a/gnuk/chopstx/doc/gdoc b/gnuk/chopstx/doc/gdoc new file mode 100644 index 0000000000..0874149f69 --- /dev/null +++ b/gnuk/chopstx/doc/gdoc @@ -0,0 +1,912 @@ +eval '(exit $?0)' && eval 'exec perl -S "$0" ${1+"$@"}' + & eval 'exec perl -S "$0" $argv:q' + if 0; +# Generate documentation from source code. + +## Copyright (c) 2002-2013 Simon Josefsson +## added -texinfo, -listfunc, -pkg-name +## man page revamp +## various improvements +## Copyright (c) 2001, 2002 Nikos Mavrogiannopoulos +## added -tex +## Copyright (c) 1998 Michael Zucchi + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This will read a C source code file and scan for embedded comments +# in the style of gnome comments (+minor extensions - see below). + +# usage: +# gdoc [ -docbook | -html | -text | -man | -tex | -texinfo | -listfunc ] +# [ -sourceversion verno ] [ -include file | -includefuncprefix ] +# [ -bugsto address ] [ -pkg-name packagename ] +# [ -seeinfo infonode ] [ -copyright notice ] [ -verbatimcopying ] +# [ -function funcname [ -function funcname ...] ] c file(s)s > outputfile +# +# Set output format using one of -docbook, -html, -text, -man, -tex, +# -texinfo, or -listfunc. Default is man. +# +# -sourceversion +# Version number for source code, e.g. '1.0.4'. Used in 'man' headers. +# Defaults to using current date. +# +# -include FILE +# For man pages, mention #include in the synopsis. +# +# -includefuncprefix +# For man pages, mention a #include in the synopsis. +# The FILE derived from the function prefix. For example, a +# function gss_init_sec_context will generate an include +# statement of #include . +# +# -bugsto address +# For man pages, include a section about reporting bugs and mention +# the given e-mail address, e.g 'bug-libidn@gnu.org'. +# +# -pkg-name packagename +# For man pages when -bugsto is used, also include help URLs to the +# the project's home page. For example, "GNU Libidn". +# +# -seeinfo infonode +# For man pages, include a section that point to an info manual +# for more information. +# +# -copyright notice +# For man pages, include a copyright section with the given +# notice after a preamble. Use, e.g., '2002, 2003 Simon Josefsson'. +# +# -verbatimcopying +# For man pages, and when the -copyright parameter is used, +# add a licensing statement that say verbatim copying is permitted. +# +# -function funcname +# If set, then only generate documentation for the given function(s). All +# other functions are ignored. +# +# c files - list of 'c' files to process +# +# All output goes to stdout, with errors to stderr. + +# +# format of comments. +# In the following table, (...)? signifies optional structure. +# (...)* signifies 0 or more structure elements +# /** +# * function_name(:)? (- short description)? +# (* @parameterx: (description of parameter x)?)* +# (* a blank line)? +# * (Description:)? (Description of function)? +# * (Section header: (section description)? )* +# (*)?*/ +# +# So .. the trivial example would be: +# +# /** +# * my_function +# **/ +# +# If the Description: header tag is ommitted, then there must be a blank line +# after the last parameter specification. +# e.g. +# /** +# * my_function - does my stuff +# * @my_arg: its mine damnit +# * +# * Does my stuff explained. +# */ +# +# or, could also use: +# /** +# * my_function - does my stuff +# * @my_arg: its mine damnit +# * Description: Does my stuff explained. +# */ +# etc. +# +# All descriptions can be multiline, apart from the short function description. +# +# All descriptive text is further processed, scanning for the following special +# patterns, which are highlighted appropriately. +# +# 'funcname()' - function +# '$ENVVAR' - environmental variable OBSOLETE (?) +# '#struct_name' - name of a structure +# '@parameter' - name of a parameter +# '%CONST' - name of a constant. + +# +# Extensions for LaTeX: +# +# 1. the symbol '->' will be replaced with a rightarrow +# 2. x^y with ${x}^{y}$. +# 3. xxx\: with xxx: + +use POSIX qw(strftime); + +# match expressions used to find embedded type information +$type_constant = "\\\%(\\w+)"; +$type_func = "(\\w+\\(\\))"; +$type_param = "\\\@(\\w+)"; +$type_struct = "\\\#(\\w+)"; +$type_env = "(\\\$\\w+)"; + + +# Output conversion substitutions. +# One for each output format + +# these work fairly well +%highlights_html = ( $type_constant, "\$1", + $type_func, "\$1", + $type_struct, "\$1", + $type_param, "\$1" ); +$blankline_html = "

"; + +%highlights_texinfo = ( $type_constant, "\\\@code{\$1}", + $type_func, "\\\@code{\$1}", + $type_struct, "\\\@code{\$1}", + $type_param, "\\\@var{\$1}" ); +$blankline_texinfo = ""; + +%highlights_tex = ( $type_constant, "{\\\\it \$1}", + $type_func, "{\\\\bf \$1}", + $type_struct, "{\\\\it \$1}", + $type_param, "{\\\\bf \$1}" ); +$blankline_tex = "\\\\"; + +# sgml, docbook format +%highlights_sgml = ( $type_constant, "\$1", + $type_func, "\$1", + $type_struct, "\$1", + $type_env, "\$1", + $type_param, "\$1" ); +$blankline_sgml = "\n"; + +# these are pretty rough +%highlights_man = ( $type_constant, "\\\\fB\$1\\\\fP", + $type_func, "\\\\fB\$1\\\\fP", + $type_struct, "\\\\fB\$1\\\\fP", + $type_param, "\\\\fI\$1\\\\fP" ); +$blankline_man = ""; + +# text-mode +%highlights_text = ( $type_constant, "\$1", + $type_func, "\$1", + $type_struct, "\$1", + $type_param, "\$1" ); +$blankline_text = ""; + + +sub usage { + print "Usage: $0 [ -v ] [ -docbook | -html | -text | -man | -tex | -texinfo -listfunc ]\n"; + print " [ -sourceversion verno ] [ -include file | -includefuncprefix ]\n"; + print " [ -bugsto address ] [ -seeinfo infonode ] [ -copyright notice]\n"; + print " [ -verbatimcopying ] [ -pkg-name packagename ]\n"; + print " [ -function funcname [ -function funcname ...] ]\n"; + print " c source file(s) > outputfile\n"; + exit 1; +} + +# read arguments +if ($#ARGV==-1) { + usage(); +} + +$verbose = 0; +$output_mode = "man"; +%highlights = %highlights_man; +$blankline = $blankline_man; +$modulename = "API Documentation"; +$sourceversion = strftime "%Y-%m-%d", localtime; +$function_only = 0; +while ($ARGV[0] =~ m/^-(.*)/) { + $cmd = shift @ARGV; + if ($cmd eq "-html") { + $output_mode = "html"; + %highlights = %highlights_html; + $blankline = $blankline_html; + } elsif ($cmd eq "-man") { + $output_mode = "man"; + %highlights = %highlights_man; + $blankline = $blankline_man; + } elsif ($cmd eq "-tex") { + $output_mode = "tex"; + %highlights = %highlights_tex; + $blankline = $blankline_tex; + } elsif ($cmd eq "-texinfo") { + $output_mode = "texinfo"; + %highlights = %highlights_texinfo; + $blankline = $blankline_texinfo; + } elsif ($cmd eq "-text") { + $output_mode = "text"; + %highlights = %highlights_text; + $blankline = $blankline_text; + } elsif ($cmd eq "-docbook") { + $output_mode = "sgml"; + %highlights = %highlights_sgml; + $blankline = $blankline_sgml; + } elsif ($cmd eq "-listfunc") { + $output_mode = "listfunc"; + } elsif ($cmd eq "-module") { # not needed for sgml, inherits from calling document + $modulename = shift @ARGV; + } elsif ($cmd eq "-sourceversion") { + $sourceversion = shift @ARGV; + } elsif ($cmd eq "-include") { + $include = shift @ARGV; + } elsif ($cmd eq "-includefuncprefix") { + $includefuncprefix = 1; + } elsif ($cmd eq "-bugsto") { + $bugsto = shift @ARGV; + } elsif ($cmd eq "-pkg-name") { + $pkgname = shift @ARGV; + } elsif ($cmd eq "-copyright") { + $copyright = shift @ARGV; + } elsif ($cmd eq "-verbatimcopying") { + $verbatimcopying = 1; + } elsif ($cmd eq "-seeinfo") { + $seeinfo = shift @ARGV; + } elsif ($cmd eq "-function") { # to only output specific functions + $function_only = 1; + $function = shift @ARGV; + $function_table{$function} = 1; + } elsif ($cmd eq "-v") { + $verbose = 1; + } elsif (($cmd eq "-h") || ($cmd eq "--help")) { + usage(); + } +} + +## +# dumps section contents to arrays/hashes intended for that purpose. +# +sub dump_section { + my $name = shift @_; + my $contents = join "\n", @_; + + if ($name =~ m/$type_constant/) { + $name = $1; +# print STDERR "constant section '$1' = '$contents'\n"; + $constants{$name} = $contents; + } elsif ($name =~ m/$type_param/) { +# print STDERR "parameter def '$1' = '$contents'\n"; + $name = $1; + $parameters{$name} = $contents; + } else { +# print STDERR "other section '$name' = '$contents'\n"; + $sections{$name} = $contents; + push @sectionlist, $name; + } +} + +## +# output function +# +# parameters, a hash. +# function => "function name" +# parameterlist => @list of parameters +# parameters => %parameter descriptions +# sectionlist => @list of sections +# sections => %descriont descriptions +# + +sub repstr { + $pattern = shift; + $repl = shift; + $match1 = shift; + $match2 = shift; + $match3 = shift; + $match4 = shift; + + $output = $repl; + $output =~ s,\$1,$match1,g; + $output =~ s,\$2,$match2,g; + $output =~ s,\$3,$match3,g; + $output =~ s,\$4,$match4,g; + + eval "\$return = qq/$output/"; + +# print "pattern $pattern matched 1=$match1 2=$match2 3=$match3 4=$match4 replace $repl yielded $output interpolated $return\n"; + + $return; +} + +sub just_highlight { + my $contents = join "\n", @_; + my $line; + my $ret = ""; + + foreach $pattern (keys %highlights) { +# print "scanning pattern $pattern ($highlights{$pattern})\n"; + $contents =~ s:$pattern:repstr($pattern, $highlights{$pattern}, $1, $2, $3, $4):gse; + } + foreach $line (split "\n", $contents) { + if ($line eq ""){ + $ret = $ret . $lineprefix . $blankline; + } else { + $ret = $ret . $lineprefix . $line; + } + $ret = $ret . "\n"; + } + + return $ret; +} + +sub output_highlight { + print (just_highlight (@_)); +} + +# output in texinfo +sub output_texinfo { + my %args = %{$_[0]}; + my ($parameter, $section); + my $count; + + print "\@subheading ".$args{'function'}."\n"; + print "\@anchor{".$args{'function'}."}\n"; + print "\@deftypefun {" . $args{'functiontype'} . "} "; + print "{".$args{'function'}."} "; + print "("; + $count = 0; + foreach $parameter (@{$args{'parameterlist'}}) { + print $args{'parametertypes'}{$parameter}." \@var{".$parameter."}"; + if ($count != $#{$args{'parameterlist'}}) { + $count++; + print ", "; + } + } + print ")\n"; + foreach $parameter (@{$args{'parameterlist'}}) { + if ($args{'parameters'}{$parameter}) { + print "\@var{".$parameter."}: "; + output_highlight($args{'parameters'}{$parameter}); + print "\n"; + } + } + foreach $section (@{$args{'sectionlist'}}) { + print "\n\@strong{$section:} " if $section ne $section_default; + $args{'sections'}{$section} =~ s:([{}]):\@\1:gs; + output_highlight($args{'sections'}{$section}); + } + print "\@end deftypefun\n\n"; +} + +# output in html +sub output_html { + my %args = %{$_[0]}; + my ($parameter, $section); + my $count; + print "\n\n 

Function

\n"; + + print "".$args{'functiontype'}."\n"; + print "".$args{'function'}."\n"; + print "("; + $count = 0; + foreach $parameter (@{$args{'parameterlist'}}) { + print "".$args{'parametertypes'}{$parameter}." ".$parameter."\n"; + if ($count != $#{$args{'parameterlist'}}) { + $count++; + print ", "; + } + } + print ")\n"; + + print "

Arguments

\n"; + print "
\n"; + foreach $parameter (@{$args{'parameterlist'}}) { + print "
".$args{'parametertypes'}{$parameter}." ".$parameter."\n"; + print "
"; + output_highlight($args{'parameters'}{$parameter}); + } + print "
\n"; + foreach $section (@{$args{'sectionlist'}}) { + print "

$section

\n"; + print "
    \n"; + output_highlight($args{'sections'}{$section}); + print "
\n"; + } + print "
\n"; +} + +# output in tex +sub output_tex { + my %args = %{$_[0]}; + my ($parameter, $section); + my $count; + my $func = $args{'function'}; + my $param; + my $param2; + my $sec; + my $check; + my $type; + + $func =~ s/_/\\_/g; + + print "\n\n\\subsection{". $func . "}\n\\label{" . $args{'function'} . "}\n"; + + $type = $args{'functiontype'}; + $type =~ s/_/\\_/g; + + print "{\\it ".$type."}\n"; + print "{\\bf ".$func."}\n"; + print "("; + $count = 0; + foreach $parameter (@{$args{'parameterlist'}}) { + $param = $args{'parametertypes'}{$parameter}; + $param2 = $parameter; + $param =~ s/_/\\_/g; + $param2 =~ s/_/\\_/g; + + print "{\\it ".$param."} {\\bf ".$param2."}"; + if ($count != $#{$args{'parameterlist'}}) { + $count++; + print ", "; + } + } + print ")\n"; + + print "\n{\\large{Arguments}}\n"; + + print "\\begin{itemize}\n"; + $check=0; + foreach $parameter (@{$args{'parameterlist'}}) { + $param1 = $args{'parametertypes'}{$parameter}; + $param1 =~ s/_/\\_/g; + $param2 = $parameter; + $param2 =~ s/_/\\_/g; + + $check = 1; + print "\\item {\\it ".$param1."} {\\bf ".$param2."}: \n"; +# print "\n"; + + $param3 = $args{'parameters'}{$parameter}; + $param3 =~ s/#([a-zA-Z\_]+)/{\\it \1}/g; + + $out = just_highlight($param3); + $out =~ s/_/\\_/g; + print $out; + } + if ($check==0) { + print "\\item void\n"; + } + print "\\end{itemize}\n"; + + foreach $section (@{$args{'sectionlist'}}) { + $sec = $section; + $sec =~ s/_/\\_/g; + $sec =~ s/#([a-zA-Z\_]+)/{\\it \1}/g; + + print "\n{\\large{$sec}}\\\\\n"; + print "\\begin{rmfamily}\n"; + + $sec = $args{'sections'}{$section}; + $sec =~ s/\\:/:/g; + $sec =~ s/#([a-zA-Z\_]+)/{\\it \1}/g; + $sec =~ s/->/\$\\rightarrow\$/g; + $sec =~ s/([0-9]+)\^([0-9]+)/\$\{\1\}\^\{\2\}\$/g; + + $out = just_highlight($sec); + $out =~ s/_/\\_/g; + + print $out; + print "\\end{rmfamily}\n"; + } + print "\n"; +} + + +# output in sgml DocBook +sub output_sgml { + my %args = %{$_[0]}; + my ($parameter, $section); + my $count; + my $id; + + $id = $args{'module'}."-".$args{'function'}; + $id =~ s/[^A-Za-z0-9]/-/g; + + print "\n"; + print "\n"; + print "".$args{'function'}."\n"; + print "\n"; + print "\n"; + print " ".$args{'function'}."\n"; + print " \n"; + print " ".$args{'purpose'}."\n"; + print " \n"; + print "\n"; + + print "\n"; + print " Synopsis\n"; + print " \n"; + print " ".$args{'functiontype'}." "; + print "".$args{'function'}." "; + print "\n"; + +# print "\n"; +# print " Synopsis\n"; +# print " \n"; +# print " ".$args{'functiontype'}." "; +# print "".$args{'function'}." "; +# print "\n"; + + $count = 0; + if ($#{$args{'parameterlist'}} >= 0) { + foreach $parameter (@{$args{'parameterlist'}}) { + print " ".$args{'parametertypes'}{$parameter}; + print " $parameter\n"; + } + } else { + print " \n"; + } + print " \n"; + print "\n"; +# print "\n"; + + # print parameters + print "\n Arguments\n"; +# print "\nArguments\n"; + if ($#{$args{'parameterlist'}} >= 0) { + print " \n"; + foreach $parameter (@{$args{'parameterlist'}}) { + print " \n $parameter\n"; + print " \n \n"; + $lineprefix=" "; + output_highlight($args{'parameters'}{$parameter}); + print " \n \n \n"; + } + print " \n"; + } else { + print " \n None\n \n"; + } + print "\n"; + + # print out each section + $lineprefix=" "; + foreach $section (@{$args{'sectionlist'}}) { + print "\n $section\n \n"; +# print "\n$section\n"; + if ($section =~ m/EXAMPLE/i) { + print "\n"; + } + output_highlight($args{'sections'}{$section}); +# print ""; + if ($section =~ m/EXAMPLE/i) { + print "\n"; + } + print " \n\n"; + } + + print "\n\n"; +} + +## +# output in man +sub output_man { + my %args = %{$_[0]}; + my ($parameter, $section); + my $count; + + print ".\\\" DO NOT MODIFY THIS FILE! It was generated by gdoc.\n"; + print ".TH \"$args{'function'}\" 3 \"$args{'sourceversion'}\" \"". $args{'module'} . "\" \"". $args{'module'} . "\"\n"; + + print ".SH NAME\n"; + + print $args{'function'}; + if ($args{'purpose'}) { + print " \\- " . $args{'purpose'} . "\n"; + } else { + print " \\- API function\n"; + } + + print ".SH SYNOPSIS\n"; + print ".B #include <". $args{'include'} . ">\n" + if $args{'include'}; + print ".B #include <". lc((split /_/, $args{'function'})[0]) . ".h>\n" + if $args{'includefuncprefix'}; + print ".sp\n"; + print ".BI \"".$args{'functiontype'}." ".$args{'function'}."("; + $count = 0; + foreach $parameter (@{$args{'parameterlist'}}) { + print $args{'parametertypes'}{$parameter}." \" ".$parameter." \""; + if ($count != $#{$args{'parameterlist'}}) { + $count++; + print ", "; + } + } + print ");\"\n"; + + print ".SH ARGUMENTS\n"; + foreach $parameter (@{$args{'parameterlist'}}) { + print ".IP \"".$args{'parametertypes'}{$parameter}." ".$parameter."\" 12\n"; + $param = $args{'parameters'}{$parameter}; + $param =~ s/-/\\-/g; + output_highlight($param); + } + foreach $section (@{$args{'sectionlist'}}) { + print ".SH \"" . uc($section) . "\"\n"; + $sec = $args{'sections'}{$section}; + $sec =~ s/-/\\-/g; + output_highlight($sec); + } + + if ($args{'bugsto'}) { + print ".SH \"REPORTING BUGS\"\n"; + print "Report bugs to <". $args{'bugsto'} . ">.\n"; + if ($args{'pkgname'}) { + print $args{'pkgname'} . " home page: " . + "http://www.gnu.org/software/" . $args{'module'} . "/\n"; + } + print "General help using GNU software: http://www.gnu.org/gethelp/\n"; + } + + if ($args{'copyright'}) { + print ".SH COPYRIGHT\n"; + print "Copyright \\(co ". $args{'copyright'} . ".\n"; + if ($args{'verbatimcopying'}) { + print ".br\n"; + print "Copying and distribution of this file, with or without modification,\n"; + print "are permitted in any medium without royalty provided the copyright\n"; + print "notice and this notice are preserved.\n"; + } + } + + if ($args{'seeinfo'}) { + print ".SH \"SEE ALSO\"\n"; + print "The full documentation for\n"; + print ".B " . $args{'module'} . "\n"; + print "is maintained as a Texinfo manual. If the\n"; + print ".B info\n"; + print "and\n"; + print ".B " . $args{'module'} . "\n"; + print "programs are properly installed at your site, the command\n"; + print ".IP\n"; + print ".B info " . $args{'seeinfo'} . "\n"; + print ".PP\n"; + print "should give you access to the complete manual.\n"; + } +} + +sub output_listfunc { + my %args = %{$_[0]}; + print $args{'function'} . "\n"; +} + +## +# output in text +sub output_text { + my %args = %{$_[0]}; + my ($parameter, $section); + + print "Function = ".$args{'function'}."\n"; + print " return type: ".$args{'functiontype'}."\n\n"; + foreach $parameter (@{$args{'parameterlist'}}) { + print " ".$args{'parametertypes'}{$parameter}." ".$parameter."\n"; + print " -> ".$args{'parameters'}{$parameter}."\n"; + } + foreach $section (@{$args{'sectionlist'}}) { + print " $section:\n"; + print " -> "; + output_highlight($args{'sections'}{$section}); + } +} + +## +# generic output function - calls the right one based +# on current output mode. +sub output_function { +# output_html(@_); + eval "output_".$output_mode."(\@_);"; +} + + +## +# takes a function prototype and spits out all the details +# stored in the global arrays/hsahes. +sub dump_function { + my $prototype = shift @_; + + if ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ || + $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ || + $prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ || + $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ || + $prototype =~ m/^(\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/) { + $return_type = $1; + $function_name = $2; + $args = $3; + +# print STDERR "ARGS = '$args'\n"; + + foreach $arg (split ',', $args) { + # strip leading/trailing spaces + $arg =~ s/^\s*//; + $arg =~ s/\s*$//; +# print STDERR "SCAN ARG: '$arg'\n"; + @args = split('\s', $arg); + +# print STDERR " -> @args\n"; + $param = pop @args; +# print STDERR " -> @args\n"; + if ($param =~ m/^(\*+)(.*)/) { + $param = $2; + push @args, $1; + } + if ($param =~ m/^(.*)(\[\])$/) { + $param = $1; + push @args, $2; + } +# print STDERR " :> @args\n"; + $type = join " ", @args; + + if ($parameters{$param} eq "" && $param != "void") { + $parameters{$param} = "-- undescribed --"; + print STDERR "warning: $lineno: Function parameter '$param' not described in '$function_name'\n"; + } + + push @parameterlist, $param; + $parametertypes{$param} = $type; + +# print STDERR "param = '$param', type = '$type'\n"; + } + } else { + print STDERR "warning: $lineno: Cannot understand prototype: '$prototype'\n"; + return; + } + + if ($function_only==0 || defined($function_table{$function_name})) { + output_function({'function' => $function_name, + 'module' => $modulename, + 'sourceversion' => $sourceversion, + 'include' => $include, + 'includefuncprefix' => $includefuncprefix, + 'bugsto' => $bugsto, + 'pkgname' => $pkgname, + 'copyright' => $copyright, + 'verbatimcopying' => $verbatimcopying, + 'seeinfo' => $seeinfo, + 'functiontype' => $return_type, + 'parameterlist' => \@parameterlist, + 'parameters' => \%parameters, + 'parametertypes' => \%parametertypes, + 'sectionlist' => \@sectionlist, + 'sections' => \%sections, + 'purpose' => $function_purpose + }); + } +} + +###################################################################### +# main +# states +# 0 - normal code +# 1 - looking for function name +# 2 - scanning field start. +# 3 - scanning prototype. +$state = 0; +$section = ""; + +$doc_special = "\@\%\$\#"; + +$doc_start = "^/\\*\\*\$"; +$doc_end = "\\*/"; +$doc_com = "\\s*\\*\\s*"; +$doc_func = $doc_com."(\\w+):?"; +$doc_sect = $doc_com."([".$doc_special."[:upper:]][\\w ]+):\\s*(.*)"; +$doc_content = $doc_com."(.*)"; + +%constants = (); +%parameters = (); +@parameterlist = (); +%sections = (); +@sectionlist = (); + +$contents = ""; +$section_default = "Description"; # default section +$section = $section_default; + +$lineno = 0; +foreach $file (@ARGV) { + if (!open(IN,"<$file")) { + print STDERR "Error: Cannot open file $file\n"; + next; + } + while () { + $lineno++; + + if ($state == 0) { + if (/$doc_start/o) { + $state = 1; # next line is always the function name + } + } elsif ($state == 1) { # this line is the function name (always) + if (/$doc_func/o) { + $function = $1; + $state = 2; + if (/-\s*(.*)/) { + $function_purpose = $1; + } else { + $function_purpose = ""; + } + if ($verbose) { + print STDERR "Info($lineno): Scanning doc for $function\n"; + } + } else { + print STDERR "warning: $lineno: Cannot understand $_ on line $lineno", + " - I thought it was a doc line\n"; + $state = 0; + } + } elsif ($state == 2) { # look for head: lines, and include content + if (/$doc_sect/o) { + $newsection = $1; + $newcontents = $2; + + if ($contents ne "") { + dump_section($section, $contents); + $section = $section_default; + } + + $contents = $newcontents; + if ($contents ne "") { + $contents .= "\n"; + } + $section = $newsection; + } elsif (/$doc_end/) { + + if ($contents ne "") { + dump_section($section, $contents); + $section = $section_default; + $contents = ""; + } + +# print STDERR "end of doc comment, looking for prototype\n"; + $prototype = ""; + $state = 3; + } elsif (/$doc_content/) { + # miguel-style comment kludge, look for blank lines after + # @parameter line to signify start of description + if ($1 eq "" && $section =~ m/^@/) { + dump_section($section, $contents); + $section = $section_default; + $contents = ""; + } else { + $contents .= $1."\n"; + } + } else { + # i dont know - bad line? ignore. + print STDERR "warning: $lineno: Bad line: $_"; + } + } elsif ($state == 3) { # scanning for function { (end of prototype) + if (m#\s*/\*\s+MACDOC\s*#io) { + # do nothing + } + elsif (/([^\{]*)/) { + $prototype .= $1; + } + if (/\{/) { + $prototype =~ s@/\*.*?\*/@@gos; # strip comments. + $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's. + $prototype =~ s@^ +@@gos; # strip leading spaces + dump_function($prototype); + + $function = ""; + %constants = (); + %parameters = (); + %parametertypes = (); + @parameterlist = (); + %sections = (); + @sectionlist = (); + $prototype = ""; + + $state = 0; + } + } + } +} diff --git a/gnuk/chopstx/doc/gdoc.orig b/gnuk/chopstx/doc/gdoc.orig new file mode 100644 index 0000000000..ef9435438d --- /dev/null +++ b/gnuk/chopstx/doc/gdoc.orig @@ -0,0 +1,912 @@ +eval '(exit $?0)' && eval 'exec perl -S "$0" ${1+"$@"}' + & eval 'exec perl -S "$0" $argv:q' + if 0; +# Generate documentation from source code. + +## Copyright (c) 2002-2013 Simon Josefsson +## added -texinfo, -listfunc, -pkg-name +## man page revamp +## various improvements +## Copyright (c) 2001, 2002 Nikos Mavrogiannopoulos +## added -tex +## Copyright (c) 1998 Michael Zucchi + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This will read a C source code file and scan for embedded comments +# in the style of gnome comments (+minor extensions - see below). + +# usage: +# gdoc [ -docbook | -html | -text | -man | -tex | -texinfo | -listfunc ] +# [ -sourceversion verno ] [ -include file | -includefuncprefix ] +# [ -bugsto address ] [ -pkg-name packagename ] +# [ -seeinfo infonode ] [ -copyright notice ] [ -verbatimcopying ] +# [ -function funcname [ -function funcname ...] ] c file(s)s > outputfile +# +# Set output format using one of -docbook, -html, -text, -man, -tex, +# -texinfo, or -listfunc. Default is man. +# +# -sourceversion +# Version number for source code, e.g. '1.0.4'. Used in 'man' headers. +# Defaults to using current date. +# +# -include FILE +# For man pages, mention #include in the synopsis. +# +# -includefuncprefix +# For man pages, mention a #include in the synopsis. +# The FILE derived from the function prefix. For example, a +# function gss_init_sec_context will generate an include +# statement of #include . +# +# -bugsto address +# For man pages, include a section about reporting bugs and mention +# the given e-mail address, e.g 'bug-libidn@gnu.org'. +# +# -pkg-name packagename +# For man pages when -bugsto is used, also include help URLs to the +# the project's home page. For example, "GNU Libidn". +# +# -seeinfo infonode +# For man pages, include a section that point to an info manual +# for more information. +# +# -copyright notice +# For man pages, include a copyright section with the given +# notice after a preamble. Use, e.g., '2002, 2003 Simon Josefsson'. +# +# -verbatimcopying +# For man pages, and when the -copyright parameter is used, +# add a licensing statement that say verbatim copying is permitted. +# +# -function funcname +# If set, then only generate documentation for the given function(s). All +# other functions are ignored. +# +# c files - list of 'c' files to process +# +# All output goes to stdout, with errors to stderr. + +# +# format of comments. +# In the following table, (...)? signifies optional structure. +# (...)* signifies 0 or more structure elements +# /** +# * function_name(:)? (- short description)? +# (* @parameterx: (description of parameter x)?)* +# (* a blank line)? +# * (Description:)? (Description of function)? +# * (Section header: (section description)? )* +# (*)?*/ +# +# So .. the trivial example would be: +# +# /** +# * my_function +# **/ +# +# If the Description: header tag is ommitted, then there must be a blank line +# after the last parameter specification. +# e.g. +# /** +# * my_function - does my stuff +# * @my_arg: its mine damnit +# * +# * Does my stuff explained. +# */ +# +# or, could also use: +# /** +# * my_function - does my stuff +# * @my_arg: its mine damnit +# * Description: Does my stuff explained. +# */ +# etc. +# +# All descriptions can be multiline, apart from the short function description. +# +# All descriptive text is further processed, scanning for the following special +# patterns, which are highlighted appropriately. +# +# 'funcname()' - function +# '$ENVVAR' - environmental variable OBSOLETE (?) +# '#struct_name' - name of a structure +# '@parameter' - name of a parameter +# '%CONST' - name of a constant. + +# +# Extensions for LaTeX: +# +# 1. the symbol '->' will be replaced with a rightarrow +# 2. x^y with ${x}^{y}$. +# 3. xxx\: with xxx: + +use POSIX qw(strftime); + +# match expressions used to find embedded type information +$type_constant = "\\\%(\\w+)"; +$type_func = "(\\w+\\(\\))"; +$type_param = "\\\@(\\w+)"; +$type_struct = "\\\#(\\w+)"; +$type_env = "(\\\$\\w+)"; + + +# Output conversion substitutions. +# One for each output format + +# these work fairly well +%highlights_html = ( $type_constant, "\$1", + $type_func, "\$1", + $type_struct, "\$1", + $type_param, "\$1" ); +$blankline_html = "

"; + +%highlights_texinfo = ( $type_constant, "\\\@code{\$1}", + $type_func, "\\\@code{\$1}", + $type_struct, "\\\@code{\$1}", + $type_param, "\\\@code{\$1}" ); +$blankline_texinfo = ""; + +%highlights_tex = ( $type_constant, "{\\\\it \$1}", + $type_func, "{\\\\bf \$1}", + $type_struct, "{\\\\it \$1}", + $type_param, "{\\\\bf \$1}" ); +$blankline_tex = "\\\\"; + +# sgml, docbook format +%highlights_sgml = ( $type_constant, "\$1", + $type_func, "\$1", + $type_struct, "\$1", + $type_env, "\$1", + $type_param, "\$1" ); +$blankline_sgml = "\n"; + +# these are pretty rough +%highlights_man = ( $type_constant, "\\\\fB\$1\\\\fP", + $type_func, "\\\\fB\$1\\\\fP", + $type_struct, "\\\\fB\$1\\\\fP", + $type_param, "\\\\fI\$1\\\\fP" ); +$blankline_man = ""; + +# text-mode +%highlights_text = ( $type_constant, "\$1", + $type_func, "\$1", + $type_struct, "\$1", + $type_param, "\$1" ); +$blankline_text = ""; + + +sub usage { + print "Usage: $0 [ -v ] [ -docbook | -html | -text | -man | -tex | -texinfo -listfunc ]\n"; + print " [ -sourceversion verno ] [ -include file | -includefuncprefix ]\n"; + print " [ -bugsto address ] [ -seeinfo infonode ] [ -copyright notice]\n"; + print " [ -verbatimcopying ] [ -pkg-name packagename ]\n"; + print " [ -function funcname [ -function funcname ...] ]\n"; + print " c source file(s) > outputfile\n"; + exit 1; +} + +# read arguments +if ($#ARGV==-1) { + usage(); +} + +$verbose = 0; +$output_mode = "man"; +%highlights = %highlights_man; +$blankline = $blankline_man; +$modulename = "API Documentation"; +$sourceversion = strftime "%Y-%m-%d", localtime; +$function_only = 0; +while ($ARGV[0] =~ m/^-(.*)/) { + $cmd = shift @ARGV; + if ($cmd eq "-html") { + $output_mode = "html"; + %highlights = %highlights_html; + $blankline = $blankline_html; + } elsif ($cmd eq "-man") { + $output_mode = "man"; + %highlights = %highlights_man; + $blankline = $blankline_man; + } elsif ($cmd eq "-tex") { + $output_mode = "tex"; + %highlights = %highlights_tex; + $blankline = $blankline_tex; + } elsif ($cmd eq "-texinfo") { + $output_mode = "texinfo"; + %highlights = %highlights_texinfo; + $blankline = $blankline_texinfo; + } elsif ($cmd eq "-text") { + $output_mode = "text"; + %highlights = %highlights_text; + $blankline = $blankline_text; + } elsif ($cmd eq "-docbook") { + $output_mode = "sgml"; + %highlights = %highlights_sgml; + $blankline = $blankline_sgml; + } elsif ($cmd eq "-listfunc") { + $output_mode = "listfunc"; + } elsif ($cmd eq "-module") { # not needed for sgml, inherits from calling document + $modulename = shift @ARGV; + } elsif ($cmd eq "-sourceversion") { + $sourceversion = shift @ARGV; + } elsif ($cmd eq "-include") { + $include = shift @ARGV; + } elsif ($cmd eq "-includefuncprefix") { + $includefuncprefix = 1; + } elsif ($cmd eq "-bugsto") { + $bugsto = shift @ARGV; + } elsif ($cmd eq "-pkg-name") { + $pkgname = shift @ARGV; + } elsif ($cmd eq "-copyright") { + $copyright = shift @ARGV; + } elsif ($cmd eq "-verbatimcopying") { + $verbatimcopying = 1; + } elsif ($cmd eq "-seeinfo") { + $seeinfo = shift @ARGV; + } elsif ($cmd eq "-function") { # to only output specific functions + $function_only = 1; + $function = shift @ARGV; + $function_table{$function} = 1; + } elsif ($cmd eq "-v") { + $verbose = 1; + } elsif (($cmd eq "-h") || ($cmd eq "--help")) { + usage(); + } +} + +## +# dumps section contents to arrays/hashes intended for that purpose. +# +sub dump_section { + my $name = shift @_; + my $contents = join "\n", @_; + + if ($name =~ m/$type_constant/) { + $name = $1; +# print STDERR "constant section '$1' = '$contents'\n"; + $constants{$name} = $contents; + } elsif ($name =~ m/$type_param/) { +# print STDERR "parameter def '$1' = '$contents'\n"; + $name = $1; + $parameters{$name} = $contents; + } else { +# print STDERR "other section '$name' = '$contents'\n"; + $sections{$name} = $contents; + push @sectionlist, $name; + } +} + +## +# output function +# +# parameters, a hash. +# function => "function name" +# parameterlist => @list of parameters +# parameters => %parameter descriptions +# sectionlist => @list of sections +# sections => %descriont descriptions +# + +sub repstr { + $pattern = shift; + $repl = shift; + $match1 = shift; + $match2 = shift; + $match3 = shift; + $match4 = shift; + + $output = $repl; + $output =~ s,\$1,$match1,g; + $output =~ s,\$2,$match2,g; + $output =~ s,\$3,$match3,g; + $output =~ s,\$4,$match4,g; + + eval "\$return = qq/$output/"; + +# print "pattern $pattern matched 1=$match1 2=$match2 3=$match3 4=$match4 replace $repl yielded $output interpolated $return\n"; + + $return; +} + +sub just_highlight { + my $contents = join "\n", @_; + my $line; + my $ret = ""; + + foreach $pattern (keys %highlights) { +# print "scanning pattern $pattern ($highlights{$pattern})\n"; + $contents =~ s:$pattern:repstr($pattern, $highlights{$pattern}, $1, $2, $3, $4):gse; + } + foreach $line (split "\n", $contents) { + if ($line eq ""){ + $ret = $ret . $lineprefix . $blankline; + } else { + $ret = $ret . $lineprefix . $line; + } + $ret = $ret . "\n"; + } + + return $ret; +} + +sub output_highlight { + print (just_highlight (@_)); +} + +# output in texinfo +sub output_texinfo { + my %args = %{$_[0]}; + my ($parameter, $section); + my $count; + + print "\@subheading ".$args{'function'}."\n"; + print "\@anchor{".$args{'function'}."}\n"; + print "\@deftypefun {" . $args{'functiontype'} . "} "; + print "{".$args{'function'}."} "; + print "("; + $count = 0; + foreach $parameter (@{$args{'parameterlist'}}) { + print $args{'parametertypes'}{$parameter}." \@var{".$parameter."}"; + if ($count != $#{$args{'parameterlist'}}) { + $count++; + print ", "; + } + } + print ")\n"; + foreach $parameter (@{$args{'parameterlist'}}) { + if ($args{'parameters'}{$parameter}) { + print "\@var{".$parameter."}: "; + output_highlight($args{'parameters'}{$parameter}); + print "\n"; + } + } + foreach $section (@{$args{'sectionlist'}}) { + print "\n\@strong{$section:} " if $section ne $section_default; + $args{'sections'}{$section} =~ s:([{}]):\@\1:gs; + output_highlight($args{'sections'}{$section}); + } + print "\@end deftypefun\n\n"; +} + +# output in html +sub output_html { + my %args = %{$_[0]}; + my ($parameter, $section); + my $count; + print "\n\n 

Function

\n"; + + print "".$args{'functiontype'}."\n"; + print "".$args{'function'}."\n"; + print "("; + $count = 0; + foreach $parameter (@{$args{'parameterlist'}}) { + print "".$args{'parametertypes'}{$parameter}." ".$parameter."\n"; + if ($count != $#{$args{'parameterlist'}}) { + $count++; + print ", "; + } + } + print ")\n"; + + print "

Arguments

\n"; + print "
\n"; + foreach $parameter (@{$args{'parameterlist'}}) { + print "
".$args{'parametertypes'}{$parameter}." ".$parameter."\n"; + print "
"; + output_highlight($args{'parameters'}{$parameter}); + } + print "
\n"; + foreach $section (@{$args{'sectionlist'}}) { + print "

$section

\n"; + print "
    \n"; + output_highlight($args{'sections'}{$section}); + print "
\n"; + } + print "
\n"; +} + +# output in tex +sub output_tex { + my %args = %{$_[0]}; + my ($parameter, $section); + my $count; + my $func = $args{'function'}; + my $param; + my $param2; + my $sec; + my $check; + my $type; + + $func =~ s/_/\\_/g; + + print "\n\n\\subsection{". $func . "}\n\\label{" . $args{'function'} . "}\n"; + + $type = $args{'functiontype'}; + $type =~ s/_/\\_/g; + + print "{\\it ".$type."}\n"; + print "{\\bf ".$func."}\n"; + print "("; + $count = 0; + foreach $parameter (@{$args{'parameterlist'}}) { + $param = $args{'parametertypes'}{$parameter}; + $param2 = $parameter; + $param =~ s/_/\\_/g; + $param2 =~ s/_/\\_/g; + + print "{\\it ".$param."} {\\bf ".$param2."}"; + if ($count != $#{$args{'parameterlist'}}) { + $count++; + print ", "; + } + } + print ")\n"; + + print "\n{\\large{Arguments}}\n"; + + print "\\begin{itemize}\n"; + $check=0; + foreach $parameter (@{$args{'parameterlist'}}) { + $param1 = $args{'parametertypes'}{$parameter}; + $param1 =~ s/_/\\_/g; + $param2 = $parameter; + $param2 =~ s/_/\\_/g; + + $check = 1; + print "\\item {\\it ".$param1."} {\\bf ".$param2."}: \n"; +# print "\n"; + + $param3 = $args{'parameters'}{$parameter}; + $param3 =~ s/#([a-zA-Z\_]+)/{\\it \1}/g; + + $out = just_highlight($param3); + $out =~ s/_/\\_/g; + print $out; + } + if ($check==0) { + print "\\item void\n"; + } + print "\\end{itemize}\n"; + + foreach $section (@{$args{'sectionlist'}}) { + $sec = $section; + $sec =~ s/_/\\_/g; + $sec =~ s/#([a-zA-Z\_]+)/{\\it \1}/g; + + print "\n{\\large{$sec}}\\\\\n"; + print "\\begin{rmfamily}\n"; + + $sec = $args{'sections'}{$section}; + $sec =~ s/\\:/:/g; + $sec =~ s/#([a-zA-Z\_]+)/{\\it \1}/g; + $sec =~ s/->/\$\\rightarrow\$/g; + $sec =~ s/([0-9]+)\^([0-9]+)/\$\{\1\}\^\{\2\}\$/g; + + $out = just_highlight($sec); + $out =~ s/_/\\_/g; + + print $out; + print "\\end{rmfamily}\n"; + } + print "\n"; +} + + +# output in sgml DocBook +sub output_sgml { + my %args = %{$_[0]}; + my ($parameter, $section); + my $count; + my $id; + + $id = $args{'module'}."-".$args{'function'}; + $id =~ s/[^A-Za-z0-9]/-/g; + + print "\n"; + print "\n"; + print "".$args{'function'}."\n"; + print "\n"; + print "\n"; + print " ".$args{'function'}."\n"; + print " \n"; + print " ".$args{'purpose'}."\n"; + print " \n"; + print "\n"; + + print "\n"; + print " Synopsis\n"; + print " \n"; + print " ".$args{'functiontype'}." "; + print "".$args{'function'}." "; + print "\n"; + +# print "\n"; +# print " Synopsis\n"; +# print " \n"; +# print " ".$args{'functiontype'}." "; +# print "".$args{'function'}." "; +# print "\n"; + + $count = 0; + if ($#{$args{'parameterlist'}} >= 0) { + foreach $parameter (@{$args{'parameterlist'}}) { + print " ".$args{'parametertypes'}{$parameter}; + print " $parameter\n"; + } + } else { + print " \n"; + } + print " \n"; + print "\n"; +# print "\n"; + + # print parameters + print "\n Arguments\n"; +# print "\nArguments\n"; + if ($#{$args{'parameterlist'}} >= 0) { + print " \n"; + foreach $parameter (@{$args{'parameterlist'}}) { + print " \n $parameter\n"; + print " \n \n"; + $lineprefix=" "; + output_highlight($args{'parameters'}{$parameter}); + print " \n \n \n"; + } + print " \n"; + } else { + print " \n None\n \n"; + } + print "\n"; + + # print out each section + $lineprefix=" "; + foreach $section (@{$args{'sectionlist'}}) { + print "\n $section\n \n"; +# print "\n$section\n"; + if ($section =~ m/EXAMPLE/i) { + print "\n"; + } + output_highlight($args{'sections'}{$section}); +# print ""; + if ($section =~ m/EXAMPLE/i) { + print "\n"; + } + print " \n\n"; + } + + print "\n\n"; +} + +## +# output in man +sub output_man { + my %args = %{$_[0]}; + my ($parameter, $section); + my $count; + + print ".\\\" DO NOT MODIFY THIS FILE! It was generated by gdoc.\n"; + print ".TH \"$args{'function'}\" 3 \"$args{'sourceversion'}\" \"". $args{'module'} . "\" \"". $args{'module'} . "\"\n"; + + print ".SH NAME\n"; + + print $args{'function'}; + if ($args{'purpose'}) { + print " \\- " . $args{'purpose'} . "\n"; + } else { + print " \\- API function\n"; + } + + print ".SH SYNOPSIS\n"; + print ".B #include <". $args{'include'} . ">\n" + if $args{'include'}; + print ".B #include <". lc((split /_/, $args{'function'})[0]) . ".h>\n" + if $args{'includefuncprefix'}; + print ".sp\n"; + print ".BI \"".$args{'functiontype'}." ".$args{'function'}."("; + $count = 0; + foreach $parameter (@{$args{'parameterlist'}}) { + print $args{'parametertypes'}{$parameter}." \" ".$parameter." \""; + if ($count != $#{$args{'parameterlist'}}) { + $count++; + print ", "; + } + } + print ");\"\n"; + + print ".SH ARGUMENTS\n"; + foreach $parameter (@{$args{'parameterlist'}}) { + print ".IP \"".$args{'parametertypes'}{$parameter}." ".$parameter."\" 12\n"; + $param = $args{'parameters'}{$parameter}; + $param =~ s/-/\\-/g; + output_highlight($param); + } + foreach $section (@{$args{'sectionlist'}}) { + print ".SH \"" . uc($section) . "\"\n"; + $sec = $args{'sections'}{$section}; + $sec =~ s/-/\\-/g; + output_highlight($sec); + } + + if ($args{'bugsto'}) { + print ".SH \"REPORTING BUGS\"\n"; + print "Report bugs to <". $args{'bugsto'} . ">.\n"; + if ($args{'pkgname'}) { + print $args{'pkgname'} . " home page: " . + "http://www.gnu.org/software/" . $args{'module'} . "/\n"; + } + print "General help using GNU software: http://www.gnu.org/gethelp/\n"; + } + + if ($args{'copyright'}) { + print ".SH COPYRIGHT\n"; + print "Copyright \\(co ". $args{'copyright'} . ".\n"; + if ($args{'verbatimcopying'}) { + print ".br\n"; + print "Copying and distribution of this file, with or without modification,\n"; + print "are permitted in any medium without royalty provided the copyright\n"; + print "notice and this notice are preserved.\n"; + } + } + + if ($args{'seeinfo'}) { + print ".SH \"SEE ALSO\"\n"; + print "The full documentation for\n"; + print ".B " . $args{'module'} . "\n"; + print "is maintained as a Texinfo manual. If the\n"; + print ".B info\n"; + print "and\n"; + print ".B " . $args{'module'} . "\n"; + print "programs are properly installed at your site, the command\n"; + print ".IP\n"; + print ".B info " . $args{'seeinfo'} . "\n"; + print ".PP\n"; + print "should give you access to the complete manual.\n"; + } +} + +sub output_listfunc { + my %args = %{$_[0]}; + print $args{'function'} . "\n"; +} + +## +# output in text +sub output_text { + my %args = %{$_[0]}; + my ($parameter, $section); + + print "Function = ".$args{'function'}."\n"; + print " return type: ".$args{'functiontype'}."\n\n"; + foreach $parameter (@{$args{'parameterlist'}}) { + print " ".$args{'parametertypes'}{$parameter}." ".$parameter."\n"; + print " -> ".$args{'parameters'}{$parameter}."\n"; + } + foreach $section (@{$args{'sectionlist'}}) { + print " $section:\n"; + print " -> "; + output_highlight($args{'sections'}{$section}); + } +} + +## +# generic output function - calls the right one based +# on current output mode. +sub output_function { +# output_html(@_); + eval "output_".$output_mode."(\@_);"; +} + + +## +# takes a function prototype and spits out all the details +# stored in the global arrays/hsahes. +sub dump_function { + my $prototype = shift @_; + + if ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ || + $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ || + $prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ || + $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ || + $prototype =~ m/^(\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/) { + $return_type = $1; + $function_name = $2; + $args = $3; + +# print STDERR "ARGS = '$args'\n"; + + foreach $arg (split ',', $args) { + # strip leading/trailing spaces + $arg =~ s/^\s*//; + $arg =~ s/\s*$//; +# print STDERR "SCAN ARG: '$arg'\n"; + @args = split('\s', $arg); + +# print STDERR " -> @args\n"; + $param = pop @args; +# print STDERR " -> @args\n"; + if ($param =~ m/^(\*+)(.*)/) { + $param = $2; + push @args, $1; + } + if ($param =~ m/^(.*)(\[\])$/) { + $param = $1; + push @args, $2; + } +# print STDERR " :> @args\n"; + $type = join " ", @args; + + if ($parameters{$param} eq "" && $param != "void") { + $parameters{$param} = "-- undescribed --"; + print STDERR "warning: $lineno: Function parameter '$param' not described in '$function_name'\n"; + } + + push @parameterlist, $param; + $parametertypes{$param} = $type; + +# print STDERR "param = '$param', type = '$type'\n"; + } + } else { + print STDERR "warning: $lineno: Cannot understand prototype: '$prototype'\n"; + return; + } + + if ($function_only==0 || defined($function_table{$function_name})) { + output_function({'function' => $function_name, + 'module' => $modulename, + 'sourceversion' => $sourceversion, + 'include' => $include, + 'includefuncprefix' => $includefuncprefix, + 'bugsto' => $bugsto, + 'pkgname' => $pkgname, + 'copyright' => $copyright, + 'verbatimcopying' => $verbatimcopying, + 'seeinfo' => $seeinfo, + 'functiontype' => $return_type, + 'parameterlist' => \@parameterlist, + 'parameters' => \%parameters, + 'parametertypes' => \%parametertypes, + 'sectionlist' => \@sectionlist, + 'sections' => \%sections, + 'purpose' => $function_purpose + }); + } +} + +###################################################################### +# main +# states +# 0 - normal code +# 1 - looking for function name +# 2 - scanning field start. +# 3 - scanning prototype. +$state = 0; +$section = ""; + +$doc_special = "\@\%\$\#"; + +$doc_start = "^/\\*\\*\$"; +$doc_end = "\\*/"; +$doc_com = "\\s*\\*\\s*"; +$doc_func = $doc_com."(\\w+):?"; +$doc_sect = $doc_com."([".$doc_special."[:upper:]][\\w ]+):\\s*(.*)"; +$doc_content = $doc_com."(.*)"; + +%constants = (); +%parameters = (); +@parameterlist = (); +%sections = (); +@sectionlist = (); + +$contents = ""; +$section_default = "Description"; # default section +$section = $section_default; + +$lineno = 0; +foreach $file (@ARGV) { + if (!open(IN,"<$file")) { + print STDERR "Error: Cannot open file $file\n"; + next; + } + while () { + $lineno++; + + if ($state == 0) { + if (/$doc_start/o) { + $state = 1; # next line is always the function name + } + } elsif ($state == 1) { # this line is the function name (always) + if (/$doc_func/o) { + $function = $1; + $state = 2; + if (/-\s*(.*)/) { + $function_purpose = $1; + } else { + $function_purpose = ""; + } + if ($verbose) { + print STDERR "Info($lineno): Scanning doc for $function\n"; + } + } else { + print STDERR "warning: $lineno: Cannot understand $_ on line $lineno", + " - I thought it was a doc line\n"; + $state = 0; + } + } elsif ($state == 2) { # look for head: lines, and include content + if (/$doc_sect/o) { + $newsection = $1; + $newcontents = $2; + + if ($contents ne "") { + dump_section($section, $contents); + $section = $section_default; + } + + $contents = $newcontents; + if ($contents ne "") { + $contents .= "\n"; + } + $section = $newsection; + } elsif (/$doc_end/) { + + if ($contents ne "") { + dump_section($section, $contents); + $section = $section_default; + $contents = ""; + } + +# print STDERR "end of doc comment, looking for prototype\n"; + $prototype = ""; + $state = 3; + } elsif (/$doc_content/) { + # miguel-style comment kludge, look for blank lines after + # @parameter line to signify start of description + if ($1 eq "" && $section =~ m/^@/) { + dump_section($section, $contents); + $section = $section_default; + $contents = ""; + } else { + $contents .= $1."\n"; + } + } else { + # i dont know - bad line? ignore. + print STDERR "warning: $lineno: Bad line: $_"; + } + } elsif ($state == 3) { # scanning for function { (end of prototype) + if (m#\s*/\*\s+MACDOC\s*#io) { + # do nothing + } + elsif (/([^\{]*)/) { + $prototype .= $1; + } + if (/\{/) { + $prototype =~ s@/\*.*?\*/@@gos; # strip comments. + $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's. + $prototype =~ s@^ +@@gos; # strip leading spaces + dump_function($prototype); + + $function = ""; + %constants = (); + %parameters = (); + %parametertypes = (); + @parameterlist = (); + %sections = (); + @sectionlist = (); + $prototype = ""; + + $state = 0; + } + } + } +} diff --git a/gnuk/chopstx/doc/gpl.texi b/gnuk/chopstx/doc/gpl.texi new file mode 100644 index 0000000000..d13e9e4ab0 --- /dev/null +++ b/gnuk/chopstx/doc/gpl.texi @@ -0,0 +1,732 @@ +@node Copying + +@unnumbered GNU General Public License +@center Version 3, 29 June 2007 + +@c This file is intended to be included in another file. + +@display +Copyright @copyright{} 2007 Free Software Foundation, Inc. @url{http://fsf.org/} + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. +@end display + +@unnumberedsec Preamble + +The GNU General Public License is a free, copyleft license for +software and other kinds of works. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom +to share and change all versions of a program--to make sure it remains +free software for all its users. We, the Free Software Foundation, +use the GNU General Public License for most of our software; it +applies also to any other work released this way by its authors. You +can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you +have certain responsibilities if you distribute copies of the +software, or if you modify it: responsibilities to respect the freedom +of others. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, +receive or can get the source code. And you must show them these +terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + +Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the +manufacturer can do so. This is fundamentally incompatible with the +aim of protecting users' freedom to change the software. The +systematic pattern of such abuse occurs in the area of products for +individuals to use, which is precisely where it is most unacceptable. +Therefore, we have designed this version of the GPL to prohibit the +practice for those products. If such problems arise substantially in +other domains, we stand ready to extend this provision to those +domains in future versions of the GPL, as needed to protect the +freedom of users. + +Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish +to avoid the special danger that patents applied to a free program +could make it effectively proprietary. To prevent this, the GPL +assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and +modification follow. + +@iftex +@unnumberedsec TERMS AND CONDITIONS +@end iftex +@ifinfo +@center TERMS AND CONDITIONS +@end ifinfo + +@enumerate 0 +@item Definitions. + +``This License'' refers to version 3 of the GNU General Public License. + +``Copyright'' also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + +``The Program'' refers to any copyrightable work licensed under this +License. Each licensee is addressed as ``you''. ``Licensees'' and +``recipients'' may be individuals or organizations. + +To ``modify'' a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of +an exact copy. The resulting work is called a ``modified version'' of +the earlier work or a work ``based on'' the earlier work. + +A ``covered work'' means either the unmodified Program or a work based +on the Program. + +To ``propagate'' a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To ``convey'' a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user +through a computer network, with no transfer of a copy, is not +conveying. + +An interactive user interface displays ``Appropriate Legal Notices'' to +the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +@item Source Code. + +The ``source code'' for a work means the preferred form of the work for +making modifications to it. ``Object code'' means any non-source form +of a work. + +A ``Standard Interface'' means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The ``System Libraries'' of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +``Major Component'', in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The ``Corresponding Source'' for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can +regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same +work. + +@item Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, +without conditions so long as your license otherwise remains in force. +You may convey covered works to others for the sole purpose of having +them make modifications exclusively for you, or provide you with +facilities for running those works, provided that you comply with the +terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for +you must do so exclusively on your behalf, under your direction and +control, on terms that prohibit them from making any copies of your +copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + +@item Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such +circumvention is effected by exercising rights under this License with +respect to the covered work, and you disclaim any intention to limit +operation or modification of the work as a means of enforcing, against +the work's users, your or third parties' legal rights to forbid +circumvention of technological measures. + +@item Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +@item Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these +conditions: + +@enumerate a +@item +The work must carry prominent notices stating that you modified it, +and giving a relevant date. + +@item +The work must carry prominent notices stating that it is released +under this License and any conditions added under section 7. This +requirement modifies the requirement in section 4 to ``keep intact all +notices''. + +@item +You must license the entire work, as a whole, under this License to +anyone who comes into possession of a copy. This License will +therefore apply, along with any applicable section 7 additional terms, +to the whole of the work, and all its parts, regardless of how they +are packaged. This License gives no permission to license the work in +any other way, but it does not invalidate such permission if you have +separately received it. + +@item +If the work has interactive user interfaces, each must display +Appropriate Legal Notices; however, if the Program has interactive +interfaces that do not display Appropriate Legal Notices, your work +need not make them do so. +@end enumerate + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +``aggregate'' if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +@item Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of +sections 4 and 5, provided that you also convey the machine-readable +Corresponding Source under the terms of this License, in one of these +ways: + +@enumerate a +@item +Convey the object code in, or embodied in, a physical product +(including a physical distribution medium), accompanied by the +Corresponding Source fixed on a durable physical medium customarily +used for software interchange. + +@item +Convey the object code in, or embodied in, a physical product +(including a physical distribution medium), accompanied by a written +offer, valid for at least three years and valid for as long as you +offer spare parts or customer support for that product model, to give +anyone who possesses the object code either (1) a copy of the +Corresponding Source for all the software in the product that is +covered by this License, on a durable physical medium customarily used +for software interchange, for a price no more than your reasonable +cost of physically performing this conveying of source, or (2) access +to copy the Corresponding Source from a network server at no charge. + +@item +Convey individual copies of the object code with a copy of the written +offer to provide the Corresponding Source. This alternative is +allowed only occasionally and noncommercially, and only if you +received the object code with such an offer, in accord with subsection +6b. + +@item +Convey the object code by offering access from a designated place +(gratis or for a charge), and offer equivalent access to the +Corresponding Source in the same way through the same place at no +further charge. You need not require recipients to copy the +Corresponding Source along with the object code. If the place to copy +the object code is a network server, the Corresponding Source may be +on a different server (operated by you or a third party) that supports +equivalent copying facilities, provided you maintain clear directions +next to the object code saying where to find the Corresponding Source. +Regardless of what server hosts the Corresponding Source, you remain +obligated to ensure that it is available for as long as needed to +satisfy these requirements. + +@item +Convey the object code using peer-to-peer transmission, provided you +inform other peers where the object code and Corresponding Source of +the work are being offered to the general public at no charge under +subsection 6d. + +@end enumerate + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A ``User Product'' is either (1) a ``consumer product'', which means any +tangible personal property which is normally used for personal, +family, or household purposes, or (2) anything designed or sold for +incorporation into a dwelling. In determining whether a product is a +consumer product, doubtful cases shall be resolved in favor of +coverage. For a particular product received by a particular user, +``normally used'' refers to a typical or common use of that class of +product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected +to use, the product. A product is a consumer product regardless of +whether the product has substantial commercial, industrial or +non-consumer uses, unless such uses represent the only significant +mode of use of the product. + +``Installation Information'' for a User Product means any methods, +procedures, authorization keys, or other information required to +install and execute modified versions of a covered work in that User +Product from a modified version of its Corresponding Source. The +information must suffice to ensure that the continued functioning of +the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or +updates for a work that has been modified or installed by the +recipient, or for the User Product in which it has been modified or +installed. Access to a network may be denied when the modification +itself materially and adversely affects the operation of the network +or violates the rules and protocols for communication across the +network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +@item Additional Terms. + +``Additional permissions'' are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders +of that material) supplement the terms of this License with terms: + +@enumerate a +@item +Disclaiming warranty or limiting liability differently from the terms +of sections 15 and 16 of this License; or + +@item +Requiring preservation of specified reasonable legal notices or author +attributions in that material or in the Appropriate Legal Notices +displayed by works containing it; or + +@item +Prohibiting misrepresentation of the origin of that material, or +requiring that modified versions of such material be marked in +reasonable ways as different from the original version; or + +@item +Limiting the use for publicity purposes of names of licensors or +authors of the material; or + +@item +Declining to grant rights under trademark law for use of some trade +names, trademarks, or service marks; or + +@item +Requiring indemnification of licensors and authors of that material by +anyone who conveys the material (or modified versions of it) with +contractual assumptions of liability to the recipient, for any +liability that these contractual assumptions directly impose on those +licensors and authors. +@end enumerate + +All other non-permissive additional terms are considered ``further +restrictions'' within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; the +above requirements apply either way. + +@item Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +@item Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run +a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +@item Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An ``entity transaction'' is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +@item Patents. + +A ``contributor'' is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's ``contributor version''. + +A contributor's ``essential patent claims'' are all patent claims owned +or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, ``control'' includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a ``patent license'' is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To ``grant'' such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. ``Knowingly relying'' means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is ``discriminatory'' if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on +the non-exercise of one or more of the rights that are specifically +granted under this License. You may not convey a covered work if you +are a party to an arrangement with a third party that is in the +business of distributing software, under which you make payment to the +third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties +who would receive the covered work from you, a discriminatory patent +license (a) in connection with copies of the covered work conveyed by +you (or copies made from those copies), or (b) primarily for and in +connection with specific products or compilations that contain the +covered work, unless you entered into that arrangement, or that patent +license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +@item No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey +a covered work so as to satisfy simultaneously your obligations under +this License and any other pertinent obligations, then as a +consequence you may not convey it at all. For example, if you agree +to terms that obligate you to collect a royalty for further conveying +from those to whom you convey the Program, the only way you could +satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +@item Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + +@item Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies that a certain numbered version of the GNU General Public +License ``or any later version'' applies to it, you have the option of +following the terms and conditions either of that numbered version or +of any later version published by the Free Software Foundation. If +the Program does not specify a version number of the GNU General +Public License, you may choose any version ever published by the Free +Software Foundation. + +If the Program specifies that a proxy can decide which future versions +of the GNU General Public License can be used, that proxy's public +statement of acceptance of a version permanently authorizes you to +choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +@item Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM ``AS IS'' WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +@item Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM +TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +@item Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + +@end enumerate + +@iftex +@heading END OF TERMS AND CONDITIONS +@end iftex +@ifinfo +@center END OF TERMS AND CONDITIONS +@end ifinfo + +@unnumberedsec How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the ``copyright'' line and a pointer to where the full notice is +found. + +@example +@var{one line to give the program's name and a brief idea of what it does.} +Copyright (C) @var{year} @var{name of author} + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at +your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see @url{http://www.gnu.org/licenses/}. +@end example + +@noindent +Also add information on how to contact you by electronic and paper mail. + +@noindent +If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + +@smallexample +@var{program} Copyright (C) @var{year} @var{name of author} +This program comes with ABSOLUTELY NO WARRANTY; for details +type @samp{show w}. This is free software, and you are +welcome to redistribute it under certain conditions; +type @samp{show c} for details. +@end smallexample + +The hypothetical commands @samp{show w} and @samp{show c} should show +the appropriate parts of the General Public License. Of course, your +program's commands might be different; for a GUI interface, you would +use an ``about box''. + +You should also get your employer (if you work as a programmer) or school, +if any, to sign a ``copyright disclaimer'' for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +@url{http://www.gnu.org/licenses/}. + +The GNU General Public License does not permit incorporating your +program into proprietary programs. If your program is a subroutine +library, you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use +the GNU Lesser General Public License instead of this License. But +first, please read @url{http://www.gnu.org/philosophy/why-not-lgpl.html}. diff --git a/gnuk/chopstx/entry.c b/gnuk/chopstx/entry.c new file mode 100644 index 0000000000..21225de31f --- /dev/null +++ b/gnuk/chopstx/entry.c @@ -0,0 +1,244 @@ +/* + * entry.c - Entry routine when reset and interrupt vectors. + * + * Copyright (C) 2013, 2014, 2015, 2016, 2017 + * Flying Stone Technology + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx 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. + * + * Chopstx 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 . + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +#include +#include +#include + +#include "board.h" + +#ifdef GNU_LINUX_EMULATION +int emulated_main (int, const char **); +void chx_init (struct chx_thread *); +void chx_systick_init (void); +extern struct chx_thread main_thread; + +int +main (int argc, const char *argv[]) +{ + chx_init (&main_thread); + chx_systick_init (); + emulated_main (argc, argv); +} +#else +#if defined(USE_SYS3) || defined(USE_SYS_CLOCK_GPIO_SETTING) +#define REQUIRE_CLOCK_GPIO_SETTING_IN_SYS +#include "sys.h" +/* + * Avoid medium density specific code and prepare for high density + * device, too. + */ +#undef STM32F10X_MD +#else +#if defined (MCU_KINETIS_L) +#include "mcu/clk_gpio_init-mkl27z.c" +#else +#include "mcu/clk_gpio_init-stm32.c" +#endif +#endif + + +#ifdef MAKE_ENTRY_PUBLIC +#define STATIC_ENTRY +#else +#define STATIC_ENTRY static +#endif + +extern uint8_t __main_stack_end__; +#if defined(__ARM_ARCH_7M__) +extern void svc (void); +#endif +extern void preempt (void); +extern void chx_timer_expired (void); +extern void chx_handle_intr (void); + +static void nmi (void) +{ + for (;;); +} + +static void hard_fault (void) +{ + for (;;); +} + +static void mem_manage (void) +{ + for (;;); +} + +static void bus_fault (void) +{ + for (;;); +} + +static void usage_fault (void) +{ + for (;;); +} + +static void none (void) +{ +} + +#define C_S_SUB(arg0, arg1, arg2) arg0 #arg1 arg2 +#define COMPOSE_STATEMENT(arg0,arg1,arg2) C_S_SUB (arg0, arg1, arg2) + +#if defined(__ARM_ARCH_6M__) +__attribute__ ((used,section(".bss.startup.0"))) +uint32_t vectors_in_ram[48]; +#endif + +/* + * This routine only changes PSP and not MSP. + */ +STATIC_ENTRY __attribute__ ((naked,section(".text.startup.0"))) +void +entry (void) +{ + asm volatile ("bl clock_init\n\t" + /* Clear BSS section. */ + "mov r0, #0\n\t" + "ldr r1, =_bss_start\n\t" + "ldr r2, =_bss_end\n" + "0:\n\t" + "cmp r1, r2\n\t" + "beq 1f\n\t" +#if defined(__ARM_ARCH_6M__) + "str r0, [r1]\n\t" + "add r1, #4\n\t" +#else + "str r0, [r1], #4\n\t" +#endif + "b 0b\n" + "1:\n\t" + /* Copy data section. */ + "ldr r1, =_data\n\t" + "ldr r2, =_edata\n\t" + "ldr r3, =_textdata\n" + "2:\n\t" + "cmp r1, r2\n\t" + "beq 3f\n\t" +#if defined(__ARM_ARCH_6M__) + "ldr r0, [r3]\n\t" + "str r0, [r1]\n\t" + "add r3, #4\n\t" + "add r1, #4\n\t" +#else + "ldr r0, [r3], #4\n\t" + "str r0, [r1], #4\n\t" +#endif + "b 2b\n" + "3:\n\t" + /* Switch to PSP. */ + "ldr r0, =__process0_stack_end__\n\t" + COMPOSE_STATEMENT ("sub r0, #", CHOPSTX_THREAD_SIZE, "\n\t") + "msr PSP, r0\n\t" /* Process (main routine) stack. */ + "mov r1, #2\n\t" + "msr CONTROL, r1\n\t" + "isb\n\t" + "bl chx_init\n\t" + "bl chx_systick_init\n\t" + "bl gpio_init\n\t" + /* Enable interrupts. */ +#if defined(__ARM_ARCH_7M__) + "mov r0, #0\n\t" + "msr BASEPRI, r0\n\t" +#endif + "cpsie i\n\t" + /* Call main. */ + "mov r1, r0\n\t" + "bl main\n" + "4:\n\t" + "b 4b" + : /* no output */ : /* no input */ : "memory"); +} + + +typedef void (*handler)(void); + +handler vector_table[] __attribute__ ((section(".startup.vectors"))) = { + (handler)(&__main_stack_end__ - 32), + entry, + nmi, /* nmi */ + hard_fault, /* hard fault */ + /* 0x10 */ + mem_manage, /* mem manage */ + bus_fault, /* bus fault */ + usage_fault, /* usage fault */ + none, + /* 0x20 */ + none, none, none, /* reserved */ +#if defined(__ARM_ARCH_6M__) + none, /* SVCall */ +#elif defined(__ARM_ARCH_7M__) + svc, /* SVCall */ +#endif + none, /* Debug */ + none, /* reserved */ + preempt, /* PendSV */ + chx_timer_expired, /* SysTick */ + /* 0x40 */ + chx_handle_intr /* WWDG */, chx_handle_intr /* PVD */, + chx_handle_intr /* TAMPER */, chx_handle_intr /* RTC */, + chx_handle_intr /* FLASH */, chx_handle_intr /* RCC */, + chx_handle_intr /* EXTI0 */, chx_handle_intr /* EXTI1 */, + /* 0x60 */ + chx_handle_intr /* EXTI2 */, chx_handle_intr /* EXTI3 */, + chx_handle_intr /* EXTI4 */, chx_handle_intr /* DMA1 CH1 */, + chx_handle_intr /* DMA1 CH2 */, chx_handle_intr /* DMA1 CH3 */, + chx_handle_intr /* DMA1 CH4 */, chx_handle_intr /* DMA1 CH5 */, + /* 0x80 */ + chx_handle_intr /* DMA1 CH6 */, chx_handle_intr /* DMA1 CH7 */, + chx_handle_intr /* ADC1_2 */, chx_handle_intr /* USB HP */, + /* 0x90 */ + chx_handle_intr /* USB LP */, chx_handle_intr /* CAN RX1 */, + chx_handle_intr /* CAN SCE */, chx_handle_intr /* EXT9_5 */, + /* 0xa0 */ + /* ... and more. TIMx, I2C, SPI, USART... */ + chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr, + chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr, + /* 0xc0 */ +#if !defined(__ARM_ARCH_6M__) + /* STM32F0 doesn't have more. */ + chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr, + chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr, + /* 0xe0 */ + chx_handle_intr /* EXT15_10 */, chx_handle_intr /* RTCAlarm */, + chx_handle_intr /* USBWakeup */, chx_handle_intr, +#endif +#if !defined(STM32F10X_MD) + /* High-density chips have more; ... DMA2_Channel4_5 */ + chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr, + chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr, + chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr, + chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr, +#endif +}; +#endif diff --git a/gnuk/chopstx/eventflag.c b/gnuk/chopstx/eventflag.c new file mode 100644 index 0000000000..c37b303a33 --- /dev/null +++ b/gnuk/chopstx/eventflag.c @@ -0,0 +1,145 @@ +/* + * eventflag.c - Eventflag + * + * Copyright (C) 2013, 2016, 2018 Flying Stone Technology + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx 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. + * + * Chopstx 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 . + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +#include +#include +#include +#include + + +void +eventflag_init (struct eventflag *ev) +{ + ev->flags = 0; + ev->mask = ~0; + chopstx_cond_init (&ev->cond); + chopstx_mutex_init (&ev->mutex); +} + + +void +eventflag_set_mask (struct eventflag *ev, eventmask_t m) +{ + chopstx_mutex_lock (&ev->mutex); + ev->mask = m; + if ((ev->flags & ev->mask)) + chopstx_cond_signal (&ev->cond); + chopstx_mutex_unlock (&ev->mutex); +} + + +static int +eventflag_check (void *arg) +{ + struct eventflag *ev = arg; + + return (ev->flags & ev->mask) != 0; +} + + +void +eventflag_prepare_poll (struct eventflag *ev, chopstx_poll_cond_t *poll_desc) +{ + poll_desc->type = CHOPSTX_POLL_COND; + poll_desc->ready = 0; + poll_desc->cond = &ev->cond; + poll_desc->mutex = &ev->mutex; + poll_desc->check = eventflag_check; + poll_desc->arg = ev; +} + + +/* When multiple events are marked, event with lower bit has precedence. + Because __builtin_ffs returns the least significant 1-bit. */ + +eventmask_t +eventflag_get (struct eventflag *ev) +{ + int n; + eventmask_t m; + + chopstx_mutex_lock (&ev->mutex); + n = __builtin_ffs ((ev->flags & ev->mask)); + if (n) + { + m = (1 << (n - 1)); + ev->flags &= ~m; + } + else + m = 0; + chopstx_mutex_unlock (&ev->mutex); + + return m; +} + + +eventmask_t +eventflag_wait (struct eventflag *ev) +{ + int n; + eventmask_t m; + + chopstx_mutex_lock (&ev->mutex); + while (!(ev->flags & ev->mask)) + chopstx_cond_wait (&ev->cond, &ev->mutex); + + n = __builtin_ffs ((ev->flags & ev->mask)); + if (n) /* Always n > 0 when waked up, but make sure no bad things. */ + { + m = (1 << (n - 1)); + ev->flags &= ~m; + } + else + m = 0; + chopstx_mutex_unlock (&ev->mutex); + + return m; +} + + +eventmask_t +eventflag_wait_timeout (struct eventflag *ev, uint32_t usec) +{ + chopstx_poll_cond_t poll_desc; + struct chx_poll_head *pd_array[1] = { (struct chx_poll_head *)&poll_desc }; + + eventflag_prepare_poll (ev, &poll_desc); + chopstx_poll (&usec, 1, pd_array); + return eventflag_get (ev); +} + + +void +eventflag_signal (struct eventflag *ev, eventmask_t m) +{ + chopstx_mutex_lock (&ev->mutex); + ev->flags |= m; + if ((ev->flags & ev->mask)) + chopstx_cond_signal (&ev->cond); + chopstx_mutex_unlock (&ev->mutex); +} diff --git a/gnuk/chopstx/eventflag.h b/gnuk/chopstx/eventflag.h new file mode 100644 index 0000000000..7a9c145568 --- /dev/null +++ b/gnuk/chopstx/eventflag.h @@ -0,0 +1,18 @@ +typedef uint32_t eventmask_t; + +struct eventflag { + eventmask_t flags; + eventmask_t mask; + chopstx_mutex_t mutex; + chopstx_cond_t cond; +}; + +void eventflag_init (struct eventflag *ev); +void eventflag_set_mask (struct eventflag *ev, eventmask_t m); +eventmask_t eventflag_wait (struct eventflag *ev); +eventmask_t eventflag_wait_timeout (struct eventflag *ev, uint32_t usec); +void eventflag_signal (struct eventflag *ev, eventmask_t m); + +/* For polling */ +void eventflag_prepare_poll (struct eventflag *ev, chopstx_poll_cond_t *p); +eventmask_t eventflag_get (struct eventflag *ev); diff --git a/gnuk/chopstx/example-cdc-gnu-linux/Makefile b/gnuk/chopstx/example-cdc-gnu-linux/Makefile new file mode 100644 index 0000000000..28896aa18a --- /dev/null +++ b/gnuk/chopstx/example-cdc-gnu-linux/Makefile @@ -0,0 +1,43 @@ +# Makefile for example application of Chopstx + +PROJECT = sample + +### This is for GNU/Linux + +CHOPSTX = .. +LDSCRIPT= +CSRC = sample.c usb-cdc.c command.c + +CHIP=gnu-linux +USE_SYS = yes +USE_USB = yes +USE_ADC = yes +EMULATION=yes + +################################### +CROSS = +CC = $(CROSS)gcc +LD = $(CROSS)gcc +OBJCOPY = $(CROSS)objcopy + +MCU = none +CWARN = -Wall -Wextra -Wstrict-prototypes +DEFS = -DGNU_LINUX_EMULATION -DUSE_SYS_BOARD_ID +OPT = -g # -O3 -Os +LIBS = -lpthread + +#################### +include ../rules.mk + +board.h: + @echo Please make a symbolic link \'board.h\' to a file in ../board; + @exit 1 + +distclean: clean + +build/flash.data: Makefile + @echo 'Generating 8192-byte flash.data' + @/bin/echo -n -e '\xff\xff\xff\xff\xff\xff\xff\xff' >$@ + @for i in $(shell seq 1023); do /bin/echo -n -e '\xff\xff\xff\xff\xff\xff\xff\xff' >>$@; done + + diff --git a/gnuk/chopstx/example-cdc-gnu-linux/README b/gnuk/chopstx/example-cdc-gnu-linux/README new file mode 100644 index 0000000000..d726009737 --- /dev/null +++ b/gnuk/chopstx/example-cdc-gnu-linux/README @@ -0,0 +1,41 @@ +(0) Build and generate initial flash data + +$ make +$ make build/flash.data + + +(1) preparation as root + +Don't run ModemManager + +# systemctl stop ModemManager + +Install USBIP host module +# modprobe vhci_hcd + + +(2) Run sample program of USBIP server + +$ ./sample + + +(3) Use the USB device as root + +Attach the Chopstx application program (USBIP device) to USBIP host. + +# usbip attach -r 127.0.0.1 -b 1-1 + + +(4) Connect CDC-ACM device + +$ cu -l /dev/ttyACM0 + +Then, you see the message from Chopstx. As you type lines, it echos. + +Type RET, ~ then . , you can terminate the session. + + +(5) Detach the USBIP device as root + +# usbip detach -p 0 + diff --git a/gnuk/chopstx/example-cdc-gnu-linux/board.h b/gnuk/chopstx/example-cdc-gnu-linux/board.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gnuk/chopstx/example-cdc-gnu-linux/command.c b/gnuk/chopstx/example-cdc-gnu-linux/command.c new file mode 100644 index 0000000000..9c14b0852c --- /dev/null +++ b/gnuk/chopstx/example-cdc-gnu-linux/command.c @@ -0,0 +1,579 @@ +#include +#include +#include +#include "tty.h" +#include "config.h" +#ifdef ADC_SUPPORT +#include "adc.h" +static int adc_initialized = 0; +#endif +#include "board.h" +#include "sys.h" + +struct command_table +{ + const char *name; + void (*handler) (struct tty *tty, const char *line); +}; + +/* + * Put a line (or lines) to TTY. + * LINE should be terminated with newline. + */ +static void +put_line (struct tty *tty, const char *line) +{ + tty_send (tty, line, strlen (line)); +} + +static const char *help_string = + "mdb ADDR [COUNT]; memory display byte\r\n" + "mwh ADDR VALUE [COUNT]; memory write halfword\r\n" + "fes ADDR [COUNT]; flash erase sector\r\n" + "fwh ADDR VALUE [COUNT]; flash write halfword\r\n" +#ifdef CRC32_SUPPORT + "crc32 string; CRC32 calc string\r\n" +#endif +#ifdef ADC_SUPPORT + "adc; get 256-byte from ADC\r\n" +#endif + "sysinfo; system information\r\n" + "help\r\n"; + +static char hexchar (uint8_t x) +{ + x &= 0x0f; + if (x <= 0x09) + return '0' + x; + else if (x <= 0x0f) + return 'a' + x - 10; + else + return '?'; +} + +#ifdef TOUCH_SUPPORT +static char * +compose_decimal (char *s, int value) +{ + uint32_t v; + int col = 1000000000; + int d; + int digit_output = 0; + + if (value < 0) + { + *s++ = '-'; + v = 1 + ~((uint32_t)value); + } + else + v = (uint32_t)value; + + while (col >= 10) + { + if (v >= (uint32_t)col) + { + d = v / col; + v = v - d * col; + *s++ = d + '0'; + digit_output = 1; + } + else if (digit_output) + *s++ = '0'; + + col = col / 10; + } + + *s++ = v + '0'; + + return s; +} +#endif + + +char * +compose_hex_ptr (char *s, uintptr_t v) +{ + s[0] = hexchar (v >> 60); + s[1] = hexchar (v >> 56); + s[2] = hexchar (v >> 52); + s[3] = hexchar (v >> 48); + s[4] = hexchar (v >> 44); + s[5] = hexchar (v >> 40); + s[6] = hexchar (v >> 36); + s[7] = hexchar (v >> 32); + s[8] = hexchar (v >> 28); + s[9] = hexchar (v >> 24); + s[10] = hexchar (v >> 20); + s[11] = hexchar (v >> 16); + s[12] = hexchar (v >> 12); + s[13] = hexchar (v >> 8); + s[14] = hexchar (v >> 4); + s[15] = hexchar (v); + return s+16; +} + +static char * +compose_hex (char *s, uint32_t v) +{ + s[0] = hexchar (v >> 28); + s[1] = hexchar (v >> 24); + s[2] = hexchar (v >> 20); + s[3] = hexchar (v >> 16); + s[4] = hexchar (v >> 12); + s[5] = hexchar (v >> 8); + s[6] = hexchar (v >> 4); + s[7] = hexchar (v); + return s+8; +} + +static char * +compose_hex_byte (char *s, uint8_t v) +{ + s[0] = hexchar (v >> 4); + s[1] = hexchar (v); + return s+2; +} + +static const char * +get_hex (struct tty *tty, const char *s, uintptr_t *v_p) +{ + uintptr_t v = 0; + char c; + + if (s[0] == '0' && s[1] == 'x') + s = s + 2; + while (1) + { + c = *s++; + + if (c == 0) + { + s--; + break; + } + + if (c == ' ') + break; + + v = (v << 4); + if (c >= '0' && c <= '9') + v += (c - '0'); + else if (c >= 'a' && c <= 'f') + v += (c - 'a') + 10; + else if (c >= 'A' && c <= 'F') + v += (c - 'A') + 10; + else + { + put_line (tty, "hex error\r\n"); + return NULL; + } + } + + *v_p = v; + return s; +} + + +#ifdef TOUCH_SUPPORT +#define TOUCH_VALUE_HIGH 100 +#define TOUCH_VALUE_LOW 50 +static void +cmd_button (struct tty *tty, const char *line) +{ + int i = 0; + extern uint16_t touch_get (void); + uint16_t v0 = 0; + int touched = 0; + + (void)line; + put_line (tty, "Please touch the bear.\r\n"); + + while (i < 16) + { + uint16_t v = touch_get (); + v0 = (v0 * 2 + v)/3; + + if (touched == 0 && v0 > TOUCH_VALUE_HIGH) + { + tty_send (tty, "!", 1); + touched = 1; + } + else if (touched == 1 && v0 < TOUCH_VALUE_LOW) + { + tty_send (tty, ".", 1); + touched = 0; + i++; + } + + chopstx_usec_wait (10*1000); + } + + tty_send (tty, "\r\n", 2); +} + +static void +cmd_touch (struct tty *tty, const char *line) +{ + int i; + extern uint16_t touch_get (void); + + (void)line; + put_line (tty, "Please touch the bear.\r\n"); + + for (i = 0; i < 20; i++) + { + uint16_t v; + char output[8]; + char *s; + + chopstx_usec_wait (1000*1000); + v = touch_get (); + s = compose_decimal (output, v); + *s++ = '\r'; + *s++ = '\n'; + tty_send (tty, output, s - output); + } +} +#endif + + +static void +cmd_mdb (struct tty *tty, const char *line) +{ + int i; + uintptr_t addr = 0; + int count = 0; + char c; + const char *s = line; + + s = get_hex (tty, s, &addr); + addr &= ~3; + if (s == NULL) + return; + + if (*s == 0) + count = 1; + else + { + while (1) + { + c = *s++; + + if (c == 0 || c == ' ') + break; + + count = count * 10; + if (c >= '0' && c <= '9') + count += c - '0'; + else + { + put_line (tty, "mdb error\r\n"); + return; + } + } + } + + i = 0; + while (i < count) + { + uint8_t v; + char output[68]; + char *s; + + s = compose_hex_ptr (output, addr); + *s++ = ':'; + *s++ = ' '; + + while (1) + { + v = *(uint8_t *)addr; + s = compose_hex_byte (s, v); + i++; + addr += 1; + if (i >= count || (i % 16) == 0) + break; + *s++ = ' '; + } + + *s++ = '\r'; + *s++ = '\n'; + tty_send (tty, output, s - output); + } +} + +static void +cmd_mwh (struct tty *tty, const char *line) +{ + (void)tty; + (void)line; + put_line (tty, "mwh not yet supported\r\n"); +} + +static void +cmd_fes (struct tty *tty, const char *line) +{ + int i; + uintptr_t addr = 0; + int count = 0; + char c; + const char *s = line; + + s = get_hex (tty, s, &addr); + if (s == NULL) + return; + + if (*s == 0) + count = 1; + else + { + while (1) + { + c = *s++; + + if (c == 0 || c == ' ') + break; + + count = count * 10; + if (c >= '0' && c <= '9') + count += c - '0'; + else + { + put_line (tty, "fww error\r\n"); + return; + } + } + } + + for (i = 0; i < count; i++) + { + flash_erase_page (addr); + addr += 1024; + } +} + +static void +cmd_fwh (struct tty *tty, const char *line) +{ + int i; + uintptr_t addr = 0; + uintptr_t d; + uint16_t value = 0; + int count = 0; + char c; + const char *s = line; + + s = get_hex (tty, s, &addr); + if (s == NULL) + return; + + if (*s == 0) + return; + + s = get_hex (tty, s, &d); + value = (uint16_t)d; + if (s == NULL) + return; + + if (*s == 0) + count = 1; + else + { + while (1) + { + c = *s++; + + if (c == 0 || c == ' ') + break; + + count = count * 10; + if (c >= '0' && c <= '9') + count += c - '0'; + else + { + put_line (tty, "fww error\r\n"); + return; + } + } + } + + for (i = 0; i < count; i++) + { + flash_program_halfword (addr, value); + addr += 4; + } +} + + +#ifdef CRC32_SUPPORT +static unsigned int crc_value; + +static void +cmd_crc32 (struct tty *tty, const char *line) +{ + uint32_t v; + char string[10]; + char *s; + + crc32_init (&crc_value); + while (*line) + crc32_u8 (&crc_value, *line++); + v = crc_value ^ 0xffffffff; + + s = compose_hex (string, v); + *s++ = '\r'; + *s++ = '\n'; + tty_send (tty, string, sizeof (string)); +} +#endif + +#ifdef ADC_SUPPORT +static void +cmd_adc (struct tty *tty, const char *line) +{ + int i; + char output[73]; + char *s; + + (void)line; + + if (!adc_initialized) + { + if (adc_init ()) + { + put_line (tty, "adc_init error\r\n"); + return; + } + else + { + adc_start (); + adc_initialized = 1; + } + } + + adc_start_conversion (0, 64); + adc_wait_completion (); + + i = 0; + s = output; + while (1) + { + s = compose_hex (s, adc_buf[i]); + i++; + if ((i % 8)) + *s++ = ' '; + else + { + *s++ = '\r'; + *s++ = '\n'; + tty_send (tty, output, s - output); + s = output; + if (i >= 64) + break; + } + } +} +#endif + +static void +cmd_sysinfo (struct tty *tty, const char *line) +{ + char output[73]; + char *s; + int i; + + (void)line; + memcpy (output, "SYS version: ", 13); + s = output + 13; + *s++ = sys_version[2]; + *s++ = sys_version[4]; + *s++ = sys_version[6]; + *s++ = '\r'; + *s++ = '\n'; + tty_send (tty, output, s - output); + + memcpy (output, "Board ID: ", 10); + s = output + 10; + s = compose_hex (s, sys_board_id); + *s++ = '\r'; + *s++ = '\n'; + tty_send (tty, output, s - output); + + memcpy (output, "Board name: ", 12); + s = output + 12; + for (i = 0; i < (int)sizeof (output) - 2; i ++) + if ((*s = sys_board_name[i]) == 0) + break; + else + s++; + + *s++ = '\r'; + *s++ = '\n'; + tty_send (tty, output, s - output); +} + + +static void +cmd_help (struct tty *tty, const char *line) +{ + (void)line; + put_line (tty, help_string); +} + + +struct command_table command_table[] = { +#ifdef TOUCH_SUPPORT + { "button", cmd_button }, + { "touch", cmd_touch }, +#endif + { "mdb", cmd_mdb }, + { "mwh", cmd_mwh }, + { "fes", cmd_fes }, + { "fwh", cmd_fwh }, +#ifdef CRC32_SUPPORT + { "crc32", cmd_crc32 }, +#endif +#ifdef ADC_SUPPORT + { "adc", cmd_adc }, +#endif + { "sysinfo", cmd_sysinfo }, + { "help", cmd_help }, +}; + +#define N_CMDS (int)(sizeof (command_table) / sizeof (struct command_table)) + + +/* + * Dispatch a command parsing LINE. + * Line is NULL terminated with no newline. + */ +void +cmd_dispatch (struct tty *tty, const char *line) +{ + int i; + const char *p; + unsigned int n = 0; + + p = line; + while (*p) + { + if (*p++ == ' ') + break; + n++; + } + + for (i = 0; i < N_CMDS; i++) + if (n == strlen (command_table[i].name) + && strncmp ((const char *)line, command_table[i].name, n) == 0) + break; + + if (i != N_CMDS) + (*command_table[i].handler) (tty, p); + else + { + char crlf[] = { '\r', '\n' }; + + put_line (tty, "No such command: "); + tty_send (tty, line, n); + tty_send (tty, crlf, sizeof (crlf)); + } +} diff --git a/gnuk/chopstx/example-cdc-gnu-linux/command.h b/gnuk/chopstx/example-cdc-gnu-linux/command.h new file mode 100644 index 0000000000..f4309b8c8f --- /dev/null +++ b/gnuk/chopstx/example-cdc-gnu-linux/command.h @@ -0,0 +1,2 @@ +void cmd_dispatch (struct tty *tty, const char *line); +char * compose_hex_ptr (char *s, uintptr_t v); diff --git a/gnuk/chopstx/example-cdc-gnu-linux/config.h b/gnuk/chopstx/example-cdc-gnu-linux/config.h new file mode 100644 index 0000000000..3305d04e9b --- /dev/null +++ b/gnuk/chopstx/example-cdc-gnu-linux/config.h @@ -0,0 +1,2 @@ +#undef CRC32_SUPPORT +#define ADC_SUPPORT diff --git a/gnuk/chopstx/example-cdc-gnu-linux/sample.c b/gnuk/chopstx/example-cdc-gnu-linux/sample.c new file mode 100644 index 0000000000..02d394ddc6 --- /dev/null +++ b/gnuk/chopstx/example-cdc-gnu-linux/sample.c @@ -0,0 +1,197 @@ +#include +#include +#include +#include + +#include + +#include "sys.h" + +#include "usb_lld.h" +#include "tty.h" +#include "command.h" + +#include +#include + +static chopstx_mutex_t mtx; +static chopstx_cond_t cnd0; +static chopstx_cond_t cnd1; + +static uint8_t u, v; +static uint8_t m; /* 0..100 */ + +static void * +pwm (void *arg) +{ + (void)arg; + + chopstx_mutex_lock (&mtx); + chopstx_cond_wait (&cnd0, &mtx); + chopstx_mutex_unlock (&mtx); + + while (1) + { + set_led (u&v); + chopstx_usec_wait (m); + set_led (0); + chopstx_usec_wait (100-m); + } + + return NULL; +} + +static void * +blk (void *arg) +{ + (void)arg; + + chopstx_mutex_lock (&mtx); + chopstx_cond_wait (&cnd1, &mtx); + chopstx_mutex_unlock (&mtx); + + while (1) + { + v = 0; + chopstx_usec_wait (200*1000); + v = 1; + chopstx_usec_wait (200*1000); + } + + return NULL; +} + + +#define PRIO_PWM 3 +#define PRIO_BLK 2 + +static char __process1_stack_base__[4096]; +static char __process2_stack_base__[4096]; + +#define STACK_ADDR_PWM ((uintptr_t)__process1_stack_base__) +#define STACK_SIZE_PWM (sizeof __process1_stack_base__) + +#define STACK_ADDR_BLK ((uintptr_t)__process2_stack_base__) +#define STACK_SIZE_BLK (sizeof __process2_stack_base__) + + +static char hexchar (uint8_t x) +{ + x &= 0x0f; + if (x <= 0x09) + return '0' + x; + else if (x <= 0x0f) + return 'a' + x - 10; + else + return '?'; +} + + +#ifdef GNU_LINUX_EMULATION +#define main emulated_main +#endif + +int +main (int argc, const char *argv[]) +{ + struct tty *tty; + uint8_t count; + uintptr_t addr; + + if (argc >= 2 && !strncmp (argv[1], "--debug=", 8)) + debug = strtol (&argv[1][8], NULL, 10); + + chopstx_mutex_init (&mtx); + chopstx_cond_init (&cnd0); + chopstx_cond_init (&cnd1); + + m = 10; + + chopstx_create (PRIO_PWM, STACK_ADDR_PWM, STACK_SIZE_PWM, pwm, NULL); + chopstx_create (PRIO_BLK, STACK_ADDR_BLK, STACK_SIZE_BLK, blk, NULL); + + chopstx_usec_wait (200*1000); + + chopstx_mutex_lock (&mtx); + chopstx_cond_signal (&cnd0); + chopstx_cond_signal (&cnd1); + chopstx_mutex_unlock (&mtx); + + addr = flash_init ("flash.data"); + flash_unlock (); + + u = 1; + + tty = tty_open (); + tty_wait_configured (tty); + + count = 0; + m = 50; + while (1) + { + char s[LINEBUFSIZE]; + + connection_loop: + u = 1; + tty_wait_connection (tty); + + chopstx_usec_wait (50*1000); + + puts("send ZLP"); + /* Send ZLP at the beginning. */ + tty_send (tty, s, 0); + + memcpy (s, "xx: Hello, World with Chopstx!\r\n", 32); + s[0] = hexchar (count >> 4); + s[1] = hexchar (count & 0x0f); + + puts("send hello"); + if (tty_send (tty, s, 32) < 0) + continue; + + s[0] = hexchar (count >> 4); + s[1] = hexchar (count & 0x0f); + s[2] = ':'; + s[3] = ' '; + compose_hex_ptr (s+4, addr); + s[20] = '\r'; + s[21] = '\n'; + + count++; + + if (tty_send (tty, s, 22) < 0) + continue; + + while (1) + { + uint32_t usec; + + /* Prompt */ + if (tty_send (tty, "> ", 2) < 0) + break; + + usec = 3000000; /* 3.0 seconds */ + while (1) + { + int size = tty_recv (tty, s, &usec); + u ^= 1; + + if (size < 0) + goto connection_loop; + + if (size == 1) + /* Do nothing but prompt again. */ + break; + else if (size) + { + /* Newline into NUL */ + s[size - 1] = 0; + cmd_dispatch (tty, (char *)s); + break; + } + } + } + } + + return 0; +} diff --git a/gnuk/chopstx/example-cdc-gnu-linux/tty.h b/gnuk/chopstx/example-cdc-gnu-linux/tty.h new file mode 100644 index 0000000000..77e7f463c0 --- /dev/null +++ b/gnuk/chopstx/example-cdc-gnu-linux/tty.h @@ -0,0 +1,9 @@ +#define LINEBUFSIZE 128 + +struct tty; + +struct tty *tty_open (void); +void tty_wait_configured (struct tty *tty); +void tty_wait_connection (struct tty *tty); +int tty_send (struct tty *tty, const char *buf, int count); +int tty_recv (struct tty *tty, char *buf, uint32_t *timeout); diff --git a/gnuk/chopstx/example-cdc-gnu-linux/usb-cdc.c b/gnuk/chopstx/example-cdc-gnu-linux/usb-cdc.c new file mode 100644 index 0000000000..90b27c5900 --- /dev/null +++ b/gnuk/chopstx/example-cdc-gnu-linux/usb-cdc.c @@ -0,0 +1,952 @@ +#include +#include +#include +#include +#include "usb_lld.h" +#include "tty.h" + +static chopstx_intr_t usb_intr; + +struct line_coding +{ + uint32_t bitrate; + uint8_t format; + uint8_t paritytype; + uint8_t datatype; +} __attribute__((packed)); + +static const struct line_coding line_coding0 = { + 115200, /* baud rate: 115200 */ + 0x00, /* stop bits: 1 */ + 0x00, /* parity: none */ + 0x08 /* bits: 8 */ +}; + +/* + * Currently, we only support a single TTY. + * + * It is possible to extend to support multiple TTYs, for multiple + * interfaces. + * + * In that case, add argument to TTY_OPEN function and + * modify TTY_GET function to get the TTY structure. Functions which + * directy accesses TTY0 (usb_device_reset and usb_set_configuration) + * should be modified, too. + * + * Modification of TTY_MAIN thread will be also needed to echo back + * input for each TTY, and the thread should run if one of TTY is + * opened. + */ + +struct tty { + chopstx_mutex_t mtx; + chopstx_cond_t cnd; + uint8_t inputline[LINEBUFSIZE]; /* Line editing is supported */ + uint8_t send_buf[LINEBUFSIZE]; /* Sending ring buffer for echo back */ + uint8_t send_buf0[64]; + uint8_t recv_buf0[64]; + uint32_t inputline_len : 8; + uint32_t send_head : 8; + uint32_t send_tail : 8; + uint32_t flag_connected : 1; + uint32_t flag_send_ready : 1; + uint32_t flag_input_avail : 1; + uint32_t : 2; + uint32_t device_state : 3; /* USB device status */ + struct line_coding line_coding; +}; + +static struct tty tty0; + +/* + * Locate TTY structure from interface number or endpoint number. + * Currently, it always returns tty0, because we only have the one. + */ +static struct tty * +tty_get (int interface, uint8_t ep_num) +{ + struct tty *t = &tty0; + + if (interface >= 0) + { + if (interface == 0) + t = &tty0; + } + else + { + if (ep_num == ENDP1 || ep_num == ENDP2 || ep_num == ENDP3) + t = &tty0; + } + + return t; +} + + +#define USB_CDC_REQ_SET_LINE_CODING 0x20 +#define USB_CDC_REQ_GET_LINE_CODING 0x21 +#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22 +#define USB_CDC_REQ_SEND_BREAK 0x23 + +/* USB Device Descriptor */ +static const uint8_t vcom_device_desc[18] = { + 18, /* bLength */ + DEVICE_DESCRIPTOR, /* bDescriptorType */ + 0x10, 0x01, /* bcdUSB = 1.1 */ + 0x02, /* bDeviceClass (CDC). */ + 0x00, /* bDeviceSubClass. */ + 0x00, /* bDeviceProtocol. */ + 0x40, /* bMaxPacketSize. */ + 0xFF, 0xFF, /* idVendor */ + 0x01, 0x00, /* idProduct */ + 0x00, 0x01, /* bcdDevice */ + 1, /* iManufacturer. */ + 2, /* iProduct. */ + 3, /* iSerialNumber. */ + 1 /* bNumConfigurations. */ +}; + +#define VCOM_FEATURE_BUS_POWERED 0x80 + +/* Configuration Descriptor tree for a CDC.*/ +static const uint8_t vcom_config_desc[67] = { + 9, + CONFIG_DESCRIPTOR, /* bDescriptorType: Configuration */ + /* Configuration Descriptor.*/ + 67, 0x00, /* wTotalLength. */ + 0x02, /* bNumInterfaces. */ + 0x01, /* bConfigurationValue. */ + 0, /* iConfiguration. */ + VCOM_FEATURE_BUS_POWERED, /* bmAttributes. */ + 50, /* bMaxPower (100mA). */ + /* Interface Descriptor.*/ + 9, + INTERFACE_DESCRIPTOR, + 0x00, /* bInterfaceNumber. */ + 0x00, /* bAlternateSetting. */ + 0x01, /* bNumEndpoints. */ + 0x02, /* bInterfaceClass (Communications Interface Class, + CDC section 4.2). */ + 0x02, /* bInterfaceSubClass (Abstract Control Model, CDC + section 4.3). */ + 0x01, /* bInterfaceProtocol (AT commands, CDC section + 4.4). */ + 0, /* iInterface. */ + /* Header Functional Descriptor (CDC section 5.2.3).*/ + 5, /* bLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x00, /* bDescriptorSubtype (Header Functional Descriptor). */ + 0x10, 0x01, /* bcdCDC. */ + /* Call Management Functional Descriptor. */ + 5, /* bFunctionLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x01, /* bDescriptorSubtype (Call Management Functional + Descriptor). */ + 0x03, /* bmCapabilities (D0+D1). */ + 0x01, /* bDataInterface. */ + /* ACM Functional Descriptor.*/ + 4, /* bFunctionLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x02, /* bDescriptorSubtype (Abstract Control Management + Descriptor). */ + 0x02, /* bmCapabilities. */ + /* Union Functional Descriptor.*/ + 5, /* bFunctionLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x06, /* bDescriptorSubtype (Union Functional + Descriptor). */ + 0x00, /* bMasterInterface (Communication Class + Interface). */ + 0x01, /* bSlaveInterface0 (Data Class Interface). */ + /* Endpoint 2 Descriptor.*/ + 7, + ENDPOINT_DESCRIPTOR, + ENDP2|0x80, /* bEndpointAddress. */ + 0x03, /* bmAttributes (Interrupt). */ + 0x08, 0x00, /* wMaxPacketSize. */ + 0xFF, /* bInterval. */ + /* Interface Descriptor.*/ + 9, + INTERFACE_DESCRIPTOR, /* bDescriptorType: */ + 0x01, /* bInterfaceNumber. */ + 0x00, /* bAlternateSetting. */ + 0x02, /* bNumEndpoints. */ + 0x0A, /* bInterfaceClass (Data Class Interface, CDC section 4.5). */ + 0x00, /* bInterfaceSubClass (CDC section 4.6). */ + 0x00, /* bInterfaceProtocol (CDC section 4.7). */ + 0x00, /* iInterface. */ + /* Endpoint 3 Descriptor.*/ + 7, + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + ENDP3, /* bEndpointAddress. */ + 0x02, /* bmAttributes (Bulk). */ + 0x40, 0x00, /* wMaxPacketSize. */ + 0x00, /* bInterval. */ + /* Endpoint 1 Descriptor.*/ + 7, + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + ENDP1|0x80, /* bEndpointAddress. */ + 0x02, /* bmAttributes (Bulk). */ + 0x40, 0x00, /* wMaxPacketSize. */ + 0x00 /* bInterval. */ +}; + + +/* + * U.S. English language identifier. + */ +static const uint8_t vcom_string0[4] = { + 4, /* bLength */ + STRING_DESCRIPTOR, + 0x09, 0x04 /* LangID = 0x0409: US-English */ +}; + +static const uint8_t vcom_string1[] = { + 23*2+2, /* bLength */ + STRING_DESCRIPTOR, /* bDescriptorType */ + /* Manufacturer: "Flying Stone Technology" */ + 'F', 0, 'l', 0, 'y', 0, 'i', 0, 'n', 0, 'g', 0, ' ', 0, 'S', 0, + 't', 0, 'o', 0, 'n', 0, 'e', 0, ' ', 0, 'T', 0, 'e', 0, 'c', 0, + 'h', 0, 'n', 0, 'o', 0, 'l', 0, 'o', 0, 'g', 0, 'y', 0, +}; + +static const uint8_t vcom_string2[] = { + 14*2+2, /* bLength */ + STRING_DESCRIPTOR, /* bDescriptorType */ + /* Product name: "Chopstx Sample" */ + 'C', 0, 'h', 0, 'o', 0, 'p', 0, 's', 0, 't', 0, 'x', 0, ' ', 0, + 'S', 0, 'a', 0, 'm', 0, 'p', 0, 'l', 0, 'e', 0, +}; + +/* + * Serial Number string. + */ +static const uint8_t vcom_string3[28] = { + 28, /* bLength */ + STRING_DESCRIPTOR, /* bDescriptorType */ + '0', 0, '.', 0, '0', 0, '0', 0, /* Version number */ +}; + + +#define NUM_INTERFACES 2 + + +static void +usb_device_reset (struct usb_dev *dev) +{ + usb_lld_reset (dev, VCOM_FEATURE_BUS_POWERED); + + /* Initialize Endpoint 0 */ + usb_lld_setup_endp (dev, ENDP0, 1, 1); + + chopstx_mutex_lock (&tty0.mtx); + tty0.inputline_len = 0; + tty0.send_head = tty0.send_tail = 0; + tty0.flag_connected = 0; + tty0.flag_send_ready = 1; + tty0.flag_input_avail = 0; + tty0.device_state = USB_DEVICE_STATE_ATTACHED; + memcpy (&tty0.line_coding, &line_coding0, sizeof (struct line_coding)); + chopstx_mutex_unlock (&tty0.mtx); +} + + +#define CDC_CTRL_DTR 0x0001 + +static void +usb_ctrl_write_finish (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT); + + if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT) && arg->index == 0 + && USB_SETUP_SET (arg->type) + && arg->request == USB_CDC_REQ_SET_CONTROL_LINE_STATE) + { + struct tty *t = tty_get (arg->index, 0); + + /* Open/close the connection. */ + chopstx_mutex_lock (&t->mtx); + t->flag_connected = ((arg->value & CDC_CTRL_DTR) != 0); + chopstx_cond_signal (&t->cnd); + chopstx_mutex_unlock (&t->mtx); + } + + /* + * The transaction was already finished. So, it is no use to call + * usb_lld_ctrl_error when the condition does not match. + */ +} + + + +static int +vcom_port_data_setup (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + + if (USB_SETUP_GET (arg->type)) + { + struct tty *t = tty_get (arg->index, 0); + + if (arg->request == USB_CDC_REQ_GET_LINE_CODING) + return usb_lld_ctrl_send (dev, &t->line_coding, + sizeof (struct line_coding)); + } + else /* USB_SETUP_SET (req) */ + { + if (arg->request == USB_CDC_REQ_SET_LINE_CODING + && arg->len == sizeof (struct line_coding)) + { + struct tty *t = tty_get (arg->index, 0); + + return usb_lld_ctrl_recv (dev, &t->line_coding, + sizeof (struct line_coding)); + } + else if (arg->request == USB_CDC_REQ_SET_CONTROL_LINE_STATE) + return usb_lld_ctrl_ack (dev); + } + + return -1; +} + +static int +usb_setup (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT); + + if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT) && arg->index == 0) + return vcom_port_data_setup (dev); + + return -1; +} + +static int +usb_get_descriptor (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + uint8_t desc_type = (arg->value >> 8); + uint8_t desc_index = (arg->value & 0xff); + + if (rcp != DEVICE_RECIPIENT) + return -1; + + if (desc_type == DEVICE_DESCRIPTOR) + return usb_lld_ctrl_send (dev, + vcom_device_desc, sizeof (vcom_device_desc)); + else if (desc_type == CONFIG_DESCRIPTOR) + return usb_lld_ctrl_send (dev, + vcom_config_desc, sizeof (vcom_config_desc)); + else if (desc_type == STRING_DESCRIPTOR) + { + const uint8_t *str; + int size; + + switch (desc_index) + { + case 0: + str = vcom_string0; + size = sizeof (vcom_string0); + break; + case 1: + str = vcom_string1; + size = sizeof (vcom_string1); + break; + case 2: + str = vcom_string2; + size = sizeof (vcom_string2); + break; + case 3: + str = vcom_string3; + size = sizeof (vcom_string3); + break; + default: + return -1; + } + + return usb_lld_ctrl_send (dev, str, size); + } + + return -1; +} + +static void +vcom_setup_endpoints_for_interface (struct usb_dev *dev, + uint16_t interface, int stop) +{ + if (interface == 0) + { + if (!stop) + usb_lld_setup_endp (dev, ENDP2, 0, 1); + else + usb_lld_stall_tx (ENDP2); + } + else if (interface == 1) + { + if (!stop) + { + usb_lld_setup_endp (dev, ENDP1, 0, 1); + usb_lld_setup_endp (dev, ENDP3, 1, 0); + /* Start with no data receiving (ENDP3 not enabled)*/ + } + else + { + usb_lld_stall_tx (ENDP1); + usb_lld_stall_rx (ENDP3); + } + } +} + +static int +usb_set_configuration (struct usb_dev *dev) +{ + int i; + uint8_t current_conf; + + current_conf = usb_lld_current_configuration (dev); + if (current_conf == 0) + { + if (dev->dev_req.value != 1) + return -1; + + usb_lld_set_configuration (dev, 1); + for (i = 0; i < NUM_INTERFACES; i++) + vcom_setup_endpoints_for_interface (dev, i, 0); + chopstx_mutex_lock (&tty0.mtx); + tty0.device_state = USB_DEVICE_STATE_CONFIGURED; + chopstx_cond_signal (&tty0.cnd); + chopstx_mutex_unlock (&tty0.mtx); + } + else if (current_conf != dev->dev_req.value) + { + if (dev->dev_req.value != 0) + return -1; + + usb_lld_set_configuration (dev, 0); + for (i = 0; i < NUM_INTERFACES; i++) + vcom_setup_endpoints_for_interface (dev, i, 1); + chopstx_mutex_lock (&tty0.mtx); + tty0.device_state = USB_DEVICE_STATE_ADDRESSED; + chopstx_cond_signal (&tty0.cnd); + chopstx_mutex_unlock (&tty0.mtx); + } + + return usb_lld_ctrl_ack (dev); +} + + +static int +usb_set_interface (struct usb_dev *dev) +{ + uint16_t interface = dev->dev_req.index; + uint16_t alt = dev->dev_req.value; + + if (interface >= NUM_INTERFACES) + return -1; + + if (alt != 0) + return -1; + else + { + vcom_setup_endpoints_for_interface (dev, interface, 0); + return usb_lld_ctrl_ack (dev); + } +} + +static int +usb_get_interface (struct usb_dev *dev) +{ + const uint8_t zero = 0; + uint16_t interface = dev->dev_req.index; + + if (interface >= NUM_INTERFACES) + return -1; + + /* We don't have alternate interface, so, always return 0. */ + return usb_lld_ctrl_send (dev, &zero, 1); +} + +static int +usb_get_status_interface (struct usb_dev *dev) +{ + const uint16_t status_info = 0; + uint16_t interface = dev->dev_req.index; + + if (interface >= NUM_INTERFACES) + return -1; + + return usb_lld_ctrl_send (dev, &status_info, 2); +} + + +/* + * Put a character into the ring buffer to be send back. + */ +static void +put_char_to_ringbuffer (struct tty *t, int c) +{ + uint32_t next = (t->send_tail + 1) % LINEBUFSIZE; + + if (t->send_head == next) + /* full */ + /* All that we can do is ignore this char. */ + return; + + t->send_buf[t->send_tail] = c; + t->send_tail = next; +} + +/* + * Get characters from ring buffer into S. + */ +static int +get_chars_from_ringbuffer (struct tty *t, uint8_t *s, int len) +{ + int i = 0; + + if (t->send_head == t->send_tail) + /* Empty */ + return i; + + do + { + s[i++] = t->send_buf[t->send_head]; + t->send_head = (t->send_head + 1) % LINEBUFSIZE; + } + while (t->send_head != t->send_tail && i < len); + + return i; +} + + +static void +tty_echo_char (struct tty *t, int c) +{ + put_char_to_ringbuffer (t, c); +} + +static void +usb_tx_done (uint8_t ep_num, uint16_t len) +{ + struct tty *t = tty_get (-1, ep_num); + + (void)len; + if (ep_num == ENDP1) + { + chopstx_mutex_lock (&t->mtx); + if (t->flag_send_ready == 0) + { + t->flag_send_ready = 1; + chopstx_cond_signal (&t->cnd); + } + chopstx_mutex_unlock (&t->mtx); + } + else if (ep_num == ENDP2) + { + /* Nothing */ + } +} + + +static int +tty_input_char (struct tty *t, int c) +{ + unsigned int i; + int r = 0; + + /* Process DEL, C-U, C-R, and RET as editing command. */ + chopstx_mutex_lock (&t->mtx); + switch (c) + { + case 0x0d: /* Control-M */ + t->inputline[t->inputline_len++] = '\n'; + tty_echo_char (t, 0x0d); + tty_echo_char (t, 0x0a); + t->flag_input_avail = 1; + r = 1; + chopstx_cond_signal (&t->cnd); + break; + case 0x12: /* Control-R */ + tty_echo_char (t, '^'); + tty_echo_char (t, 'R'); + tty_echo_char (t, 0x0d); + tty_echo_char (t, 0x0a); + for (i = 0; i < t->inputline_len; i++) + tty_echo_char (t, t->inputline[i]); + break; + case 0x15: /* Control-U */ + for (i = 0; i < t->inputline_len; i++) + { + tty_echo_char (t, 0x08); + tty_echo_char (t, 0x20); + tty_echo_char (t, 0x08); + } + t->inputline_len = 0; + break; + case 0x7f: /* DEL */ + if (t->inputline_len > 0) + { + tty_echo_char (t, 0x08); + tty_echo_char (t, 0x20); + tty_echo_char (t, 0x08); + t->inputline_len--; + } + break; + default: + if (t->inputline_len < sizeof (t->inputline) - 1) + { + tty_echo_char (t, c); + t->inputline[t->inputline_len++] = c; + } + else + /* Beep */ + tty_echo_char (t, 0x0a); + break; + } + chopstx_mutex_unlock (&t->mtx); + return r; +} + +static void +usb_rx_ready (uint8_t ep_num, uint16_t len) +{ + struct tty *t = tty_get (-1, ep_num); + if (ep_num == ENDP3) + { + int i; + + for (i = 0; i < len; i++) + if (tty_input_char (t, t->recv_buf0[i])) + break; + + chopstx_mutex_lock (&t->mtx); + if (t->flag_input_avail == 0) + usb_lld_rx_enable_buf (ENDP3, t->recv_buf0, 64); + chopstx_mutex_unlock (&t->mtx); + } +} + +static void *tty_main (void *arg); + +#define PRIO_TTY 4 + +static char __process3_stack_base__[4096]; +#define STACK_ADDR_TTY ((uintptr_t)__process3_stack_base__) +#define STACK_SIZE_TTY (sizeof __process3_stack_base__) + +struct tty * +tty_open (void) +{ + chopstx_mutex_init (&tty0.mtx); + chopstx_cond_init (&tty0.cnd); + tty0.inputline_len = 0; + tty0.send_head = tty0.send_tail = 0; + tty0.flag_connected = 0; + tty0.flag_send_ready = 1; + tty0.flag_input_avail = 0; + tty0.device_state = USB_DEVICE_STATE_UNCONNECTED; + memcpy (&tty0.line_coding, &line_coding0, sizeof (struct line_coding)); + + chopstx_create (PRIO_TTY, STACK_ADDR_TTY, STACK_SIZE_TTY, tty_main, &tty0); + return &tty0; +} + + +static void * +tty_main (void *arg) +{ + struct tty *t = arg; + struct usb_dev dev; + int e; + +#if defined(OLDER_SYS_H) + /* + * Historically (before sys < 3.0), NVIC priority setting for USB + * interrupt was done in usb_lld_sys_init. Thus this code. + * + * When USB interrupt occurs between usb_lld_init (which assumes + * ISR) and chopstx_claim_irq (which clears pending interrupt), + * invocation of usb_lld_event_handler won't occur. + * + * Calling usb_lld_event_handler is no harm even if there were no + * interrupts, thus, we call it unconditionally here, just in case + * if there is a request. + * + * We can't call usb_lld_init after chopstx_claim_irq, as + * usb_lld_init does its own setting for NVIC. Calling + * chopstx_claim_irq after usb_lld_init overrides that. + * + */ + usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED); + chopstx_claim_irq (&usb_intr, INTR_REQ_USB); + goto event_handle; +#else + chopstx_claim_irq (&usb_intr, INTR_REQ_USB); + usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED); +#endif + + while (1) + { + chopstx_intr_wait (&usb_intr); + if (usb_intr.ready) + { + uint8_t ep_num; +#if defined(OLDER_SYS_H) + event_handle: +#endif + /* + * When interrupt is detected, call usb_lld_event_handler. + * The event may be one of following: + * (1) Transfer to endpoint (bulk or interrupt) + * In this case EP_NUM is encoded in the variable E. + * (2) "NONE" event: some trasfer was done, but all was + * done by lower layer, no other work is needed in + * upper layer. + * (3) Device events: Reset or Suspend + * (4) Device requests to the endpoint zero. + * + */ + e = usb_lld_event_handler (&dev); + chopstx_intr_done (&usb_intr); + ep_num = USB_EVENT_ENDP (e); + + if (ep_num != 0) + { + if (USB_EVENT_TXRX (e)) + usb_tx_done (ep_num, USB_EVENT_LEN (e)); + else + usb_rx_ready (ep_num, USB_EVENT_LEN (e)); + } + else + switch (USB_EVENT_ID (e)) + { + case USB_EVENT_DEVICE_RESET: + usb_device_reset (&dev); + continue; + + case USB_EVENT_DEVICE_ADDRESSED: + /* The addres is assigned to the device. We don't + * need to do anything for this actually, but in this + * application, we maintain the USB status of the + * device. Usually, just "continue" as EVENT_OK is + * OK. + */ + chopstx_mutex_lock (&tty0.mtx); + tty0.device_state = USB_DEVICE_STATE_ADDRESSED; + chopstx_cond_signal (&tty0.cnd); + chopstx_mutex_unlock (&tty0.mtx); + continue; + + case USB_EVENT_GET_DESCRIPTOR: + if (usb_get_descriptor (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_SET_CONFIGURATION: + if (usb_set_configuration (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_SET_INTERFACE: + if (usb_set_interface (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_CTRL_REQUEST: + /* Device specific device request. */ + if (usb_setup (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_GET_STATUS_INTERFACE: + if (usb_get_status_interface (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_GET_INTERFACE: + if (usb_get_interface (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_SET_FEATURE_DEVICE: + case USB_EVENT_SET_FEATURE_ENDPOINT: + case USB_EVENT_CLEAR_FEATURE_DEVICE: + case USB_EVENT_CLEAR_FEATURE_ENDPOINT: + usb_lld_ctrl_ack (&dev); + continue; + + case USB_EVENT_CTRL_WRITE_FINISH: + /* Control WRITE transfer finished. */ + usb_ctrl_write_finish (&dev); + continue; + + case USB_EVENT_OK: + case USB_EVENT_DEVICE_SUSPEND: + default: + continue; + } + } + + chopstx_mutex_lock (&t->mtx); + if (t->device_state == USB_DEVICE_STATE_CONFIGURED && t->flag_connected + && t->flag_send_ready) + { + uint8_t line[32]; + int len = get_chars_from_ringbuffer (t, line, sizeof (len)); + + if (len) + { + memcpy (t->send_buf0, line, len); + usb_lld_tx_enable_buf (ENDP1, t->send_buf0, len); + t->flag_send_ready = 0; + } + } + chopstx_mutex_unlock (&t->mtx); + } + + return NULL; +} + + +void +tty_wait_configured (struct tty *t) +{ + chopstx_mutex_lock (&t->mtx); + while (t->device_state != USB_DEVICE_STATE_CONFIGURED) + chopstx_cond_wait (&t->cnd, &t->mtx); + chopstx_mutex_unlock (&t->mtx); +} + + +void +tty_wait_connection (struct tty *t) +{ + chopstx_mutex_lock (&t->mtx); + while (t->flag_connected == 0) + chopstx_cond_wait (&t->cnd, &t->mtx); + t->flag_send_ready = 1; + t->flag_input_avail = 0; + t->send_head = t->send_tail = 0; + t->inputline_len = 0; + usb_lld_rx_enable_buf (ENDP3, t->recv_buf0, 64); /* Accept input for line */ + chopstx_mutex_unlock (&t->mtx); +} + +static int +check_tx (struct tty *t) +{ + if (t->flag_send_ready) + /* TX done */ + return 1; + if (t->flag_connected == 0) + /* Disconnected */ + return -1; + return 0; +} + +int +tty_send (struct tty *t, const char *buf, int len) +{ + int r; + const char *p; + int count; + + p = buf; + count = len >= 64 ? 64 : len; + + while (1) + { + chopstx_mutex_lock (&t->mtx); + while ((r = check_tx (t)) == 0) + chopstx_cond_wait (&t->cnd, &t->mtx); + if (r > 0) + { + usb_lld_tx_enable_buf (ENDP1, p, count); + t->flag_send_ready = 0; + } + chopstx_mutex_unlock (&t->mtx); + + len -= count; + p += count; + if (len == 0 && count != 64) + /* + * The size of the last packet should be != 0 + * If 64, send ZLP (zelo length packet) + */ + break; + count = len >= 64 ? 64 : len; + } + + /* Wait until all sent. */ + chopstx_mutex_lock (&t->mtx); + while ((r = check_tx (t)) == 0) + chopstx_cond_wait (&t->cnd, &t->mtx); + chopstx_mutex_unlock (&t->mtx); + return r; +} + + +static int +check_rx (void *arg) +{ + struct tty *t = arg; + + if (t->flag_input_avail) + /* RX */ + return 1; + if (t->flag_connected == 0) + /* Disconnected */ + return 1; + return 0; +} + +/* + * Returns -1 on connection close + * 0 on timeout. + * >0 length of the inputline (including final \n) + * + */ +int +tty_recv (struct tty *t, char *buf, uint32_t *timeout) +{ + int r; + chopstx_poll_cond_t poll_desc; + + poll_desc.type = CHOPSTX_POLL_COND; + poll_desc.ready = 0; + poll_desc.cond = &t->cnd; + poll_desc.mutex = &t->mtx; + poll_desc.check = check_rx; + poll_desc.arg = t; + + while (1) + { + struct chx_poll_head *pd_array[1] = { + (struct chx_poll_head *)&poll_desc + }; + chopstx_poll (timeout, 1, pd_array); + chopstx_mutex_lock (&t->mtx); + r = check_rx (t); + chopstx_mutex_unlock (&t->mtx); + if (r || (timeout != NULL && *timeout == 0)) + break; + } + + chopstx_mutex_lock (&t->mtx); + if (t->flag_connected == 0) + r = -1; + else if (t->flag_input_avail) + { + r = t->inputline_len; + memcpy (buf, t->inputline, r); + t->flag_input_avail = 0; + usb_lld_rx_enable_buf (ENDP3, t->recv_buf0, 64); + t->inputline_len = 0; + } + else + r = 0; + chopstx_mutex_unlock (&t->mtx); + + return r; +} diff --git a/gnuk/chopstx/example-cdc/Makefile b/gnuk/chopstx/example-cdc/Makefile new file mode 100644 index 0000000000..1d64f781e0 --- /dev/null +++ b/gnuk/chopstx/example-cdc/Makefile @@ -0,0 +1,37 @@ +# Makefile for example application of Chopstx + +PROJECT = sample + +CHOPSTX = .. +LDSCRIPT= sample.ld +CSRC = sample.c usb-cdc.c + +CHIP=stm32f103 + +USE_SYS = yes +USE_USB = yes +ENABLE_OUTPUT_HEX=yes + +################################### +CROSS = arm-none-eabi- +CC = $(CROSS)gcc +LD = $(CROSS)gcc +OBJCOPY = $(CROSS)objcopy + +MCU = cortex-m3 +CWARN = -Wall -Wextra -Wstrict-prototypes +DEFS = -DUSE_SYS3 -DFREE_STANDING -DMHZ=72 +OPT = -O3 -Os -g +LIBS = + +#################### +include ../rules.mk + +board.h: + @echo Please make a symbolic link \'board.h\' to a file in ../board; + @exit 1 + +sys.c: board.h + +distclean: clean + rm -f board.h diff --git a/gnuk/chopstx/example-cdc/board.h b/gnuk/chopstx/example-cdc/board.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gnuk/chopstx/example-cdc/sample.c b/gnuk/chopstx/example-cdc/sample.c new file mode 100644 index 0000000000..13860e772b --- /dev/null +++ b/gnuk/chopstx/example-cdc/sample.c @@ -0,0 +1,167 @@ +#include +#include +#include +#include + +#include "usb_lld.h" +#include "tty.h" + +/* For set_led */ +#include "board.h" +#include "sys.h" + +static chopstx_mutex_t mtx; +static chopstx_cond_t cnd0; +static chopstx_cond_t cnd1; + +static uint8_t u, v; +static uint8_t m; /* 0..100 */ + +static void * +pwm (void *arg) +{ + (void)arg; + + chopstx_mutex_lock (&mtx); + chopstx_cond_wait (&cnd0, &mtx); + chopstx_mutex_unlock (&mtx); + + while (1) + { + set_led (u&v); + chopstx_usec_wait (m); + set_led (0); + chopstx_usec_wait (100-m); + } + + return NULL; +} + +static void * +blk (void *arg) +{ + (void)arg; + + chopstx_mutex_lock (&mtx); + chopstx_cond_wait (&cnd1, &mtx); + chopstx_mutex_unlock (&mtx); + + while (1) + { + v = 0; + chopstx_usec_wait (200*1000); + v = 1; + chopstx_usec_wait (200*1000); + } + + return NULL; +} + + +#define PRIO_PWM 3 +#define PRIO_BLK 2 + +#define STACK_MAIN +#define STACK_PROCESS_1 +#define STACK_PROCESS_2 +#include "stack-def.h" +#define STACK_ADDR_PWM ((uint32_t)process1_base) +#define STACK_SIZE_PWM (sizeof process1_base) +#define STACK_ADDR_BLK ((uint32_t)process2_base) +#define STACK_SIZE_BLK (sizeof process2_base) + + +static char hexchar (uint8_t x) +{ + x &= 0x0f; + if (x <= 0x09) + return '0' + x; + else if (x <= 0x0f) + return 'a' + x - 10; + else + return '?'; +} + + +int +main (int argc, const char *argv[]) +{ + struct tty *tty; + uint8_t count; + + (void)argc; + (void)argv; + + chopstx_mutex_init (&mtx); + chopstx_cond_init (&cnd0); + chopstx_cond_init (&cnd1); + + m = 10; + + chopstx_create (PRIO_PWM, STACK_ADDR_PWM, STACK_SIZE_PWM, pwm, NULL); + chopstx_create (PRIO_BLK, STACK_ADDR_BLK, STACK_SIZE_BLK, blk, NULL); + + chopstx_usec_wait (200*1000); + + chopstx_mutex_lock (&mtx); + chopstx_cond_signal (&cnd0); + chopstx_cond_signal (&cnd1); + chopstx_mutex_unlock (&mtx); + + u = 1; + + tty = tty_open (); + tty_wait_configured (tty); + + count = 0; + m = 50; + while (1) + { + char s[LINEBUFSIZE]; + + u = 1; + tty_wait_connection (tty); + + chopstx_usec_wait (50*1000); + + /* Send ZLP at the beginning. */ + tty_send (tty, s, 0); + + memcpy (s, "xx: Hello, World with Chopstx!\r\n", 32); + s[0] = hexchar (count >> 4); + s[1] = hexchar (count & 0x0f); + count++; + + if (tty_send (tty, s, 32) < 0) + continue; + + while (1) + { + int size; + uint32_t usec; + + usec = 3000000; /* 3.0 seconds */ + size = tty_recv (tty, s + 4, &usec); + if (size < 0) + break; + + if (size) + { + size--; + + s[0] = hexchar (size >> 4); + s[1] = hexchar (size & 0x0f); + s[2] = ':'; + s[3] = ' '; + s[size + 4] = '\r'; + s[size + 5] = '\n'; + if (tty_send (tty, s, size + 6) < 0) + break; + } + + u ^= 1; + } + } + + return 0; +} diff --git a/gnuk/chopstx/example-cdc/sample.ld b/gnuk/chopstx/example-cdc/sample.ld new file mode 100644 index 0000000000..e37ba916f8 --- /dev/null +++ b/gnuk/chopstx/example-cdc/sample.ld @@ -0,0 +1,124 @@ +/* + * ST32F103 memory setup. + */ + +MEMORY +{ + flash0 : org = 0x08000000, len = 4k + flash : org = 0x08000000+0x1000, len = 60k + ram : org = 0x20000000, len = 20k +} + +__ram_start__ = ORIGIN(ram); +__ram_size__ = 20k; +__ram_end__ = __ram_start__ + __ram_size__; + +SECTIONS +{ + . = 0; + + .sys : ALIGN(4) SUBALIGN(4) + { + _sys = .; + KEEP(*(.vectors)) + . = ALIGN(16); + KEEP(*(.sys.version)) + KEEP(*(.sys.board_id)) + KEEP(*(.sys.board_name)) + build/sys-*.o(.text) + build/sys-*.o(.text.*) + build/sys-*.o(.rodata) + build/sys-*.o(.rodata.*) + . = ALIGN(1024); + *(.sys.0) + *(.sys.1) + *(.sys.2) + } > flash0 + + _text = .; + + .startup : ALIGN(128) SUBALIGN(128) + { + KEEP(*(.startup.vectors)) + . = ALIGN (16); + } > flash =0xffffffff + + .text : ALIGN(16) SUBALIGN(16) + { + *(.text.startup.*) + *(.text) + *(.text.*) + *(.rodata) + *(.rodata.*) + *(.glue_7t) + *(.glue_7) + *(.gcc*) + . = ALIGN(8); + } > flash + + .ARM.extab : {*(.ARM.extab* .gnu.linkonce.armextab.*)} > flash + + .ARM.exidx : { + PROVIDE(__exidx_start = .); + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + PROVIDE(__exidx_end = .); + } > flash + + .eh_frame_hdr : {*(.eh_frame_hdr)} > flash + + .eh_frame : ONLY_IF_RO {*(.eh_frame)} > flash + + .textalign : ONLY_IF_RO { . = ALIGN(8); } > flash + + _etext = .; + _textdata = _etext; + + .process_stack (NOLOAD) : + { + . = ALIGN(8); + *(.process_stack.3) + *(.process_stack.2) + *(.process_stack.1) + *(.process_stack.0) + . = ALIGN(8); + } > ram + + .main_stack (NOLOAD) : + { + . = ALIGN(8); + *(.main_stack) + . = ALIGN(8); + } > ram + + .data : + { + . = ALIGN(4); + PROVIDE(_data = .); + *(.data) + . = ALIGN(4); + *(.data.*) + . = ALIGN(4); + *(.ramtext) + . = ALIGN(4); + PROVIDE(_edata = .); + } > ram AT > flash + + .bss : + { + . = ALIGN(4); + PROVIDE(_bss_start = .); + *(.bss) + . = ALIGN(4); + *(.bss.*) + . = ALIGN(4); + *(COMMON) + . = ALIGN(4); + PROVIDE(_bss_end = .); + } > ram + + PROVIDE(end = .); + _end = .; +} + +__heap_base__ = _end; +__heap_end__ = __ram_end__; diff --git a/gnuk/chopstx/example-cdc/stack-def.h b/gnuk/chopstx/example-cdc/stack-def.h new file mode 100644 index 0000000000..cc7babb799 --- /dev/null +++ b/gnuk/chopstx/example-cdc/stack-def.h @@ -0,0 +1,24 @@ +#if defined(STACK_MAIN) +/* Idle+Exception handlers */ +char __main_stack_end__[0] __attribute__ ((section(".main_stack"))); +char main_base[0x0100] __attribute__ ((section(".main_stack"))); + +/* Main program */ +char __process0_stack_end__[0] __attribute__ ((section(".process_stack.0"))); +char process0_base[0x0400] __attribute__ ((section(".process_stack.0"))); +#endif + +/* First thread program */ +#if defined(STACK_PROCESS_1) +char process1_base[0x0200] __attribute__ ((section(".process_stack.1"))); +#endif + +/* Second thread program */ +#if defined(STACK_PROCESS_2) +char process2_base[0x0200] __attribute__ ((section(".process_stack.2"))); +#endif + +/* Third thread program */ +#if defined(STACK_PROCESS_3) +char process3_base[0x0200] __attribute__ ((section(".process_stack.3"))); +#endif diff --git a/gnuk/chopstx/example-cdc/tty.h b/gnuk/chopstx/example-cdc/tty.h new file mode 100644 index 0000000000..77e7f463c0 --- /dev/null +++ b/gnuk/chopstx/example-cdc/tty.h @@ -0,0 +1,9 @@ +#define LINEBUFSIZE 128 + +struct tty; + +struct tty *tty_open (void); +void tty_wait_configured (struct tty *tty); +void tty_wait_connection (struct tty *tty); +int tty_send (struct tty *tty, const char *buf, int count); +int tty_recv (struct tty *tty, char *buf, uint32_t *timeout); diff --git a/gnuk/chopstx/example-cdc/usb-cdc.c b/gnuk/chopstx/example-cdc/usb-cdc.c new file mode 100644 index 0000000000..52e3863323 --- /dev/null +++ b/gnuk/chopstx/example-cdc/usb-cdc.c @@ -0,0 +1,959 @@ +#include +#include +#include +#include +#include "usb_lld.h" +#include "tty.h" + +static chopstx_intr_t usb_intr; + +struct line_coding +{ + uint32_t bitrate; + uint8_t format; + uint8_t paritytype; + uint8_t datatype; +} __attribute__((packed)); + +static const struct line_coding line_coding0 = { + 115200, /* baud rate: 115200 */ + 0x00, /* stop bits: 1 */ + 0x00, /* parity: none */ + 0x08 /* bits: 8 */ +}; + +/* + * Currently, we only support a single TTY. + * + * It is possible to extend to support multiple TTYs, for multiple + * interfaces. + * + * In that case, add argument to TTY_OPEN function and + * modify TTY_GET function to get the TTY structure. Functions which + * directy accesses TTY0 (usb_device_reset and usb_set_configuration) + * should be modified, too. + * + * Modification of TTY_MAIN thread will be also needed to echo back + * input for each TTY, and the thread should run if one of TTY is + * opened. + */ + +struct tty { + chopstx_mutex_t mtx; + chopstx_cond_t cnd; + uint8_t inputline[LINEBUFSIZE]; /* Line editing is supported */ + uint8_t send_buf[LINEBUFSIZE]; /* Sending ring buffer for echo back */ + uint32_t inputline_len : 8; + uint32_t send_head : 8; + uint32_t send_tail : 8; + uint32_t flag_connected : 1; + uint32_t flag_send_ready : 1; + uint32_t flag_input_avail : 1; + uint32_t : 2; + uint32_t device_state : 3; /* USB device status */ + struct line_coding line_coding; +}; + +static struct tty tty0; + +/* + * Locate TTY structure from interface number or endpoint number. + * Currently, it always returns tty0, because we only have the one. + */ +static struct tty * +tty_get (int interface, uint8_t ep_num) +{ + struct tty *t = &tty0; + + if (interface >= 0) + { + if (interface == 0) + t = &tty0; + } + else + { + if (ep_num == ENDP1 || ep_num == ENDP2 || ep_num == ENDP3) + t = &tty0; + } + + return t; +} + + +#define ENDP0_RXADDR (0x40) +#define ENDP0_TXADDR (0x80) +#define ENDP1_TXADDR (0xc0) +#define ENDP2_TXADDR (0x100) +#define ENDP3_RXADDR (0x140) + +#define USB_CDC_REQ_SET_LINE_CODING 0x20 +#define USB_CDC_REQ_GET_LINE_CODING 0x21 +#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22 +#define USB_CDC_REQ_SEND_BREAK 0x23 + +/* USB Device Descriptor */ +static const uint8_t vcom_device_desc[18] = { + 18, /* bLength */ + DEVICE_DESCRIPTOR, /* bDescriptorType */ + 0x10, 0x01, /* bcdUSB = 1.1 */ + 0x02, /* bDeviceClass (CDC). */ + 0x00, /* bDeviceSubClass. */ + 0x00, /* bDeviceProtocol. */ + 0x40, /* bMaxPacketSize. */ + 0xFF, 0xFF, /* idVendor */ + 0x01, 0x00, /* idProduct */ + 0x00, 0x01, /* bcdDevice */ + 1, /* iManufacturer. */ + 2, /* iProduct. */ + 3, /* iSerialNumber. */ + 1 /* bNumConfigurations. */ +}; + +#define VCOM_FEATURE_BUS_POWERED 0x80 + +/* Configuration Descriptor tree for a CDC.*/ +static const uint8_t vcom_config_desc[67] = { + 9, + CONFIG_DESCRIPTOR, /* bDescriptorType: Configuration */ + /* Configuration Descriptor.*/ + 67, 0x00, /* wTotalLength. */ + 0x02, /* bNumInterfaces. */ + 0x01, /* bConfigurationValue. */ + 0, /* iConfiguration. */ + VCOM_FEATURE_BUS_POWERED, /* bmAttributes. */ + 50, /* bMaxPower (100mA). */ + /* Interface Descriptor.*/ + 9, + INTERFACE_DESCRIPTOR, + 0x00, /* bInterfaceNumber. */ + 0x00, /* bAlternateSetting. */ + 0x01, /* bNumEndpoints. */ + 0x02, /* bInterfaceClass (Communications Interface Class, + CDC section 4.2). */ + 0x02, /* bInterfaceSubClass (Abstract Control Model, CDC + section 4.3). */ + 0x01, /* bInterfaceProtocol (AT commands, CDC section + 4.4). */ + 0, /* iInterface. */ + /* Header Functional Descriptor (CDC section 5.2.3).*/ + 5, /* bLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x00, /* bDescriptorSubtype (Header Functional Descriptor). */ + 0x10, 0x01, /* bcdCDC. */ + /* Call Management Functional Descriptor. */ + 5, /* bFunctionLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x01, /* bDescriptorSubtype (Call Management Functional + Descriptor). */ + 0x03, /* bmCapabilities (D0+D1). */ + 0x01, /* bDataInterface. */ + /* ACM Functional Descriptor.*/ + 4, /* bFunctionLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x02, /* bDescriptorSubtype (Abstract Control Management + Descriptor). */ + 0x02, /* bmCapabilities. */ + /* Union Functional Descriptor.*/ + 5, /* bFunctionLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x06, /* bDescriptorSubtype (Union Functional + Descriptor). */ + 0x00, /* bMasterInterface (Communication Class + Interface). */ + 0x01, /* bSlaveInterface0 (Data Class Interface). */ + /* Endpoint 2 Descriptor.*/ + 7, + ENDPOINT_DESCRIPTOR, + ENDP2|0x80, /* bEndpointAddress. */ + 0x03, /* bmAttributes (Interrupt). */ + 0x08, 0x00, /* wMaxPacketSize. */ + 0xFF, /* bInterval. */ + /* Interface Descriptor.*/ + 9, + INTERFACE_DESCRIPTOR, /* bDescriptorType: */ + 0x01, /* bInterfaceNumber. */ + 0x00, /* bAlternateSetting. */ + 0x02, /* bNumEndpoints. */ + 0x0A, /* bInterfaceClass (Data Class Interface, CDC section 4.5). */ + 0x00, /* bInterfaceSubClass (CDC section 4.6). */ + 0x00, /* bInterfaceProtocol (CDC section 4.7). */ + 0x00, /* iInterface. */ + /* Endpoint 3 Descriptor.*/ + 7, + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + ENDP3, /* bEndpointAddress. */ + 0x02, /* bmAttributes (Bulk). */ + 0x40, 0x00, /* wMaxPacketSize. */ + 0x00, /* bInterval. */ + /* Endpoint 1 Descriptor.*/ + 7, + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + ENDP1|0x80, /* bEndpointAddress. */ + 0x02, /* bmAttributes (Bulk). */ + 0x40, 0x00, /* wMaxPacketSize. */ + 0x00 /* bInterval. */ +}; + + +/* + * U.S. English language identifier. + */ +static const uint8_t vcom_string0[4] = { + 4, /* bLength */ + STRING_DESCRIPTOR, + 0x09, 0x04 /* LangID = 0x0409: US-English */ +}; + +static const uint8_t vcom_string1[] = { + 23*2+2, /* bLength */ + STRING_DESCRIPTOR, /* bDescriptorType */ + /* Manufacturer: "Flying Stone Technology" */ + 'F', 0, 'l', 0, 'y', 0, 'i', 0, 'n', 0, 'g', 0, ' ', 0, 'S', 0, + 't', 0, 'o', 0, 'n', 0, 'e', 0, ' ', 0, 'T', 0, 'e', 0, 'c', 0, + 'h', 0, 'n', 0, 'o', 0, 'l', 0, 'o', 0, 'g', 0, 'y', 0, +}; + +static const uint8_t vcom_string2[] = { + 14*2+2, /* bLength */ + STRING_DESCRIPTOR, /* bDescriptorType */ + /* Product name: "Chopstx Sample" */ + 'C', 0, 'h', 0, 'o', 0, 'p', 0, 's', 0, 't', 0, 'x', 0, ' ', 0, + 'S', 0, 'a', 0, 'm', 0, 'p', 0, 'l', 0, 'e', 0, +}; + +/* + * Serial Number string. + */ +static const uint8_t vcom_string3[28] = { + 28, /* bLength */ + STRING_DESCRIPTOR, /* bDescriptorType */ + '0', 0, '.', 0, '0', 0, '0', 0, /* Version number */ +}; + + +#define NUM_INTERFACES 2 + + +static void +usb_device_reset (struct usb_dev *dev) +{ + usb_lld_reset (dev, VCOM_FEATURE_BUS_POWERED); + + /* Initialize Endpoint 0 */ + usb_lld_setup_endpoint (ENDP0, EP_CONTROL, 0, ENDP0_RXADDR, ENDP0_TXADDR, 64); + + chopstx_mutex_lock (&tty0.mtx); + tty0.inputline_len = 0; + tty0.send_head = tty0.send_tail = 0; + tty0.flag_connected = 0; + tty0.flag_send_ready = 1; + tty0.flag_input_avail = 0; + tty0.device_state = USB_DEVICE_STATE_ATTACHED; + memcpy (&tty0.line_coding, &line_coding0, sizeof (struct line_coding)); + chopstx_mutex_unlock (&tty0.mtx); +} + + +#define CDC_CTRL_DTR 0x0001 + +static void +usb_ctrl_write_finish (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT); + + if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT) && arg->index == 0 + && USB_SETUP_SET (arg->type) + && arg->request == USB_CDC_REQ_SET_CONTROL_LINE_STATE) + { + struct tty *t = tty_get (arg->index, 0); + + /* Open/close the connection. */ + chopstx_mutex_lock (&t->mtx); + t->flag_connected = ((arg->value & CDC_CTRL_DTR) != 0); + chopstx_cond_signal (&t->cnd); + chopstx_mutex_unlock (&t->mtx); + } + + /* + * The transaction was already finished. So, it is no use to call + * usb_lld_ctrl_error when the condition does not match. + */ +} + + + +static int +vcom_port_data_setup (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + + if (USB_SETUP_GET (arg->type)) + { + struct tty *t = tty_get (arg->index, 0); + + if (arg->request == USB_CDC_REQ_GET_LINE_CODING) + return usb_lld_ctrl_send (dev, &t->line_coding, + sizeof (struct line_coding)); + } + else /* USB_SETUP_SET (req) */ + { + if (arg->request == USB_CDC_REQ_SET_LINE_CODING + && arg->len == sizeof (struct line_coding)) + { + struct tty *t = tty_get (arg->index, 0); + + return usb_lld_ctrl_recv (dev, &t->line_coding, + sizeof (struct line_coding)); + } + else if (arg->request == USB_CDC_REQ_SET_CONTROL_LINE_STATE) + return usb_lld_ctrl_ack (dev); + } + + return -1; +} + +static int +usb_setup (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT); + + if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT) && arg->index == 0) + return vcom_port_data_setup (dev); + + return -1; +} + +static int +usb_get_descriptor (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + uint8_t desc_type = (arg->value >> 8); + uint8_t desc_index = (arg->value & 0xff); + + if (rcp != DEVICE_RECIPIENT) + return -1; + + if (desc_type == DEVICE_DESCRIPTOR) + return usb_lld_ctrl_send (dev, + vcom_device_desc, sizeof (vcom_device_desc)); + else if (desc_type == CONFIG_DESCRIPTOR) + return usb_lld_ctrl_send (dev, + vcom_config_desc, sizeof (vcom_config_desc)); + else if (desc_type == STRING_DESCRIPTOR) + { + const uint8_t *str; + int size; + + switch (desc_index) + { + case 0: + str = vcom_string0; + size = sizeof (vcom_string0); + break; + case 1: + str = vcom_string1; + size = sizeof (vcom_string1); + break; + case 2: + str = vcom_string2; + size = sizeof (vcom_string2); + break; + case 3: + str = vcom_string3; + size = sizeof (vcom_string3); + break; + default: + return -1; + } + + return usb_lld_ctrl_send (dev, str, size); + } + + return -1; +} + +static void +vcom_setup_endpoints_for_interface (uint16_t interface, int stop) +{ + if (interface == 0) + { + if (!stop) + usb_lld_setup_endpoint (ENDP2, EP_INTERRUPT, 0, 0, ENDP2_TXADDR, 0); + else + usb_lld_stall_tx (ENDP2); + } + else if (interface == 1) + { + if (!stop) + { + usb_lld_setup_endpoint (ENDP1, EP_BULK, 0, 0, ENDP1_TXADDR, 0); + usb_lld_setup_endpoint (ENDP3, EP_BULK, 0, ENDP3_RXADDR, 0, 64); + /* Start with no data receiving (ENDP3 not enabled)*/ + } + else + { + usb_lld_stall_tx (ENDP1); + usb_lld_stall_rx (ENDP3); + } + } +} + +static int +usb_set_configuration (struct usb_dev *dev) +{ + int i; + uint8_t current_conf; + + current_conf = usb_lld_current_configuration (dev); + if (current_conf == 0) + { + if (dev->dev_req.value != 1) + return -1; + + usb_lld_set_configuration (dev, 1); + for (i = 0; i < NUM_INTERFACES; i++) + vcom_setup_endpoints_for_interface (i, 0); + chopstx_mutex_lock (&tty0.mtx); + tty0.device_state = USB_DEVICE_STATE_CONFIGURED; + chopstx_cond_signal (&tty0.cnd); + chopstx_mutex_unlock (&tty0.mtx); + } + else if (current_conf != dev->dev_req.value) + { + if (dev->dev_req.value != 0) + return -1; + + usb_lld_set_configuration (dev, 0); + for (i = 0; i < NUM_INTERFACES; i++) + vcom_setup_endpoints_for_interface (i, 1); + chopstx_mutex_lock (&tty0.mtx); + tty0.device_state = USB_DEVICE_STATE_ADDRESSED; + chopstx_cond_signal (&tty0.cnd); + chopstx_mutex_unlock (&tty0.mtx); + } + + usb_lld_ctrl_ack (dev); + return 0; +} + + +static int +usb_set_interface (struct usb_dev *dev) +{ + uint16_t interface = dev->dev_req.index; + uint16_t alt = dev->dev_req.value; + + if (interface >= NUM_INTERFACES) + return -1; + + if (alt != 0) + return -1; + else + { + vcom_setup_endpoints_for_interface (interface, 0); + usb_lld_ctrl_ack (dev); + return 0; + } +} + +static int +usb_get_interface (struct usb_dev *dev) +{ + const uint8_t zero = 0; + uint16_t interface = dev->dev_req.index; + + if (interface >= NUM_INTERFACES) + return -1; + + /* We don't have alternate interface, so, always return 0. */ + return usb_lld_ctrl_send (dev, &zero, 1); +} + +static int +usb_get_status_interface (struct usb_dev *dev) +{ + const uint16_t status_info = 0; + uint16_t interface = dev->dev_req.index; + + if (interface >= NUM_INTERFACES) + return -1; + + return usb_lld_ctrl_send (dev, &status_info, 2); +} + + +/* + * Put a character into the ring buffer to be send back. + */ +static void +put_char_to_ringbuffer (struct tty *t, int c) +{ + uint32_t next = (t->send_tail + 1) % LINEBUFSIZE; + + if (t->send_head == next) + /* full */ + /* All that we can do is ignore this char. */ + return; + + t->send_buf[t->send_tail] = c; + t->send_tail = next; +} + +/* + * Get characters from ring buffer into S. + */ +static int +get_chars_from_ringbuffer (struct tty *t, uint8_t *s, int len) +{ + int i = 0; + + if (t->send_head == t->send_tail) + /* Empty */ + return i; + + do + { + s[i++] = t->send_buf[t->send_head]; + t->send_head = (t->send_head + 1) % LINEBUFSIZE; + } + while (t->send_head != t->send_tail && i < len); + + return i; +} + + +static void +tty_echo_char (struct tty *t, int c) +{ + put_char_to_ringbuffer (t, c); +} + + +static void +usb_tx_done (uint8_t ep_num, uint16_t len) +{ + struct tty *t = tty_get (-1, ep_num); + + (void)len; + + if (ep_num == ENDP1) + { + chopstx_mutex_lock (&t->mtx); + if (t->flag_send_ready == 0) + { + t->flag_send_ready = 1; + chopstx_cond_signal (&t->cnd); + } + chopstx_mutex_unlock (&t->mtx); + } + else if (ep_num == ENDP2) + { + /* Nothing */ + } +} + + +static int +tty_input_char (struct tty *t, int c) +{ + unsigned int i; + int r = 0; + + /* Process DEL, C-U, C-R, and RET as editing command. */ + chopstx_mutex_lock (&t->mtx); + switch (c) + { + case 0x0d: /* Control-M */ + t->inputline[t->inputline_len++] = '\n'; + tty_echo_char (t, 0x0d); + tty_echo_char (t, 0x0a); + t->flag_input_avail = 1; + r = 1; + chopstx_cond_signal (&t->cnd); + break; + case 0x12: /* Control-R */ + tty_echo_char (t, '^'); + tty_echo_char (t, 'R'); + tty_echo_char (t, 0x0d); + tty_echo_char (t, 0x0a); + for (i = 0; i < t->inputline_len; i++) + tty_echo_char (t, t->inputline[i]); + break; + case 0x15: /* Control-U */ + for (i = 0; i < t->inputline_len; i++) + { + tty_echo_char (t, 0x08); + tty_echo_char (t, 0x20); + tty_echo_char (t, 0x08); + } + t->inputline_len = 0; + break; + case 0x7f: /* DEL */ + if (t->inputline_len > 0) + { + tty_echo_char (t, 0x08); + tty_echo_char (t, 0x20); + tty_echo_char (t, 0x08); + t->inputline_len--; + } + break; + default: + if (t->inputline_len < sizeof (t->inputline) - 1) + { + tty_echo_char (t, c); + t->inputline[t->inputline_len++] = c; + } + else + /* Beep */ + tty_echo_char (t, 0x0a); + break; + } + chopstx_mutex_unlock (&t->mtx); + return r; +} + +static void +usb_rx_ready (uint8_t ep_num, uint16_t len) +{ + uint8_t recv_buf[64]; + struct tty *t = tty_get (-1, ep_num); + + if (ep_num == ENDP3) + { + int i; + + usb_lld_rxcpy (recv_buf, ep_num, 0, len); + for (i = 0; i < len; i++) + if (tty_input_char (t, recv_buf[i])) + break; + + chopstx_mutex_lock (&t->mtx); + if (t->flag_input_avail == 0) + usb_lld_rx_enable (ENDP3); + chopstx_mutex_unlock (&t->mtx); + } +} + +static void *tty_main (void *arg); + +#define PRIO_TTY 4 + +#define STACK_PROCESS_3 +#include "stack-def.h" +#define STACK_ADDR_TTY ((uint32_t)process3_base) +#define STACK_SIZE_TTY (sizeof process3_base) + +struct tty * +tty_open (void) +{ + chopstx_mutex_init (&tty0.mtx); + chopstx_cond_init (&tty0.cnd); + tty0.inputline_len = 0; + tty0.send_head = tty0.send_tail = 0; + tty0.flag_connected = 0; + tty0.flag_send_ready = 1; + tty0.flag_input_avail = 0; + tty0.device_state = USB_DEVICE_STATE_UNCONNECTED; + memcpy (&tty0.line_coding, &line_coding0, sizeof (struct line_coding)); + + chopstx_create (PRIO_TTY, STACK_ADDR_TTY, STACK_SIZE_TTY, tty_main, &tty0); + return &tty0; +} + + +static void * +tty_main (void *arg) +{ + struct tty *t = arg; + struct usb_dev dev; + int e; + +#if defined(OLDER_SYS_H) + /* + * Historically (before sys < 3.0), NVIC priority setting for USB + * interrupt was done in usb_lld_sys_init. Thus this code. + * + * When USB interrupt occurs between usb_lld_init (which assumes + * ISR) and chopstx_claim_irq (which clears pending interrupt), + * invocation of usb_lld_event_handler won't occur. + * + * Calling usb_lld_event_handler is no harm even if there were no + * interrupts, thus, we call it unconditionally here, just in case + * if there is a request. + * + * We can't call usb_lld_init after chopstx_claim_irq, as + * usb_lld_init does its own setting for NVIC. Calling + * chopstx_claim_irq after usb_lld_init overrides that. + * + */ + usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED); + chopstx_claim_irq (&usb_intr, INTR_REQ_USB); + goto event_handle; +#else + chopstx_claim_irq (&usb_intr, INTR_REQ_USB); + usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED); +#endif + + while (1) + { + chopstx_intr_wait (&usb_intr); + if (usb_intr.ready) + { + uint8_t ep_num; +#if defined(OLDER_SYS_H) + event_handle: +#endif + /* + * When interrupt is detected, call usb_lld_event_handler. + * The event may be one of following: + * (1) Transfer to endpoint (bulk or interrupt) + * In this case EP_NUM is encoded in the variable E. + * (2) "NONE" event: some trasfer was done, but all was + * done by lower layer, no other work is needed in + * upper layer. + * (3) Device events: Reset or Suspend + * (4) Device requests to the endpoint zero. + * + */ + e = usb_lld_event_handler (&dev); + chopstx_intr_done (&usb_intr); + ep_num = USB_EVENT_ENDP (e); + + if (ep_num != 0) + { + if (USB_EVENT_TXRX (e)) + usb_tx_done (ep_num, USB_EVENT_LEN (e)); + else + usb_rx_ready (ep_num, USB_EVENT_LEN (e)); + } + else + switch (USB_EVENT_ID (e)) + { + case USB_EVENT_DEVICE_RESET: + usb_device_reset (&dev); + continue; + + case USB_EVENT_DEVICE_ADDRESSED: + /* The addres is assigned to the device. We don't + * need to do anything for this actually, but in this + * application, we maintain the USB status of the + * device. Usually, just "continue" as EVENT_OK is + * OK. + */ + chopstx_mutex_lock (&tty0.mtx); + tty0.device_state = USB_DEVICE_STATE_ADDRESSED; + chopstx_cond_signal (&tty0.cnd); + chopstx_mutex_unlock (&tty0.mtx); + continue; + + case USB_EVENT_GET_DESCRIPTOR: + if (usb_get_descriptor (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_SET_CONFIGURATION: + if (usb_set_configuration (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_SET_INTERFACE: + if (usb_set_interface (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_CTRL_REQUEST: + /* Device specific device request. */ + if (usb_setup (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_GET_STATUS_INTERFACE: + if (usb_get_status_interface (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_GET_INTERFACE: + if (usb_get_interface (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_SET_FEATURE_DEVICE: + case USB_EVENT_SET_FEATURE_ENDPOINT: + case USB_EVENT_CLEAR_FEATURE_DEVICE: + case USB_EVENT_CLEAR_FEATURE_ENDPOINT: + usb_lld_ctrl_ack (&dev); + continue; + + case USB_EVENT_CTRL_WRITE_FINISH: + /* Control WRITE transfer finished. */ + usb_ctrl_write_finish (&dev); + continue; + + case USB_EVENT_OK: + case USB_EVENT_DEVICE_SUSPEND: + default: + continue; + } + } + + chopstx_mutex_lock (&t->mtx); + if (t->device_state == USB_DEVICE_STATE_CONFIGURED && t->flag_connected + && t->flag_send_ready) + { + uint8_t line[32]; + int len = get_chars_from_ringbuffer (t, line, sizeof (len)); + + if (len) + { + usb_lld_txcpy (line, ENDP1, 0, len); + usb_lld_tx_enable (ENDP1, len); + t->flag_send_ready = 0; + } + } + chopstx_mutex_unlock (&t->mtx); + } + + return NULL; +} + + +void +tty_wait_configured (struct tty *t) +{ + chopstx_mutex_lock (&t->mtx); + while (t->device_state != USB_DEVICE_STATE_CONFIGURED) + chopstx_cond_wait (&t->cnd, &t->mtx); + chopstx_mutex_unlock (&t->mtx); +} + + +void +tty_wait_connection (struct tty *t) +{ + chopstx_mutex_lock (&t->mtx); + while (t->flag_connected == 0) + chopstx_cond_wait (&t->cnd, &t->mtx); + t->flag_send_ready = 1; + t->flag_input_avail = 0; + t->send_head = t->send_tail = 0; + t->inputline_len = 0; + usb_lld_rx_enable (ENDP3); /* Accept input for line */ + chopstx_mutex_unlock (&t->mtx); +} + +static int +check_tx (struct tty *t) +{ + if (t->flag_send_ready) + /* TX done */ + return 1; + if (t->flag_connected == 0) + /* Disconnected */ + return -1; + return 0; +} + +int +tty_send (struct tty *t, const char *buf, int len) +{ + int r; + const char *p; + int count; + + p = buf; + count = len >= 64 ? 64 : len; + + while (1) + { + chopstx_mutex_lock (&t->mtx); + while ((r = check_tx (t)) == 0) + chopstx_cond_wait (&t->cnd, &t->mtx); + if (r > 0) + { + usb_lld_txcpy (p, ENDP1, 0, count); + usb_lld_tx_enable (ENDP1, count); + t->flag_send_ready = 0; + } + chopstx_mutex_unlock (&t->mtx); + + len -= count; + p += count; + if (len == 0 && count != 64) + /* + * The size of the last packet should be != 0 + * If 64, send ZLP (zelo length packet) + */ + break; + count = len >= 64 ? 64 : len; + } + + return r; +} + + +static int +check_rx (void *arg) +{ + struct tty *t = arg; + + if (t->flag_input_avail) + /* RX */ + return 1; + if (t->flag_connected == 0) + /* Disconnected */ + return 1; + return 0; +} + +/* + * Returns -1 on connection close + * 0 on timeout. + * >0 length of the inputline (including final \n) + * + */ +int +tty_recv (struct tty *t, char *buf, uint32_t *timeout) +{ + int r; + chopstx_poll_cond_t poll_desc; + + poll_desc.type = CHOPSTX_POLL_COND; + poll_desc.ready = 0; + poll_desc.cond = &t->cnd; + poll_desc.mutex = &t->mtx; + poll_desc.check = check_rx; + poll_desc.arg = t; + + while (1) + { + struct chx_poll_head *pd_array[1] = { + (struct chx_poll_head *)&poll_desc + }; + chopstx_poll (timeout, 1, pd_array); + chopstx_mutex_lock (&t->mtx); + r = check_rx (t); + chopstx_mutex_unlock (&t->mtx); + if (r || (timeout != NULL && *timeout == 0)) + break; + } + + chopstx_mutex_lock (&t->mtx); + if (t->flag_connected == 0) + r = -1; + else if (t->flag_input_avail) + { + r = t->inputline_len; + memcpy (buf, t->inputline, r); + t->flag_input_avail = 0; + usb_lld_rx_enable (ENDP3); + t->inputline_len = 0; + } + else + r = 0; + chopstx_mutex_unlock (&t->mtx); + + return r; +} diff --git a/gnuk/chopstx/example-fraucheky/EXAMPLE-README b/gnuk/chopstx/example-fraucheky/EXAMPLE-README new file mode 100644 index 0000000000..f214497ac0 --- /dev/null +++ b/gnuk/chopstx/example-fraucheky/EXAMPLE-README @@ -0,0 +1,43 @@ +(0) configure and make + +This example assumes that you have fraucheky source code +along with chopstx. + + --- chopstx --- chostx --- example-fraucheky + | + \- fraucheky + + +If you use vendor id and product id of 234b:0004 under +the condition of FSIJ, you can configure this program as: + + $ ./configure --vidpid=234b:0004 + +then, invoke make. + + $ make + + +(1) preparation as root + +Install USBIP host module +# modprobe vhci_hcd + + +(2) Run sample program of USBIP server + +$ ./sample-msc + + +(3) Use the USB device as root + +Attach the Chopstx application program (USBIP device) to USBIP host. + +# usbip attach -r 127.0.0.1 -b 1-1 + + +(4) Use the USB Mass Storage Class device + +You can mount the storage. + +Unmounting the storage, the program will exit. diff --git a/gnuk/chopstx/example-fraucheky/INDEX.HTM b/gnuk/chopstx/example-fraucheky/INDEX.HTM new file mode 100644 index 0000000000..b72e7d73e7 --- /dev/null +++ b/gnuk/chopstx/example-fraucheky/INDEX.HTM @@ -0,0 +1,62 @@ + + +How to use this device + + + + +

How to use this device

+

Introduction

+

This is an example program for GNU/Linux to see how + Chopstx works with USB emulation by USBIP.

+

Now, you are accessing this file. + It means that the device is running as USB memory + (USB Mass Storage Class device).

+ +

README, GNU GPL and Corresponding Source code

+

In this program, it contains copyrighted software that is licensed under + the GPLv3. Please see README for the explanation of the software and see + GNU GPLv3 for terms and conditions.

+

You may obtain the complete Corresponding Source code from git repositories. + This program uses Chopstx for RTOS, and Fraucheky for + GPL container. Those code are availabe in the Chopstx repository.

+

You can also download tarballs from the repository.

+ + +

Source code by physical media

+

For those who want source code by physical media, Flying Stone Technology sells a microSD card (with SD card adapter) which contains all repositories at git.gniibe.org.

+

The price is JPY1000 for a single media including tax and shipment in Japan. Please use YUUBIN-FURIKAE account of Flying Stone Technology below. Note that the account name is in Japanese (which means: Flying Stone Technology).

+

On the form of YUUBIN-FURIKAE, you specify your address and you can write some message. The media will be sent to your address written in the form.

+
    +
  • Account No: 00110-2-346446
  • +
  • Account name: Tobiishi Gijutsu
  • +
+ +

If it is difficult for you to use YUUBIN-FURIKAE, please write to the following address.

+
    +
  • Niibe Yutaka
  • +
  • Flying Stone Technology
  • +
  • 1-5-14 Iwagami, Maebashi
  • +
  • Gunma, 371-0031 JAPAN
  • +
+ +

This source code offer will be valid for three years after the shipment of this device or at least until 2021-03-31.

+ +

Links

+ + + diff --git a/gnuk/chopstx/example-fraucheky/Makefile b/gnuk/chopstx/example-fraucheky/Makefile new file mode 100644 index 0000000000..7458b5f16a --- /dev/null +++ b/gnuk/chopstx/example-fraucheky/Makefile @@ -0,0 +1,45 @@ +# Makefile for NeuG + +# Define project name here +PROJECT = sample-msc + +CHOPSTX = .. +FRAUCHEKY = ../../fraucheky +LDSCRIPT= + +CSRC = main.c +CHIP=gnu-linux +USE_SYS = yes +USE_USB = yes +EMULATION=yes +# USE_ADC = yes + +include $(FRAUCHEKY)/src.mk + +################################### +CROSS = +CC = $(CROSS)gcc +LD = $(CROSS)gcc +OBJCOPY = $(CROSS)objcopy + +MCU = none +CWARN = -Wall -Wextra -Wstrict-prototypes +DEFS = -DGNU_LINUX_EMULATION +OPT = -O3 -g +LIBS = -lpthread + +BFDNAME_OBJ=elf64-x86-64 +BFDARCH=i386:x86-64 + +####################### +include $(CHOPSTX)/rules.mk + +include $(FRAUCHEKY)/build.mk +################################### + +board.h: + @echo Please run configure to have a symbolic link \'board.h\' + @exit 1 + +distclean:: clean + -rm -f config.h diff --git a/gnuk/chopstx/example-fraucheky/board.h b/gnuk/chopstx/example-fraucheky/board.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gnuk/chopstx/example-fraucheky/config.h.in b/gnuk/chopstx/example-fraucheky/config.h.in new file mode 100644 index 0000000000..10699bc464 --- /dev/null +++ b/gnuk/chopstx/example-fraucheky/config.h.in @@ -0,0 +1,2 @@ +@FRAUCHEKY_DEFINE@ +@FRAUCHEKY_MSC_DEFINE@ diff --git a/gnuk/chopstx/example-fraucheky/configure b/gnuk/chopstx/example-fraucheky/configure new file mode 100644 index 0000000000..7bc023cc58 --- /dev/null +++ b/gnuk/chopstx/example-fraucheky/configure @@ -0,0 +1,137 @@ +#! /bin/bash + +# This is bash which supports ANSI-C Quoting +nl=$'\n' + +# Default settings +help=no +vidpid=none +verbose=no +debug=no +with_fraucheky=yes +with_index=./INDEX.HTM + +if test -d ../.git; then + REVISION=`git describe --dirty="-modified"` + REVISION_CHOPSTX=`cd .. && git describe --dirty="-modified"` + REVISION_FRAUCHEKY=`cd ../../fraucheky &&git describe --dirty="-modified"` +else + REVISION=`cat ../VERSION` + REVISION_CHOPSTX=`cat ../VERSION` + REVISION_FRAUCHEKY=`cat ../../fraucheky/VERSION` +fi + +# Process each option +for option; do + case $option in + *=*) optarg=`expr "X$option" : '[^=]*=\(.*\)'` ;; + *) optarg=yes ;; + esac + + case $option in + -h | --help) + help=yes ;; + -v | --verbose) + verbose=yes ;; + --vidpid=*) + vidpid=$optarg ;; + *) + echo "Unrecognized option \`$option'" >&2 + echo "Try \`$0 --help' for more information." >&2 + exit 1 + ;; + esac +done + +if test "$help" = "yes"; then + cat <] + --with-index=INDEX specify INDEX file [none] +EOF + exit 0 +fi + +if test "$vidpid" = "none"; then + echo "Please specify Vendor ID and Product ID by --vidpid option." >&2 + exit 1 +fi + +VIDPID="$vidpid" +VERSION="0100" +PRODUCT="Fraucheky" +VENDOR="Free Software Initiative of Japan" +SERIALNO="FSIJ-`cat ../VERSION | sed -e 's%^[^/]*/%%'`-" + + +../../fraucheky/configure "$vidpid" $with_index $REVISION $REVISION_CHOPSTX $REVISION_FRAUCHEKY + +ENABLE_FRAUCHEKY="" +FRAUCHEKY_DEFINE="#define FRAUCHEKY_SUPPORT 1" +FRAUCHEKY_MSC_DEFINE="#define MSC_INTERFACE_NO 2" +if ! test -f ../../fraucheky/build.mk; then + echo "'fraucheky' not found" >&2 + exit 1 +fi + +output_vid_pid_version () { + echo $VIDPID | sed -n -e "s%^\([0-9a-f][0-9a-f]\)\([0-9a-f][0-9a-f]\):\([0-9a-f][0-9a-f]\)\([0-9a-f][0-9a-f]\)$% 0x\2, 0x\1, /* idVendor */\\${nl} 0x\4, 0x\3, /* idProduct */%p" + echo $VERSION | sed -n -e "s%^\([0-9a-f][0-9a-f]\)\([0-9a-f][0-9a-f]\)$% 0x\2, 0x\1, /* bcdDevice */%p" +} + +output_vendor_product_serial_strings () { + prefix=$1 + + echo "static const uint8_t ${prefix}string_vendor[] = {" + echo " ${#VENDOR}*2+2, /* bLength */" + echo " STRING_DESCRIPTOR, /* bDescriptorType */" + echo " /* Manufacturer: \"$VENDOR\" */" + echo $VENDOR | sed -e "s/\(........\)/\1\\${nl}/g" | sed -n -e "s/\(.\)/'\1', 0, /g" -e "s/^/ /" -e "/^ ./s/ $//p" + echo '};' + echo + echo "static const uint8_t ${prefix}string_product[] = {" + echo " ${#PRODUCT}*2+2, /* bLength */" + echo " STRING_DESCRIPTOR, /* bDescriptorType */" + echo " /* Product name: \"$PRODUCT\" */" + echo $PRODUCT | sed -e "s/\(........\)/\1\\${nl}/g" | sed -n -e "s/\(.\)/'\1', 0, /g" -e "s/^/ /" -e "/^ ./s/ $//p" + echo '};' + + if test -n "$prefix"; then + echo + echo "static uint8_t ${prefix}string_serial[] = {" + echo " ${#SERIALNO}*2+2+16, /* bLength */" + echo " STRING_DESCRIPTOR, /* bDescriptorType */" + echo " /* Serial number: \"$SERIALNO\" */" + echo $SERIALNO | sed -e "s/\(........\)/\1\\${nl}/g" | sed -n -e "s/\(.\)/'\1', 0, /g" -e "s/^/ /" -e "/^ ./s/ $//p" + echo " 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff," + echo " 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff," + echo '};' + echo + echo "static const uint8_t ${prefix}revision_detail[] = {" + echo " ${#REVISION}*2+2, /* bLength */" + echo " STRING_DESCRIPTOR, /* bDescriptorType */" + echo " /* revision detail: \"$REVISION\" */" + echo $REVISION | sed -e "s/\(........\)/\1\\${nl}/g" | sed -n -e "s/\(.\)/'\1', 0, /g" -e "s/^/ /" -e "/^ ./s/ $//p" + echo '};' + echo + echo "static const uint8_t ${prefix}config_options[] = {" + echo " ${#CONFIG}*2+2, /* bLength */" + echo " STRING_DESCRIPTOR, /* bDescriptorType */" + echo " /* configure options: \"$CONFIG\" */" + echo $CONFIG | sed -e "s/\(........\)/\1\\${nl}/g" | sed -n -e "s/\(.\)/'\1', 0, /g" -e "s/^/ /" -e "/^ ./s/ $//p" + echo '};' + fi +} + +output_vid_pid_version > fraucheky-vid-pid-ver.c.inc +output_vendor_product_serial_strings >fraucheky-usb-strings.c.inc + +sed -e "s/@FRAUCHEKY_DEFINE@/$FRAUCHEKY_DEFINE/" \ + -e "s/@FRAUCHEKY_MSC_DEFINE@/$FRAUCHEKY_MSC_DEFINE/" \ + < config.h.in > config.h +exit 0 diff --git a/gnuk/chopstx/example-fraucheky/main.c b/gnuk/chopstx/example-fraucheky/main.c new file mode 100644 index 0000000000..34b4a01549 --- /dev/null +++ b/gnuk/chopstx/example-fraucheky/main.c @@ -0,0 +1,318 @@ +#include +#include +#include +#include + +#include "config.h" +#include "usb_lld.h" + +#define NUM_INTERFACES 1 +#define FEATURE_BUS_POWERED 0x80 + +static chopstx_mutex_t usb_mtx; +static chopstx_cond_t usb_cnd; +static uint32_t bDeviceState = USB_DEVICE_STATE_UNCONNECTED; + +extern void EP6_IN_Callback (uint16_t len); +extern void EP6_OUT_Callback (uint16_t len); + +#define MSC_MASS_STORAGE_RESET_COMMAND 0xFF +extern int fraucheky_enabled (void); +extern void fraucheky_init (void); +extern void fraucheky_main (void); + +extern void fraucheky_setup_endpoints_for_interface (struct usb_dev *dev, int stop); +extern int fraucheky_setup (struct usb_dev *dev); +extern int fraucheky_get_descriptor (struct usb_dev *dev); + +static void +setup_endpoints_for_interface (struct usb_dev *dev, uint16_t interface, int stop) +{ + if (interface == 0) + fraucheky_setup_endpoints_for_interface (dev, stop); +} + +static void +usb_device_reset (struct usb_dev *dev) +{ + int i; + + usb_lld_reset (dev, FEATURE_BUS_POWERED); + + /* Initialize Endpoint 0. */ + usb_lld_setup_endp (dev, ENDP0, 1, 1); + + /* Notify upper layer. */ + chopstx_mutex_lock (&usb_mtx); + bDeviceState = USB_DEVICE_STATE_ATTACHED; + chopstx_cond_signal (&usb_cnd); + chopstx_mutex_unlock (&usb_mtx); +} + +static void +usb_ctrl_write_finish (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT); + + if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT) && arg->index == 0 + && USB_SETUP_SET (arg->type)) + { + if (arg->request == MSC_MASS_STORAGE_RESET_COMMAND) + fraucheky_setup_endpoints_for_interface (dev, 0); + } +} + +static int +usb_setup (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT); + + if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT) + && arg->index == 0) + return fraucheky_setup (dev); + + return -1; +} + +static int +usb_set_configuration (struct usb_dev *dev) +{ + int i; + uint8_t current_conf; + + current_conf = usb_lld_current_configuration (dev); + if (current_conf == 0) + { + if (dev->dev_req.value != 1) + return -1; + + usb_lld_set_configuration (dev, 1); + for (i = 0; i < NUM_INTERFACES; i++) + setup_endpoints_for_interface (dev, i, 0); + chopstx_mutex_lock (&usb_mtx); + bDeviceState = USB_DEVICE_STATE_CONFIGURED; + chopstx_mutex_unlock (&usb_mtx); + } + else if (current_conf != dev->dev_req.value) + { + if (dev->dev_req.value != 0) + return -1; + + usb_lld_set_configuration (dev, 0); + for (i = 0; i < NUM_INTERFACES; i++) + setup_endpoints_for_interface (dev, i, 1); + chopstx_mutex_lock (&usb_mtx); + bDeviceState = USB_DEVICE_STATE_ADDRESSED; + chopstx_cond_signal (&usb_cnd); + chopstx_mutex_unlock (&usb_mtx); + } + + /* Do nothing when current_conf == value */ + return usb_lld_ctrl_ack (dev); +} + + +static int +usb_set_interface (struct usb_dev *dev) +{ + uint16_t interface = dev->dev_req.index; + uint16_t alt = dev->dev_req.value; + + if (interface >= NUM_INTERFACES) + return -1; + + if (alt != 0) + return -1; + else + { + setup_endpoints_for_interface (dev, interface, 0); + return usb_lld_ctrl_ack (dev); + } +} + +static int +usb_get_interface (struct usb_dev *dev) +{ + const uint8_t zero = 0; + uint16_t interface = dev->dev_req.index; + + if (interface >= NUM_INTERFACES) + return -1; + + return usb_lld_ctrl_send (dev, &zero, 1); +} + + +static int +usb_get_status_interface (struct usb_dev *dev) +{ + const uint16_t status_info = 0; + uint16_t interface = dev->dev_req.index; + + if (interface >= NUM_INTERFACES) + return -1; + + return usb_lld_ctrl_send (dev, &status_info, 2); +} + +static void usb_tx_done (uint8_t ep_num, uint16_t len); +static void usb_rx_ready (uint8_t ep_num, uint16_t len); + + +#define PRIO_USB 3 + +static void * +usb_main (void *arg) +{ + chopstx_intr_t interrupt; + struct usb_dev dev; + int e; + + (void)arg; + chopstx_claim_irq (&interrupt, INTR_REQ_USB); + usb_lld_init (&dev, FEATURE_BUS_POWERED); + goto event_handle; /* For old SYS < 3.0 */ + + while (1) + { + chopstx_intr_wait (&interrupt); + + if (interrupt.ready) + { + uint8_t ep_num; + + event_handle: + e = usb_lld_event_handler (&dev); + chopstx_intr_done (&interrupt); + ep_num = USB_EVENT_ENDP (e); + + if (ep_num != 0) + { + if (USB_EVENT_TXRX (e)) + usb_tx_done (ep_num, USB_EVENT_LEN (e)); + else + usb_rx_ready (ep_num, USB_EVENT_LEN (e)); + } + else + switch (USB_EVENT_ID (e)) + { + case USB_EVENT_DEVICE_RESET: + usb_device_reset (&dev); + continue; + + case USB_EVENT_DEVICE_ADDRESSED: + chopstx_mutex_lock (&usb_mtx); + bDeviceState = USB_DEVICE_STATE_ADDRESSED; + chopstx_cond_signal (&usb_cnd); + chopstx_mutex_unlock (&usb_mtx); + continue; + + case USB_EVENT_GET_DESCRIPTOR: + if (fraucheky_get_descriptor (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_SET_CONFIGURATION: + if (usb_set_configuration (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_SET_INTERFACE: + if (usb_set_interface (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_CTRL_REQUEST: + /* Device specific device request. */ + if (usb_setup (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_GET_STATUS_INTERFACE: + if (usb_get_status_interface (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_GET_INTERFACE: + if (usb_get_interface (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_SET_FEATURE_DEVICE: + case USB_EVENT_SET_FEATURE_ENDPOINT: + case USB_EVENT_CLEAR_FEATURE_DEVICE: + case USB_EVENT_CLEAR_FEATURE_ENDPOINT: + usb_lld_ctrl_ack (&dev); + continue; + + case USB_EVENT_CTRL_WRITE_FINISH: + /* Control WRITE transfer finished. */ + usb_ctrl_write_finish (&dev); + continue; + + case USB_EVENT_OK: + case USB_EVENT_DEVICE_SUSPEND: + default: + continue; + } + } + } + + return NULL; +} + +static void +usb_tx_done (uint8_t ep_num, uint16_t len) +{ + if (ep_num == ENDP6) + EP6_IN_Callback (len); +} + +static void +usb_rx_ready (uint8_t ep_num, uint16_t len) +{ + if (ep_num == ENDP6) + EP6_OUT_Callback (len); +} + +static char __process3_stack_base__[4096]; + +#define STACK_ADDR_USB ((uintptr_t)__process3_stack_base__) +#define STACK_SIZE_USB (sizeof __process3_stack_base__) + +#ifdef GNU_LINUX_EMULATION +#define main emulated_main +#endif + +/* + * Entry point. + * + * NOTE: the main function is already a thread in the system on entry. + */ +int +main (int argc, char **argv) +{ + chopstx_t usb_thd; + + (void)argc; + (void)argv; + + chopstx_mutex_init (&usb_mtx); + chopstx_cond_init (&usb_cnd); + + bDeviceState = USB_DEVICE_STATE_UNCONNECTED; + usb_thd = chopstx_create (PRIO_USB, STACK_ADDR_USB, STACK_SIZE_USB, + usb_main, NULL); + fraucheky_init (); + while (bDeviceState != USB_DEVICE_STATE_CONFIGURED) + chopstx_usec_wait (250*1000); + fraucheky_main (); + chopstx_cancel (usb_thd); + chopstx_join (usb_thd, NULL); + usb_lld_shutdown (); + bDeviceState = USB_DEVICE_STATE_UNCONNECTED; + + return 0; +} diff --git a/gnuk/chopstx/example-fs-bb48/Makefile b/gnuk/chopstx/example-fs-bb48/Makefile new file mode 100644 index 0000000000..e4d0c73dd1 --- /dev/null +++ b/gnuk/chopstx/example-fs-bb48/Makefile @@ -0,0 +1,36 @@ +# Makefile for example application of Chopstx + +PROJECT = sample + +### Currently, it's for FS-BB48. + +CHOPSTX = .. +LDSCRIPT= sample.ld +CSRC = sample.c usb-cdc.c command.c touch.c +CHIP=mkl27z + +USE_SYS = yes +USE_USB = yes +USE_ADC = yes + +################################### +CROSS = arm-none-eabi- +CC = $(CROSS)gcc +LD = $(CROSS)gcc +OBJCOPY = $(CROSS)objcopy + +MCU = cortex-m0plus +CWARN = -Wall -Wextra -Wstrict-prototypes +DEFS = -DFREE_STANDING -DMHZ=48 -DUSE_SYS3 +OPT = -O3 -Os -g +LIBS = + +#################### +include ../rules.mk + +board.h: + @echo Please make a symbolic link \'board.h\' to a file in ../board; + @exit 1 + +distclean: clean + rm -f board.h diff --git a/gnuk/chopstx/example-fs-bb48/board.h b/gnuk/chopstx/example-fs-bb48/board.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gnuk/chopstx/example-fs-bb48/command.c b/gnuk/chopstx/example-fs-bb48/command.c new file mode 100644 index 0000000000..485b11621d --- /dev/null +++ b/gnuk/chopstx/example-fs-bb48/command.c @@ -0,0 +1,542 @@ +#include +#include +#include +#include "tty.h" +#include "config.h" +#ifdef ADC_SUPPORT +#include "adc.h" +static int adc_initialized = 0; +#endif +#include "board.h" +#include "sys.h" + +struct command_table +{ + const char *name; + void (*handler) (struct tty *tty, const char *line); +}; + +/* + * Put a line (or lines) to TTY. + * LINE should be terminated with newline. + */ +static void +put_line (struct tty *tty, const char *line) +{ + tty_send (tty, line, strlen (line)); +} + +static const char *help_string = + "mdw ADDR [COUNT]; memory display word\r\n" + "mww ADDR VALUE [COUNT]; memory write word\r\n" + "fes ADDR [COUNT]; flash erase sector\r\n" + "fww ADDR VALUE [COUNT]; flash write word\r\n" +#ifdef CRC32_SUPPORT + "crc32 string; CRC32 calc string\r\n" +#endif +#ifdef ADC_SUPPORT + "adc; get 256-byte from ADC\r\n" +#endif + "sysinfo; system information\r\n" + "help\r\n"; + +static char hexchar (uint8_t x) +{ + x &= 0x0f; + if (x <= 0x09) + return '0' + x; + else if (x <= 0x0f) + return 'a' + x - 10; + else + return '?'; +} + +static char * +compose_decimal (char *s, int value) +{ + uint32_t v; + int col = 1000000000; + int d; + int digit_output = 0; + + if (value < 0) + { + *s++ = '-'; + v = 1 + ~((uint32_t)value); + } + else + v = (uint32_t)value; + + while (col >= 10) + { + if (v >= (uint32_t)col) + { + d = v / col; + v = v - d * col; + *s++ = d + '0'; + digit_output = 1; + } + else if (digit_output) + *s++ = '0'; + + col = col / 10; + } + + *s++ = v + '0'; + + return s; +} + + +static char * +compose_hex (char *s, uint32_t v) +{ + s[0] = hexchar (v >> 28); + s[1] = hexchar (v >> 24); + s[2] = hexchar (v >> 20); + s[3] = hexchar (v >> 16); + s[4] = hexchar (v >> 12); + s[5] = hexchar (v >> 8); + s[6] = hexchar (v >> 4); + s[7] = hexchar (v); + return s+8; +} + +static const char * +get_hex (struct tty *tty, const char *s, uint32_t *v_p) +{ + uint32_t v = 0; + char c; + + if (s[0] == '0' && s[1] == 'x') + s = s + 2; + while (1) + { + c = *s++; + + if (c == 0) + { + s--; + break; + } + + if (c == ' ') + break; + + v = (v << 4); + if (c >= '0' && c <= '9') + v += (c - '0'); + else if (c >= 'a' && c <= 'f') + v += (c - 'a') + 10; + else if (c >= 'A' && c <= 'F') + v += (c - 'A') + 10; + else + { + put_line (tty, "hex error\r\n"); + return NULL; + } + } + + *v_p = v; + return s; +} + + +#define TOUCH_VALUE_HIGH 100 +#define TOUCH_VALUE_LOW 50 +static void +cmd_button (struct tty *tty, const char *line) +{ + int i = 0; + extern uint16_t touch_get (void); + uint16_t v0 = 0; + int touched = 0; + + (void)line; + put_line (tty, "Please touch the bear.\r\n"); + + while (i < 16) + { + uint16_t v = touch_get (); + v0 = (v0 * 2 + v)/3; + + if (touched == 0 && v0 > TOUCH_VALUE_HIGH) + { + tty_send (tty, "!", 1); + touched = 1; + } + else if (touched == 1 && v0 < TOUCH_VALUE_LOW) + { + tty_send (tty, ".", 1); + touched = 0; + i++; + } + + chopstx_usec_wait (10*1000); + } + + tty_send (tty, "\r\n", 2); +} + + +static void +cmd_touch (struct tty *tty, const char *line) +{ + int i; + extern uint16_t touch_get (void); + + (void)line; + put_line (tty, "Please touch the bear.\r\n"); + + for (i = 0; i < 20; i++) + { + uint16_t v; + char output[8]; + char *s; + + chopstx_usec_wait (1000*1000); + v = touch_get (); + s = compose_decimal (output, v); + *s++ = '\r'; + *s++ = '\n'; + tty_send (tty, output, s - output); + } +} + + +static void +cmd_mdw (struct tty *tty, const char *line) +{ + int i; + uint32_t addr = 0; + int count = 0; + char c; + const char *s = line; + + s = get_hex (tty, s, &addr); + addr &= ~3; + if (s == NULL) + return; + + if (*s == 0) + count = 1; + else + { + while (1) + { + c = *s++; + + if (c == 0 || c == ' ') + break; + + count = count * 10; + if (c >= '0' && c <= '9') + count += c - '0'; + else + { + put_line (tty, "mdw error\r\n"); + return; + } + } + } + + i = 0; + while (i < count) + { + uint32_t v; + char output[48]; + char *s; + + s = compose_hex (output, addr); + *s++ = ':'; + *s++ = ' '; + + while (1) + { + v = *(uint32_t *)addr; + s = compose_hex (s, v); + i++; + addr += 4; + if (i >= count || (i % 4) == 0) + break; + *s++ = ' '; + } + + *s++ = '\r'; + *s++ = '\n'; + tty_send (tty, output, s - output); + } +} + +static void +cmd_mww (struct tty *tty, const char *line) +{ + (void)tty; + (void)line; + put_line (tty, "mww not yet supported\r\n"); +} + +static void +cmd_fes (struct tty *tty, const char *line) +{ + int i; + uint32_t addr = 0; + int count = 0; + char c; + const char *s = line; + + s = get_hex (tty, s, &addr); + if (s == NULL) + return; + + if (*s == 0) + count = 1; + else + { + while (1) + { + c = *s++; + + if (c == 0 || c == ' ') + break; + + count = count * 10; + if (c >= '0' && c <= '9') + count += c - '0'; + else + { + put_line (tty, "fww error\r\n"); + return; + } + } + } + + for (i = 0; i < count; i++) + { + flash_erase_page (addr); + addr += 1024; + } +} + +static void +cmd_fww (struct tty *tty, const char *line) +{ + int i; + uint32_t addr = 0; + uint32_t value = 0; + int count = 0; + char c; + const char *s = line; + + s = get_hex (tty, s, &addr); + if (s == NULL) + return; + + if (*s == 0) + return; + + s = get_hex (tty, s, &value); + if (s == NULL) + return; + + if (*s == 0) + count = 1; + else + { + while (1) + { + c = *s++; + + if (c == 0 || c == ' ') + break; + + count = count * 10; + if (c >= '0' && c <= '9') + count += c - '0'; + else + { + put_line (tty, "fww error\r\n"); + return; + } + } + } + + for (i = 0; i < count; i++) + { + flash_program_word (addr, value); + addr += 4; + } +} + + +#ifdef CRC32_SUPPORT +static unsigned int crc_value; + +static void +cmd_crc32 (struct tty *tty, const char *line) +{ + uint32_t v; + char string[10]; + char *s; + + crc32_init (&crc_value); + while (*line) + crc32_u8 (&crc_value, *line++); + v = crc_value ^ 0xffffffff; + + s = compose_hex (string, v); + *s++ = '\r'; + *s++ = '\n'; + tty_send (tty, string, sizeof (string)); +} +#endif + +#ifdef ADC_SUPPORT +static void +cmd_adc (struct tty *tty, const char *line) +{ + int i; + char output[73]; + char *s; + + (void)line; + + if (!adc_initialized) + { + if (adc_init ()) + { + put_line (tty, "adc_init error\r\n"); + return; + } + else + { + adc_start (); + adc_initialized = 1; + } + } + + adc_start_conversion (0, 64); + adc_wait_completion (); + + i = 0; + s = output; + while (1) + { + s = compose_hex (s, adc_buf[i]); + i++; + if ((i % 8)) + *s++ = ' '; + else + { + *s++ = '\r'; + *s++ = '\n'; + tty_send (tty, output, s - output); + s = output; + if (i >= 64) + break; + } + } +} +#endif + +static void +cmd_sysinfo (struct tty *tty, const char *line) +{ + char output[73]; + char *s; + int i; + + (void)line; + memcpy (output, "SYS version: ", 13); + s = output + 13; + *s++ = sys_version[2]; + *s++ = sys_version[4]; + *s++ = sys_version[6]; + *s++ = '\r'; + *s++ = '\n'; + tty_send (tty, output, s - output); + + memcpy (output, "Board ID: ", 10); + s = output + 10; + s = compose_hex (s, sys_board_id); + *s++ = '\r'; + *s++ = '\n'; + tty_send (tty, output, s - output); + + memcpy (output, "Board name: ", 12); + s = output + 12; + for (i = 0; i < (int)sizeof (output) - 2; i ++) + if ((*s = sys_board_name[i]) == 0) + break; + else + s++; + + *s++ = '\r'; + *s++ = '\n'; + tty_send (tty, output, s - output); +} + + +static void +cmd_help (struct tty *tty, const char *line) +{ + (void)line; + put_line (tty, help_string); +} + + +struct command_table command_table[] = { + { "button", cmd_button }, + { "touch", cmd_touch }, + { "mdw", cmd_mdw }, + { "mww", cmd_mww }, + { "fes", cmd_fes }, + { "fww", cmd_fww }, +#ifdef CRC32_SUPPORT + { "crc32", cmd_crc32 }, +#endif +#ifdef ADC_SUPPORT + { "adc", cmd_adc }, +#endif + { "sysinfo", cmd_sysinfo }, + { "help", cmd_help }, +}; + +#define N_CMDS (int)(sizeof (command_table) / sizeof (struct command_table)) + + +/* + * Dispatch a command parsing LINE. + * Line is NULL terminated with no newline. + */ +void +cmd_dispatch (struct tty *tty, const char *line) +{ + int i; + const char *p; + unsigned int n = 0; + + p = line; + while (*p) + { + if (*p++ == ' ') + break; + n++; + } + + for (i = 0; i < N_CMDS; i++) + if (n == strlen (command_table[i].name) + && strncmp ((const char *)line, command_table[i].name, n) == 0) + break; + + if (i != N_CMDS) + (*command_table[i].handler) (tty, p); + else + { + char crlf[] = { '\r', '\n' }; + + put_line (tty, "No such command: "); + tty_send (tty, line, n); + tty_send (tty, crlf, sizeof (crlf)); + } +} diff --git a/gnuk/chopstx/example-fs-bb48/command.h b/gnuk/chopstx/example-fs-bb48/command.h new file mode 100644 index 0000000000..139ab4a626 --- /dev/null +++ b/gnuk/chopstx/example-fs-bb48/command.h @@ -0,0 +1 @@ +void cmd_dispatch (struct tty *tty, const char *line); diff --git a/gnuk/chopstx/example-fs-bb48/config.h b/gnuk/chopstx/example-fs-bb48/config.h new file mode 100644 index 0000000000..ceb916afd6 --- /dev/null +++ b/gnuk/chopstx/example-fs-bb48/config.h @@ -0,0 +1,2 @@ +#define CRC32_SUPPORT +#define ADC_SUPPORT diff --git a/gnuk/chopstx/example-fs-bb48/crc32.c b/gnuk/chopstx/example-fs-bb48/crc32.c new file mode 100644 index 0000000000..953532492b --- /dev/null +++ b/gnuk/chopstx/example-fs-bb48/crc32.c @@ -0,0 +1,22 @@ +const unsigned int *const crc32_table= (const unsigned int *)0x00000480; + +void +crc32_init (unsigned int *p) +{ + *p = 0xffffffff; +} + +static void +crc32_u8 (unsigned int *p, unsigned char v) +{ + *p = crc32_table[(*p & 0xff) ^ v] ^ (*p >> 8); +} + +void +crc32_u32 (unsigned int *p, unsigned int u) +{ + crc32_u8 (p, u & 0xff); + crc32_u8 (p, (u >> 8)& 0xff); + crc32_u8 (p, (u >> 16)& 0xff); + crc32_u8 (p, (u >> 24)& 0xff); +} diff --git a/gnuk/chopstx/example-fs-bb48/gen_crc_table.py b/gnuk/chopstx/example-fs-bb48/gen_crc_table.py new file mode 100644 index 0000000000..908531aac4 --- /dev/null +++ b/gnuk/chopstx/example-fs-bb48/gen_crc_table.py @@ -0,0 +1,22 @@ +# +# Polynomial for CRC32: +# x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1 +# +# When it is represented in binary, it's: +# 0x04C11DB7 +# = +# 0000 0100 1100 0001 0001 1101 1011 0111 +# +# When we put in reverse bit-order, it's +# 0xedb88320 + +for i in range(0,256): + c = i + for j in range(0,8): + if (c&1): + c = 0xEDB88320 ^ (c >> 1) + else: + c = c >> 1 + print("0x%08x," % c), + if (i % 6) == 5: + print("") diff --git a/gnuk/chopstx/example-fs-bb48/sample.c b/gnuk/chopstx/example-fs-bb48/sample.c new file mode 100644 index 0000000000..a91cd62b5f --- /dev/null +++ b/gnuk/chopstx/example-fs-bb48/sample.c @@ -0,0 +1,189 @@ +#include +#include +#include +#include + +#include "usb_lld.h" +#include "tty.h" +#include "board.h" +#include "command.h" +#include + +static chopstx_mutex_t mtx; +static chopstx_cond_t cnd0; +static chopstx_cond_t cnd1; + +uint8_t u; +static uint8_t v; +static uint8_t m; /* 0..100 */ + +static void +wait_for (uint32_t usec) +{ +#if defined(BUSY_LOOP) + uint32_t count = usec * 6; + uint32_t i; + + for (i = 0; i < count; i++) + asm volatile ("" : : "r" (i) : "memory"); +#else + chopstx_usec_wait (usec); +#endif +} + +static void * +pwm (void *arg) +{ + (void)arg; + + chopstx_mutex_lock (&mtx); + chopstx_cond_wait (&cnd0, &mtx); + chopstx_mutex_unlock (&mtx); + + while (1) + { + set_led (u&v); + wait_for (m); + set_led (0); + wait_for (100-m); + } + + return NULL; +} + +static void * +blk (void *arg) +{ + (void)arg; + + chopstx_mutex_lock (&mtx); + chopstx_cond_wait (&cnd1, &mtx); + chopstx_mutex_unlock (&mtx); + + while (1) + { + v = 0; + wait_for (200*1000); + v = 1; + wait_for (200*1000); + } + + return NULL; +} + + +#define PRIO_PWM 3 +#define PRIO_BLK 2 + +extern uint8_t __process1_stack_base__[], __process1_stack_size__[]; +extern uint8_t __process2_stack_base__[], __process2_stack_size__[]; + +#define STACK_ADDR_PWM ((uint32_t)__process1_stack_base__) +#define STACK_SIZE_PWM ((uint32_t)__process1_stack_size__) + +#define STACK_ADDR_BLK ((uint32_t)__process2_stack_base__) +#define STACK_SIZE_BLK ((uint32_t)__process2_stack_size__) + + +static char hexchar (uint8_t x) +{ + x &= 0x0f; + if (x <= 0x09) + return '0' + x; + else if (x <= 0x0f) + return 'a' + x - 10; + else + return '?'; +} + + +extern void touch_init (void); + +int +main (int argc, const char *argv[]) +{ + struct tty *tty; + uint8_t count; + + (void)argc; + (void)argv; + + chopstx_mutex_init (&mtx); + chopstx_cond_init (&cnd0); + chopstx_cond_init (&cnd1); + + m = 10; + + chopstx_create (PRIO_PWM, STACK_ADDR_PWM, STACK_SIZE_PWM, pwm, NULL); + chopstx_create (PRIO_BLK, STACK_ADDR_BLK, STACK_SIZE_BLK, blk, NULL); + + chopstx_usec_wait (200*1000); + + chopstx_mutex_lock (&mtx); + chopstx_cond_signal (&cnd0); + chopstx_cond_signal (&cnd1); + chopstx_mutex_unlock (&mtx); + + u = 1; + + touch_init (); + + tty = tty_open (); + tty_wait_configured (tty); + + count = 0; + m = 50; + while (1) + { + char s[LINEBUFSIZE]; + + connection_loop: + u = 1; + tty_wait_connection (tty); + + chopstx_usec_wait (50*1000); + + /* Send ZLP at the beginning. */ + tty_send (tty, s, 0); + + memcpy (s, "xx: Hello, World with Chopstx!\r\n", 32); + s[0] = hexchar (count >> 4); + s[1] = hexchar (count & 0x0f); + count++; + + if (tty_send (tty, s, 32) < 0) + continue; + + while (1) + { + uint32_t usec; + + /* Prompt */ + if (tty_send (tty, "> ", 2) < 0) + break; + + usec = 3000000; /* 3.0 seconds */ + while (1) + { + int size = tty_recv (tty, s, &usec); + u ^= 1; + + if (size < 0) + goto connection_loop; + + if (size == 1) + /* Do nothing but prompt again. */ + break; + else if (size) + { + /* Newline into NUL */ + s[size - 1] = 0; + cmd_dispatch (tty, (char *)s); + break; + } + } + } + } + + return 0; +} diff --git a/gnuk/chopstx/example-fs-bb48/sample.ld b/gnuk/chopstx/example-fs-bb48/sample.ld new file mode 100644 index 0000000000..bf24e5c54d --- /dev/null +++ b/gnuk/chopstx/example-fs-bb48/sample.ld @@ -0,0 +1,150 @@ +/* + * MK27Z memory setup. + */ +__main_stack_size__ = 0x0100; /* Idle+Exception handlers */ +__process0_stack_size__ = 0x0300; /* Main program */ +__process1_stack_size__ = 0x0200; /* first thread program */ +__process2_stack_size__ = 0x0100; /* second thread program */ +__process3_stack_size__ = 0x0200; /* third thread program */ + +MEMORY +{ + flash : org = 0x00000000, len = 256k + ram : org = 0x1fffe000, len = 32k +} + +__ram_start__ = ORIGIN(ram); +__ram_size__ = 32k; +__ram_end__ = __ram_start__ + __ram_size__; + +SECTIONS +{ + . = 0; + + _text = .; + + .text : ALIGN(16) SUBALIGN(8) + { + KEEP(*(.first_page.first_words)) + KEEP(*(.sys.version)) + KEEP(*(.sys.board_info)) + KEEP(*(.sys.vectors)) + build/sys-*.o(.text) + build/sys-*.o(.text.*) + build/sys-*.o(.rodata) + build/sys-*.o(.rodata.*) + . = ALIGN(1024); + KEEP(*(.flash_config)) + KEEP(*(.fixed_function.reset)) + KEEP(*(.fixed_function.flash_do_internal)) + KEEP(*(.fixed_function.flash_do)) + KEEP(*(.fixed_function.flash_erase_page)) + KEEP(*(.fixed_function.flash_program_word)) + KEEP(*(.fixed_function.crc32_init)) + KEEP(*(.fixed_function.crc32_u8)) + KEEP(*(.fixed_function.crc32_u32)) + /* + * Because of alignment requirement + * of startup.vectors, align to 256. + */ + . = ALIGN(256); + KEEP(*(.crc32_table)) + KEEP(*(.startup.vectors)) + . = ALIGN(16); + *(.text.startup.*) + *(.text) + *(.text.*) + *(.rodata) + *(.rodata.*) + *(.glue_7t) + *(.glue_7) + *(.gcc*) + . = ALIGN(8); + } > flash =0xffffffff + + /DISCARD/ : + { + *(.bss.startup.0) + } + + .ARM.extab : {*(.ARM.extab* .gnu.linkonce.armextab.*)} > flash + + .ARM.exidx : { + PROVIDE(__exidx_start = .); + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + PROVIDE(__exidx_end = .); + } > flash =0xffffffff + + .eh_frame_hdr : {*(.eh_frame_hdr)} > flash + + .eh_frame : ONLY_IF_RO {*(.eh_frame)} > flash + + .textalign : ONLY_IF_RO { . = ALIGN(8); } > flash + + _etext = .; + _textdata = _etext; + + .process_stack : + { + . = ALIGN(8); + __process3_stack_base__ = .; + . += __process3_stack_size__; + . = ALIGN(8); + __process3_stack_end__ = .; + __process2_stack_base__ = .; + . += __process2_stack_size__; + . = ALIGN(8); + __process2_stack_end__ = .; + __process1_stack_base__ = .; + . += __process1_stack_size__; + . = ALIGN(8); + __process1_stack_end__ = .; + __process0_stack_base__ = .; + . += __process0_stack_size__; + . = ALIGN(8); + __process0_stack_end__ = .; + } > ram + + .main_stack : + { + . = ALIGN(8); + __main_stack_base__ = .; + . += __main_stack_size__; + . = ALIGN(8); + __main_stack_end__ = .; + } > ram + + .data : + { + . = ALIGN(4); + PROVIDE(_data = .); + *(.data) + . = ALIGN(4); + *(.data.*) + . = ALIGN(4); + *(.ramtext) + . = ALIGN(4); + PROVIDE(_edata = .); + } > ram AT > flash =0xffffffff + + .bss : + { + . = ALIGN(4); + PROVIDE(_bss_start = .); + *(.bss) + . = ALIGN(4); + *(.bss.*) + . = ALIGN(4); + *(COMMON) + . = ALIGN(512); + __usb_bdt__ = .; + . += 512; + PROVIDE(_bss_end = .); + } > ram + + PROVIDE(end = .); + _end = .; +} + +__heap_base__ = _end; +__heap_end__ = __ram_end__; diff --git a/gnuk/chopstx/example-fs-bb48/touch.c b/gnuk/chopstx/example-fs-bb48/touch.c new file mode 100644 index 0000000000..0a8a441f96 --- /dev/null +++ b/gnuk/chopstx/example-fs-bb48/touch.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include + +struct TPM { + volatile uint32_t SC; + volatile uint32_t CNT; + volatile uint32_t MOD; + volatile uint32_t C0SC; + volatile uint32_t C0V; + volatile uint32_t C1SC; + volatile uint32_t C1V; + uint32_t rsvd0[13]; + volatile uint32_t STATUS; + uint32_t rsvd1[7]; + volatile uint32_t POL; + uint32_t rsvd2[4]; + volatile uint32_t CONF; +}; + +static struct TPM *const TPM1 = (struct TPM *)0x40039000; + +static chopstx_intr_t tpm1_intr; +#define INTR_REQ_TPM1 18 + +static void +gpio_assert_low (void) +{ + /* Assert LOW. */ + PORTB->PCR1 = (1<<8) /* GPIO */ + | (0<<6) /* DriveStrengthEnable=0 */ + | (0<<4) /* PassiveFilterEnable=0 */ + | (1<<2) /* SlewRateEnable = slow */ + | (0<<1) /* pull enable = 0 */ + | (0<<0) /* pull up select= 0 */ + ; + + GPIOB->PCOR = (1 << 1); /* PTB1: Clear: Output 0 */ +} + +uint16_t +touch_get (void) +{ + chopstx_prio_t prio_old; + uint16_t v; + + prio_old = chopstx_setpriority (CHOPSTX_PRIO_INHIBIT_PREEMPTION); + /* + * Start the timer's counter. + * TOF clear, TOIE=1, CPWMS=0, CMOD=1, PS=011. + */ + TPM1->SC = 0xcb; + + /* Let the register to pull it up. */ + PORTB->PCR1 = (3<<8) /* TPM1_CH1 */ + | (0<<6) /* DriveStrengthEnable=0 */ + | (0<<4) /* PassiveFilterEnable=0 */ + | (1<<2) /* SlewRateEnable = slow */ + | (0<<1) /* pull enable = 0 */ + | (0<<0) /* pullup select= 0 */ + ; + + chopstx_setpriority (prio_old); + + chopstx_intr_wait (&tpm1_intr); + gpio_assert_low (); + + v = TPM1->C1V; + + /* Clear overflow and CH1 capture. */ + TPM1->STATUS = 0x102; + /* Stop the timer. */ + TPM1->SC = 0; + TPM1->CNT = 0xffff; /* Writing causes reset of the counter. */ + + chopstx_intr_done (&tpm1_intr); + return v; +} + + +void +touch_init (void) +{ + chopstx_claim_irq (&tpm1_intr, INTR_REQ_TPM1); + + /* Input capture mode: MSB = 0, MSA = 0 */ + /* Rising edge: ELSB=0 ELSA=1 */ + TPM1->C1SC = 0x84; + TPM1->POL=0; + + /* No trigger. */ + /* Stop on overflow: CSOO=1 */ + /* Run the timer in the debug mode */ + TPM1->CONF = 0x000200c0; + + TPM1->CNT = 0xffff; /* Writing causes reset of the counter. */ + gpio_assert_low (); +} diff --git a/gnuk/chopstx/example-fs-bb48/tty.h b/gnuk/chopstx/example-fs-bb48/tty.h new file mode 100644 index 0000000000..77e7f463c0 --- /dev/null +++ b/gnuk/chopstx/example-fs-bb48/tty.h @@ -0,0 +1,9 @@ +#define LINEBUFSIZE 128 + +struct tty; + +struct tty *tty_open (void); +void tty_wait_configured (struct tty *tty); +void tty_wait_connection (struct tty *tty); +int tty_send (struct tty *tty, const char *buf, int count); +int tty_recv (struct tty *tty, char *buf, uint32_t *timeout); diff --git a/gnuk/chopstx/example-fs-bb48/usb-cdc.c b/gnuk/chopstx/example-fs-bb48/usb-cdc.c new file mode 100644 index 0000000000..f96244d71c --- /dev/null +++ b/gnuk/chopstx/example-fs-bb48/usb-cdc.c @@ -0,0 +1,952 @@ +#include +#include +#include +#include +#include "board.h" +#include "usb_lld.h" +#include "tty.h" + +static chopstx_intr_t usb_intr; + +struct line_coding +{ + uint32_t bitrate; + uint8_t format; + uint8_t paritytype; + uint8_t datatype; +} __attribute__((packed)); + +static const struct line_coding line_coding0 = { + 115200, /* baud rate: 115200 */ + 0x00, /* stop bits: 1 */ + 0x00, /* parity: none */ + 0x08 /* bits: 8 */ +}; + +/* + * Currently, we only support a single TTY. + * + * It is possible to extend to support multiple TTYs, for multiple + * interfaces. + * + * In that case, add argument to TTY_OPEN function and + * modify TTY_GET function to get the TTY structure. Functions which + * directy accesses TTY0 (usb_device_reset and usb_set_configuration) + * should be modified, too. + * + * Modification of TTY_MAIN thread will be also needed to echo back + * input for each TTY, and the thread should run if one of TTY is + * opened. + */ + +struct tty { + chopstx_mutex_t mtx; + chopstx_cond_t cnd; + uint8_t inputline[LINEBUFSIZE]; /* Line editing is supported */ + uint8_t send_buf[LINEBUFSIZE]; /* Sending ring buffer for echo back */ + uint8_t send_buf0[64]; + uint8_t recv_buf0[64]; + uint32_t inputline_len : 8; + uint32_t send_head : 8; + uint32_t send_tail : 8; + uint32_t flag_connected : 1; + uint32_t flag_send_ready : 1; + uint32_t flag_input_avail : 1; + uint32_t : 2; + uint32_t device_state : 3; /* USB device status */ + struct line_coding line_coding; +}; + +static struct tty tty0; + +/* + * Locate TTY structure from interface number or endpoint number. + * Currently, it always returns tty0, because we only have the one. + */ +static struct tty * +tty_get (int interface, uint8_t ep_num) +{ + struct tty *t = &tty0; + + if (interface >= 0) + { + if (interface == 0) + t = &tty0; + } + else + { + if (ep_num == ENDP1 || ep_num == ENDP2 || ep_num == ENDP3) + t = &tty0; + } + + return t; +} + + +#define USB_CDC_REQ_SET_LINE_CODING 0x20 +#define USB_CDC_REQ_GET_LINE_CODING 0x21 +#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22 +#define USB_CDC_REQ_SEND_BREAK 0x23 + +/* USB Device Descriptor */ +static const uint8_t vcom_device_desc[18] = { + 18, /* bLength */ + DEVICE_DESCRIPTOR, /* bDescriptorType */ + 0x10, 0x01, /* bcdUSB = 1.1 */ + 0x02, /* bDeviceClass (CDC). */ + 0x00, /* bDeviceSubClass. */ + 0x00, /* bDeviceProtocol. */ + 0x40, /* bMaxPacketSize. */ + 0xFF, 0xFF, /* idVendor */ + 0x01, 0x00, /* idProduct */ + 0x00, 0x01, /* bcdDevice */ + 1, /* iManufacturer. */ + 2, /* iProduct. */ + 3, /* iSerialNumber. */ + 1 /* bNumConfigurations. */ +}; + +#define VCOM_FEATURE_BUS_POWERED 0x80 + +/* Configuration Descriptor tree for a CDC.*/ +static const uint8_t vcom_config_desc[67] = { + 9, + CONFIG_DESCRIPTOR, /* bDescriptorType: Configuration */ + /* Configuration Descriptor.*/ + 67, 0x00, /* wTotalLength. */ + 0x02, /* bNumInterfaces. */ + 0x01, /* bConfigurationValue. */ + 0, /* iConfiguration. */ + VCOM_FEATURE_BUS_POWERED, /* bmAttributes. */ + 50, /* bMaxPower (100mA). */ + /* Interface Descriptor.*/ + 9, + INTERFACE_DESCRIPTOR, + 0x00, /* bInterfaceNumber. */ + 0x00, /* bAlternateSetting. */ + 0x01, /* bNumEndpoints. */ + 0x02, /* bInterfaceClass (Communications Interface Class, + CDC section 4.2). */ + 0x02, /* bInterfaceSubClass (Abstract Control Model, CDC + section 4.3). */ + 0x01, /* bInterfaceProtocol (AT commands, CDC section + 4.4). */ + 0, /* iInterface. */ + /* Header Functional Descriptor (CDC section 5.2.3).*/ + 5, /* bLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x00, /* bDescriptorSubtype (Header Functional Descriptor). */ + 0x10, 0x01, /* bcdCDC. */ + /* Call Management Functional Descriptor. */ + 5, /* bFunctionLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x01, /* bDescriptorSubtype (Call Management Functional + Descriptor). */ + 0x03, /* bmCapabilities (D0+D1). */ + 0x01, /* bDataInterface. */ + /* ACM Functional Descriptor.*/ + 4, /* bFunctionLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x02, /* bDescriptorSubtype (Abstract Control Management + Descriptor). */ + 0x02, /* bmCapabilities. */ + /* Union Functional Descriptor.*/ + 5, /* bFunctionLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x06, /* bDescriptorSubtype (Union Functional + Descriptor). */ + 0x00, /* bMasterInterface (Communication Class + Interface). */ + 0x01, /* bSlaveInterface0 (Data Class Interface). */ + /* Endpoint 2 Descriptor.*/ + 7, + ENDPOINT_DESCRIPTOR, + ENDP2|0x80, /* bEndpointAddress. */ + 0x03, /* bmAttributes (Interrupt). */ + 0x08, 0x00, /* wMaxPacketSize. */ + 0xFF, /* bInterval. */ + /* Interface Descriptor.*/ + 9, + INTERFACE_DESCRIPTOR, /* bDescriptorType: */ + 0x01, /* bInterfaceNumber. */ + 0x00, /* bAlternateSetting. */ + 0x02, /* bNumEndpoints. */ + 0x0A, /* bInterfaceClass (Data Class Interface, CDC section 4.5). */ + 0x00, /* bInterfaceSubClass (CDC section 4.6). */ + 0x00, /* bInterfaceProtocol (CDC section 4.7). */ + 0x00, /* iInterface. */ + /* Endpoint 3 Descriptor.*/ + 7, + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + ENDP3, /* bEndpointAddress. */ + 0x02, /* bmAttributes (Bulk). */ + 0x40, 0x00, /* wMaxPacketSize. */ + 0x00, /* bInterval. */ + /* Endpoint 1 Descriptor.*/ + 7, + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + ENDP1|0x80, /* bEndpointAddress. */ + 0x02, /* bmAttributes (Bulk). */ + 0x40, 0x00, /* wMaxPacketSize. */ + 0x00 /* bInterval. */ +}; + + +/* + * U.S. English language identifier. + */ +static const uint8_t vcom_string0[4] = { + 4, /* bLength */ + STRING_DESCRIPTOR, + 0x09, 0x04 /* LangID = 0x0409: US-English */ +}; + +static const uint8_t vcom_string1[] = { + 23*2+2, /* bLength */ + STRING_DESCRIPTOR, /* bDescriptorType */ + /* Manufacturer: "Flying Stone Technology" */ + 'F', 0, 'l', 0, 'y', 0, 'i', 0, 'n', 0, 'g', 0, ' ', 0, 'S', 0, + 't', 0, 'o', 0, 'n', 0, 'e', 0, ' ', 0, 'T', 0, 'e', 0, 'c', 0, + 'h', 0, 'n', 0, 'o', 0, 'l', 0, 'o', 0, 'g', 0, 'y', 0, +}; + +static const uint8_t vcom_string2[] = { + 14*2+2, /* bLength */ + STRING_DESCRIPTOR, /* bDescriptorType */ + /* Product name: "Chopstx Sample" */ + 'C', 0, 'h', 0, 'o', 0, 'p', 0, 's', 0, 't', 0, 'x', 0, ' ', 0, + 'S', 0, 'a', 0, 'm', 0, 'p', 0, 'l', 0, 'e', 0, +}; + +/* + * Serial Number string. + */ +static const uint8_t vcom_string3[28] = { + 28, /* bLength */ + STRING_DESCRIPTOR, /* bDescriptorType */ + '0', 0, '.', 0, '0', 0, '0', 0, /* Version number */ +}; + + +#define NUM_INTERFACES 2 + + +static void +usb_device_reset (struct usb_dev *dev) +{ + usb_lld_reset (dev, VCOM_FEATURE_BUS_POWERED); + + /* Initialize Endpoint 0 */ + usb_lld_setup_endp (dev, ENDP0, 1, 1); + + chopstx_mutex_lock (&tty0.mtx); + tty0.inputline_len = 0; + tty0.send_head = tty0.send_tail = 0; + tty0.flag_connected = 0; + tty0.flag_send_ready = 1; + tty0.flag_input_avail = 0; + tty0.device_state = USB_DEVICE_STATE_ATTACHED; + memcpy (&tty0.line_coding, &line_coding0, sizeof (struct line_coding)); + chopstx_mutex_unlock (&tty0.mtx); +} + + +#define CDC_CTRL_DTR 0x0001 + +static void +usb_ctrl_write_finish (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT); + + if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT) && arg->index == 0 + && USB_SETUP_SET (arg->type) + && arg->request == USB_CDC_REQ_SET_CONTROL_LINE_STATE) + { + struct tty *t = tty_get (arg->index, 0); + + /* Open/close the connection. */ + chopstx_mutex_lock (&t->mtx); + t->flag_connected = ((arg->value & CDC_CTRL_DTR) != 0); + chopstx_cond_signal (&t->cnd); + chopstx_mutex_unlock (&t->mtx); + } +} + + +static int +vcom_port_data_setup (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + + if (USB_SETUP_GET (arg->type)) + { + struct tty *t = tty_get (arg->index, 0); + + if (arg->request == USB_CDC_REQ_GET_LINE_CODING) + return usb_lld_ctrl_send (dev, &t->line_coding, + sizeof (struct line_coding)); + } + else /* USB_SETUP_SET (req) */ + { + if (arg->request == USB_CDC_REQ_SET_LINE_CODING + && arg->len == sizeof (struct line_coding)) + { + struct tty *t = tty_get (arg->index, 0); + + return usb_lld_ctrl_recv (dev, &t->line_coding, + sizeof (struct line_coding)); + } + else if (arg->request == USB_CDC_REQ_SET_CONTROL_LINE_STATE) + return usb_lld_ctrl_ack (dev); + } + + return -1; +} + +static int +usb_setup (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT); + + if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT) && arg->index == 0) + return vcom_port_data_setup (dev); + + return -1; +} + +static int +usb_get_descriptor (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + uint8_t desc_type = (arg->value >> 8); + uint8_t desc_index = (arg->value & 0xff); + + if (rcp != DEVICE_RECIPIENT) + return -1; + + if (desc_type == DEVICE_DESCRIPTOR) + return usb_lld_ctrl_send (dev, + vcom_device_desc, sizeof (vcom_device_desc)); + else if (desc_type == CONFIG_DESCRIPTOR) + return usb_lld_ctrl_send (dev, + vcom_config_desc, sizeof (vcom_config_desc)); + else if (desc_type == STRING_DESCRIPTOR) + { + const uint8_t *str; + int size; + + switch (desc_index) + { + case 0: + str = vcom_string0; + size = sizeof (vcom_string0); + break; + case 1: + str = vcom_string1; + size = sizeof (vcom_string1); + break; + case 2: + str = vcom_string2; + size = sizeof (vcom_string2); + break; + case 3: + str = vcom_string3; + size = sizeof (vcom_string3); + break; + default: + return -1; + } + + return usb_lld_ctrl_send (dev, str, size); + } + + return -1; +} + +static void +vcom_setup_endpoints_for_interface (struct usb_dev *dev, + uint16_t interface, int stop) +{ + if (interface == 0) + { + if (!stop) + usb_lld_setup_endp (dev, ENDP2, 0, 1); + else + usb_lld_stall (ENDP2); + } + else if (interface == 1) + { + if (!stop) + { + usb_lld_setup_endp (dev, ENDP1, 0, 1); + usb_lld_setup_endp (dev, ENDP3, 1, 0); + /* Start with no data receiving (ENDP3 not enabled)*/ + } + else + { + usb_lld_stall (ENDP1); + usb_lld_stall (ENDP3); + } + } +} + +static int +usb_set_configuration (struct usb_dev *dev) +{ + int i; + uint8_t current_conf; + + current_conf = usb_lld_current_configuration (dev); + if (current_conf == 0) + { + if (dev->dev_req.value != 1) + return -1; + + usb_lld_set_configuration (dev, 1); + for (i = 0; i < NUM_INTERFACES; i++) + vcom_setup_endpoints_for_interface (dev, i, 0); + chopstx_mutex_lock (&tty0.mtx); + tty0.device_state = USB_DEVICE_STATE_CONFIGURED; + chopstx_cond_signal (&tty0.cnd); + chopstx_mutex_unlock (&tty0.mtx); + } + else if (current_conf != dev->dev_req.value) + { + if (dev->dev_req.value != 0) + return -1; + + usb_lld_set_configuration (dev, 0); + for (i = 0; i < NUM_INTERFACES; i++) + vcom_setup_endpoints_for_interface (dev, i, 1); + chopstx_mutex_lock (&tty0.mtx); + tty0.device_state = USB_DEVICE_STATE_ADDRESSED; + chopstx_cond_signal (&tty0.cnd); + chopstx_mutex_unlock (&tty0.mtx); + } + + return usb_lld_ctrl_ack (dev); +} + + +static int +usb_set_interface (struct usb_dev *dev) +{ + uint16_t interface = dev->dev_req.index; + uint16_t alt = dev->dev_req.value; + + if (interface >= NUM_INTERFACES) + return -1; + + if (alt != 0) + return -1; + else + { + vcom_setup_endpoints_for_interface (dev, interface, 0); + return usb_lld_ctrl_ack (dev); + } +} + +static int +usb_get_interface (struct usb_dev *dev) +{ + const uint8_t zero = 0; + uint16_t interface = dev->dev_req.index; + + if (interface >= NUM_INTERFACES) + return -1; + + /* We don't have alternate interface, so, always return 0. */ + return usb_lld_ctrl_send (dev, &zero, 1); +} + +static int +usb_get_status_interface (struct usb_dev *dev) +{ + const uint16_t status_info = 0; + uint16_t interface = dev->dev_req.index; + + if (interface >= NUM_INTERFACES) + return -1; + + return usb_lld_ctrl_send (dev, &status_info, 2); +} + + +/* + * Put a character into the ring buffer to be send back. + */ +static void +put_char_to_ringbuffer (struct tty *t, int c) +{ + uint32_t next = (t->send_tail + 1) % LINEBUFSIZE; + + if (t->send_head == next) + /* full */ + /* All that we can do is ignore this char. */ + return; + + t->send_buf[t->send_tail] = c; + t->send_tail = next; +} + +/* + * Get characters from ring buffer into S. + */ +static int +get_chars_from_ringbuffer (struct tty *t, uint8_t *s, int len) +{ + int i = 0; + + if (t->send_head == t->send_tail) + /* Empty */ + return i; + + do + { + s[i++] = t->send_buf[t->send_head]; + t->send_head = (t->send_head + 1) % LINEBUFSIZE; + } + while (t->send_head != t->send_tail && i < len); + + return i; +} + + +static void +tty_echo_char (struct tty *t, int c) +{ + put_char_to_ringbuffer (t, c); +} + +static void +usb_tx_done (uint8_t ep_num, uint16_t len) +{ + struct tty *t = tty_get (-1, ep_num); + + (void)len; + + if (ep_num == ENDP1) + { + chopstx_mutex_lock (&t->mtx); + if (t->flag_send_ready == 0) + { + t->flag_send_ready = 1; + chopstx_cond_signal (&t->cnd); + } + chopstx_mutex_unlock (&t->mtx); + } + else if (ep_num == ENDP2) + { + /* Nothing */ + } +} + + +static int +tty_input_char (struct tty *t, int c) +{ + unsigned int i; + int r = 0; + + /* Process DEL, C-U, C-R, and RET as editing command. */ + chopstx_mutex_lock (&t->mtx); + switch (c) + { + case 0x0d: /* Control-M */ + t->inputline[t->inputline_len++] = '\n'; + tty_echo_char (t, 0x0d); + tty_echo_char (t, 0x0a); + t->flag_input_avail = 1; + r = 1; + chopstx_cond_signal (&t->cnd); + break; + case 0x12: /* Control-R */ + tty_echo_char (t, '^'); + tty_echo_char (t, 'R'); + tty_echo_char (t, 0x0d); + tty_echo_char (t, 0x0a); + for (i = 0; i < t->inputline_len; i++) + tty_echo_char (t, t->inputline[i]); + break; + case 0x15: /* Control-U */ + for (i = 0; i < t->inputline_len; i++) + { + tty_echo_char (t, 0x08); + tty_echo_char (t, 0x20); + tty_echo_char (t, 0x08); + } + t->inputline_len = 0; + break; + case 0x7f: /* DEL */ + if (t->inputline_len > 0) + { + tty_echo_char (t, 0x08); + tty_echo_char (t, 0x20); + tty_echo_char (t, 0x08); + t->inputline_len--; + } + break; + default: + if (t->inputline_len < sizeof (t->inputline) - 1) + { + tty_echo_char (t, c); + t->inputline[t->inputline_len++] = c; + } + else + /* Beep */ + tty_echo_char (t, 0x0a); + break; + } + chopstx_mutex_unlock (&t->mtx); + return r; +} + +static void +usb_rx_ready (uint8_t ep_num, uint16_t len) +{ + struct tty *t = tty_get (-1, ep_num); + + if (ep_num == ENDP3) + { + int i; + + for (i = 0; i < len; i++) + if (tty_input_char (t, t->recv_buf0[i])) + break; + + chopstx_mutex_lock (&t->mtx); + if (t->flag_input_avail == 0) + usb_lld_rx_enable_buf (ENDP3, t->recv_buf0, 64); + chopstx_mutex_unlock (&t->mtx); + } +} + +static void *tty_main (void *arg); + +#define PRIO_TTY 4 + +extern uint8_t __process3_stack_base__[], __process3_stack_size__[]; +#define STACK_ADDR_TTY ((uint32_t)__process3_stack_base__) +#define STACK_SIZE_TTY ((uint32_t)__process3_stack_size__) + +struct tty * +tty_open (void) +{ + chopstx_mutex_init (&tty0.mtx); + chopstx_cond_init (&tty0.cnd); + tty0.inputline_len = 0; + tty0.send_head = tty0.send_tail = 0; + tty0.flag_connected = 0; + tty0.flag_send_ready = 1; + tty0.flag_input_avail = 0; + tty0.device_state = USB_DEVICE_STATE_UNCONNECTED; + memcpy (&tty0.line_coding, &line_coding0, sizeof (struct line_coding)); + + chopstx_create (PRIO_TTY, STACK_ADDR_TTY, STACK_SIZE_TTY, tty_main, &tty0); + return &tty0; +} + + +static void * +tty_main (void *arg) +{ + struct tty *t = arg; + struct usb_dev dev; + int e; + +#if defined(OLDER_SYS_H) + /* + * Historically (before sys < 3.0), NVIC priority setting for USB + * interrupt was done in usb_lld_sys_init. Thus this code. + * + * When USB interrupt occurs between usb_lld_init (which assumes + * ISR) and chopstx_claim_irq (which clears pending interrupt), + * invocation of usb_lld_event_handler won't occur. + * + * Calling usb_lld_event_handler is no harm even if there were no + * interrupts, thus, we call it unconditionally here, just in case + * if there is a request. + * + * We can't call usb_lld_init after chopstx_claim_irq, as + * usb_lld_init does its own setting for NVIC. Calling + * chopstx_claim_irq after usb_lld_init overrides that. + * + */ + usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED); + chopstx_claim_irq (&usb_intr, INTR_REQ_USB); + goto event_handle; +#else + chopstx_claim_irq (&usb_intr, INTR_REQ_USB); + usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED); +#endif + + while (1) + { + struct chx_poll_head *pd_array[1] = { + (struct chx_poll_head *)&usb_intr + }; + chopstx_poll (NULL, 1, pd_array); + if (usb_intr.ready) + { + uint8_t ep_num; +#if defined(OLDER_SYS_H) + event_handle: +#endif + /* + * When interrupt is detected, call usb_lld_event_handler. + * The event may be one of following: + * (1) Transfer to endpoint (bulk or interrupt) + * In this case EP_NUM is encoded in the variable E. + * (2) "NONE" event: some trasfer was done, but all was + * done by lower layer, no other work is needed in + * upper layer. + * (3) Device events: Reset or Suspend + * (4) Device requests to the endpoint zero. + * + */ + e = usb_lld_event_handler (&dev); + chopstx_intr_done (&usb_intr); + ep_num = USB_EVENT_ENDP (e); + + if (ep_num != 0) + { + if (USB_EVENT_TXRX (e)) + usb_tx_done (ep_num, USB_EVENT_LEN (e)); + else + usb_rx_ready (ep_num, USB_EVENT_LEN (e)); + } + else + switch (USB_EVENT_ID (e)) + { + case USB_EVENT_DEVICE_RESET: + usb_device_reset (&dev); + continue; + + case USB_EVENT_DEVICE_ADDRESSED: + /* The addres is assigned to the device. We don't + * need to do anything for this actually, but in this + * application, we maintain the USB status of the + * device. Usually, just "continue" as EVENT_OK is + * OK. + */ + chopstx_mutex_lock (&tty0.mtx); + tty0.device_state = USB_DEVICE_STATE_ADDRESSED; + chopstx_cond_signal (&tty0.cnd); + chopstx_mutex_unlock (&tty0.mtx); + continue; + + case USB_EVENT_GET_DESCRIPTOR: + if (usb_get_descriptor (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_SET_CONFIGURATION: + if (usb_set_configuration (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_SET_INTERFACE: + if (usb_set_interface (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_CTRL_REQUEST: + /* Device specific device request. */ + if (usb_setup (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_GET_STATUS_INTERFACE: + if (usb_get_status_interface (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_GET_INTERFACE: + if (usb_get_interface (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_SET_FEATURE_DEVICE: + case USB_EVENT_SET_FEATURE_ENDPOINT: + case USB_EVENT_CLEAR_FEATURE_DEVICE: + case USB_EVENT_CLEAR_FEATURE_ENDPOINT: + usb_lld_ctrl_ack (&dev); + continue; + + case USB_EVENT_CTRL_WRITE_FINISH: + /* Control WRITE transfer finished. */ + usb_ctrl_write_finish (&dev); + continue; + + case USB_EVENT_OK: + case USB_EVENT_DEVICE_SUSPEND: + default: + continue; + } + } + + chopstx_mutex_lock (&t->mtx); + if (t->device_state == USB_DEVICE_STATE_CONFIGURED && t->flag_connected + && t->flag_send_ready) + { + uint8_t line[32]; + int len = get_chars_from_ringbuffer (t, line, sizeof (len)); + + if (len) + { + memcpy (t->send_buf0, line, len); + usb_lld_tx_enable_buf (ENDP1, t->send_buf0, len); + t->flag_send_ready = 0; + } + } + chopstx_mutex_unlock (&t->mtx); + } + + return NULL; +} + + +void +tty_wait_configured (struct tty *t) +{ + chopstx_mutex_lock (&t->mtx); + while (t->device_state != USB_DEVICE_STATE_CONFIGURED) + chopstx_cond_wait (&t->cnd, &t->mtx); + chopstx_mutex_unlock (&t->mtx); +} + + +void +tty_wait_connection (struct tty *t) +{ + chopstx_mutex_lock (&t->mtx); + while (t->flag_connected == 0) + chopstx_cond_wait (&t->cnd, &t->mtx); + t->flag_send_ready = 1; + t->flag_input_avail = 0; + t->send_head = t->send_tail = 0; + t->inputline_len = 0; + usb_lld_rx_enable_buf (ENDP3, t->recv_buf0, 64); /* Accept input for line */ + chopstx_mutex_unlock (&t->mtx); +} + +static int +check_tx (struct tty *t) +{ + if (t->flag_send_ready) + /* TX done */ + return 1; + if (t->flag_connected == 0) + /* Disconnected */ + return -1; + return 0; +} + +int +tty_send (struct tty *t, const char *buf, int len) +{ + int r; + const char *p; + int count; + + p = buf; + count = len >= 64 ? 64 : len; + + while (1) + { + chopstx_mutex_lock (&t->mtx); + while ((r = check_tx (t)) == 0) + chopstx_cond_wait (&t->cnd, &t->mtx); + if (r > 0) + { + usb_lld_tx_enable_buf (ENDP1, p, count); + t->flag_send_ready = 0; + } + chopstx_mutex_unlock (&t->mtx); + + len -= count; + p += count; + if (len == 0 && count != 64) + /* + * The size of the last packet should be != 0 + * If 64, send ZLP (zelo length packet) + */ + break; + count = len >= 64 ? 64 : len; + } + + /* Wait until all sent. */ + chopstx_mutex_lock (&t->mtx); + while ((r = check_tx (t)) == 0) + chopstx_cond_wait (&t->cnd, &t->mtx); + chopstx_mutex_unlock (&t->mtx); + return r; +} + + +static int +check_rx (void *arg) +{ + struct tty *t = arg; + + if (t->flag_input_avail) + /* RX */ + return 1; + if (t->flag_connected == 0) + /* Disconnected */ + return 1; + return 0; +} + +/* + * Returns -1 on connection close + * 0 on timeout. + * >0 length of the inputline (including final \n) + * + */ +int +tty_recv (struct tty *t, char *buf, uint32_t *timeout) +{ + int r; + chopstx_poll_cond_t poll_desc; + + poll_desc.type = CHOPSTX_POLL_COND; + poll_desc.ready = 0; + poll_desc.cond = &t->cnd; + poll_desc.mutex = &t->mtx; + poll_desc.check = check_rx; + poll_desc.arg = t; + + while (1) + { + struct chx_poll_head *pd_array[1] = { + (struct chx_poll_head *)&poll_desc + }; + chopstx_poll (timeout, 1, pd_array); + chopstx_mutex_lock (&t->mtx); + r = check_rx (t); + chopstx_mutex_unlock (&t->mtx); + if (r || (timeout != NULL && *timeout == 0)) + break; + } + + chopstx_mutex_lock (&t->mtx); + if (t->flag_connected == 0) + r = -1; + else if (t->flag_input_avail) + { + r = t->inputline_len; + memcpy (buf, t->inputline, r); + t->flag_input_avail = 0; + usb_lld_rx_enable_buf (ENDP3, t->recv_buf0, 64); + t->inputline_len = 0; + } + else + r = 0; + chopstx_mutex_unlock (&t->mtx); + + return r; +} diff --git a/gnuk/chopstx/example-fsm-55/Makefile b/gnuk/chopstx/example-fsm-55/Makefile new file mode 100644 index 0000000000..791b2b9342 --- /dev/null +++ b/gnuk/chopstx/example-fsm-55/Makefile @@ -0,0 +1,44 @@ +# Makefile for Hacker Emblem application of Chopstx + +PROJECT = hacker-emblem + +CHOPSTX = .. +LDSCRIPT= hacker-emblem.ld + +CSRC = reset.c hh.c + +CHIP=stm32f0 + +# Hacker Emblem and "Happy Hacking!" demonstration +# CSRC = reset.c hh.c + +# Debian logo demonstration +# CSRC = reset.c debian-logo.c + +# "Hiroshi & Ayumi with Tulip" demonstration +# CSRC = reset.c hiroshi-ayumi.c + +################################### +CROSS = arm-none-eabi- +CC = $(CROSS)gcc +LD = $(CROSS)gcc +OBJCOPY = $(CROSS)objcopy + +MCU = cortex-m0 # -save-temps +CWARN = -Wall -Wextra -Wstrict-prototypes +DEFS = -DMAKE_ENTRY_PUBLIC \ + -DFREE_STANDING -DMHZ=48 +OPT = -O3 -Os -g +LIBS = + +#################### +include ../rules.mk + +board.h: + @echo Please make a symbolic link \'board.h\' to a file in ../board; + @exit 1 + +reset.c: board.h + +distclean: clean + rm -f board.h diff --git a/gnuk/chopstx/example-fsm-55/README b/gnuk/chopstx/example-fsm-55/README new file mode 100644 index 0000000000..370325948e --- /dev/null +++ b/gnuk/chopstx/example-fsm-55/README @@ -0,0 +1,17 @@ +FSM-55 LED Matrix Display Board is a simple board to play 5x5 LED +Display. Please see the product page: + + http://www.seeedstudio.com/depot/FSM55-LED-Matrix-Display-p-2121.html + +There are three demonstrations and default is Hacker Emblem and "Happy +Hacking!". Please edit CSRC variable in the Makefile to select other +applications. + + +The file stlink-v2.cfg can be used for ST-Link/V2 with OpenOCD. You +can flash by typing: + + $ openocd -f ./stlink-v2.cfg -c "program build/hacker-emblem.elf; reset run; shutdown" + +Thanks to Kiwamu Okabe who kindly tested OpenOCD and wrote the +configuration. diff --git a/gnuk/chopstx/example-fsm-55/board.h b/gnuk/chopstx/example-fsm-55/board.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gnuk/chopstx/example-fsm-55/debian-logo.c b/gnuk/chopstx/example-fsm-55/debian-logo.c new file mode 100644 index 0000000000..4129b13a10 --- /dev/null +++ b/gnuk/chopstx/example-fsm-55/debian-logo.c @@ -0,0 +1,425 @@ +#include +#include +#include + +#include "board.h" + +static uint8_t main_finished; + +#define PERIPH_BASE 0x40000000 +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) +#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000) +#define AHB2PERIPH_BASE (PERIPH_BASE + 0x08000000) + +struct GPIO { + volatile uint32_t MODER; + volatile uint16_t OTYPER; + uint16_t dummy0; + volatile uint32_t OSPEEDR; + volatile uint32_t PUPDR; + volatile uint16_t IDR; + uint16_t dummy1; + volatile uint16_t ODR; + uint16_t dummy2; + volatile uint16_t BSRR; + uint16_t dummy3; + volatile uint32_t LCKR; + volatile uint32_t AFR[2]; + volatile uint16_t BRR; + uint16_t dummy4; +}; +#define GPIOA_BASE (AHB2PERIPH_BASE + 0x0000) +#define GPIOA ((struct GPIO *) GPIOA_BASE) +#define GPIOF_BASE (AHB2PERIPH_BASE + 0x1400) +#define GPIOF ((struct GPIO *) GPIOF_BASE) + +static struct GPIO *const GPIO_LED = ((struct GPIO *)GPIO_LED_BASE); +static struct GPIO *const GPIO_OTHER = ((struct GPIO *)GPIO_OTHER_BASE); + +static chopstx_mutex_t mtx; +static chopstx_cond_t cnd0, cnd1; + +#define BUTTON_PUSHED 1 +static uint8_t button_state; + +static uint8_t +user_button (void) +{ + return button_state; +} + + +static uint8_t l_data[5]; +#define LED_FULL ((0x1f << 20)|(0x1f << 15)|(0x1f << 10)|(0x1f << 5)|0x1f) + +static void +set_led_display (uint32_t data) +{ + l_data[0] = (data >> 0) & 0x1f; + l_data[1] = (data >> 5) & 0x1f; + l_data[2] = (data >> 10) & 0x1f; + l_data[3] = (data >> 15) & 0x1f; + l_data[4] = (data >> 20) & 0x1f; +} + +static void +scroll_led_display (uint8_t row) +{ + l_data[0] = (l_data[0] << 1) | ((row >> 0) & 1); + l_data[1] = (l_data[1] << 1) | ((row >> 1) & 1); + l_data[2] = (l_data[2] << 1) | ((row >> 2) & 1); + l_data[3] = (l_data[3] << 1) | ((row >> 3) & 1); + l_data[4] = (l_data[4] << 1) | ((row >> 4) & 1); +} + + +static void +wait_for (uint32_t usec) +{ + chopstx_usec_wait (usec); +} + +static void +led_prepare_row (uint8_t col) +{ + uint16_t data = 0x1f; + + data |= ((l_data[0] & (1 << col)) ? 1 : 0) << 5; + data |= ((l_data[1] & (1 << col)) ? 1 : 0) << 6; + data |= ((l_data[2] & (1 << col)) ? 1 : 0) << 7; + data |= ((l_data[3] & (1 << col)) ? 1 : 0) << 9; + data |= ((l_data[4] & (1 << col)) ? 1 : 0) << 10; + GPIO_LED->ODR = data; +} + + +static void +led_enable_column (uint8_t col) +{ + GPIO_LED->BRR = (1 << col); +} + +static void * +led (void *arg) +{ + (void)arg; + + chopstx_mutex_lock (&mtx); + chopstx_cond_wait (&cnd0, &mtx); + chopstx_mutex_unlock (&mtx); + + while (!main_finished) + { + int i; + + for (i = 0; i < 5; i++) + { + led_prepare_row (i); + led_enable_column (i); + wait_for (1000); + } + } + + GPIO_LED->ODR = 0x0000; /* Off all LEDs. */ + GPIO_LED->OSPEEDR = 0; + GPIO_LED->OTYPER = 0; + GPIO_LED->MODER = 0; /* Input mode. */ + GPIO_OTHER->PUPDR = 0x0000; /* No pull-up. */ + + return NULL; +} + + +static uint8_t get_button_sw (void) { return (GPIO_OTHER->IDR & 1) == 0; } + +static void * +button (void *arg) +{ + uint8_t last_button = 0; + + (void)arg; + + chopstx_mutex_lock (&mtx); + chopstx_cond_wait (&cnd1, &mtx); + chopstx_mutex_unlock (&mtx); + + while (!main_finished) + { + uint8_t button = get_button_sw (); + + if (last_button == button && button != button_state) + { + wait_for (1000); + button = get_button_sw (); + if (last_button == button) + button_state = button; + } + + wait_for (2000); + last_button = button; + } + + return NULL; +} + +#define STACK_MAIN +#define STACK_PROCESS_1 +#define STACK_PROCESS_2 +#include "stack-def.h" + +#define PRIO_LED 3 +#define PRIO_BUTTON 2 + +#define STACK_ADDR_LED ((uint32_t)process1_base) +#define STACK_SIZE_LED (sizeof process1_base) + +#define STACK_ADDR_BUTTON ((uint32_t)process2_base) +#define STACK_SIZE_BUTTON (sizeof process2_base) + +#define DATA55(x0,x1,x2,x3,x4) (x0<<20)|(x1<<15)|(x2<<10)|(x3<< 5)|(x4<< 0) +#define SIZE55(img) (sizeof (img) / sizeof (uint32_t)) + +static uint32_t logo55[] = { + DATA55 (0x00, 0x00, 0x00, 0x00, 0x00), + DATA55 (0x00, 0x00, 0x04, 0x00, 0x00), + DATA55 (0x00, 0x00, 0x04, 0x02, 0x00), + DATA55 (0x00, 0x00, 0x05, 0x02, 0x00), + DATA55 (0x00, 0x01, 0x05, 0x02, 0x00), + DATA55 (0x02, 0x01, 0x05, 0x02, 0x00), + DATA55 (0x06, 0x01, 0x05, 0x02, 0x00), + DATA55 (0x0e, 0x01, 0x05, 0x02, 0x00), + DATA55 (0x0e, 0x11, 0x05, 0x02, 0x00), + DATA55 (0x0e, 0x11, 0x15, 0x02, 0x00), + DATA55 (0x0e, 0x11, 0x15, 0x12, 0x00), + DATA55 (0x0e, 0x11, 0x15, 0x12, 0x08), + DATA55 (0x0e, 0x11, 0x15, 0x12, 0x08), + DATA55 (0x0e, 0x11, 0x16, 0x10, 0x0c), + DATA55 (0x0e, 0x11, 0x16, 0x10, 0x0c), + DATA55 (0x0e, 0x11, 0x16, 0x10, 0x0e), + DATA55 (0x0c, 0x12, 0x14, 0x10, 0x0f), + DATA55 (0x0c, 0x12, 0x14, 0x11, 0x0e), + DATA55 (0x08, 0x14, 0x15, 0x11, 0x0e), + DATA55 (0x08, 0x15, 0x15, 0x11, 0x0e), + DATA55 (0x01, 0x09, 0x15, 0x11, 0x0e), + DATA55 (0x02, 0x09, 0x15, 0x11, 0x0e), + DATA55 (0x06, 0x01, 0x0d, 0x11, 0x0e), + DATA55 (0x0e, 0x01, 0x0d, 0x11, 0x0e), + DATA55 (0x1e, 0x01, 0x0d, 0x11, 0x0e), + DATA55 (0x0e, 0x11, 0x05, 0x09, 0x06), + DATA55 (0x0e, 0x11, 0x15, 0x05, 0x02), + DATA55 (0x0e, 0x11, 0x15, 0x15, 0x02), + DATA55 (0x0e, 0x11, 0x15, 0x12, 0x10), + DATA55 (0x0e, 0x11, 0x15, 0x12, 0x08), + DATA55 (0x0e, 0x11, 0x15, 0x12, 0x08), +}; + +#define DATA55V(x0,x1,x2,x3,x4) (x0<<0)|(x1<<5)|(x2<<10)|(x3<< 15)|(x4<< 20) + +#define CHAR_SPC 0 +#define CHAR_H 1 +#define CHAR_A 2 +#define CHAR_P 3 +#define CHAR_Y 4 +#define CHAR_C 5 +#define CHAR_K 6 +#define CHAR_I 7 +#define CHAR_N 8 +#define CHAR_G 9 +#define CHAR_EXC 10 +#define CHAR_W 11 +#define CHAR_h 12 +#define CHAR_t 13 +#define CHAR_AP 14 +#define CHAR_s 15 +#define CHAR_U 16 +#define CHAR_QT 17 +#define CHAR_o 18 +#define CHAR_X 19 +#define CHAR_D 20 +#define CHAR_e 21 +#define CHAR_b 22 +#define CHAR_i 23 +#define CHAR_a 24 +#define CHAR_n 25 + +static uint8_t hh[] = { + CHAR_H, CHAR_A, CHAR_P, CHAR_P, CHAR_Y, + CHAR_SPC, + CHAR_H, CHAR_A, CHAR_C, CHAR_K, CHAR_I, CHAR_N, CHAR_G, + CHAR_EXC, + CHAR_SPC, CHAR_SPC, CHAR_SPC, +}; + +static uint8_t debian[] = { + CHAR_SPC, + CHAR_D, CHAR_e, CHAR_b, CHAR_i, CHAR_a, CHAR_n, + CHAR_SPC, CHAR_SPC, +}; + +struct { uint8_t width; uint32_t data; } chargen[] = { + { 3, 0 }, /* SPACE */ + { 4, DATA55V (0x1f, 0x04, 0x04, 0x1f, 0x00) }, /* H */ + { 3, DATA55V (0x17, 0x15, 0x0f, 0x00, 0x00) }, /* A */ + { 4, DATA55V (0x1f, 0x14, 0x14, 0x08, 0x00) }, /* P */ + { 4, DATA55V (0x19, 0x05, 0x05, 0x1e, 0x00) }, /* Y */ + { 4, DATA55V (0x0e, 0x11, 0x11, 0x0a, 0x00) }, /* C */ + { 4, DATA55V (0x1f, 0x04, 0x0c, 0x13, 0x00) }, /* K */ + { 3, DATA55V (0x11, 0x1f, 0x11, 0x00, 0x00) }, /* I */ + { 4, DATA55V (0x1f, 0x08, 0x06, 0x1f, 0x00) }, /* N */ + { 4, DATA55V (0x0e, 0x11, 0x15, 0x07, 0x00) }, /* G */ + { 2, DATA55V (0x1d, 0x1c, 0x00, 0x00, 0x00) }, /* ! */ + { 5, DATA55V (0x1e, 0x01, 0x0e, 0x01, 0x1e) }, /* W */ + { 3, DATA55V (0x1f, 0x04, 0x07, 0x00, 0x00) }, /* h */ + { 4, DATA55V (0x08, 0x1e, 0x09, 0x09, 0x00) }, /* t */ + { 3, DATA55V (0x04, 0x18, 0x18, 0x00, 0x00) }, /* ' */ + { 4, DATA55V (0x09, 0x15, 0x15, 0x12, 0x00) }, /* s */ + { 4, DATA55V (0x1e, 0x01, 0x01, 0x1e, 0x00) }, /* U */ + { 4, DATA55V (0x08, 0x10, 0x15, 0x08, 0x00) }, /* ? */ + { 4, DATA55V (0x06, 0x09, 0x09, 0x06, 0x00) }, /* o */ + { 5, DATA55V (0x11, 0x0a, 0x04, 0x0a, 0x11) }, /* X */ + { 4, DATA55V (0x1f, 0x11, 0x11, 0x0e, 0x00) }, /* D */ + { 4, DATA55V (0x0e, 0x15, 0x15, 0x0d, 0x00) }, /* e */ + { 4, DATA55V (0x1f, 0x05, 0x05, 0x06, 0x00) }, /* b */ + { 1, DATA55V (0x17, 0x00, 0x00, 0x00, 0x00) }, /* i */ + { 4, DATA55V (0x02, 0x15, 0x15, 0x0f, 0x00) }, /* a */ + { 4, DATA55V (0x1f, 0x08, 0x10, 0x1f, 0x00) }, /* n */ +}; + + +#define REPEAT_COUNT 10 + +static int +logo_display (void) +{ + unsigned int i; + uint8_t state = 0; + + for (i = 0; i < SIZE55 (logo55); i++) + { + if (user_button ()) + { + set_led_display (LED_FULL); + state = 1; + } + else if (state == 1) + return 0; + else + set_led_display (logo55[i]); + wait_for (350*1000); + } + + return 1; +} + + +static int +text_display (uint8_t kind) +{ + unsigned int i, j; + uint8_t *text; + uint8_t len; + uint8_t state = 0; + + if (kind) + { + text = debian; + len = sizeof (debian); + } + else + { + text = hh; + len = sizeof (hh); + } + +#if 0 + set_led_display (0); +#endif + for (i = 0; i < len; i++) + { + for (j = 0; j < chargen[text[i]].width; j++) + { + if (user_button ()) + { + set_led_display (LED_FULL); + state = 1; + } + else if (state == 1) + return 0; + else + scroll_led_display ((chargen[text[i]].data >> j * 5) & 0x1f); + wait_for (120*1000); + } + + if (user_button ()) + { + set_led_display (LED_FULL); + state = 1; + } + else if (state == 1) + return 0; + else + scroll_led_display (0); + wait_for (120*1000); + } + + return 1; +} + + +int +main (int argc, const char *argv[]) +{ + chopstx_t led_thd; + chopstx_t button_thd; + uint8_t happy = 1; + uint8_t count = 0; + + (void)argc; + (void)argv; + + chopstx_conf_idle (1); + + chopstx_mutex_init (&mtx); + chopstx_cond_init (&cnd0); + chopstx_cond_init (&cnd1); + + led_thd = chopstx_create (PRIO_LED, STACK_ADDR_LED, + STACK_SIZE_LED, led, NULL); + button_thd = chopstx_create (PRIO_BUTTON, STACK_ADDR_BUTTON, + STACK_SIZE_BUTTON, button, NULL); + + chopstx_usec_wait (200*1000); + + chopstx_mutex_lock (&mtx); + chopstx_cond_signal (&cnd0); + chopstx_cond_signal (&cnd1); + chopstx_mutex_unlock (&mtx); + + wait_for (100*1000); + if (user_button ()) + { + /* Wait button release. */ + while (user_button ()) + wait_for (100*1000); + + happy = 0; + goto do_text; + } + + while (count++ < REPEAT_COUNT) + { + if (!logo_display ()) + break; + do_text: + if (!text_display (happy)) + break; + } + + main_finished = 1; + chopstx_join (button_thd, NULL); + chopstx_join (led_thd, NULL); + + chopstx_conf_idle (4); + return 0; +} diff --git a/gnuk/chopstx/example-fsm-55/debian-logo.txt b/gnuk/chopstx/example-fsm-55/debian-logo.txt new file mode 100644 index 0000000000..c540526cd7 --- /dev/null +++ b/gnuk/chopstx/example-fsm-55/debian-logo.txt @@ -0,0 +1,594 @@ +..... +..... +..... +..... +..... + +..... +..... +..o.. +..... +..... + +..... +..... +..o.. +...o. +..... + +..... +..... +..o.o +...o. +..... + +..... +....o +..o.o +...o. +..... + +...o. +....o +..o.o +...o. +..... + +..oo. +....o +..o.o +...o. +..... + +.ooo. +....o +..o.o +...o. +..... + +.ooo. +o...o +..o.o +...o. +..... + +.ooo. +o...o +o.o.o +...o. +..... + +.ooo. +o...o +o.o.o +o..o. +..... + +.ooo. +o...o +o.o.o +o..o. +.o... + +.ooo. +o...o +o.o.o +o..o. +.o... + +.ooo. +o...o +o.oo. +o.... +.oo.. + +.ooo. +o...o +o.oo. +o.... +.ooo. + +.oo.. +o..o. +o.o.. +o.... +.oooo + +.oo.. +o..o. +o.o.. +o...o +.ooo. + +.oo.. +o.o.. +o.o.o +o...o +.ooo. + +.o... +o.o.o +o.o.o +o...o +.ooo. + +....o +.o..o +o.o.o +o...o +.ooo. + +...o. +....o +.oo.o +o...o +.ooo. + +.ooo. +....o +.oo.o +o...o +.ooo. + +oooo. +....o +.oo.o +o...o +.ooo. + +.ooo. +o...o +..o.o +.o..o +..oo. + +.ooo. +o...o +o.o.o +..o.o +...o. + +.ooo. +o...o +o.o.o +o.o.o +...o. + +.ooo. +o...o +o.o.o +o..o. +o.... + +.ooo. +o...o +o.o.o +o..o. +.o... + +.ooo. +o...o +o.o.o +o..o. +.o... + +ooo. +o..o +o..o +o..o +ooo. + +.oo. +o..o +oooo +o... +.ooo + +o... +o... +oooo +o..o +ooo. + +o +. +o +o +o + +.oo. +...o +.ooo +o..o +oooo + +.... +oooo +o..o +o..o +o..o + +..... 00 +..... 00 +..... 00 +..... 00 +..... 00 + +..... 00 +..... 00 +..o.. 04 +..... 00 +..... 00 + +..... 00 +..... 00 +..o.. 04 +...o. 02 +..... 00 + +..... 00 +..... 00 +..o.o 05 +...o. 02 +..... 00 + +..... 00 +....o 01 +..o.o 05 +...o. 02 +..... 00 + +...o. 02 +....o 01 +..o.o 05 +...o. 02 +..... 00 + +..oo. 06 +....o 01 +..o.o 05 +...o. 02 +..... 00 + +.ooo. 0e +....o 01 +..o.o 05 +...o. 02 +..... 00 + +.ooo. 0e +o...o 11 +..o.o 05 +...o. 02 +..... 00 + +.ooo. 0e +o...o 11 +o.o.o 15 +...o. 02 +..... 00 + +.ooo. 0e +o...o 11 +o.o.o 15 +o..o. 12 +..... 00 + +.ooo. 0e +o...o 11 +o.o.o 15 +o..o. 12 +.o... 08 + +.ooo. 0e +o...o 11 +o.o.o 15 +o..o. 12 +.o... 08 + +.ooo. 0e +o...o 11 +o.oo. 16 +o.... 10 +.oo.. 0c + +.ooo. 0e +o...o 11 +o.oo. 16 +o.... 10 +.ooo. 0e + +.oo.. 0c +o..o. 12 +o.o.. 14 +o.... 10 +.oooo 0f + +.oo.. 0c +o..o. 12 +o.o.. 14 +o...o 11 +.ooo. 0e + +.o... 08 +o.o.. 14 +o.o.o 15 +o...o 11 +.ooo. 0e + +.o... 08 +o.o.o 15 +o.o.o 15 +o...o 11 +.ooo. 0e + +...o. 02 +.o..o 09 +o.o.o 15 +o...o 11 +.ooo. 0e + +....o 01 +.o..o 09 +o.o.o 15 +o...o 11 +.ooo. 0e + +...o. 02 +.o..o 09 +o.o.o 15 +o...o 11 +.ooo. 0e + +..oo. 06 +....o 01 +.oo.o 0d +o...o 11 +.ooo. 0e + +.ooo. 0e +....o 01 +.oo.o 0d +o...o 11 +.ooo. 0e + +oooo. 1e +....o 01 +.oo.o 0d +o...o 11 +.ooo. 0e + +.ooo. 0e +o...o 11 +..o.o 05 +.o..o 09 +..oo. 06 + +.ooo. 0e +o...o 11 +o.o.o 15 +..o.o 05 +...o. 02 + +.ooo. 0e +o...o 11 +o.o.o 15 +o.o.o 15 +...o. 02 + +.ooo. 0e +o...o 11 +o.o.o 15 +o..o. 12 +o.... 10 + +.ooo. 0e +o...o 11 +o.o.o 15 +o..o. 12 +.o... 08 + +.ooo. 0e +o...o 11 +o.o.o 15 +o..o. 12 +.o... 08 + +ooo. +o..o +o..o +o..o +ooo. + +.oo. +o..o +oooo +o... +.ooo + +o... +o... +oooo +o..o +ooo. + +o +. +o +o +o + +.oo. +...o +.ooo +o..o +.ooo + +o.oo +oo.o +o..o +o..o +o..o + + +00 +00 +00 +00 +00 + +00 +00 +04 +00 +00 + +00 +00 +04 +02 +00 + +00 +00 +05 +02 +00 + +00 +01 +05 +02 +00 + +02 +01 +05 +02 +00 + +06 +01 +05 +02 +00 + +0e +01 +05 +02 +00 + +0e +11 +05 +02 +00 + +0e +11 +15 +02 +00 + +0e +11 +15 +12 +00 + +0e +11 +15 +12 +08 + +0e +11 +15 +12 +08 + +0e +11 +16 +10 +0e + +0c +12 +14 +11 +0e + +0c +14 +15 +11 +0e + +08 +15 +15 +11 +0e + +02 +09 +15 +11 +0e + +06 +01 +0d +11 +0e + +0e +01 +1d +11 +0e + +1e +01 +0d +11 +0e + +0e +11 +05 +09 +0e + +0e +11 +15 +05 +06 + +0e +11 +15 +15 +02 + +0e +11 +15 +13 +08 + +0e +11 +15 +12 +08 + +0e +11 +15 +12 +08 diff --git a/gnuk/chopstx/example-fsm-55/gnu.txt b/gnuk/chopstx/example-fsm-55/gnu.txt new file mode 100644 index 0000000000..974c8a830c --- /dev/null +++ b/gnuk/chopstx/example-fsm-55/gnu.txt @@ -0,0 +1,54 @@ +o...o +o.o.o +o.o.o +o.o.o +.o.o. + +o.. +o.. +ooo +o.o +o.o + +.o.. +oooo +.o.. +.o.. +..oo + +.oo +.oo +o.. +... +... + +.... +.ooo +o... +.oo. +...o +ooo. + +o..o +o..o +o..o +o..o +.oo. + +.oo. +o..o +..o. +.... +..o. + +.... +.oo. +o..o +o..o +.oo. + +o...o +.o.o. +..o.. +.o.o. +o...o diff --git a/gnuk/chopstx/example-fsm-55/hacker-emblem.ld b/gnuk/chopstx/example-fsm-55/hacker-emblem.ld new file mode 100644 index 0000000000..74cb9d943a --- /dev/null +++ b/gnuk/chopstx/example-fsm-55/hacker-emblem.ld @@ -0,0 +1,100 @@ +/* + * ST32F0 memory setup. + */ +MEMORY +{ + flash : org = 0x08000000, len = 16k + ram : org = 0x20000000, len = 4k +} + +__ram_start__ = ORIGIN(ram); +__ram_size__ = 4k; +__ram_end__ = __ram_start__ + __ram_size__; + +SECTIONS +{ + . = 0; + + _text = .; + + .text : ALIGN(16) SUBALIGN(16) + { + KEEP(*(.vectors)) + . = ALIGN(16); + *(.text.startup.*) + *(.text) + *(.text.*) + *(.rodata) + *(.rodata.*) + *(.glue_7t) + *(.glue_7) + *(.gcc*) + . = ALIGN(8); + } > flash + + /DISCARD/ : + { + *(.startup.vectors) + *(.bss.startup.0) + } + + .ARM.extab : {*(.ARM.extab* .gnu.linkonce.armextab.*)} > flash + + .ARM.exidx : { + PROVIDE(__exidx_start = .); + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + PROVIDE(__exidx_end = .); + } > flash + + .eh_frame_hdr : {*(.eh_frame_hdr)} > flash + + .eh_frame : ONLY_IF_RO {*(.eh_frame)} > flash + + .textalign : ONLY_IF_RO { . = ALIGN(8); } > flash + + _etext = .; + _textdata = _etext; + + .stacks (NOLOAD) : + { + . = ALIGN(8); + *(.main_stack) + *(.process_stack.0) + *(.process_stack.1) + *(.process_stack.2) + *(.process_stack.3) + . = ALIGN(8); + } > ram + + .data : + { + . = ALIGN(4); + PROVIDE(_data = .); + *(.data) + . = ALIGN(4); + *(.data.*) + . = ALIGN(4); + *(.ramtext) + . = ALIGN(4); + PROVIDE(_edata = .); + } > ram AT > flash + + .bss : + { + . = ALIGN(4); + PROVIDE(_bss_start = .); + *(.bss) + . = ALIGN(4); + *(.bss.*) + . = ALIGN(4); + *(COMMON) + . = ALIGN(4); + PROVIDE(_bss_end = .); + } > ram + + PROVIDE(end = .); + _end = .; +} + +__heap_base__ = _end; +__heap_end__ = __ram_end__; diff --git a/gnuk/chopstx/example-fsm-55/hh.c b/gnuk/chopstx/example-fsm-55/hh.c new file mode 100644 index 0000000000..d6578b0ceb --- /dev/null +++ b/gnuk/chopstx/example-fsm-55/hh.c @@ -0,0 +1,413 @@ +#include +#include +#include + +#include "board.h" + +static uint8_t main_finished; + +#define PERIPH_BASE 0x40000000 +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) +#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000) +#define AHB2PERIPH_BASE (PERIPH_BASE + 0x08000000) + +struct GPIO { + volatile uint32_t MODER; + volatile uint16_t OTYPER; + uint16_t dummy0; + volatile uint32_t OSPEEDR; + volatile uint32_t PUPDR; + volatile uint16_t IDR; + uint16_t dummy1; + volatile uint16_t ODR; + uint16_t dummy2; + volatile uint16_t BSRR; + uint16_t dummy3; + volatile uint32_t LCKR; + volatile uint32_t AFR[2]; + volatile uint16_t BRR; + uint16_t dummy4; +}; +#define GPIOA_BASE (AHB2PERIPH_BASE + 0x0000) +#define GPIOA ((struct GPIO *) GPIOA_BASE) +#define GPIOF_BASE (AHB2PERIPH_BASE + 0x1400) +#define GPIOF ((struct GPIO *) GPIOF_BASE) + +static struct GPIO *const GPIO_LED = ((struct GPIO *)GPIO_LED_BASE); +static struct GPIO *const GPIO_OTHER = ((struct GPIO *)GPIO_OTHER_BASE); + +static chopstx_mutex_t mtx; +static chopstx_cond_t cnd0, cnd1; + +#define BUTTON_PUSHED 1 +static uint8_t button_state; + +static uint8_t +user_button (void) +{ + return button_state; +} + + +static uint8_t l_data[5]; +#define LED_FULL ((0x1f << 20)|(0x1f << 15)|(0x1f << 10)|(0x1f << 5)|0x1f) + +static void +set_led_display (uint32_t data) +{ + l_data[0] = (data >> 0) & 0x1f; + l_data[1] = (data >> 5) & 0x1f; + l_data[2] = (data >> 10) & 0x1f; + l_data[3] = (data >> 15) & 0x1f; + l_data[4] = (data >> 20) & 0x1f; +} + +static void +scroll_led_display (uint8_t row) +{ + l_data[0] = (l_data[0] << 1) | ((row >> 0) & 1); + l_data[1] = (l_data[1] << 1) | ((row >> 1) & 1); + l_data[2] = (l_data[2] << 1) | ((row >> 2) & 1); + l_data[3] = (l_data[3] << 1) | ((row >> 3) & 1); + l_data[4] = (l_data[4] << 1) | ((row >> 4) & 1); +} + + +static void +wait_for (uint32_t usec) +{ + chopstx_usec_wait (usec); +} + +static void +led_prepare_row (uint8_t col) +{ + uint16_t data = 0x1f; + + data |= ((l_data[0] & (1 << col)) ? 1 : 0) << 5; + data |= ((l_data[1] & (1 << col)) ? 1 : 0) << 6; + data |= ((l_data[2] & (1 << col)) ? 1 : 0) << 7; + data |= ((l_data[3] & (1 << col)) ? 1 : 0) << 9; + data |= ((l_data[4] & (1 << col)) ? 1 : 0) << 10; + GPIO_LED->ODR = data; +} + + +static void +led_enable_column (uint8_t col) +{ + GPIO_LED->BRR = (1 << col); +} + +static void * +led (void *arg) +{ + (void)arg; + + chopstx_mutex_lock (&mtx); + chopstx_cond_wait (&cnd0, &mtx); + chopstx_mutex_unlock (&mtx); + + while (!main_finished) + { + int i; + + for (i = 0; i < 5; i++) + { + led_prepare_row (i); + led_enable_column (i); + wait_for (1000); + } + } + + GPIO_LED->ODR = 0x0000; /* Off all LEDs. */ + GPIO_LED->OSPEEDR = 0; + GPIO_LED->OTYPER = 0; + GPIO_LED->MODER = 0; /* Input mode. */ + GPIO_OTHER->PUPDR = 0x0000; /* No pull-up. */ + + return NULL; +} + + +static uint8_t get_button_sw (void) { return (GPIO_OTHER->IDR & 1) == 0; } + +static void * +button (void *arg) +{ + uint8_t last_button = 0; + + (void)arg; + + chopstx_mutex_lock (&mtx); + chopstx_cond_wait (&cnd1, &mtx); + chopstx_mutex_unlock (&mtx); + + while (!main_finished) + { + uint8_t button = get_button_sw (); + + if (last_button == button && button != button_state) + { + wait_for (1000); + button = get_button_sw (); + if (last_button == button) + button_state = button; + } + + wait_for (2000); + last_button = button; + } + + return NULL; +} + +#define PRIO_LED 3 +#define PRIO_BUTTON 2 + +#define STACK_MAIN +#define STACK_PROCESS_1 +#define STACK_PROCESS_2 +#include "stack-def.h" + +#define STACK_ADDR_LED ((uint32_t)process1_base) +#define STACK_SIZE_LED (sizeof process1_base) + +#define STACK_ADDR_BUTTON ((uint32_t)process2_base) +#define STACK_SIZE_BUTTON (sizeof process2_base) + +#define DATA55(x0,x1,x2,x3,x4) (x0<<20)|(x1<<15)|(x2<<10)|(x3<< 5)|(x4<< 0) +#define SIZE55(img) (sizeof (img) / sizeof (uint32_t)) + +static uint32_t l55[] = { + DATA55 (0x08, 0x04, 0x1c, 0x00, 0x00), + DATA55 (0x00, 0x14, 0x0c, 0x08, 0x00), + DATA55 (0x00, 0x04, 0x14, 0x0c, 0x00), + DATA55 (0x00, 0x08, 0x06, 0x0c, 0x00), + DATA55 (0x00, 0x04, 0x02, 0x0e, 0x00), + DATA55 (0x00, 0x00, 0x0a, 0x06, 0x04), + DATA55 (0x00, 0x00, 0x02, 0x0a, 0x06), + DATA55 (0x00, 0x00, 0x04, 0x03, 0x06), + DATA55 (0x00, 0x00, 0x02, 0x01, 0x07), + DATA55 (0x02, 0x00, 0x00, 0x05, 0x03), + DATA55 (0x03, 0x00, 0x00, 0x01, 0x05), + DATA55 (0x03, 0x00, 0x00, 0x02, 0x11), + DATA55 (0x13, 0x00, 0x00, 0x01, 0x10), + DATA55 (0x11, 0x01, 0x00, 0x00, 0x12), + DATA55 (0x12, 0x11, 0x00, 0x00, 0x10), + DATA55 (0x18, 0x11, 0x00, 0x00, 0x01), + DATA55 (0x08, 0x19, 0x00, 0x00, 0x10), + DATA55 (0x09, 0x18, 0x10, 0x00, 0x00), + DATA55 (0x08, 0x09, 0x18, 0x00, 0x00), + DATA55 (0x10, 0x0c, 0x18, 0x00, 0x00), +}; + +#define DATA55V(x0,x1,x2,x3,x4) (x0<<0)|(x1<<5)|(x2<<10)|(x3<< 15)|(x4<< 20) + +#define CHAR_SPC 0 +#define CHAR_H 1 +#define CHAR_A 2 +#define CHAR_P 3 +#define CHAR_Y 4 +#define CHAR_C 5 +#define CHAR_K 6 +#define CHAR_I 7 +#define CHAR_N 8 +#define CHAR_G 9 +#define CHAR_EXC 10 +#define CHAR_W 11 +#define CHAR_h 12 +#define CHAR_t 13 +#define CHAR_AP 14 +#define CHAR_s 15 +#define CHAR_U 16 +#define CHAR_QT 17 +#define CHAR_o 18 +#define CHAR_X 19 + +static uint8_t hh[] = { + CHAR_H, CHAR_A, CHAR_P, CHAR_P, CHAR_Y, + CHAR_SPC, + CHAR_H, CHAR_A, CHAR_C, CHAR_K, CHAR_I, CHAR_N, CHAR_G, + CHAR_EXC, + CHAR_SPC, CHAR_SPC, CHAR_SPC, +}; + +static uint8_t gnu[] = { + CHAR_W, CHAR_h, CHAR_A, CHAR_t, CHAR_AP, CHAR_s, CHAR_SPC, + CHAR_G, CHAR_N, CHAR_U, CHAR_QT, + CHAR_SPC, CHAR_SPC, + CHAR_G, CHAR_N, CHAR_U, CHAR_AP, CHAR_s, CHAR_SPC, + CHAR_N, CHAR_o, CHAR_t, CHAR_SPC, + CHAR_U, CHAR_N, CHAR_I, CHAR_X, + CHAR_EXC, + CHAR_SPC, CHAR_SPC, +}; + +struct { uint8_t width; uint32_t data; } chargen[] = { + { 3, 0 }, /* SPACE */ + { 4, DATA55V (0x1f, 0x04, 0x04, 0x1f, 0x00) }, /* H */ + { 3, DATA55V (0x17, 0x15, 0x0f, 0x00, 0x00) }, /* A */ + { 4, DATA55V (0x1f, 0x14, 0x14, 0x08, 0x00) }, /* P */ + { 4, DATA55V (0x19, 0x05, 0x05, 0x1e, 0x00) }, /* Y */ + { 4, DATA55V (0x0e, 0x11, 0x11, 0x0a, 0x00) }, /* C */ + { 4, DATA55V (0x1f, 0x04, 0x0c, 0x13, 0x00) }, /* K */ + { 3, DATA55V (0x11, 0x1f, 0x11, 0x00, 0x00) }, /* I */ + { 4, DATA55V (0x1f, 0x08, 0x06, 0x1f, 0x00) }, /* N */ + { 4, DATA55V (0x0e, 0x11, 0x15, 0x07, 0x00) }, /* G */ + { 2, DATA55V (0x1d, 0x1c, 0x00, 0x00, 0x00) }, /* ! */ + { 5, DATA55V (0x1e, 0x01, 0x0e, 0x01, 0x1e) }, /* W */ + { 3, DATA55V (0x1f, 0x04, 0x07, 0x00, 0x00) }, /* h */ + { 4, DATA55V (0x08, 0x1e, 0x09, 0x09, 0x00) }, /* t */ + { 3, DATA55V (0x04, 0x18, 0x18, 0x00, 0x00) }, /* ' */ + { 4, DATA55V (0x09, 0x15, 0x15, 0x12, 0x00) }, /* s */ + { 4, DATA55V (0x1e, 0x01, 0x01, 0x1e, 0x00) }, /* U */ + { 4, DATA55V (0x08, 0x10, 0x15, 0x08, 0x00) }, /* ? */ + { 4, DATA55V (0x06, 0x09, 0x09, 0x06, 0x00) }, /* o */ + { 5, DATA55V (0x11, 0x0a, 0x04, 0x0a, 0x11) }, /* X */ + { 4, DATA55V (0x1f, 0x11, 0x11, 0x0e, 0x00) }, /* D */ + { 4, DATA55V (0x0e, 0x15, 0x15, 0x0d, 0x00) }, /* e */ + { 4, DATA55V (0x1f, 0x05, 0x05, 0x06, 0x00) }, /* b */ + { 1, DATA55V (0x17, 0x00, 0x00, 0x00, 0x00) }, /* i */ + { 4, DATA55V (0x02, 0x15, 0x15, 0x0f, 0x00) }, /* a */ + { 4, DATA55V (0x0f, 0x08, 0x08, 0x0f, 0x00) }, /* n */ +}; + + +#define REPEAT_COUNT 10 + +static int +life_display (void) +{ + unsigned int i; + uint8_t count = 0; + uint8_t state = 0; + + while (count++ < REPEAT_COUNT) + for (i = 0; i < SIZE55 (l55); i++) + { + if (user_button ()) + { + set_led_display (LED_FULL); + state = 1; + } + else if (state == 1) + return 0; + else + set_led_display (l55[i]); + wait_for (350*1000); + } + + return 1; +} + + +static int +text_display (uint8_t kind) +{ + unsigned int i, j; + uint8_t *text; + uint8_t len; + uint8_t count = 0; + uint8_t state = 0; + + if (kind) + { + text = hh; + len = sizeof (hh); + } + else + { + text = gnu; + len = sizeof (gnu); + } + + set_led_display (0); + while (count++ < REPEAT_COUNT) + for (i = 0; i < len; i++) + { + for (j = 0; j < chargen[text[i]].width; j++) + { + if (user_button ()) + { + set_led_display (LED_FULL); + state = 1; + } + else if (state == 1) + return 0; + else + scroll_led_display ((chargen[text[i]].data >> j * 5) & 0x1f); + wait_for (120*1000); + } + + if (user_button ()) + { + set_led_display (LED_FULL); + state = 1; + } + else if (state == 1) + return 0; + else + scroll_led_display (0); + wait_for (120*1000); + } + + return 1; +} + + +int +main (int argc, const char *argv[]) +{ + chopstx_t led_thd; + chopstx_t button_thd; + uint8_t happy = 1; + (void)argc; + (void)argv; + + chopstx_conf_idle (1); + + chopstx_mutex_init (&mtx); + chopstx_cond_init (&cnd0); + chopstx_cond_init (&cnd1); + + led_thd = chopstx_create (PRIO_LED, STACK_ADDR_LED, + STACK_SIZE_LED, led, NULL); + button_thd = chopstx_create (PRIO_BUTTON, STACK_ADDR_BUTTON, + STACK_SIZE_BUTTON, button, NULL); + + chopstx_usec_wait (200*1000); + + chopstx_mutex_lock (&mtx); + chopstx_cond_signal (&cnd0); + chopstx_cond_signal (&cnd1); + chopstx_mutex_unlock (&mtx); + + wait_for (100*1000); + if (user_button ()) + { + /* Wait button release. */ + while (user_button ()) + wait_for (100*1000); + + happy = 0; + goto do_text; + } + + while (1) + { + if (life_display ()) + break; + do_text: + if (text_display (happy)) + break; + } + + main_finished = 1; + chopstx_join (button_thd, NULL); + chopstx_join (led_thd, NULL); + + chopstx_conf_idle (4); + return 0; +} diff --git a/gnuk/chopstx/example-fsm-55/hh.txt b/gnuk/chopstx/example-fsm-55/hh.txt new file mode 100644 index 0000000000..95b2bbeb99 --- /dev/null +++ b/gnuk/chopstx/example-fsm-55/hh.txt @@ -0,0 +1,59 @@ +o..o +o..o +oooo +o..o +o..o + +oo. +..o +ooo +o.o +ooo + +ooo. +o..o +ooo. +o... +o... + +o..o +o..o +.ooo +...o +ooo. + +.oo. +o..o +o... +o..o +.oo. + +o..o +o.o. +ooo. +o..o +o..o + +ooo +.o. +.o. +.o. +ooo + +o..o +oo.o +o.oo +o.oo +o..o + +.oo. +o... +o.oo +o..o +.ooo + +oo +oo +oo +.. +o. diff --git a/gnuk/chopstx/example-fsm-55/hiroshi-ayumi.c b/gnuk/chopstx/example-fsm-55/hiroshi-ayumi.c new file mode 100644 index 0000000000..4261a5f9ea --- /dev/null +++ b/gnuk/chopstx/example-fsm-55/hiroshi-ayumi.c @@ -0,0 +1,387 @@ +#include +#include +#include +#include + +#include "board.h" + +#define PERIPH_BASE 0x40000000 +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) +#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000) +#define AHB2PERIPH_BASE (PERIPH_BASE + 0x08000000) + +struct GPIO { + volatile uint32_t MODER; + volatile uint16_t OTYPER; + uint16_t dummy0; + volatile uint32_t OSPEEDR; + volatile uint32_t PUPDR; + volatile uint16_t IDR; + uint16_t dummy1; + volatile uint16_t ODR; + uint16_t dummy2; + volatile uint16_t BSRR; + uint16_t dummy3; + volatile uint32_t LCKR; + volatile uint32_t AFR[2]; + volatile uint16_t BRR; + uint16_t dummy4; +}; +#define GPIOA_BASE (AHB2PERIPH_BASE + 0x0000) +#define GPIOA ((struct GPIO *) GPIOA_BASE) +#define GPIOF_BASE (AHB2PERIPH_BASE + 0x1400) +#define GPIOF ((struct GPIO *) GPIOF_BASE) + +#define GPIO_SPEAKER_PIN 1 + +static struct GPIO *const GPIO_LED = ((struct GPIO *)GPIO_LED_BASE); +static struct GPIO *const GPIO_OTHER = ((struct GPIO *)GPIO_OTHER_BASE); + +static chopstx_mutex_t mtx; +static chopstx_cond_t cnd; + +static uint8_t +user_button (void) +{ + return (GPIO_OTHER->IDR & 1) == 0; +} + +static uint8_t l_data[5]; + +static void +set_led_display (uint32_t data) +{ + l_data[0] = (data >> 0) & 0x1f; + l_data[1] = (data >> 5) & 0x1f; + l_data[2] = (data >> 10) & 0x1f; + l_data[3] = (data >> 15) & 0x1f; + l_data[4] = (data >> 20) & 0x1f; +} + + +static void +wait_for (uint32_t usec) +{ + chopstx_usec_wait (usec); +} + +static void +led_prepare_row (uint8_t col) +{ + uint16_t data = 0x1f; + + data |= ((l_data[0] & (1 << col)) ? 1 : 0) << 5; + data |= ((l_data[1] & (1 << col)) ? 1 : 0) << 6; + data |= ((l_data[2] & (1 << col)) ? 1 : 0) << 7; + data |= ((l_data[3] & (1 << col)) ? 1 : 0) << 9; + data |= ((l_data[4] & (1 << col)) ? 1 : 0) << 10; + GPIO_LED->ODR = data; +} + +static void +led_enable_column (uint8_t col) +{ + GPIO_LED->BRR = (1 << col); +} + + +#define STACK_MAIN +#define STACK_PROCESS_1 +#define STACK_PROCESS_2 +#define STACK_PROCESS_3 +#include "stack-def.h" + +#define PRIO_LED 3 + +#define STACK_ADDR_LED ((uint32_t)process1_base) +#define STACK_SIZE_LED (sizeof process1_base) + +static void * +led (void *arg) +{ + (void)arg; + + chopstx_mutex_lock (&mtx); + chopstx_cond_wait (&cnd, &mtx); + chopstx_mutex_unlock (&mtx); + + while (1) + { + int i; + + for (i = 0; i < 5; i++) + { + led_prepare_row (i); + led_enable_column (i); + wait_for (1000); + } + } + + return NULL; +} + + +#define PRIO_SPK 4 + +#define STACK_ADDR_SPK ((uint32_t)process2_base) +#define STACK_SIZE_SPK (sizeof process2_base) + +static chopstx_mutex_t spk_mtx; +static chopstx_cond_t spk_cnd; +static chopstx_cond_t spk_cnd_no_tone; + +static uint8_t tone; +#define NO_TONE 255 + + +static uint16_t tone_table[] = { + 568, /* a = 880Hz */ + /* 536, */ + 506, /* b */ + 478, /* c */ + /* 451, */ + 426, /* d */ + /* 402, */ + 379, /* e */ + 358, /* f*/ + /* 338, */ + 319, /* g */ + /* 301, */ + 284, /* A = 1760Hz */ + /* 268 */ + 253, /* B */ + 239, /* C */ +}; + +static void +change_tone (uint8_t t) +{ + chopstx_mutex_lock (&spk_mtx); + tone = t; + chopstx_cond_signal (&spk_cnd_no_tone); + chopstx_cond_wait (&spk_cnd, &spk_mtx); + chopstx_mutex_unlock (&spk_mtx); +} + +static void * +spk (void *arg) +{ + (void)arg; + + while (1) + { + uint8_t t; + uint16_t w; + + chopstx_mutex_lock (&spk_mtx); + t = tone; + chopstx_cond_signal (&spk_cnd); + if (t == NO_TONE) + { + chopstx_cond_wait (&spk_cnd_no_tone, &spk_mtx); + t = tone; + } + chopstx_mutex_unlock (&spk_mtx); + + w = tone_table[t]*5/3; + GPIO_OTHER->BSRR = (1 << GPIO_SPEAKER_PIN); + wait_for (w); + GPIO_OTHER->BRR = (1 << GPIO_SPEAKER_PIN); + wait_for (w); + } + + return NULL; +} + + +#define PRIO_MUSIC 2 + +#define STACK_ADDR_MUSIC ((uint32_t)process3_base) +#define STACK_SIZE_MUSIC (sizeof process3_base) + +#define C 0 +#define D 1 +#define E 2 +#define F 3 +#define G 4 +#define A 5 +#define B 6 + +#if 0 /* twinkle stars */ +static const char *musical_score = + "c4c4g4g4A4A4g2f4f4e4e4d4d4c2" + "g4g4f4f4e4e4d2g4g4f4f4e4e4d2" + "c4c4g4g4A4A4g2f4f4e4e4d4d4c2"; +#else /* tulip */ +static const char *musical_score = + "c4d4e2c4d4e2g4e4d4c4d4e4d2" + "c4d4e2c4d4e2g4e4d4c4d4e4c2" + "g4g4e4g4A4A4g2e4e4d4d4c2"; +#endif + +static int get_t_and_l (char *tp, char *lp) +{ + static unsigned int i = 0; + char tl, ll; + + tl = musical_score[i++]; + ll = musical_score[i++]; + + if (tl >= 'a') + *tp = tl - 'a'; + else + *tp = tl - 'A' + 7; + + *lp = ll - '0'; + + if (i >= strlen (musical_score)) + { + i = 0; + return 0; + } + else + return 1; +} + +#define WAIT_FOR_NO_TONE (1000*20) +#define WAIT_FOR_TONE (1000000*3/2) + +static void * +music (void *arg) +{ + (void)arg; + + chopstx_mutex_init (&spk_mtx); + chopstx_cond_init (&spk_cnd); + chopstx_cond_init (&spk_cnd_no_tone); + + chopstx_create (PRIO_SPK, STACK_ADDR_SPK, STACK_SIZE_SPK, spk, NULL); + + while (1) + { + char t, l; + int r; + + r = get_t_and_l (&t, &l); + + change_tone (NO_TONE); + wait_for (WAIT_FOR_NO_TONE); + change_tone (t); + wait_for (WAIT_FOR_TONE / l); + + if (!r) + { + change_tone (NO_TONE); + wait_for (WAIT_FOR_TONE * 3); + } + } + + return NULL; +} + + + +#define DATA55(x0,x1,x2,x3,x4) (x0<<20)|(x1<<15)|(x2<<10)|(x3<< 5)|(x4<< 0) +#define SIZE55(img) (sizeof (img) / sizeof (uint32_t)) + +static uint32_t image0[] = { + DATA55 (0x00,0x00,0x00,0x00,0x00), + DATA55 (0x00,0x01,0x00,0x00,0x00), + DATA55 (0x01,0x03,0x01,0x01,0x00), + DATA55 (0x02,0x06,0x02,0x02,0x01), + DATA55 (0x05,0x0d,0x05,0x05,0x03), + DATA55 (0x0b,0x1a,0x0a,0x0a,0x06), + DATA55 (0x16,0x14,0x14,0x14,0x0c), + DATA55 (0x0d,0x08,0x09,0x08,0x19), + DATA55 (0x1b,0x11,0x12,0x10,0x13), + DATA55 (0x17,0x03,0x04,0x00,0x07), + DATA55 (0x0f,0x06,0x09,0x01,0x0e), + DATA55 (0x1e,0x0c,0x12,0x02,0x1c), + DATA55 (0x1d,0x19,0x05,0x05,0x18), + DATA55 (0x1a,0x12,0x0a,0x0a,0x11), + DATA55 (0x14,0x04,0x14,0x14,0x03), + DATA55 (0x08,0x08,0x08,0x09,0x06), + DATA55 (0x10,0x10,0x10,0x12,0x0c), + DATA55 (0x00,0x00,0x00,0x04,0x18), + DATA55 (0x00,0x00,0x00,0x08,0x10), + DATA55 (0x00,0x00,0x00,0x10,0x00), + DATA55 (0x00,0x00,0x00,0x00,0x00), + DATA55 (0x00,0x00,0x00,0x00,0x00), + DATA55 (0x00,0x00,0x00,0x00,0x00), + DATA55 (0x00,0x00,0x00,0x00,0x00), + DATA55 (0x00,0x00,0x00,0x00,0x00), +}; + +static uint32_t image1[] = { + DATA55 (0x00,0x00,0x00,0x00,0x00), + DATA55 (0x01,0x00,0x00,0x00,0x01), + DATA55 (0x03,0x00,0x00,0x01,0x03), + DATA55 (0x07,0x01,0x01,0x03,0x06), + DATA55 (0x0f,0x02,0x02,0x06,0x0c), + DATA55 (0x1f,0x05,0x05,0x0c,0x18), + DATA55 (0x1e,0x0a,0x0a,0x18,0x11), + DATA55 (0x1d,0x14,0x14,0x10,0x03), + DATA55 (0x1b,0x08,0x08,0x00,0x07), + DATA55 (0x17,0x11,0x11,0x01,0x0f), + DATA55 (0x0e,0x02,0x02,0x02,0x1f), + DATA55 (0x1c,0x04,0x04,0x04,0x1e), + DATA55 (0x19,0x08,0x09,0x08,0x1d), + DATA55 (0x13,0x10,0x13,0x10,0x1b), + DATA55 (0x07,0x00,0x07,0x00,0x17), + DATA55 (0x0f,0x00,0x0f,0x00,0x0f), + DATA55 (0x1e,0x00,0x1e,0x00,0x1e), + DATA55 (0x1c,0x00,0x1c,0x00,0x1c), + DATA55 (0x18,0x00,0x18,0x00,0x18), + DATA55 (0x10,0x00,0x10,0x00,0x10), + DATA55 (0x00,0x00,0x00,0x00,0x00), + DATA55 (0x00,0x00,0x00,0x00,0x00), + DATA55 (0x00,0x00,0x00,0x00,0x00), + DATA55 (0x00,0x00,0x00,0x00,0x00), + DATA55 (0x00,0x00,0x00,0x00,0x00), +}; + +int +main (int argc, const char *argv[]) +{ + uint8_t state = 0; + + (void)argc; + (void)argv; + + chopstx_mutex_init (&mtx); + chopstx_cond_init (&cnd); + + chopstx_create (PRIO_LED, STACK_ADDR_LED, STACK_SIZE_LED, led, NULL); + chopstx_create (PRIO_MUSIC, STACK_ADDR_MUSIC, STACK_SIZE_MUSIC, music, NULL); + + chopstx_usec_wait (200*1000); + + chopstx_mutex_lock (&mtx); + chopstx_cond_signal (&cnd); + chopstx_mutex_unlock (&mtx); + + while (1) + { + unsigned int i; + + if (state) + for (i = 0; i < SIZE55 (image1); i++) + { + if (user_button ()) + state = 0; + set_led_display (image1[i]); + wait_for (200*1000); + } + else + for (i = 0; i < SIZE55 (image0); i++) + { + if (user_button ()) + state = 1; + set_led_display (image0[i]); + wait_for (200*1000); + } + } + + return 0; +} diff --git a/gnuk/chopstx/example-fsm-55/l55.txt b/gnuk/chopstx/example-fsm-55/l55.txt new file mode 100644 index 0000000000..701248edd8 --- /dev/null +++ b/gnuk/chopstx/example-fsm-55/l55.txt @@ -0,0 +1,119 @@ +_*___ 08 +__*__ 04 +***__ 1c +_____ 00 +_____ 00 + +_____ 00 +*_*__ 14 +_**__ 0c +_*___ 08 +_____ 00 + +_____ 00 +__*__ 04 +*_*__ 14 +_**__ 0c +_____ 00 + +_____ 00 +_*__ 08 +__**_ 06 +_**__ 0c +_____ 00 + +_____ 00 +__*__ 04 +___*_ 02 +_***_ 0e +_____ 00 + +_____ 00 +_____ 00 +_*_*_ 0a +__**_ 06 +__*__ 04 + +_____ 00 +_____ 00 +___*_ 02 +_*_*_ 0a +__**_ 06 + +_____ 00 +_____ 00 +__*__ 04 +___** 03 +__**_ 06 + +_____ 00 +_____ 00 + __*_ 02 +____* 01 +__*** 07 + +___*_ 02 +_____ 00 +_____ 00 +__*_* 05 +___** 03 + +___** 03 +_____ 00 +_____ 00 +____* 01 +__*_* 05 + +___** 03 +_____ 00 +_____ 00 +_ _*_ 02 +*___* 11 + +*__** 13 +_____ 00 +_____ 00 +____* 01 +*____ 10 + +*___* 11 +____* 01 +_____ 00 +_____ 00 +*__*_ 12 + +*__*_ 12 +*___* 11 +_____ 00 +_____ 00 +*____ 10 + +**___ 18 +*___* 11 +_____ 00 +_____ 00 +____* 01 + +_*___ 08 +**__* 19 +_____ 00 +_____ 00 +*____ 10 + +_*__* 09 +**___ 18 +*____ 10 +_____ 00 +_____ 00 + +_*___ 08 +_*__* 09 +**___ 18 +_____ 00 +_____ 00 + +*____ 10 +_**__ 0c +**___ 18 +_____ 00 +_____ 00 diff --git a/gnuk/chopstx/example-fsm-55/name.txt b/gnuk/chopstx/example-fsm-55/name.txt new file mode 100644 index 0000000000..e54ee584cf --- /dev/null +++ b/gnuk/chopstx/example-fsm-55/name.txt @@ -0,0 +1,287 @@ +_____ 00 +_____ 00 +_____ 00 +_____ 00 +_____ 00 + +_____ 00 +____* 01 +_____ 00 +_____ 00 +_____ 00 + +____* 01 +___** 03 +____* 01 +____* 01 +_____ 00 + +___*_ 02 +__**_ 06 +___*_ 02 +___*_ 02 +____* 01 + +__*_* 05 +_**_* 0d +__*_* 05 +__*_* 05 +___** 03 + +_*_** 0b +**_*_ 1a +_*_*_ 0a +_*_*_ 0a +__**_ 06 + +*_**_ 16 +*_*__ 14 +*_*__ 14 +*_*__ 14 +_**__ 0c + +_**_* 0d +_*___ 08 +_*__* 09 +_*___ 08 +**__* 19 + +**_** 1b +*___* 11 +*__*_ 12 +*____ 10 +*__** 13 + +*_*** 17 +___** 03 +__*__ 04 +_____ 00 +__*** 07 + +_**** 0f +__**_ 06 +_*__* 09 +____* 01 +_***_ 0e + +****_ 1e +_**__ 0c +*__*_ 12 +___*_ 02 +***__ 1c + +***_* 1d +**__* 19 +__*_* 05 +__*_* 05 +**___ 18 + +**_*_ 1a +*__*_ 12 +_*_*_ 0a +_*_*_ 0a +*___* 11 + +*_*__ 14 +__*__ 04 +*_*__ 14 +*_*__ 14 +___** 03 + +_*___ 08 +_*___ 08 +_*___ 08 +_*__* 09 +__**_ 06 + +*____ 10 +*____ 10 +*____ 10 +*__*_ 12 +_**__ 0c + +_____ 00 +_____ 00 +_____ 00 +__*__ 04 +**___ 18 + +_____ 00 +_____ 00 +_____ 00 +_*___ 08 +*____ 10 + +_____ 00 +_____ 00 +_____ 00 +*____ 10 +_____ 00 + +_____ 00 +_____ 00 +_____ 00 +_____ 00 +_____ 00 + +_____ 00 +_____ 00 +_____ 00 +_____ 00 +_____ 00 + +_____ 00 +_____ 00 +_____ 00 +_____ 00 +_____ 00 + +_____ 00 +_____ 00 +_____ 00 +_____ 00 +_____ 00 + +_____ 00 +_____ 00 +_____ 00 +_____ 00 +_____ 00 + +_____ 00 +_____ 00 +_____ 00 +_____ 00 +_____ 00 + +____* 01 +_____ 00 +_____ 00 +_____ 00 +____* 01 + +___** 03 +_____ 00 +_____ 00 +____* 01 +___** 03 + +__*** 07 +____* 01 +____* 01 +___** 03 +__**_ 06 + +_**** 0f +___*_ 02 +___*_ 02 +__**_ 06 +_**__ 0c + +***** 1f +__*_* 05 +__*_* 05 +_**__ 0c +**___ 18 + +****_ 1e +_*_*_ 0a +_*_*_ 0a +**___ 18 +*___* 11 + +***_* 1d +*_*__ 14 +*_*__ 14 +*____ 10 +___** 03 + +**_** 1b +_*___ 08 +_*___ 08 +_____ 00 +__*** 07 + +*_*** 17 +*___* 11 +*___* 11 +____* 01 +_**** 0f + +_***_ 0e +___*_ 02 +___*_ 02 +___*_ 02 +***** 1f + +***__ 1c +__*__ 04 +__*__ 04 +__*__ 04 +****_ 1e + +**__* 19 +_*___ 08 +_*__* 09 +_*___ 08 +***_* 1d + +*__** 13 +*____ 10 +*__** 13 +*____ 10 +**_** 1b + +__*** 07 +_____ 00 +__*** 07 +_____ 00 +*_*** 17 + +_**** 0f +_____ 00 +_**** 0f +_____ 00 +_**** 0f + +****_ 1e +_____ 00 +****_ 1e +_____ 00 +****_ 1e + +***__ 1c +_____ 00 +***__ 1c +_____ 00 +***__ 1c + +**___ 18 +_____ 00 +**___ 18 +_____ 00 +**___ 18 + +*____ 10 +_____ 00 +*____ 10 +_____ 00 +*____ 10 + +_____ 00 +_____ 00 +_____ 00 +_____ 00 +_____ 00 + +_____ 00 +_____ 00 +_____ 00 +_____ 00 +_____ 00 + +_____ 00 +_____ 00 +_____ 00 +_____ 00 +_____ 00 diff --git a/gnuk/chopstx/example-fsm-55/reset.c b/gnuk/chopstx/example-fsm-55/reset.c new file mode 100644 index 0000000000..6b7d842b31 --- /dev/null +++ b/gnuk/chopstx/example-fsm-55/reset.c @@ -0,0 +1,110 @@ +/* + * reset.c - No system routines, but only RESET handler for STM32F030. + * + * Copyright (C) 2015 Flying Stone Technology + * Author: NIIBE Yutaka + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. This file is offered as-is, + * without any warranty. + * + */ + +#include +#include + +static void __attribute__ ((naked)) +reset (void) +{ + asm volatile ("cpsid i\n\t" /* Mask all interrupts. */ + "mov r0, pc\n\t" /* r0 = PC & ~0x0fff */ + "mov r1, #0x10\n\t" + "lsl r1, #8\n\t" + "sub r1, r1, #1\n\t" + "bic r0, r0, r1\n\t" + "ldr r2, [r0]\n\t" + "msr MSP, r2\n\t" /* Main (exception handler) stack. */ + "b entry\n\t" + : /* no output */ : /* no input */ : "memory"); + /* Never reach here. */ +} + +extern uint8_t __main_stack_end__; +extern void preempt (void); +extern void chx_timer_expired (void); +extern void chx_handle_intr (void); + +static void nmi (void) +{ + for (;;); +} + +static void __attribute__ ((naked)) +hard_fault (void) +{ + for (;;); +} + +static void mem_manage (void) +{ + for (;;); +} + +static void bus_fault (void) +{ + for (;;); +} + +static void usage_fault (void) +{ + for (;;); +} + +static void none (void) +{ +} + + +typedef void (*handler)(void); +extern uint8_t __main_stack_end__; + +handler vector[] __attribute__ ((section(".vectors"))) = { + (handler)(&__main_stack_end__ - 32), + reset, + nmi, /* nmi */ + hard_fault, /* hard fault */ + /* 0x10 */ + mem_manage, /* mem manage */ + bus_fault, /* bus fault */ + usage_fault, /* usage fault */ + none, + /* 0x20 */ + none, none, none, /* reserved */ + none, /* SVCall */ + none, /* Debug */ + none, /* reserved */ + preempt, /* PendSV */ + chx_timer_expired, /* SysTick */ + /* 0x40 */ + chx_handle_intr /* WWDG */, chx_handle_intr /* PVD */, + chx_handle_intr /* TAMPER */, chx_handle_intr /* RTC */, + chx_handle_intr /* FLASH */, chx_handle_intr /* RCC */, + chx_handle_intr /* EXTI0 */, chx_handle_intr /* EXTI1 */, + /* 0x60 */ + chx_handle_intr /* EXTI2 */, chx_handle_intr /* EXTI3 */, + chx_handle_intr /* EXTI4 */, chx_handle_intr /* DMA1 CH1 */, + chx_handle_intr /* DMA1 CH2 */, chx_handle_intr /* DMA1 CH3 */, + chx_handle_intr /* DMA1 CH4 */, chx_handle_intr /* DMA1 CH5 */, + /* 0x80 */ + chx_handle_intr /* DMA1 CH6 */, chx_handle_intr /* DMA1 CH7 */, + chx_handle_intr /* ADC1_2 */, chx_handle_intr /* USB HP */, + /* 0x90 */ + chx_handle_intr /* USB LP */, chx_handle_intr /* CAN */, + /* ... and more. EXT9_5, TIMx, I2C, SPI, USART, EXT15_10 */ + chx_handle_intr, chx_handle_intr, + /* 0xA0 */ + chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr, + chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr, + /* 0xc0 */ +}; diff --git a/gnuk/chopstx/example-fsm-55/stack-def.h b/gnuk/chopstx/example-fsm-55/stack-def.h new file mode 100644 index 0000000000..880dfd93f8 --- /dev/null +++ b/gnuk/chopstx/example-fsm-55/stack-def.h @@ -0,0 +1,54 @@ +#define MAIN_SIZE 0x0080 /* Idle+Exception handlers */ +#define SIZE_0 0x0100 /* Main program */ +#define SIZE_1 0x0100 /* first thread program */ +#define SIZE_2 0x0100 /* second thread program */ +#define SIZE_3 0x0100 /* third thread program */ + +#if defined(STACK_MAIN) +/* + * The terminology of "main" is confusing in ARM architecture. + * Here, "main_base" is for exception handlers. + */ +/* Idle+Exception handlers */ +char __main_stack_end__[0] __attribute__ ((section(".main_stack"))); +char main_base[MAIN_SIZE] __attribute__ ((section(".main_stack"))); + +/* Main program */ +char __process0_stack_end__[0] __attribute__ ((section(".process_stack.0"))); +char process0_base[SIZE_0] __attribute__ ((section(".process_stack.0"))); +#endif + +/* First thread program */ +#if defined(STACK_PROCESS_1) +char process1_base[SIZE_1] __attribute__ ((section(".process_stack.1"))); +#endif + +/* Second thread program */ +#if defined(STACK_PROCESS_2) +char process2_base[SIZE_2] __attribute__ ((section(".process_stack.2"))); +#endif + +/* Third thread program */ +#if defined(STACK_PROCESS_3) +char process3_base[SIZE_3] __attribute__ ((section(".process_stack.3"))); +#endif + +/* Fourth thread program */ +#if defined(STACK_PROCESS_4) +char process4_base[SIZE_4] __attribute__ ((section(".process_stack.4"))); +#endif + +/* Fifth thread program */ +#if defined(STACK_PROCESS_5) +char process5_base[SIZE_5] __attribute__ ((section(".process_stack.5"))); +#endif + +/* Sixth thread program */ +#if defined(STACK_PROCESS_6) +char process6_base[SIZE_6] __attribute__ ((section(".process_stack.6"))); +#endif + +/* Seventh thread program */ +#if defined(STACK_PROCESS_7) +char process7_base[SIZE_7] __attribute__ ((section(".process_stack.7"))); +#endif diff --git a/gnuk/chopstx/example-fsm-55/stlink-v2.cfg b/gnuk/chopstx/example-fsm-55/stlink-v2.cfg new file mode 100644 index 0000000000..f772767c8e --- /dev/null +++ b/gnuk/chopstx/example-fsm-55/stlink-v2.cfg @@ -0,0 +1,12 @@ +# Contributed by Kiwamu Okabe + +source [find interface/stlink-v2.cfg] +transport select hla_swd + +# The STM32F030F4P6 is a *tightly* constrained chip; the work area size must be +# similarly constrained. +set WORKAREASIZE 0x1000 +source [find target/stm32f0x.cfg] + +# use hardware reset, connect under reset +reset_config srst_only srst_nogate connect_assert_srst diff --git a/gnuk/chopstx/example-led/Makefile b/gnuk/chopstx/example-led/Makefile new file mode 100644 index 0000000000..d7d978c18e --- /dev/null +++ b/gnuk/chopstx/example-led/Makefile @@ -0,0 +1,40 @@ +# Makefile for example application of Chopstx + +PROJECT = sample + +### Currently, it's for STM32F0 Discovery. +### Please change lines started with '###' for Cortex-M3 board. + +CHOPSTX = .. +LDSCRIPT= sample.ld +### LDSCRIPT= sample.ld.m3 +CSRC = sample.c + +CHIP=stm32f0 +USE_SYS = yes + +################################### +CROSS = arm-none-eabi- +CC = $(CROSS)gcc +LD = $(CROSS)gcc +OBJCOPY = $(CROSS)objcopy + +### MCU = cortex-m3 +MCU = cortex-m0 +CWARN = -Wall -Wextra -Wstrict-prototypes +DEFS = -DUSE_SYS3 -DFREE_STANDING -DMHZ=48 +### DEFS = -DFREE_STANDING -DUSE_SYS3 -DBUSY_LOOP -DCHX_FLAGS_MAIN=CHOPSTX_SCHED_RR +OPT = -O3 -Os -g +LIBS = + +#################### +include ../rules.mk + +board.h: + @echo Please make a symbolic link \'board.h\' to a file in ../board; + @exit 1 + +sys.c: board.h + +distclean: clean + rm -f board.h diff --git a/gnuk/chopstx/example-led/board.h b/gnuk/chopstx/example-led/board.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gnuk/chopstx/example-led/sample.c b/gnuk/chopstx/example-led/sample.c new file mode 100644 index 0000000000..7e7bcff28f --- /dev/null +++ b/gnuk/chopstx/example-led/sample.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include "sys.h" /* for set_led */ + +static chopstx_mutex_t mtx; +static chopstx_cond_t cnd0; +static chopstx_cond_t cnd1; + +static uint8_t u, v; +static uint8_t m; /* 0..100 */ + +static void +wait_for (uint32_t usec) +{ +#if defined(BUSY_LOOP) + uint32_t count = usec * 6; + uint32_t i; + + for (i = 0; i < count; i++) + asm volatile ("" : : "r" (i) : "memory"); +#else + chopstx_usec_wait (usec); +#endif +} + +static void * +pwm (void *arg) +{ + (void)arg; + + chopstx_mutex_lock (&mtx); + chopstx_cond_wait (&cnd0, &mtx); + chopstx_mutex_unlock (&mtx); + + while (1) + { + set_led (u&v); + wait_for (m); + set_led (0); + wait_for (100-m); + } + + return NULL; +} + +static void * +blk (void *arg) +{ + (void)arg; + + chopstx_mutex_lock (&mtx); + chopstx_cond_wait (&cnd1, &mtx); + chopstx_mutex_unlock (&mtx); + + while (1) + { + v = 0; + wait_for (200*1000); + v = 1; + wait_for (200*1000); + } + + return NULL; +} + +#if defined(BUSY_LOOP) +#define PRIO_PWM (CHOPSTX_SCHED_RR|1) +#define PRIO_BLK (CHOPSTX_SCHED_RR|1) +#else +#define PRIO_PWM 3 +#define PRIO_BLK 2 +#endif + +#define STACK_MAIN +#define STACK_PROCESS_1 +#define STACK_PROCESS_2 +#include "stack-def.h" + + +#define STACK_ADDR_PWM ((uint32_t)process1_base) +#define STACK_SIZE_PWM (sizeof process1_base) + +#define STACK_ADDR_BLK ((uint32_t)process2_base) +#define STACK_SIZE_BLK (sizeof process2_base) + + +int +main (int argc, const char *argv[]) +{ + (void)argc; + (void)argv; + + chopstx_mutex_init (&mtx); + chopstx_cond_init (&cnd0); + chopstx_cond_init (&cnd1); + + m = 10; + + chopstx_create (PRIO_PWM, STACK_ADDR_PWM, STACK_SIZE_PWM, pwm, NULL); + chopstx_create (PRIO_BLK, STACK_ADDR_BLK, STACK_SIZE_BLK, blk, NULL); + + chopstx_usec_wait (200*1000); + + chopstx_mutex_lock (&mtx); + chopstx_cond_signal (&cnd0); + chopstx_cond_signal (&cnd1); + chopstx_mutex_unlock (&mtx); + + while (1) + { + u ^= 1; + wait_for (200*1000*6); + } + + return 0; +} diff --git a/gnuk/chopstx/example-led/sample.ld b/gnuk/chopstx/example-led/sample.ld new file mode 100644 index 0000000000..a9be67ffb6 --- /dev/null +++ b/gnuk/chopstx/example-led/sample.ld @@ -0,0 +1,126 @@ +/* + * ST32F0 memory setup. + */ +MEMORY +{ + flash0 : org = 0x08000000, len = 4k + flash : org = 0x08000000+0x1000, len = 60k + ram : org = 0x20000000, len = 20k +} + +__ram_start__ = ORIGIN(ram); +__ram_size__ = 20k; +__ram_end__ = __ram_start__ + __ram_size__; + +SECTIONS +{ + . = 0; + + .sys : ALIGN(4) SUBALIGN(4) + { + _sys = .; + KEEP(*(.vectors)) + . = ALIGN(16); + KEEP(*(.sys.version)) + KEEP(*(.sys.board_id)) + KEEP(*(.sys.board_name)) + build/sys-*.o(.text) + build/sys-*.o(.text.*) + build/sys-*.o(.rodata) + build/sys-*.o(.rodata.*) + . = ALIGN(1024); +/* + *(.sys.0) + *(.sys.1) + *(.sys.2) +*/ + } > flash0 =0xffffffff + + _text = .; + + .startup : ALIGN(128) SUBALIGN(128) + { + KEEP(*(.startup.vectors)) + . = ALIGN (16); + } > flash =0xffffffff + + .text : ALIGN(16) SUBALIGN(16) + { + *(.text.startup.*) + *(.text) + *(.text.*) + *(.rodata) + *(.rodata.*) + *(.glue_7t) + *(.glue_7) + *(.gcc*) + . = ALIGN(8); + } > flash + + .ARM.extab : {*(.ARM.extab* .gnu.linkonce.armextab.*)} > flash + + .ARM.exidx : { + PROVIDE(__exidx_start = .); + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + PROVIDE(__exidx_end = .); + } > flash + + .eh_frame_hdr : {*(.eh_frame_hdr)} > flash + + .eh_frame : ONLY_IF_RO {*(.eh_frame)} > flash + + .textalign : ONLY_IF_RO { . = ALIGN(8); } > flash + + _etext = .; + _textdata = _etext; + + .vectors_in_ram : + { + . = ALIGN(8); + __vector_ram_addr__ = .; + KEEP(*(.bss.startup.*)) + } > ram + + .stacks (NOLOAD) : + { + . = ALIGN(8); + *(.main_stack) + *(.process_stack.0) + *(.process_stack.1) + *(.process_stack.2) + *(.process_stack.3) + . = ALIGN(8); + } > ram + + .data : + { + . = ALIGN(4); + PROVIDE(_data = .); + *(.data) + . = ALIGN(4); + *(.data.*) + . = ALIGN(4); + *(.ramtext) + . = ALIGN(4); + PROVIDE(_edata = .); + } > ram AT > flash + + .bss : + { + . = ALIGN(4); + PROVIDE(_bss_start = .); + *(.bss) + . = ALIGN(4); + *(.bss.*) + . = ALIGN(4); + *(COMMON) + . = ALIGN(4); + PROVIDE(_bss_end = .); + } > ram + + PROVIDE(end = .); + _end = .; +} + +__heap_base__ = _end; +__heap_end__ = __ram_end__; diff --git a/gnuk/chopstx/example-led/sample.ld.m3 b/gnuk/chopstx/example-led/sample.ld.m3 new file mode 100644 index 0000000000..57ddb83341 --- /dev/null +++ b/gnuk/chopstx/example-led/sample.ld.m3 @@ -0,0 +1,116 @@ +/* + * ST32F103 memory setup. + */ +MEMORY +{ + flash0 : org = 0x08000000, len = 4k + flash : org = 0x08000000+0x1000, len = 60k + ram : org = 0x20000000, len = 20k +} + +__ram_start__ = ORIGIN(ram); +__ram_size__ = 20k; +__ram_end__ = __ram_start__ + __ram_size__; + +SECTIONS +{ + . = 0; + + .sys : ALIGN(4) SUBALIGN(4) + { + _sys = .; + KEEP(*(.vectors)) + . = ALIGN(16); + KEEP(*(.sys.version)) + KEEP(*(.sys.board_id)) + KEEP(*(.sys.board_name)) + build/sys-*.o(.text) + build/sys-*.o(.text.*) + build/sys-*.o(.rodata) + build/sys-*.o(.rodata.*) + . = ALIGN(1024); + *(.sys.0) + *(.sys.1) + *(.sys.2) + } > flash0 + + _text = .; + + .startup : ALIGN(128) SUBALIGN(128) + { + KEEP(*(.startup.vectors)) + . = ALIGN (16); + } > flash =0xffffffff + + .text : ALIGN(16) SUBALIGN(16) + { + *(.text.startup.*) + *(.text) + *(.text.*) + *(.rodata) + *(.rodata.*) + *(.glue_7t) + *(.glue_7) + *(.gcc*) + . = ALIGN(8); + } > flash + + .ARM.extab : {*(.ARM.extab* .gnu.linkonce.armextab.*)} > flash + + .ARM.exidx : { + PROVIDE(__exidx_start = .); + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + PROVIDE(__exidx_end = .); + } > flash + + .eh_frame_hdr : {*(.eh_frame_hdr)} > flash + + .eh_frame : ONLY_IF_RO {*(.eh_frame)} > flash + + .textalign : ONLY_IF_RO { . = ALIGN(8); } > flash + + _etext = .; + _textdata = _etext; + + .stacks (NOLOAD) : + { + *(.main_stack) + *(.process_stack.0) + *(.process_stack.1) + *(.process_stack.2) + *(.process_stack.3) + . = ALIGN(8); + } > ram + + .data : + { + . = ALIGN(4); + PROVIDE(_data = .); + *(.data) + . = ALIGN(4); + *(.data.*) + . = ALIGN(4); + *(.ramtext) + . = ALIGN(4); + PROVIDE(_edata = .); + } > ram AT > flash + + .bss : + { + . = ALIGN(4); + PROVIDE(_bss_start = .); + *(.bss) + . = ALIGN(4); + *(.bss.*) + . = ALIGN(4); + *(COMMON) + . = ALIGN(4); + PROVIDE(_bss_end = .); + } > ram + + PROVIDE(end = .); + _end = .; +} + +__heap_base__ = _end; +__heap_end__ = __ram_end__; diff --git a/gnuk/chopstx/example-led/stack-def.h b/gnuk/chopstx/example-led/stack-def.h new file mode 100644 index 0000000000..90284f00f7 --- /dev/null +++ b/gnuk/chopstx/example-led/stack-def.h @@ -0,0 +1,53 @@ +#define MAIN_SIZE 0x0080 /* Idle+Exception handlers */ +#define SIZE_0 0x0100 /* Main program */ +#define SIZE_1 0x0100 /* first thread program */ +#define SIZE_2 0x0100 /* second thread program */ + +#if defined(STACK_MAIN) +/* + * The terminology of "main" is confusing in ARM architecture. + * Here, "main_base" is for exception handlers. + */ +/* Idle+Exception handlers */ +char __main_stack_end__[0] __attribute__ ((section(".main_stack"))); +char main_base[MAIN_SIZE] __attribute__ ((section(".main_stack"))); + +/* Main program */ +char __process0_stack_end__[0] __attribute__ ((section(".process_stack.0"))); +char process0_base[SIZE_0] __attribute__ ((section(".process_stack.0"))); +#endif + +/* First thread program */ +#if defined(STACK_PROCESS_1) +char process1_base[SIZE_1] __attribute__ ((section(".process_stack.1"))); +#endif + +/* Second thread program */ +#if defined(STACK_PROCESS_2) +char process2_base[SIZE_2] __attribute__ ((section(".process_stack.2"))); +#endif + +/* Third thread program */ +#if defined(STACK_PROCESS_3) +char process3_base[SIZE_3] __attribute__ ((section(".process_stack.3"))); +#endif + +/* Fourth thread program */ +#if defined(STACK_PROCESS_4) +char process4_base[SIZE_4] __attribute__ ((section(".process_stack.4"))); +#endif + +/* Fifth thread program */ +#if defined(STACK_PROCESS_5) +char process5_base[SIZE_5] __attribute__ ((section(".process_stack.5"))); +#endif + +/* Sixth thread program */ +#if defined(STACK_PROCESS_6) +char process6_base[SIZE_6] __attribute__ ((section(".process_stack.6"))); +#endif + +/* Seventh thread program */ +#if defined(STACK_PROCESS_7) +char process7_base[SIZE_7] __attribute__ ((section(".process_stack.7"))); +#endif diff --git a/gnuk/chopstx/example-primer2/Makefile b/gnuk/chopstx/example-primer2/Makefile new file mode 100644 index 0000000000..0a0f3977d1 --- /dev/null +++ b/gnuk/chopstx/example-primer2/Makefile @@ -0,0 +1,45 @@ +# Makefile for example application of Chopstx + +PROJECT = lcd + +CHOPSTX = .. +NEUGSRC = ./neug/src +LDSCRIPT= lcd.ld +CSRC = primer2-switches.c primer2-ts.c lcd.c main.c \ + neug.c sha256.c + +CHIP=stm32f103 +USE_SYS = yes +USE_USB = yes +USE_ADC = yes + +################################### +CROSS = arm-none-eabi- +CC = $(CROSS)gcc +LD = $(CROSS)gcc +OBJCOPY = $(CROSS)objcopy +MCU = cortex-m3 +CWARN = -Wall -Wextra -Wstrict-prototypes +DEFS = -DUSE_SYS3 -DFREE_STANDING -DMHZ=48 +# DEFS = -DFREE_STANDING -DUSE_SYS3 -DBUSY_LOOP -DCHX_FLAGS_MAIN=CHOPSTX_SCHED_RR +OPT = -O3 -Os -g +INCDIR = $(NEUGSRC) +LIBS = + +#################### +include ../rules.mk + +board.h: ../board/board-stm32-primer2.h neug + ln -s ../board/board-stm32-primer2.h ./board.h + +sys.c: board.h + +neug: + @echo Please make a symbolic link \'neug\' to the neug directory; + @exit 1 + +adc_stm32f103.c neug.c sha256.c: + ln -s $(NEUGSRC)/$@ $@ + +distclean: clean + rm -f board.h neug adc_stm32f103.c neug.c sha256.c diff --git a/gnuk/chopstx/example-primer2/README b/gnuk/chopstx/example-primer2/README new file mode 100644 index 0000000000..b27a52ae12 --- /dev/null +++ b/gnuk/chopstx/example-primer2/README @@ -0,0 +1,18 @@ +Example for STM32 Primer2 by Kazmoto Kojima + +It is taken from: + + http://www.rr.iij4u.or.jp/~kkojima/letschopstx.html + +From the original tar archive, chopstx-logo.data is removed because +it's too big. + +You can generate it by ImageMagick. + + $ convert ../chopstx.svg -depth 8 -flip \( +clone -channel B -fx R \) +swap -channel R -fx v.B -resize 128x160 -background white -compress none -flatten chopstx.ppm + + $ tail -n +4 chopstx.ppm | sed -e 's/ /, /g' > chopstx-logo.data + + +Since it was written for Chopstx 0.06, you would need to change a bit, +perhaps. diff --git a/gnuk/chopstx/example-primer2/lcd.c b/gnuk/chopstx/example-primer2/lcd.c new file mode 100644 index 0000000000..9dab1d3fd6 --- /dev/null +++ b/gnuk/chopstx/example-primer2/lcd.c @@ -0,0 +1,388 @@ +#include +#include +#include +#include "sys.h" /* for set_led */ +#include "st7732.h" +#include "board.h" + +#define PERIPH_BASE 0x40000000 +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) + +struct GPIO { + volatile uint32_t CRL; + volatile uint32_t CRH; + volatile uint32_t IDR; + volatile uint32_t ODR; + volatile uint32_t BSRR; + volatile uint32_t BRR; + volatile uint32_t LCKR; +}; + +#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800) +#define GPIOA ((struct GPIO *) GPIOA_BASE) +#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00) +#define GPIOB ((struct GPIO *) GPIOB_BASE) +#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000) +#define GPIOC ((struct GPIO *) GPIOC_BASE) +#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400) +#define GPIOD ((struct GPIO *) GPIOD_BASE) +#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800) +#define GPIOE ((struct GPIO *) GPIOE_BASE) + +static struct GPIO *const GPIO_LCD = ((struct GPIO *const) GPIO_LED_BASE); +static struct GPIO *const GPIO_LCD_CTRL = ((struct GPIO *const) GPIO_USB_BASE); + +#define GPIO_LCD_RD 4 +#define GPIO_LCD_WR 5 +#define GPIO_LCD_RST 6 +#define GPIO_LCD_CS 7 +#define GPIO_LCD_RS 11 +/* PE7:LCD_D0 - PE14:LCD_D7 */ +#define GPIO_DATA_SHIFT 7 +#define GPIO_DATA_MASK (0xff << GPIO_DATA_SHIFT) + +static void +lcd_command_common (st7732_cmd_t cmd) +{ + /* Set command. */ + GPIO_LCD->BRR = GPIO_DATA_MASK & ~(cmd << GPIO_DATA_SHIFT); + GPIO_LCD->BSRR = GPIO_DATA_MASK & (cmd << GPIO_DATA_SHIFT); + /* Set CMD mode. */ + GPIO_LCD_CTRL->BRR = (1 << GPIO_LCD_RS); + /* Asert /CS. */ + GPIO_LCD_CTRL->BRR = (1<< GPIO_LCD_CS); + /* Asert /WR. */ + GPIO_LCD_CTRL->BRR = (1<< GPIO_LCD_WR); + // chopstx_usec_wait (1); + /* Negate /WR. */ + GPIO_LCD_CTRL->BSRR = (1<< GPIO_LCD_WR); + + /* Return DATA mode. */ + GPIO_LCD_CTRL->BSRR = (1 << GPIO_LCD_RS); +} + +/* Issue command with no data read/write. */ +void +lcd_command_no (st7732_cmd_t cmd) +{ + lcd_command_common (cmd); + + /* Negate /CS. */ + GPIO_LCD_CTRL->BSRR = (1<< GPIO_LCD_CS); +} + +#if 0 +void +lcd_command_readn (st7732_cmd_t cmd, uint8_t *data, size_t n) +{ + volatile int dummy __attribute__ ((unused)); + + lcd_command_common (cmd); + + /* Set GPIO_LCD to input mode. */ + GPIO_LCD->CRH = 0x88888888; + GPIO_LCD->CRL = 0x88888833; + + /* Assert /RD. */ + GPIO_LCD_CTRL->BRR = (1<< GPIO_LCD_RD); + // chopstx_usec_wait (1); + /* Dummy read. */ + dummy = GPIO_LCD->IDR; + /* Negate /RD. */ + GPIO_LCD_CTRL->BSRR = (1<< GPIO_LCD_RD); + + /* Read loop. */ + while (n-- > 0) + { + /* Assert /RD. */ + GPIO_LCD_CTRL->BRR = (1<< GPIO_LCD_RD); + // chopstx_usec_wait (1); + /* Negate /RD. */ + GPIO_LCD_CTRL->BSRR = (1<< GPIO_LCD_RD); + *data++ = GPIO_LCD->IDR >> GPIO_DATA_SHIFT; + } + + /* Negate /CS. */ + GPIO_LCD_CTRL->BSRR = (1<< GPIO_LCD_CS); + + /* Set GPIO_LCD to output mode. */ + GPIO_LCD->CRH = 0x83333333; + GPIO_LCD->CRL = 0x38888833; +} +#endif + +/* Issue command with N data write. */ +void +lcd_command_writen (st7732_cmd_t cmd, uint8_t *data, size_t n) +{ + lcd_command_common (cmd); + + /* Write loop. */ + while (n-- > 0) + { + uint8_t b = *data++; + + GPIO_LCD->BRR = GPIO_DATA_MASK & ~(b << GPIO_DATA_SHIFT); + GPIO_LCD->BSRR = GPIO_DATA_MASK & (b << GPIO_DATA_SHIFT); + /* Assert /WR. */ + GPIO_LCD_CTRL->BRR = (1<< GPIO_LCD_WR); + // chopstx_usec_wait (1); + /* Negate /WR. */ + GPIO_LCD_CTRL->BSRR = (1<< GPIO_LCD_WR); + } + + /* Negate /CS. */ + GPIO_LCD_CTRL->BSRR = (1<< GPIO_LCD_CS); +} + +/* Issue command with N same data write. */ +void +lcd_command_filln (st7732_cmd_t cmd, uint8_t b, size_t n) +{ + lcd_command_common (cmd); + + /* Write loop. */ + while (n-- > 0) + { + GPIO_LCD->BRR = GPIO_DATA_MASK & ~(b << GPIO_DATA_SHIFT); + GPIO_LCD->BSRR = GPIO_DATA_MASK & (b << GPIO_DATA_SHIFT); + /* Assert /WR. */ + GPIO_LCD_CTRL->BRR = (1<< GPIO_LCD_WR); + // chopstx_usec_wait (1); + /* Negate /WR. */ + GPIO_LCD_CTRL->BSRR = (1<< GPIO_LCD_WR); + } + + /* Negate /CS. */ + GPIO_LCD_CTRL->BSRR = (1<< GPIO_LCD_CS); +} + +static chopstx_mutex_t lcd_mtx; +static chopstx_cond_t lcd_cnd0; +static chopstx_cond_t lcd_cnd1; + +/* Process for initializing ST7732. */ +static void * +lcd_initializer (void *arg __attribute__((unused))) +{ + uint8_t args[16]; + + chopstx_mutex_lock (&lcd_mtx); + chopstx_cond_wait (&lcd_cnd0, &lcd_mtx); + chopstx_mutex_unlock (&lcd_mtx); + + /* Set GPIO_LCD to write mode. */ + GPIO_LCD->CRH = 0x83333333; + GPIO_LCD->CRL = 0x38888833; + + /* Set GPIO_LCD_CTRL IO mode. */ + GPIO_LCD_CTRL->CRH = 0x88883888; + GPIO_LCD_CTRL->CRL = 0x33333888; + + /* Restart ST7732. */ + /* Hard reset. */ + chopstx_usec_wait (100000); + GPIO_LCD_CTRL->BRR = (1 << GPIO_LCD_RST); + chopstx_usec_wait (100000); + GPIO_LCD_CTRL->BSRR = (1 << GPIO_LCD_RST); + chopstx_usec_wait (100000); + + /* Software reset. */ + lcd_command_no (SWRESET); + chopstx_usec_wait (150000); + + /* Sleep in. */ + lcd_command_no (SLPIN); + chopstx_usec_wait (100000); + + /* Sleep out. */ + lcd_command_no (SLPOUT); + chopstx_usec_wait (100000); + + /* Configure ST7732. Set display mode, pixel mode, etc. */ + /* FRMCTR1, 6, 3, 2 */ + args[0] = 0x06; args[1] = 0x03; args[2] = 0x02; + lcd_command_writen (FRMCTR1, args, 3); + /* INVCTR, 3 */ + args[0] = 0x03; + lcd_command_writen (INVCTR, args, 1); + /* DISSET5, 2, eh */ + args[0] = 0x02; args[1] = 0x0e; + lcd_command_writen (DISSET5, args, 2); + /* DISPCTRL, 1ah */ + args[0] = 0x1a; + lcd_command_writen (DISPCTRL, args, 1); + /* PWCTR1, 2, 0 */ + args[0] = 0x02; args[1] = 0x00; + lcd_command_writen (PWCTR1, args, 2); + /* PWCTR2, 5 */ + args[0] = 0x05; + lcd_command_writen (PWCTR2, args, 1); + /* PWCTR3, 2, 2 */ + args[0] = 0x02; args[1] = 0x02; + lcd_command_writen (PWCTR3, args, 2); + /* PWCTR4, 1, 2 */ + args[0] = 0x01; args[1] = 0x00; + lcd_command_writen (PWCTR4, args, 2); + /* PWCTR5, 1, 2 */ + args[0] = 0x01; args[1] = 0x00; + lcd_command_writen (PWCTR5, args, 2); + /* VMCTR1, 47h, 2ah */ + args[0] = 0x47; args[1] = 0x2a; + lcd_command_writen (VMCTR1, args, 2); + /* OSCADJ, 4ch */ + args[0] = 0x4c; + lcd_command_writen (OSCADJ, args, 1); + /* DEFADJ, 6 */ + args[0] = 0x06; + lcd_command_writen (DEFADJ, args, 1); + + /* gamma adjust */ + + /* MADCTL, c0h MY=1, MX=1 */ + args[0] = 0xc0; + lcd_command_writen (MADCTL, args, 1); + + /* Set RA and CA. */ + /* RASET, 0, 0, 0, 159 */ + args[0] = 0x00; args[1] = 0x00; args[2] = 0x00; args[3] = LCD_ROW-1; + lcd_command_writen (RASET, args, 4); + /* CASET, 0, 0, 0, 127 */ + args[0] = 0x00; args[1] = 0x00; args[2] = 0x00; args[3] = LCD_COLUMN-1; + lcd_command_writen (CASET, args, 4); + + /* 0x06: RGB 6-6-6-bit. */ + args[0] = 0x06; + lcd_command_writen (COLMOD, args, 1); + + args[0] = 0; + lcd_command_writen (TEON, args, 1); + + lcd_command_no (DISPON); + + /* Wait 20ms. */ + chopstx_usec_wait (20000); + + chopstx_mutex_lock (&lcd_mtx); + chopstx_cond_signal (&lcd_cnd1); + chopstx_mutex_unlock (&lcd_mtx); + + return NULL; +} + +/* Plot a point with rgb color. 2 LSBs of rgb values will be ignored. */ +void +lcd_draw_point (int x, int y, int r, int g, int b) +{ + uint8_t args[4]; + + /* Set RA and CA. */ + /* RASET, 0, y, 0, y */ + args[0] = 0x00; args[1] = y; args[2] = 0x00; args[3] = y; + lcd_command_writen (RASET, args, 4); + /* CASET, 0, x, 0, x */ + args[0] = 0x00; args[1] = x; args[2] = 0x00; args[3] = x; + lcd_command_writen (CASET, args, 4); + + args[0] = r; args[1] = g; args[2] = b; + lcd_command_writen (RAMWR, args, 3); +} + +static uint8_t hexfont5x8[16*5] = { + 0x7e, 0x89, 0x91, 0xa1, 0x7e, /* 0 */ + 0x00, 0x41, 0xff, 0x01, 0x00, /* 1 */ + 0x43, 0x85, 0x89, 0x91, 0x61, /* 2 */ + 0x42, 0x81, 0x91, 0x91, 0x6e, /* 3 */ + 0x18, 0x28, 0x48, 0xff, 0x08, /* 4 */ + 0xf2, 0x91, 0x91, 0x91, 0x8e, /* 5 */ + 0x1e, 0x29, 0x49, 0x89, 0x86, /* 6 */ + 0x80, 0x8f, 0x90, 0xa0, 0xc0, /* 7 */ + 0x6e, 0x91, 0x91, 0x91, 0x6e, /* 8 */ + 0x70, 0x89, 0x89, 0x8a, 0x7c, /* 9 */ + 0x7f, 0x88, 0x88, 0x88, 0x7f, /* A */ + 0xff, 0x91, 0x91, 0x91, 0x6e, /* B */ + 0x7e, 0x81, 0x81, 0x81, 0x42, /* C */ + 0xff, 0x81, 0x81, 0x42, 0x3c, /* D */ + 0xff, 0x91, 0x91, 0x91, 0x81, /* E */ + 0xff, 0x90, 0x90, 0x90, 0x80, /* F */ +}; + +/* Draw hex number with rgb color. */ +void +lcd_draw_hexfont5x8 (uint32_t hex, int x, int y, int r, int g, int b, int bg) +{ + int i, j; + uint8_t *p; + uint8_t args[5*8*3]; + + p = &hexfont5x8[(hex & 0xf)*5]; + + /* Set RA and CA. */ + /* RASET, 0, y, 0, y+8-1 */ + args[0] = 0x00; args[1] = y; args[2] = 0x00; args[3] = y+7; + lcd_command_writen (RASET, args, 4); + /* CASET, 0, x, 0, x+5-1 */ + args[0] = 0x00; args[1] = x; args[2] = 0x00; args[3] = x+4; + lcd_command_writen (CASET, args, 4); + + for (i = 0; i < 5; i++) + { + uint8_t rb = *p++; + for (j = 0; j < 8; j++) + { + int k = (5*j+i)*3; + if (rb & (0x80 >> j)) + { + args[k] = r; args[k+1] = g; args[k+2] = b; + } + else + { + args[k] = bg; args[k+1] = bg; args[k+2] = bg; + } + } + } + + lcd_command_writen (RAMWR, args, 5*8*3); +} + +void +lcd_printhex (uint32_t hex, int x, int y, int r, int g, int b, int bg) +{ + int i; + + if (y < 0 || y >= LCD_ROW - 8) + return; + for (i = 7; i >= 0; i--) + { + lcd_draw_hexfont5x8 ((hex >> 4*i)&0xf, x, y, r, g, b, bg); + x += 5; + if (x >= LCD_COLUMN - 5) + break; + } +} + +#define PRIO_LCD 3 + +extern uint8_t __process1_stack_base__, __process1_stack_size__; +const uint32_t __stackaddr_lcd = (uint32_t)&__process1_stack_base__; +const size_t __stacksize_lcd = (size_t)&__process1_stack_size__; + +/* Initialize LCD. */ +void +lcd_init (void) +{ + chopstx_mutex_init (&lcd_mtx); + chopstx_cond_init (&lcd_cnd0); + chopstx_cond_init (&lcd_cnd1); + + chopstx_create (PRIO_LCD, __stackaddr_lcd, __stacksize_lcd, + lcd_initializer, NULL); + + chopstx_usec_wait (200*1000); + + chopstx_mutex_lock (&lcd_mtx); + chopstx_cond_signal (&lcd_cnd0); + chopstx_cond_wait (&lcd_cnd1, &lcd_mtx); + chopstx_mutex_unlock (&lcd_mtx); +} diff --git a/gnuk/chopstx/example-primer2/lcd.ld b/gnuk/chopstx/example-primer2/lcd.ld new file mode 100644 index 0000000000..6ad00a3f33 --- /dev/null +++ b/gnuk/chopstx/example-primer2/lcd.ld @@ -0,0 +1,148 @@ +/* + * ST32F103 memory setup. + */ +__main_stack_size__ = 0x0100; /* Idle+Exception handlers */ +__process0_stack_size__ = 0x0200; /* main */ +__process1_stack_size__ = 0x0100; /* lcd init */ +__process2_stack_size__ = 0x0180; /* rng */ +__process3_stack_size__ = 0x0100; /* None yet */ + +MEMORY +{ + flash0 : org = 0x08000000, len = 4k + flash : org = 0x08000000+0x1000, len = 512k - 4k + ram : org = 0x20000000, len = 64k +} + +/* __flash_start__: flash ROM start address regardless of DFU_SUPPORT */ +__flash_start__ = 0x08001000; +__flash_end__ = ORIGIN(flash) + LENGTH(flash); + +__ram_start__ = ORIGIN(ram); +__ram_size__ = LENGTH(ram); +__ram_end__ = __ram_start__ + __ram_size__; + +SECTIONS +{ + . = 0; + + .sys : ALIGN(4) SUBALIGN(4) + { + _sys = .; + KEEP(*(.vectors)) + . = ALIGN(16); + *(.sys.version) + build/sys.o(.text) + build/sys.o(.text.*) + build/sys.o(.rodata) + build/sys.o(.rodata.*) + . = ALIGN(1024); + *(.sys.0) + *(.sys.1) + *(.sys.2) + } > flash0 + + _text = .; + + .startup : ALIGN(128) SUBALIGN(128) + { + KEEP(*(.startup.vectors)) + . = ALIGN (16); + } > flash =0xffffffff + + .text : ALIGN(16) SUBALIGN(16) + { + *(.text.startup.*) + *(.text) + *(.text.*) + *(.rodata) + *(.rodata.*) + *(.glue_7t) + *(.glue_7) + *(.gcc*) + . = ALIGN(8); + } > flash + + .ARM.extab : {*(.ARM.extab* .gnu.linkonce.armextab.*)} > flash + + .ARM.exidx : { + PROVIDE(__exidx_start = .); + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + PROVIDE(__exidx_end = .); + } > flash + + .eh_frame_hdr : {*(.eh_frame_hdr)} > flash + + .eh_frame : ONLY_IF_RO {*(.eh_frame)} > flash + + .textalign : ONLY_IF_RO { . = ALIGN(8); } > flash + + _etext = .; + _textdata = _etext; + + .stacks : + { + . = ALIGN(8); + __main_stack_base__ = .; + . += __main_stack_size__; + . = ALIGN(8); + __main_stack_end__ = .; + __process0_stack_base__ = .; + . += __process0_stack_size__; + . = ALIGN(8); + __process0_stack_end__ = .; + __process1_stack_base__ = .; + . += __process1_stack_size__; + . = ALIGN(8); + __process1_stack_end__ = .; + __process2_stack_base__ = .; + . += __process2_stack_size__; + . = ALIGN(8); + __process2_stack_end__ = .; + __process3_stack_base__ = .; + . += __process3_stack_size__; + . = ALIGN(8); + __process3_stack_end__ = .; + } > ram + + .data : + { + . = ALIGN(4); + PROVIDE(_data = .); + *(.data) + . = ALIGN(4); + *(.data.*) + . = ALIGN(4); + *(.ramtext) + . = ALIGN(4); + PROVIDE(_edata = .); + } > ram AT > flash + + .bss : + { + . = ALIGN(4); + PROVIDE(_bss_start = .); + *(.bss) + . = ALIGN(4); + *(.bss.*) + . = ALIGN(4); + *(COMMON) + . = ALIGN(4); + PROVIDE(_bss_end = .); + } > ram + + PROVIDE(end = .); + _end = .; + . = ALIGN(512); + /* reGNUal is now relocatable, it's OK not using fixed address. */ + _regnual_start = .; + + .fill_ffff : + { + . = ALIGN (2048); + *(.passwd) + } > flash =0xffffffff +} + +__heap_base__ = _end; +__heap_end__ = __ram_end__; diff --git a/gnuk/chopstx/example-primer2/main.c b/gnuk/chopstx/example-primer2/main.c new file mode 100644 index 0000000000..c83a810451 --- /dev/null +++ b/gnuk/chopstx/example-primer2/main.c @@ -0,0 +1,266 @@ +#include +#include +#include +#include "neug.h" +#include "sys.h" /* for set_led */ +#include "stm32f103.h" +#include "adc.h" +#include "st7732.h" +#include "primer2-switches.h" +#include "primer2-ts.h" +#include "board.h" + +#ifdef TEST_DISPLAY_LOGO +static uint8_t buf[LCD_COLUMN*LCD_ROW*BYTES_PER_PIXEL] = { +#include "chopstx-logo.data" +}; + +void +lcd_logo (void) +{ + uint8_t args[4]; + + /* Set RA and CA. */ + /* RASET, 0, 0, 0, 159 */ + args[0] = 0x00; args[1] = 0; args[2] = 0x00; args[3] = LCD_ROW-1; + lcd_command_writen (RASET, args, 4); + /* CASET, 0, 0, 0, 127 */ + args[0] = 0x00; args[1] = 0; args[2] = 0x00; args[3] = LCD_COLUMN-1; + lcd_command_writen (CASET, args, 4); + + /* Write logo. */ + lcd_command_writen (RAMWR, buf, LCD_COLUMN*LCD_ROW*BYTES_PER_PIXEL); +} +#endif + +/* Table of 32*(cos, sin) for range 0 to pi/2 with step pi/256. */ +static uint8_t ctable[128*2] = { +32, 0, 31, 0, 31, 0, 31, 1, 31, 1, 31, 1, 31, 2, 31, 2, +31, 3, 31, 3, 31, 3, 31, 4, 31, 4, 31, 5, 31, 5, 31, 5, +31, 6, 31, 6, 31, 7, 31, 7, 31, 7, 30, 8, 30, 8, 30, 8, +30, 9, 30, 9, 30, 10, 30, 10, 30, 10, 29, 11, 29, 11, 29, 11, +29, 12, 29, 12, 29, 12, 29, 13, 28, 13, 28, 14, 28, 14, 28, 14, +28, 15, 28, 15, 27, 15, 27, 16, 27, 16, 27, 16, 27, 17, 26, 17, +26, 17, 26, 18, 26, 18, 25, 18, 25, 19, 25, 19, 25, 19, 24, 19, +24, 20, 24, 20, 24, 20, 23, 21, 23, 21, 23, 21, 23, 22, 22, 22, +22, 22, 22, 22, 22, 23, 21, 23, 21, 23, 21, 23, 20, 24, 20, 24, +20, 24, 19, 24, 19, 25, 19, 25, 19, 25, 18, 25, 18, 26, 18, 26, +17, 26, 17, 26, 17, 27, 16, 27, 16, 27, 16, 27, 15, 27, 15, 28, +15, 28, 14, 28, 14, 28, 14, 28, 13, 28, 13, 29, 12, 29, 12, 29, +12, 29, 11, 29, 11, 29, 11, 29, 10, 30, 10, 30, 10, 30, 9, 30, +9, 30, 8, 30, 8, 30, 8, 30, 7, 31, 7, 31, 7, 31, 6, 31, +6, 31, 5, 31, 5, 31, 5, 31, 4, 31, 4, 31, 3, 31, 3, 31, +3, 31, 2, 31, 2, 31, 1, 31, 1, 31, 1, 31, 0, 31, 0, 31, +}; + +#ifdef TEST_LCD_CIRCLE +void +lcd_circle (void) +{ + int i, j; + uint8_t *p; + int x, y; + + /* Clear display. */ + /* Set RA and CA. */ + /* RASET, 0, 0, 0, 159 */ + args[0] = 0x00; args[1] = 0; args[2] = 0x00; args[3] = LCD_ROW-1; + lcd_command_writen (RASET, args, 4); + /* CASET, 0, 0, 0, 127 */ + args[0] = 0x00; args[1] = 0; args[2] = 0x00; args[3] = LCD_COLUMN-1; + lcd_command_writen (CASET, args, 4); + + lcd_command_writen (RAMWR, 0, LCD_COLUMN*LCD_ROW*BYTES_PER_PIXEL); + + /* Draw a circle. */ + for (i = 0; i < 128; i++) + { + x = 64 + ctable[2*i]; + y = 80 + ctable[2*i+1]; + lcd_draw_point (x, y, 0xfc, 0xfc, 0xfc); + } + for (i = 0; i < 128; i++) + { + x = 64 - ctable[2*i+1]; + y = 80 + ctable[2*i]; + lcd_draw_point (x, y, 0xfc, 0, 0xfc); + } + for (i = 0; i < 128; i++) + { + x = 64 - ctable[2*i]; + y = 80 - ctable[2*i+1]; + lcd_draw_point (x, y, 0, 0xfc, 0xfc); + } + for (i = 0; i < 128; i++) + { + x = 64 + ctable[2*i+1]; + y = 80 - ctable[2*i]; + lcd_draw_point (x, y, 0xfc, 0xfc, 0); + } +} +#endif + +#define RANDOM_BYTES_LENGTH 64 +static uint32_t random_word[RANDOM_BYTES_LENGTH/sizeof (uint32_t)]; + +int +main (int argc, const char *argv[]) +{ + int count; + int vx, vy; + int r, g, b; + uint8_t args[4]; + + (void)argc; + (void)argv; + + set_led (1); + set_backlight (1); + + adc_init (); + neug_init (random_word, RANDOM_BYTES_LENGTH/sizeof (uint32_t)); + + lcd_init (); + +#ifdef TEST_LCD_LOGO + lcd_logo (); + + while (! joystick ()) + chopstx_usec_wait (500*1000); +#endif + + /* Set RA and CA. */ + /* RASET, 0, 0, 0, 159 */ + args[0] = 0x00; args[1] = 0; args[2] = 0x00; args[3] = LCD_ROW-1; + lcd_command_writen (RASET, args, 4); + /* CASET, 0, 0, 0, 127 */ + args[0] = 0x00; args[1] = 0; args[2] = 0x00; args[3] = LCD_COLUMN-1; + lcd_command_writen (CASET, args, 4); + + /* Fill display. */ + lcd_command_filln (RAMWR, 0xfc, LCD_COLUMN*LCD_ROW*BYTES_PER_PIXEL); + + vx = (LCD_COLUMN/2) << 5; + vy = (LCD_ROW/2) << 5; + r = g = b = 0; +#if 1 + adc3_init (); + adc3_start (); + + count = 0; + while (1) + { + uint32_t resv[4]; + + adc3_conversion (resv); + if (ts_pushed (resv[2])) + { + int reg[3], point[2]; + + ts_conversion (resv, reg); +#if 0 + lcd_printhex (reg[0], 5, 8, 0x00, 0x00, 0xfc, 0xfc); + lcd_printhex (reg[1], 5, 18, 0x00, 0x00, 0xfc, 0xfc); + lcd_printhex (reg[2], 5, 28, 0x00, 0x00, 0xfc, 0xfc); +#endif + if (!ts_adjust (reg, point)) + { + chopstx_usec_wait (50*1000); + continue; + } + + lcd_draw_point (point[0], point[1], r, g, b); + } + else + ts_adjust (NULL, NULL); + + chopstx_usec_wait (50*1000); + count++; + if ((count/10) & 1) + set_led (0); + else + set_led (1); + if (pbutton()) + break; + } + + adc3_stop (); +#endif + + count = 0; + while (1) + { + int jo; + uint32_t th = neug_get (NEUG_KICK_FILLING) & 0x1ff; + + /* Get random point on a circle with the radius of 32 and walk + towards it. */ + if (th < 128) + { + vx += ctable[2*th]; + vy += ctable[2*th+1]; + } + else if (th < 256) + { + vx -= ctable[2*(th & 0x7f)+1]; + vy += ctable[2*(th & 0x7f)]; + } + else if (th < 384) + { + vx -= ctable[2*(th & 0x7f)]; + vy -= ctable[2*(th & 0x7f)+1]; + } + else + { + vx += ctable[2*(th & 0x7f)+1]; + vy -= ctable[2*(th & 0x7f)]; + } + + if (vx < 0) + vx += (LCD_COLUMN << 5); + if (vy < 0) + vy += (LCD_ROW << 5); + + /* Change draw color with joystick. */ + jo = joystick (); + if (JOYSTICK_L (jo)) + r = 0xfc; + if (JOYSTICK_R (jo)) + g = 0xfc; + if (JOYSTICK_U (jo)) + b = 0xfc; + if (JOYSTICK_D (jo)) + r = g = b = 0; + + /* 2-dim random walk on torus. */ + lcd_draw_point ((vx>>5)%LCD_COLUMN, (vy>>5)%LCD_ROW, r, g, b); + chopstx_usec_wait (10*1000); + + if (pbutton ()) + count++; + /* Shutdown when p-button is held down for 5 sec. */ + if (count > 500) + { + set_led (0); + shutdown (); + } + else + { + /* Disable backlight when p-button is held down for 3 sec. */ + if (count > 300) + set_backlight (0); + + /* Blink led when p-button is held. */ + if ((count/50) & 1) + set_led (0); + else + set_led (1); + } +#if 1 + lcd_printhex (count, 5, 8, 0x00, 0x00, 0xfc, 0xfc); +#endif + + } + + return 0; +} diff --git a/gnuk/chopstx/example-primer2/primer2-switches.c b/gnuk/chopstx/example-primer2/primer2-switches.c new file mode 100644 index 0000000000..9b4a3cbe36 --- /dev/null +++ b/gnuk/chopstx/example-primer2/primer2-switches.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include "board.h" + +#define PERIPH_BASE 0x40000000 +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) + +struct GPIO { + volatile uint32_t CRL; + volatile uint32_t CRH; + volatile uint32_t IDR; + volatile uint32_t ODR; + volatile uint32_t BSRR; + volatile uint32_t BRR; + volatile uint32_t LCKR; +}; + +#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800) +#define GPIOA ((struct GPIO *) GPIOA_BASE) +#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00) +#define GPIOB ((struct GPIO *) GPIOB_BASE) +#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000) +#define GPIOC ((struct GPIO *) GPIOC_BASE) +#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400) +#define GPIOD ((struct GPIO *) GPIOD_BASE) +#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800) +#define GPIOE ((struct GPIO *) GPIOE_BASE) + +static struct GPIO *const GPIO_STICK = ((struct GPIO *const) GPIO_LED_BASE); +static struct GPIO *const GPIO_OTHER = ((struct GPIO *const) GPIO_OTHER_BASE); +static struct GPIO *const GPIO_OTHER1 = ((struct GPIO *const) GPIOC_BASE); +static struct GPIO *const GPIO_OTHER2 = ((struct GPIO *const) GPIOB_BASE); + +#define GPIO_STICK_L 3 +#define GPIO_STICK_R 4 +#define GPIO_STICK_U 5 +#define GPIO_STICK_D 6 +#define GPIO_SHUTDOWN 13 +#define GPIO_PBUTTON 8 +#define GPIO_BACKLIGHT 8 + +void +shutdown (void) +{ + GPIO_OTHER1->BRR = (1 << GPIO_SHUTDOWN); + GPIO_OTHER1->BSRR = (1 << GPIO_SHUTDOWN); + + while (1) + chopstx_usec_wait (500*1000); +} + +void +set_backlight (int on) +{ + if (on) + GPIO_OTHER2->BSRR = (1 << GPIO_BACKLIGHT); + else + GPIO_OTHER2->BRR = (1 << GPIO_BACKLIGHT); +} + +int +joystick (void) +{ + return (GPIO_STICK->IDR >> GPIO_STICK_L) & 0xf; +} + +int +pbutton (void) +{ + return (GPIO_OTHER->IDR >> GPIO_PBUTTON) & 1; +} diff --git a/gnuk/chopstx/example-primer2/primer2-switches.h b/gnuk/chopstx/example-primer2/primer2-switches.h new file mode 100644 index 0000000000..e1d0c85e6a --- /dev/null +++ b/gnuk/chopstx/example-primer2/primer2-switches.h @@ -0,0 +1,9 @@ +extern void shutdown (void); +extern void set_backlight (int on); +extern int pbutton (void); +extern int joystick (void); + +#define JOYSTICK_L(x) ((x) & 1) +#define JOYSTICK_R(x) ((x) & 2) +#define JOYSTICK_U(x) ((x) & 4) +#define JOYSTICK_D(x) ((x) & 8) diff --git a/gnuk/chopstx/example-primer2/primer2-ts.c b/gnuk/chopstx/example-primer2/primer2-ts.c new file mode 100644 index 0000000000..3872517447 --- /dev/null +++ b/gnuk/chopstx/example-primer2/primer2-ts.c @@ -0,0 +1,223 @@ +#include +#include +#include +#include "stm32f103.h" +#include "st7732.h" +#include "primer2-ts.h" + +/* ADC3 routines. */ + +#define ADC3_BASE (APB2PERIPH_BASE + 0x3c00) +static struct ADC *const ADC3 = (struct ADC *const)ADC3_BASE; + +#define RCC_APB2ENR_ADC3EN 0x8000 +#define RCC_APB2RSTR_ADC3RST 0x8000 + +#define ADC_SR_JEOC 0x0004 + +#define ADC_CR1_JEOCIE (1 << 7) + +#define ADC_CR2_JSWSTART (1 << 21) +#define ADC_CR2_JEXTTRIG (1 << 15) +#define ADC_CR2_JEXTSEL(n) ((n) << 12) + +#define ADC_JSQR_NUM_CH(n) (((n) - 1) << 20) +#define ADC_JSQR_JSQ1_N(n) ((n) << 0) +#define ADC_JSQR_JSQ2_N(n) ((n) << 5) +#define ADC_JSQR_JSQ3_N(n) ((n) << 10) +#define ADC_JSQR_JSQ4_N(n) ((n) << 15) + +#define ADC_CHANNEL_IN10 10 +#define ADC_CHANNEL_IN11 11 +#define ADC_CHANNEL_IN12 12 +#define ADC_CHANNEL_IN13 13 + +#define USE_ADC3_INTR 1 +#define INTR_REQ_ADC3 47 + +/* + * Do calibration for ADC3. + */ +void adc3_init (void) +{ + RCC->APB2ENR |= RCC_APB2ENR_ADC3EN; + RCC->APB2RSTR = RCC_APB2RSTR_ADC3RST; + RCC->APB2RSTR = 0; + + ADC3->CR1 = 0; + ADC3->CR2 = ADC_CR2_ADON; + ADC3->CR2 = ADC_CR2_ADON | ADC_CR2_RSTCAL; + while ((ADC3->CR2 & ADC_CR2_RSTCAL) != 0) + ; + ADC3->CR2 = ADC_CR2_ADON | ADC_CR2_CAL; + while ((ADC3->CR2 & ADC_CR2_CAL) != 0) + ; + ADC3->CR2 = 0; + RCC->APB2ENR &= ~RCC_APB2ENR_ADC3EN; +} + +static chopstx_intr_t adc3_intr; + +void +adc3_start (void) +{ + RCC->APB2ENR |= RCC_APB2ENR_ADC3EN; + +#if USE_ADC3_INTR + ADC3->CR1 = ADC_CR1_SCAN | ADC_CR1_JEOCIE; +#else + ADC3->CR1 = ADC_CR1_SCAN; +#endif + ADC3->CR2 = ADC_CR2_JEXTTRIG | ADC_CR2_JEXTSEL(7) | ADC_CR2_ADON; + ADC3->SMPR1 = 0xfff; + ADC3->SMPR2 = 0; + ADC3->JSQR = (ADC_JSQR_NUM_CH(4) | ADC_JSQR_JSQ4_N(ADC_CHANNEL_IN13) + | ADC_JSQR_JSQ3_N(ADC_CHANNEL_IN12) + | ADC_JSQR_JSQ2_N(ADC_CHANNEL_IN11) + | ADC_JSQR_JSQ1_N(ADC_CHANNEL_IN10)); + +#if USE_ADC3_INTR + chopstx_claim_irq (&adc3_intr, INTR_REQ_ADC3); +#endif +} + +void adc3_conversion (uint32_t *result) +{ + + /* Start conversion. */ + ADC3->CR2 |= ADC_CR2_JSWSTART; + +#if USE_ADC3_INTR + chopstx_intr_wait (&adc3_intr); +#else + while (1) + { + chopstx_usec_wait (1000); + if (ADC3->SR & ADC_SR_JEOC) + break; + } +#endif + ADC3->SR &= ~ADC_SR_JEOC; + + result[0] = ADC3->JDR1; + result[1] = ADC3->JDR2; + result[2] = ADC3->JDR3; + result[3] = ADC3->JDR4; + + /* Stop conversion. */ + ADC3->CR2 &= ~ADC_CR2_JSWSTART; + +#if USE_ADC3_INTR + chopstx_intr_done (&adc3_intr); +#endif + + return; +} + +void adc3_stop (void) +{ + + /* Power off. */ + ADC3->CR1 = 0; + ADC3->CR2 = 0; + + RCC->APB2ENR &= ~RCC_APB2ENR_ADC3EN; +} + +/* Touch screen routines. */ + +int +ts_pushed (uint32_t u) +{ + return (u < 0xc00); +} + +#define FILTER_SIZE 8 + +static void +ts_filter (int buf[FILTER_SIZE][2], int result[2]) +{ + int s0, s1; + int i; + + s0 = 0; + s1 = 0; + for (i = 0; i < FILTER_SIZE; i++) + { + s0 += buf[i][0]; + s1 += buf[i][1]; + } + + result[0] = s0/FILTER_SIZE; + result[1] = s1/FILTER_SIZE; +} + +/* Simple model of primer2 touch screen: + Vdd-[R1]-[Ry]-[Rp]-[Rx]-Vss + U D l R + where R1=1k external register, Rx(resp. Ry)=resisitive component on + X(resp. Y) film and Rp=resisitive component of contact point. + Convert [L, R, U, D] to [Rx, Ry, Rp]. */ +void +ts_conversion (uint32_t a[], int r[]) +{ + int l, u, d, ir1; + int x, y, rp; + + l = a[0] & 0xfff; + u = a[2] & 0xfff; + d = a[3] & 0xfff; + ir1 = 4096 - u; + /* r1 = 1000 */ + x = (1000 * l)/ir1; + y = (1000 * (u - d))/ir1; + rp = (1000 * (d - l))/ir1; + r[0] = x; + r[1] = y; + r[2] = rp; +} + +int +ts_adjust (int *r, int *cord) +{ + int x, y; + static int buf[FILTER_SIZE][2]; + static int i = 0; + static int fill = 0; + + if (!r) + { + i = 0; + fill = 0; + return 0; + } + + /* TODO: We might need calibration. */ + x = (LCD_COLUMN * (r[0] - 0x20))/0x100; + y = (LCD_ROW * (0x1e0 - r[1]))/0x1c0; + + if (x < 0) + x = 0; + if (x >= LCD_COLUMN) + x = LCD_COLUMN - 1; + if (y < 0) + y = 0; + if (y >= LCD_ROW) + y = LCD_ROW - 1; + + buf[i][0] = x; + buf[i][1] = y; + + i++; + if (i >= FILTER_SIZE) + { + i = 0; + fill = 1; + } + + if (!fill) + return 0; + + ts_filter (buf, cord); + return 1; +} diff --git a/gnuk/chopstx/example-primer2/primer2-ts.h b/gnuk/chopstx/example-primer2/primer2-ts.h new file mode 100644 index 0000000000..552d6dbe09 --- /dev/null +++ b/gnuk/chopstx/example-primer2/primer2-ts.h @@ -0,0 +1,8 @@ +extern void adc3_init (void); +extern void adc3_start (void); +extern void adc3_conversion (uint32_t *result); +extern void adc3_stop (void); + +extern int ts_pushed (uint32_t u); +extern void ts_conversion (uint32_t a[], int r[]); +extern int ts_adjust (int r[], int cord[]); diff --git a/gnuk/chopstx/example-primer2/st7732.h b/gnuk/chopstx/example-primer2/st7732.h new file mode 100644 index 0000000000..bc684d7068 --- /dev/null +++ b/gnuk/chopstx/example-primer2/st7732.h @@ -0,0 +1,83 @@ +/* ST7732 LCD driver chip command byte. + command_name = value read_n_bytes:write_n_bytes: simple description */ +enum st7732_cmd { + NOP = 0x00, /* 0:0: No Operatin */ + SWRESET = 0x01, /* 0:0: Software reset */ + RDDID = 0x04, /* 0:3: Read Display ID */ + RDRST = 0x09, /* 0:4: Read Display Status */ + RDDPM = 0x0a, /* 0:1: Read Display Power Mode */ + RDD_MADCTL = 0x0b, /* 0:1: Read Display MADCTL */ + RDD_COLMOD = 0x0c, /* 0:1: Read Display Pixel Format */ + RDDIM = 0x0d, /* 0:1: Read Display Image Mode */ + RDDSM = 0x0e, /* 0:1: Read Display Signal Mode */ + RDDSDR = 0x0f, /* 0:1: Read Display Self-diagnostic result */ + SLPIN = 0x10, /* 0:0: Sleep in & booster off */ + SLPOUT = 0x11, /* 0:0: Sleep out & booster on */ + PTLON = 0x12, /* 0:0: Pertial mode on */ + NORON = 0x13, /* 0:0: Normal mode on (Pertial off) */ + INVOFF = 0x20, /* 0:0: Display inversion off */ + INVON = 0x21, /* 0:0: Display inversion on */ + GAMSET = 0x26, /* 1:0: Gamma curve select */ + DISPOFF = 0x28, /* 0:0: Display off */ + DISPON = 0x29, /* 0:0: Display on */ + CASET = 0x2a, /* 4:0: Column address set */ + RASET = 0x2b, /* 4:0: Raw address set */ + RAMWR = 0x2c, /* 1:0: Memory write */ + RAMRD = 0x2e, /* 0:1: Memory read */ + PTLAR = 0x30, /* 4:0: Partial start/end address set */ + SCRLAR = 0x33, /* 6:0: Scroll area set */ + TEOFF = 0x34, /* 0:0: Tearing effect line off */ + TEON = 0x35, /* 1:0: Tearing effect mode set & on */ + MADCTL = 0x36, /* 1:0: Memory data access control */ + VSCSAD = 0x37, /* 2:0: Scroll start address of RAM */ + IDMOFF = 0x38, /* 0:0: Idle mode off */ + IDMON = 0x39, /* 0:0: Idle mode on */ + COLMOD = 0x3a, /* 1:0: Interface pixel format */ + RDID1 = 0xda, /* 0:1: Read ID1 */ + RDID2 = 0xdb, /* 0:1: Read ID2 */ + RDID3 = 0xdc, /* 0:1: Read ID3 */ + RGBCTR = 0xb0, /* 1:0: Set RGB signal control */ + FRMCTR1 = 0xb1, /* 3:0: In normal mode */ + FRMCTR2 = 0xb2, /* 3:0: In Idel mode (8-colors) */ + FRMCTR3 = 0xb3, /* 6:0: In partial mode + Full colors */ + INVCTR = 0xb4, /* 1:0: Display inversion control */ + RGB_BPCTR = 0xb5, /* 4:0: RGB I/F Blanking porch setting */ + DISSET5 = 0xb6, /* 2:0: Display function setting */ + PWCTR1 = 0xc0, /* 2:0: Power control setting */ + PWCTR2 = 0xc1, /* 1:0: Power control setting */ + PWCTR3 = 0xc2, /* 2:0: Power control setting (Full colors) */ + PWCTR4 = 0xc3, /* 2:0: Power control setting (8-colors) */ + PWCTR5 = 0xc4, /* 2:0: Power control setting (In partial mode) */ + VMCTR1 = 0xc5, /* 2:0: VCOM control */ + VMOFCTR = 0xc6, /* 1:0: VCOM offset control */ + WRID2 = 0xd1, /* 1:0: Write ID2 value to NV */ + WRID3 = 0xd2, /* 1:0: Write ID3 value to NV */ + RDID4 = 0xd3, /* 0:4: IC Vender code */ + NVCTR1 = 0xd9, /* 0:1:no-fummy NVM control status */ + NVCTR2 = 0xde, /* 3:0: NVM read command (aa, 0f, a5) */ + NVCTR3 = 0xdf, /* 3:0: NVM write command (55, f0, 5a) */ + GAMCTRP1 = 0xe0, /* 13:0: Set Gamma correction + */ + GAMCTRN1 = 0xe1, /* 13:0: Set Gamma correction - */ + AUTO_CTRL = 0xf1, /* 1:0: NVM write function ON/OFF */ + OSCADJ = 0xf2, /* 1:0: Osillator frequency setting */ + DISPCTRL = 0xf5, /* 1:0: Display function control */ + DEFADJ = 0xf6, /* 1:0: Default mode setting */ +}; + +typedef enum st7732_cmd st7732_cmd_t; + +extern void lcd_command_no (st7732_cmd_t cmd); +extern void lcd_command_readn (st7732_cmd_t cmd, uint8_t *p, size_t n); +extern void lcd_command_writen (st7732_cmd_t cmd, uint8_t *p, size_t n); +extern void lcd_command_filln (st7732_cmd_t cmd, uint8_t b, size_t n); +extern void lcd_init (void); + +extern void lcd_draw_point (int x, int y, int r, int g, int b); +extern void lcd_draw_hexfont5x8 (uint32_t hex, int x, int y, int r, int g, + int b, int bg); +extern void lcd_printhex (uint32_t hex, int x, int y, int r, int g, int b, + int bg); + +#define LCD_COLUMN 128 +#define LCD_ROW 160 +#define BYTES_PER_PIXEL 3 diff --git a/gnuk/chopstx/example-usb-serial/Makefile b/gnuk/chopstx/example-usb-serial/Makefile new file mode 100644 index 0000000000..168b9134c3 --- /dev/null +++ b/gnuk/chopstx/example-usb-serial/Makefile @@ -0,0 +1,37 @@ +# Makefile for example application of Chopstx + +PROJECT = sample + +CHOPSTX = .. +LDSCRIPT= sample.ld +CSRC = sample.c usb-cdc.c + +CHIP=stm32f103 + +USE_SYS = yes +USE_USB = yes +USE_USART = yes + +################################### +CROSS = arm-none-eabi- +CC = $(CROSS)gcc +LD = $(CROSS)gcc +OBJCOPY = $(CROSS)objcopy + +MCU = cortex-m3 +CWARN = -Wall -Wextra -Wstrict-prototypes +DEFS = -DUSE_SYS3 -DFREE_STANDING -DMHZ=72 +OPT = -O3 -Os -g +LIBS = + +#################### +include ../rules.mk + +board.h: + @echo Please make a symbolic link \'board.h\' to a file in ../board; + @exit 1 + +sys.c: board.h + +distclean: clean + rm -f board.h diff --git a/gnuk/chopstx/example-usb-serial/README b/gnuk/chopstx/example-usb-serial/README new file mode 100644 index 0000000000..f613dc6839 --- /dev/null +++ b/gnuk/chopstx/example-usb-serial/README @@ -0,0 +1,29 @@ +This is an application example using ST Nucleo F103 board. + +SB62 and SB63 should be soldered to connect PA2 and PA3 to CN10 +connector. + +ST-Link/V2's TX and RX are disconnected (by removing SB13 and SB14). + +Smartcard can be connected, by using USART3_TX for I/O and USART3_CK +for CLK, PB6 for Vcc, PB8 for Vpp, and PB9 for RST. + + +NOTE: + +Using the USB CDC-ACM for serial communication is a kind of wrong, +because it's designed for modem; In the protocol, we have no way to +control the CTSRTS signal. + +TIOCGICOUNT + + +TODO: +* Use of DMA for serial communication +* RS-232 support: GPIO with DTR (out), DCD (in), DSR (in), RI (in) +* serial config setting of CTSRTS? + By vendor specific control? +* stats report control + By vendor specific control? +* Half-duplex support +* Support of other communication mode: IrDA, etc. diff --git a/gnuk/chopstx/example-usb-serial/board.h b/gnuk/chopstx/example-usb-serial/board.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gnuk/chopstx/example-usb-serial/cdc.h b/gnuk/chopstx/example-usb-serial/cdc.h new file mode 100644 index 0000000000..da7d216f1f --- /dev/null +++ b/gnuk/chopstx/example-usb-serial/cdc.h @@ -0,0 +1,17 @@ +#define BUFSIZE 64 + +struct cdc; + +void +cdc_init (uint16_t prio, uintptr_t stack_addr, size_t stack_size, + void (*sendbrk_callback) (uint8_t dev_no, uint16_t duration), + void (*config_callback) (uint8_t dev_no, + uint32_t bitrate, uint8_t format, + uint8_t paritytype, uint8_t databits)); +void cdc_wait_configured (void); + +struct cdc *cdc_open (uint8_t num); +void cdc_wait_connection (struct cdc *); +int cdc_send (struct cdc *s, const char *buf, int count); +int cdc_recv (struct cdc *s, char *buf, uint32_t *timeout); +int cdc_ss_notify (struct cdc *s, uint16_t state_bits); diff --git a/gnuk/chopstx/example-usb-serial/sample.c b/gnuk/chopstx/example-usb-serial/sample.c new file mode 100644 index 0000000000..07f6d31f0c --- /dev/null +++ b/gnuk/chopstx/example-usb-serial/sample.c @@ -0,0 +1,260 @@ +#include +#include +#include +#include +#include + +#include +#include "cdc.h" + +/* For set_led */ +#include "board.h" +#include "sys.h" + +static void * +blk (void *arg) +{ + (void)arg; + + while (1) + { + set_led (0); + chopstx_usec_wait (200*1000); + set_led (1); + chopstx_usec_wait (200*1000); + } + + return NULL; +} + +#define PRIO_USART 4 +#define PRIO_CDC2USART 3 +#define PRIO_USART2CDC 3 +#define PRIO_CDC 2 + +#define STACK_MAIN +#define STACK_PROCESS_1 +#define STACK_PROCESS_2 +#define STACK_PROCESS_3 +#define STACK_PROCESS_4 +#define STACK_PROCESS_5 +#define STACK_PROCESS_6 +#include "stack-def.h" +#define STACK_ADDR_CDC ((uintptr_t)process1_base) +#define STACK_SIZE_CDC (sizeof process1_base) + +#define STACK_ADDR_USART ((uint32_t)process2_base) +#define STACK_SIZE_USART (sizeof process2_base) + +#define STACK_ADDR_CDC2USART0 ((uint32_t)process3_base) +#define STACK_SIZE_CDC2USART0 (sizeof process3_base) + +#define STACK_ADDR_USART2CDC0 ((uint32_t)process4_base) +#define STACK_SIZE_USART2CDC0 (sizeof process4_base) + +#define STACK_ADDR_CDC2USART1 ((uint32_t)process5_base) +#define STACK_SIZE_CDC2USART1 (sizeof process5_base) + +#define STACK_ADDR_USART2CDC1 ((uint32_t)process6_base) +#define STACK_SIZE_USART2CDC1 (sizeof process6_base) + + +struct cdc_usart { + uint8_t dev_no; + struct cdc *cdc; +}; + +static void * +usart_to_cdc_loop (void *arg) +{ + struct cdc_usart *cdc_usart = arg; + + while (1) + { + char s[BUFSIZE]; + + cdc_wait_connection (cdc_usart->cdc); + + /* Flush USART buffers */ + usart_read (cdc_usart->dev_no, NULL, 0); + usart_write (cdc_usart->dev_no, NULL, 0); + + chopstx_usec_wait (100*1000); + + while (1) + { + int size = usart_read (cdc_usart->dev_no, s, BUFSIZE); + + if (size > 0) + { + if (cdc_send (cdc_usart->cdc, s, size) < 0) + break; + } + } + } + + return NULL; +} + +static void * +cdc_to_usart_loop (void *arg) +{ + struct cdc_usart *cdc_usart = arg; + + while (1) + { + char s[BUFSIZE]; + + cdc_wait_connection (cdc_usart->cdc); + + /* Flush USART buffers */ + usart_read (cdc_usart->dev_no, NULL, 0); + usart_write (cdc_usart->dev_no, NULL, 0); + + chopstx_usec_wait (50*1000); + + /* Send ZLP at the beginning. */ + cdc_send (cdc_usart->cdc, s, 0); + + while (1) + { + int size; + uint32_t usec = 3000000; /* 3.0 seconds */ + + size = cdc_recv (cdc_usart->cdc, s, &usec); + if (size < 0) + break; + + if (size) + usart_write (cdc_usart->dev_no, s, size); + } + } + + return NULL; +} + +static struct cdc_usart cdc_usart0; +static struct cdc_usart cdc_usart1; + +static int +ss_notify (uint8_t dev_no, uint16_t state_bits) +{ + struct cdc *s; + + if (dev_no == cdc_usart0.dev_no) + s = cdc_usart0.cdc; + else if (dev_no == cdc_usart1.dev_no) + s = cdc_usart1.cdc; + else + return -1; + + return cdc_ss_notify (s, state_bits); +} + +static void +send_break (uint8_t dev_no, uint16_t duration) +{ + (void)duration; /* Not supported by USART. */ + usart_send_break (dev_no); +} + +static void +setup_usart_config (uint8_t dev_no, uint32_t bitrate, uint8_t format, + uint8_t paritytype, uint8_t databits) +{ + /* Check supported config(s) */ + uint32_t config_bits; + + if (bitrate == 9600) + config_bits = B9600; + else if (bitrate == 19200) + config_bits = B19200; + else if (bitrate == 57600) + config_bits = B57600; + else if (bitrate == 115200) + config_bits = B115200; + else + { + bitrate = 115200; + config_bits = B115200; + } + + if (format == 0) + config_bits |= STOP1B; + else if (format == 1) + config_bits |= STOP1B5; + else if (format == 2) + config_bits |= STOP2B; + else + { + format = 0; + config_bits |= STOP1B; + } + + if (paritytype == 0) + config_bits |= 0; + else if (paritytype == 1) + config_bits |= (PARENB | PARODD); + else if (paritytype == 2) + config_bits |= PARENB; + else + { + paritytype = 0; + config_bits |= 0; + } + + if (databits == 7) + config_bits |= CS7; + else if (databits == 7) + config_bits |= CS8; + else + { + databits = 8; + config_bits |= CS8; + } + + if (databits == 7 && paritytype == 0) + { + databits = 8; + config_bits &= ~MASK_CS; + config_bits |= CS8; + } + + usart_config (dev_no, config_bits); +} + + +int +main (int argc, const char *argv[]) +{ + (void)argc; + (void)argv; + + chopstx_usec_wait (200*1000); + + cdc_init (PRIO_CDC, STACK_ADDR_CDC, STACK_SIZE_CDC, + send_break, setup_usart_config); + cdc_wait_configured (); + + usart_init (PRIO_USART, STACK_ADDR_USART, STACK_SIZE_USART, ss_notify); + + usart_config (2, B115200 | CS8 | STOP1B); + usart_config (3, B115200 | CS8 | STOP1B); + + cdc_usart0.cdc = cdc_open (0); + cdc_usart0.dev_no = 2; + cdc_usart1.cdc = cdc_open (1); + cdc_usart1.dev_no = 3; + + chopstx_create (PRIO_USART2CDC, STACK_ADDR_USART2CDC0, + STACK_SIZE_USART2CDC0, usart_to_cdc_loop, &cdc_usart0); + chopstx_create (PRIO_USART2CDC, STACK_ADDR_USART2CDC1, + STACK_SIZE_USART2CDC1, usart_to_cdc_loop, &cdc_usart1); + chopstx_create (PRIO_CDC2USART, STACK_ADDR_CDC2USART0, + STACK_SIZE_CDC2USART0, cdc_to_usart_loop, &cdc_usart0); + chopstx_create (PRIO_CDC2USART, STACK_ADDR_CDC2USART1, + STACK_SIZE_CDC2USART1, cdc_to_usart_loop, &cdc_usart1); + + blk (NULL); + return 0; +} diff --git a/gnuk/chopstx/example-usb-serial/sample.ld b/gnuk/chopstx/example-usb-serial/sample.ld new file mode 100644 index 0000000000..0aaabc91ac --- /dev/null +++ b/gnuk/chopstx/example-usb-serial/sample.ld @@ -0,0 +1,127 @@ +/* + * ST32F103 memory setup. + */ + +MEMORY +{ + flash0 : org = 0x08000000, len = 4k + flash : org = 0x08000000+0x1000, len = 60k + ram : org = 0x20000000, len = 20k +} + +__ram_start__ = ORIGIN(ram); +__ram_size__ = 20k; +__ram_end__ = __ram_start__ + __ram_size__; + +SECTIONS +{ + . = 0; + + .sys : ALIGN(4) SUBALIGN(4) + { + _sys = .; + KEEP(*(.vectors)) + . = ALIGN(16); + KEEP(*(.sys.version)) + KEEP(*(.sys.board_id)) + KEEP(*(.sys.board_name)) + build/sys-*.o(.text) + build/sys-*.o(.text.*) + build/sys-*.o(.rodata) + build/sys-*.o(.rodata.*) + . = ALIGN(1024); + *(.sys.0) + *(.sys.1) + *(.sys.2) + } > flash0 + + _text = .; + + .startup : ALIGN(128) SUBALIGN(128) + { + KEEP(*(.startup.vectors)) + . = ALIGN (16); + } > flash =0xffffffff + + .text : ALIGN(16) SUBALIGN(16) + { + *(.text.startup.*) + *(.text) + *(.text.*) + *(.rodata) + *(.rodata.*) + *(.glue_7t) + *(.glue_7) + *(.gcc*) + . = ALIGN(8); + } > flash + + .ARM.extab : {*(.ARM.extab* .gnu.linkonce.armextab.*)} > flash + + .ARM.exidx : { + PROVIDE(__exidx_start = .); + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + PROVIDE(__exidx_end = .); + } > flash + + .eh_frame_hdr : {*(.eh_frame_hdr)} > flash + + .eh_frame : ONLY_IF_RO {*(.eh_frame)} > flash + + .textalign : ONLY_IF_RO { . = ALIGN(8); } > flash + + _etext = .; + _textdata = _etext; + + .process_stack (NOLOAD) : + { + . = ALIGN(8); + *(.process_stack.6) + *(.process_stack.5) + *(.process_stack.4) + *(.process_stack.3) + *(.process_stack.2) + *(.process_stack.1) + *(.process_stack.0) + . = ALIGN(8); + } > ram + + .main_stack (NOLOAD) : + { + . = ALIGN(8); + *(.main_stack) + . = ALIGN(8); + } > ram + + .data : + { + . = ALIGN(4); + PROVIDE(_data = .); + *(.data) + . = ALIGN(4); + *(.data.*) + . = ALIGN(4); + *(.ramtext) + . = ALIGN(4); + PROVIDE(_edata = .); + } > ram AT > flash + + .bss : + { + . = ALIGN(4); + PROVIDE(_bss_start = .); + *(.bss) + . = ALIGN(4); + *(.bss.*) + . = ALIGN(4); + *(COMMON) + . = ALIGN(4); + PROVIDE(_bss_end = .); + } > ram + + PROVIDE(end = .); + _end = .; +} + +__heap_base__ = _end; +__heap_end__ = __ram_end__; diff --git a/gnuk/chopstx/example-usb-serial/stack-def.h b/gnuk/chopstx/example-usb-serial/stack-def.h new file mode 100644 index 0000000000..385cc0608d --- /dev/null +++ b/gnuk/chopstx/example-usb-serial/stack-def.h @@ -0,0 +1,39 @@ +#if defined(STACK_MAIN) +/* Idle+Exception handlers */ +char __main_stack_end__[0] __attribute__ ((section(".main_stack"))); +char main_base[0x0080] __attribute__ ((section(".main_stack"))); + +/* Main program */ +char __process0_stack_end__[0] __attribute__ ((section(".process_stack.0"))); +char process0_base[0x0400] __attribute__ ((section(".process_stack.0"))); +#endif + +/* First thread program */ +#if defined(STACK_PROCESS_1) +char process1_base[0x0200] __attribute__ ((section(".process_stack.1"))); +#endif + +/* Second thread program */ +#if defined(STACK_PROCESS_2) +char process2_base[0x0200] __attribute__ ((section(".process_stack.2"))); +#endif + +/* Third thread program */ +#if defined(STACK_PROCESS_3) +char process3_base[0x0200] __attribute__ ((section(".process_stack.3"))); +#endif + +/* Fourth thread program */ +#if defined(STACK_PROCESS_4) +char process4_base[0x0200] __attribute__ ((section(".process_stack.4"))); +#endif + +/* Fifth thread program */ +#if defined(STACK_PROCESS_5) +char process5_base[0x0200] __attribute__ ((section(".process_stack.5"))); +#endif + +/* Sixth thread program */ +#if defined(STACK_PROCESS_6) +char process6_base[0x0200] __attribute__ ((section(".process_stack.6"))); +#endif diff --git a/gnuk/chopstx/example-usb-serial/usb-cdc.c b/gnuk/chopstx/example-usb-serial/usb-cdc.c new file mode 100644 index 0000000000..898217b1f5 --- /dev/null +++ b/gnuk/chopstx/example-usb-serial/usb-cdc.c @@ -0,0 +1,1034 @@ +#include +#include +#include +#include +#include +#include "cdc.h" + +static chopstx_intr_t usb_intr; + +struct line_coding +{ + uint32_t bitrate; + uint8_t format; + uint8_t paritytype; + uint8_t databits; +} __attribute__((packed)); + +static const struct line_coding lc_default = { + 115200, /* baud rate: 115200 */ + 0x00, /* stop bits: 1 */ + 0x00, /* parity: none */ + 0x08 /* bits: 8 */ +}; + +static uint8_t device_state; /* USB device status */ + +struct cdc { + uint8_t dev_no; + + uint8_t endp1; + uint8_t endp2; + uint8_t endp3; + + chopstx_mutex_t mtx; + chopstx_cond_t cnd_rx; + chopstx_cond_t cnd_tx; + uint8_t input[BUFSIZE]; + uint32_t input_len : 7; + uint32_t flag_connected : 1; + uint32_t flag_output_ready: 1; + uint32_t flag_input_avail : 1; + uint32_t flag_notify_busy : 1; + uint32_t :21; + struct line_coding line_coding; +}; + +#define MAX_CDC 2 +static struct cdc cdc_table[MAX_CDC]; + + +/* + * Locate CDC structure from interface number or endpoint number. + */ +static struct cdc * +cdc_get (int interface, uint8_t ep_num) +{ + struct cdc *s; + + if (interface >= 0) + { + if (interface == 0 || interface == 1) + s = &cdc_table[0]; + else + s = &cdc_table[1]; + } + else + { + if (ep_num == ENDP1 || ep_num == ENDP2 || ep_num == ENDP3) + s = &cdc_table[0]; + else + s = &cdc_table[1]; + } + + return s; +} + + +#define ENDP0_RXADDR (0x40) +#define ENDP0_TXADDR (0x80) +#define ENDP1_TXADDR (0xc0) +#define ENDP2_TXADDR (0x100) +#define ENDP3_RXADDR (0x10A) +#define ENDP4_TXADDR (0x14A) +#define ENDP5_TXADDR (0x18A) +#define ENDP6_RXADDR (0x194) +/* 0x1d4 = 468, 44-byte available */ + +#define USB_CDC_REQ_SET_LINE_CODING 0x20 +#define USB_CDC_REQ_GET_LINE_CODING 0x21 +#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22 +#define USB_CDC_REQ_SEND_BREAK 0x23 + +#define USB_CDC_NOTIFY_SERIAL_STATE 0x20 + +/* USB Device Descriptor */ +static const uint8_t vcom_device_desc[18] = { + 18, /* bLength */ + DEVICE_DESCRIPTOR, /* bDescriptorType */ + 0x10, 0x01, /* bcdUSB = 1.1 */ + 0x02, /* bDeviceClass (CDC). */ + 0x00, /* bDeviceSubClass. */ + 0x00, /* bDeviceProtocol. */ + 0x40, /* bMaxPacketSize. */ + 0xFF, 0xFF, /* idVendor */ + 0x01, 0x00, /* idProduct */ + 0x00, 0x01, /* bcdDevice */ + 1, /* iManufacturer. */ + 2, /* iProduct. */ + 3, /* iSerialNumber. */ + 1 /* bNumConfigurations. */ +}; + +#define VCOM_FEATURE_BUS_POWERED 0x80 + +/* Configuration Descriptor tree for a CDC.*/ +static const uint8_t vcom_config_desc[] = { + 9, + CONFIG_DESCRIPTOR, /* bDescriptorType: Configuration */ + /* Configuration Descriptor.*/ + 58*2+9, 0x00, /* wTotalLength. */ + 2*2, /* bNumInterfaces. */ + 1, /* bConfigurationValue. */ + 0, /* iConfiguration. */ + VCOM_FEATURE_BUS_POWERED, /* bmAttributes. */ + 50, /* bMaxPower (100mA). */ + /* Interface Descriptor.*/ + 9, + INTERFACE_DESCRIPTOR, + 0x00, /* bInterfaceNumber. */ + 0x00, /* bAlternateSetting. */ + 0x01, /* bNumEndpoints. */ + 0x02, /* bInterfaceClass (Communications Interface Class, + CDC section 4.2). */ + 0x02, /* bInterfaceSubClass (Abstract Control Model, CDC + section 4.3). */ + 0x01, /* bInterfaceProtocol (AT commands, CDC section + 4.4). */ + 0, /* iInterface. */ + /* Header Functional Descriptor (CDC section 5.2.3).*/ + 5, /* bLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x00, /* bDescriptorSubtype (Header Functional Descriptor). */ + 0x10, 0x01, /* bcdCDC. */ + /* Call Management Functional Descriptor. */ + 5, /* bFunctionLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x01, /* bDescriptorSubtype (Call Management Functional + Descriptor). */ + 0x03, /* bmCapabilities (D0+D1). */ + 0x01, /* bDataInterface. */ + /* ACM Functional Descriptor.*/ + 4, /* bFunctionLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x02, /* bDescriptorSubtype (Abstract Control Management + Descriptor). */ + 0x02, /* bmCapabilities. */ + /* Union Functional Descriptor.*/ + 5, /* bFunctionLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x06, /* bDescriptorSubtype (Union Functional + Descriptor). */ + 0x00, /* bMasterInterface (Communication Class + Interface). */ + 0x01, /* bSlaveInterface0 (Data Class Interface). */ + /* Endpoint 2 Descriptor.*/ + 7, + ENDPOINT_DESCRIPTOR, + ENDP2|0x80, /* bEndpointAddress. */ + 0x03, /* bmAttributes (Interrupt). */ + 0x0A, 0x00, /* wMaxPacketSize. */ + 0xFF, /* bInterval. */ + /* Interface Descriptor.*/ + 9, + INTERFACE_DESCRIPTOR, /* bDescriptorType: */ + 0x01, /* bInterfaceNumber. */ + 0x00, /* bAlternateSetting. */ + 0x02, /* bNumEndpoints. */ + 0x0A, /* bInterfaceClass (Data Class Interface, CDC section 4.5). */ + 0x00, /* bInterfaceSubClass (CDC section 4.6). */ + 0x00, /* bInterfaceProtocol (CDC section 4.7). */ + 0x00, /* iInterface. */ + /* Endpoint 3 Descriptor.*/ + 7, + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + ENDP3, /* bEndpointAddress. */ + 0x02, /* bmAttributes (Bulk). */ + 0x40, 0x00, /* wMaxPacketSize. */ + 0x00, /* bInterval. */ + /* Endpoint 1 Descriptor.*/ + 7, + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + ENDP1|0x80, /* bEndpointAddress. */ + 0x02, /* bmAttributes (Bulk). */ + 0x40, 0x00, /* wMaxPacketSize. */ + 0x00, /* bInterval. */ + + /******************************************************************/ + + /* Interface Descriptor (second). */ + 9, + INTERFACE_DESCRIPTOR, + 0x02, /* bInterfaceNumber. */ + 0x00, /* bAlternateSetting. */ + 0x01, /* bNumEndpoints. */ + 0x02, /* bInterfaceClass (Communications Interface Class, + CDC section 4.2). */ + 0x02, /* bInterfaceSubClass (Abstract Control Model, CDC + section 4.3). */ + 0x01, /* bInterfaceProtocol (AT commands, CDC section + 4.4). */ + 0, /* iInterface. */ + /* Header Functional Descriptor (CDC section 5.2.3).*/ + 5, /* bLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x00, /* bDescriptorSubtype (Header Functional Descriptor). */ + 0x10, 0x01, /* bcdCDC. */ + /* Call Management Functional Descriptor. */ + 5, /* bFunctionLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x01, /* bDescriptorSubtype (Call Management Functional + Descriptor). */ + 0x03, /* bmCapabilities (D0+D1). */ + 0x03, /* bDataInterface. */ + /* ACM Functional Descriptor.*/ + 4, /* bFunctionLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x02, /* bDescriptorSubtype (Abstract Control Management + Descriptor). */ + 0x02, /* bmCapabilities. */ + /* Union Functional Descriptor.*/ + 5, /* bFunctionLength. */ + 0x24, /* bDescriptorType (CS_INTERFACE). */ + 0x06, /* bDescriptorSubtype (Union Functional + Descriptor). */ + 0x02, /* bMasterInterface (Communication Class + Interface). */ + 0x03, /* bSlaveInterface0 (Data Class Interface). */ + /* Endpoint 2 Descriptor.*/ + 7, + ENDPOINT_DESCRIPTOR, + ENDP5|0x80, /* bEndpointAddress. */ + 0x03, /* bmAttributes (Interrupt). */ + 0x0A, 0x00, /* wMaxPacketSize. */ + 0xFF, /* bInterval. */ + /* Interface Descriptor.*/ + 9, + INTERFACE_DESCRIPTOR, /* bDescriptorType: */ + 0x03, /* bInterfaceNumber. */ + 0x00, /* bAlternateSetting. */ + 0x02, /* bNumEndpoints. */ + 0x0A, /* bInterfaceClass (Data Class Interface, CDC section 4.5). */ + 0x00, /* bInterfaceSubClass (CDC section 4.6). */ + 0x00, /* bInterfaceProtocol (CDC section 4.7). */ + 0x00, /* iInterface. */ + /* Endpoint 3 Descriptor.*/ + 7, + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + ENDP6, /* bEndpointAddress. */ + 0x02, /* bmAttributes (Bulk). */ + 0x40, 0x00, /* wMaxPacketSize. */ + 0x00, /* bInterval. */ + /* Endpoint 1 Descriptor.*/ + 7, + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + ENDP4|0x80, /* bEndpointAddress. */ + 0x02, /* bmAttributes (Bulk). */ + 0x40, 0x00, /* wMaxPacketSize. */ + 0x00 /* bInterval. */ +}; + + +/* + * U.S. English language identifier. + */ +static const uint8_t vcom_string0[4] = { + 4, /* bLength */ + STRING_DESCRIPTOR, + 0x09, 0x04 /* LangID = 0x0409: US-English */ +}; + +static const uint8_t vcom_string1[] = { + 23*2+2, /* bLength */ + STRING_DESCRIPTOR, /* bDescriptorType */ + /* Manufacturer: "Flying Stone Technology" */ + 'F', 0, 'l', 0, 'y', 0, 'i', 0, 'n', 0, 'g', 0, ' ', 0, 'S', 0, + 't', 0, 'o', 0, 'n', 0, 'e', 0, ' ', 0, 'T', 0, 'e', 0, 'c', 0, + 'h', 0, 'n', 0, 'o', 0, 'l', 0, 'o', 0, 'g', 0, 'y', 0, +}; + +static const uint8_t vcom_string2[] = { + 14*2+2, /* bLength */ + STRING_DESCRIPTOR, /* bDescriptorType */ + /* Product name: "Chopstx Serial" */ + 'C', 0, 'h', 0, 'o', 0, 'p', 0, 's', 0, 't', 0, 'x', 0, ' ', 0, + 'S', 0, 'e', 0, 'r', 0, 'i', 0, 'a', 0, 'l', 0, +}; + +/* + * Serial Number string. + */ +static const uint8_t vcom_string3[28] = { + 28, /* bLength */ + STRING_DESCRIPTOR, /* bDescriptorType */ + '0', 0, '.', 0, '0', 0, '0', 0, /* Version number */ +}; + + +#define NUM_INTERFACES 4 + + +static void +usb_device_reset (struct usb_dev *dev) +{ + int i; + + usb_lld_reset (dev, VCOM_FEATURE_BUS_POWERED); + + /* Initialize Endpoint 0 */ + usb_lld_setup_endpoint (ENDP0, EP_CONTROL, 0, ENDP0_RXADDR, ENDP0_TXADDR, 64); + + device_state = USB_DEVICE_STATE_ATTACHED; + for (i = 0; i < MAX_CDC; i++) + { + struct cdc *s = &cdc_table[i]; + + chopstx_mutex_lock (&s->mtx); + s->input_len = 0; + s->flag_connected = 0; + s->flag_output_ready = 1; + s->flag_input_avail = 0; + memcpy (&s->line_coding, &lc_default, sizeof (struct line_coding)); + chopstx_mutex_unlock (&s->mtx); + } +} + + +void (*send_break) (uint8_t dev_no, uint16_t duration); +static void (*setup_usart_config) (uint8_t dev_no, uint32_t bitrate, + uint8_t format, uint8_t paritytype, + uint8_t databits); + +#define CDC_CTRL_DTR 0x0001 + +static void +usb_ctrl_write_finish (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT); + + if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT) + && USB_SETUP_SET (arg->type)) + { + struct cdc *s = cdc_get (arg->index, 0); + + if (arg->request == USB_CDC_REQ_SET_LINE_CODING) + (*setup_usart_config) (s->dev_no, s->line_coding.bitrate, + s->line_coding.format, + s->line_coding.paritytype, + s->line_coding.databits); + else if (arg->request == USB_CDC_REQ_SET_CONTROL_LINE_STATE) + { + /* Open/close the connection. */ + chopstx_mutex_lock (&s->mtx); + s->flag_connected = ((arg->value & CDC_CTRL_DTR) != 0); + chopstx_cond_broadcast (&s->cnd_rx); + chopstx_mutex_unlock (&s->mtx); + } + else if (arg->request == USB_CDC_REQ_SEND_BREAK) + { + chopstx_mutex_lock (&s->mtx); + send_break (s->dev_no, arg->value); + chopstx_mutex_unlock (&s->mtx); + } + } + /* + * The transaction was already finished. So, it is no use to call + * usb_lld_ctrl_error when the condition does not match. + */ +} + + + +static int +vcom_port_data_setup (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + + if (USB_SETUP_GET (arg->type)) + { + struct cdc *s = cdc_get (arg->index, 0); + + if (arg->request == USB_CDC_REQ_GET_LINE_CODING) + return usb_lld_ctrl_send (dev, &s->line_coding, + sizeof (struct line_coding)); + } + else /* USB_SETUP_SET (req) */ + { + if (arg->request == USB_CDC_REQ_SET_LINE_CODING + && arg->len == sizeof (struct line_coding)) + { + struct cdc *s = cdc_get (arg->index, 0); + + return usb_lld_ctrl_recv (dev, &s->line_coding, + sizeof (struct line_coding)); + } + else if (arg->request == USB_CDC_REQ_SET_CONTROL_LINE_STATE) + return usb_lld_ctrl_ack (dev); + else if (arg->request == USB_CDC_REQ_SEND_BREAK) + return usb_lld_ctrl_ack (dev); + } + + return -1; +} + +static int +usb_setup (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT); + + if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT)) + return vcom_port_data_setup (dev); + + return -1; +} + +static int +usb_get_descriptor (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + uint8_t desc_type = (arg->value >> 8); + uint8_t desc_index = (arg->value & 0xff); + + if (rcp != DEVICE_RECIPIENT) + return -1; + + if (desc_type == DEVICE_DESCRIPTOR) + return usb_lld_ctrl_send (dev, + vcom_device_desc, sizeof (vcom_device_desc)); + else if (desc_type == CONFIG_DESCRIPTOR) + return usb_lld_ctrl_send (dev, + vcom_config_desc, sizeof (vcom_config_desc)); + else if (desc_type == STRING_DESCRIPTOR) + { + const uint8_t *str; + int size; + + switch (desc_index) + { + case 0: + str = vcom_string0; + size = sizeof (vcom_string0); + break; + case 1: + str = vcom_string1; + size = sizeof (vcom_string1); + break; + case 2: + str = vcom_string2; + size = sizeof (vcom_string2); + break; + case 3: + str = vcom_string3; + size = sizeof (vcom_string3); + break; + default: + return -1; + } + + return usb_lld_ctrl_send (dev, str, size); + } + + return -1; +} + +static void +vcom_setup_endpoints_for_interface (uint16_t interface, int stop) +{ + struct cdc *s = cdc_get (interface, 0); + + if (interface == 0) + { + if (!stop) + usb_lld_setup_endpoint (s->endp2, EP_INTERRUPT, 0, 0, ENDP2_TXADDR, 0); + else + usb_lld_stall_tx (s->endp2); + } + else if (interface == 1) + { + if (!stop) + { + usb_lld_setup_endpoint (s->endp1, EP_BULK, 0, 0, ENDP1_TXADDR, 0); + usb_lld_setup_endpoint (s->endp3, EP_BULK, 0, ENDP3_RXADDR, 0, 64); + /* Start with no data receiving (ENDP3 not enabled)*/ + } + else + { + usb_lld_stall_tx (s->endp1); + usb_lld_stall_rx (s->endp3); + } + } + else if (interface == 2) + { + if (!stop) + usb_lld_setup_endpoint (s->endp2, EP_INTERRUPT, 0, 0, ENDP5_TXADDR, 0); + else + usb_lld_stall_tx (s->endp2); + } + else if (interface == 3) + { + if (!stop) + { + usb_lld_setup_endpoint (s->endp1, EP_BULK, 0, 0, ENDP4_TXADDR, 0); + usb_lld_setup_endpoint (s->endp3, EP_BULK, 0, ENDP6_RXADDR, 0, 64); + /* Start with no data receiving (ENDP6 not enabled)*/ + } + else + { + usb_lld_stall_tx (s->endp1); + usb_lld_stall_rx (s->endp3); + } + } +} + +static int +usb_set_configuration (struct usb_dev *dev) +{ + int i; + uint8_t current_conf; + + current_conf = usb_lld_current_configuration (dev); + if (current_conf == 0) + { + if (dev->dev_req.value != 1) + return -1; + + usb_lld_set_configuration (dev, 1); + for (i = 0; i < NUM_INTERFACES; i++) + vcom_setup_endpoints_for_interface (i, 0); + device_state = USB_DEVICE_STATE_CONFIGURED; + for (i = 0; i < MAX_CDC; i++) + { + struct cdc *s = &cdc_table[i]; + + chopstx_mutex_lock (&s->mtx); + chopstx_cond_signal (&s->cnd_rx); + chopstx_mutex_unlock (&s->mtx); + } + } + else if (current_conf != dev->dev_req.value) + { + if (dev->dev_req.value != 0) + return -1; + + usb_lld_set_configuration (dev, 0); + for (i = 0; i < NUM_INTERFACES; i++) + vcom_setup_endpoints_for_interface (i, 1); + device_state = USB_DEVICE_STATE_ADDRESSED; + for (i = 0; i < MAX_CDC; i++) + { + struct cdc *s = &cdc_table[i]; + + chopstx_mutex_lock (&s->mtx); + chopstx_cond_signal (&s->cnd_rx); + chopstx_mutex_unlock (&s->mtx); + } + } + + usb_lld_ctrl_ack (dev); + return 0; +} + + +static int +usb_set_interface (struct usb_dev *dev) +{ + uint16_t interface = dev->dev_req.index; + uint16_t alt = dev->dev_req.value; + + if (interface >= NUM_INTERFACES) + return -1; + + if (alt != 0) + return -1; + else + { + vcom_setup_endpoints_for_interface (interface, 0); + return usb_lld_ctrl_ack (dev); + } +} + +static int +usb_get_interface (struct usb_dev *dev) +{ + const uint8_t zero = 0; + uint16_t interface = dev->dev_req.index; + + if (interface >= NUM_INTERFACES) + return -1; + + /* We don't have alternate interface, so, always return 0. */ + return usb_lld_ctrl_send (dev, &zero, 1); +} + +static int +usb_get_status_interface (struct usb_dev *dev) +{ + const uint16_t status_info = 0; + uint16_t interface = dev->dev_req.index; + + if (interface >= NUM_INTERFACES) + return -1; + + return usb_lld_ctrl_send (dev, &status_info, 2); +} + + +static void +usb_tx_done (uint8_t ep_num, uint16_t len) +{ + struct cdc *s = cdc_get (-1, ep_num); + + (void)len; + + chopstx_mutex_lock (&s->mtx); + if (ep_num == s->endp1) + { + if (s->flag_output_ready == 0) + { + s->flag_output_ready = 1; + chopstx_cond_signal (&s->cnd_tx); + } + } + else if (ep_num == s->endp2) + { + s->flag_notify_busy = 0; + } + chopstx_mutex_unlock (&s->mtx); +} + + +static void +usb_rx_ready (uint8_t ep_num, uint16_t len) +{ + struct cdc *s = cdc_get (-1, ep_num); + + if (ep_num == s->endp3) + { + usb_lld_rxcpy (s->input, ep_num, 0, len); + s->flag_input_avail = 1; + s->input_len = len; + chopstx_cond_signal (&s->cnd_rx); + } +} + +static void *cdc_main (void *arg); + + +void +cdc_init (uint16_t prio, uintptr_t stack_addr, size_t stack_size, + void (*sendbrk_callback) (uint8_t dev_no, uint16_t duration), + void (*config_callback) (uint8_t dev_no, + uint32_t bitrate, uint8_t format, + uint8_t paritytype, uint8_t databits)) +{ + int i; + + send_break = sendbrk_callback; + setup_usart_config = config_callback; + for (i = 0; i < MAX_CDC; i++) + { + struct cdc *s = &cdc_table[i]; + + chopstx_mutex_init (&s->mtx); + chopstx_cond_init (&s->cnd_tx); + chopstx_cond_init (&s->cnd_rx); + s->input_len = 0; + s->flag_connected = 0; + s->flag_output_ready = 1; + s->flag_input_avail = 0; + memcpy (&s->line_coding, &lc_default, sizeof (struct line_coding)); + + if (i == 0) + { + s->dev_no = 2; + s->endp1 = ENDP1; + s->endp2 = ENDP2; + s->endp3 = ENDP3; + } + else + { + s->dev_no = 3; + s->endp1 = ENDP4; + s->endp2 = ENDP5; + s->endp3 = ENDP6; + } + } + + device_state = USB_DEVICE_STATE_UNCONNECTED; + chopstx_create (prio, stack_addr, stack_size, cdc_main, NULL); +} + +struct cdc * +cdc_open (uint8_t cdc_num) +{ + struct cdc *s; + + if (cdc_num >= MAX_CDC) + return NULL; + + s = &cdc_table[cdc_num]; + return s; +} + + +static void * +cdc_main (void *arg) +{ + struct usb_dev dev; + int e; + + (void)arg; + +#if defined(OLDER_SYS_H) + /* + * Historically (before sys < 3.0), NVIC priority setting for USB + * interrupt was done in usb_lld_sys_init. Thus this code. + * + * When USB interrupt occurs between usb_lld_init (which assumes + * ISR) and chopstx_claim_irq (which clears pending interrupt), + * invocation of usb_lld_event_handler won't occur. + * + * Calling usb_lld_event_handler is no harm even if there were no + * interrupts, thus, we call it unconditionally here, just in case + * if there is a request. + * + * We can't call usb_lld_init after chopstx_claim_irq, as + * usb_lld_init does its own setting for NVIC. Calling + * chopstx_claim_irq after usb_lld_init overrides that. + * + */ + usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED); + chopstx_claim_irq (&usb_intr, INTR_REQ_USB); + goto event_handle; +#else + chopstx_claim_irq (&usb_intr, INTR_REQ_USB); + usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED); +#endif + + while (1) + { + chopstx_intr_wait (&usb_intr); + if (usb_intr.ready) + { + uint8_t ep_num; +#if defined(OLDER_SYS_H) + event_handle: +#endif + /* + * When interrupt is detected, call usb_lld_event_handler. + * The event may be one of following: + * (1) Transfer to endpoint (bulk or interrupt) + * In this case EP_NUM is encoded in the variable E. + * (2) "NONE" event: some trasfer was done, but all was + * done by lower layer, no other work is needed in + * upper layer. + * (3) Device events: Reset or Suspend + * (4) Device requests to the endpoint zero. + * + */ + e = usb_lld_event_handler (&dev); + chopstx_intr_done (&usb_intr); + ep_num = USB_EVENT_ENDP (e); + + if (ep_num != 0) + { + if (USB_EVENT_TXRX (e)) + usb_tx_done (ep_num, USB_EVENT_LEN (e)); + else + usb_rx_ready (ep_num, USB_EVENT_LEN (e)); + } + else + switch (USB_EVENT_ID (e)) + { + case USB_EVENT_DEVICE_RESET: + usb_device_reset (&dev); + continue; + + case USB_EVENT_DEVICE_ADDRESSED: + /* The addres is assigned to the device. We don't + * need to do anything for this actually, but in this + * application, we maintain the USB status of the + * device. Usually, just "continue" as EVENT_OK is + * OK. + */ + device_state = USB_DEVICE_STATE_ADDRESSED; + { + struct cdc *s = &cdc_table[0]; + chopstx_mutex_lock (&s->mtx); + chopstx_cond_signal (&s->cnd_rx); + chopstx_mutex_unlock (&s->mtx); + } + continue; + + case USB_EVENT_GET_DESCRIPTOR: + if (usb_get_descriptor (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_SET_CONFIGURATION: + if (usb_set_configuration (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_SET_INTERFACE: + if (usb_set_interface (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_CTRL_REQUEST: + /* Device specific device request. */ + if (usb_setup (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_GET_STATUS_INTERFACE: + if (usb_get_status_interface (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_GET_INTERFACE: + if (usb_get_interface (&dev) < 0) + usb_lld_ctrl_error (&dev); + continue; + + case USB_EVENT_SET_FEATURE_DEVICE: + case USB_EVENT_SET_FEATURE_ENDPOINT: + case USB_EVENT_CLEAR_FEATURE_DEVICE: + case USB_EVENT_CLEAR_FEATURE_ENDPOINT: + usb_lld_ctrl_ack (&dev); + continue; + + case USB_EVENT_CTRL_WRITE_FINISH: + /* Control WRITE transfer finished. */ + usb_ctrl_write_finish (&dev); + continue; + + case USB_EVENT_OK: + case USB_EVENT_DEVICE_SUSPEND: + default: + continue; + } + } + } + + return NULL; +} + + +void +cdc_wait_configured (void) +{ + struct cdc *s = &cdc_table[0]; + + chopstx_mutex_lock (&s->mtx); + while (device_state != USB_DEVICE_STATE_CONFIGURED) + chopstx_cond_wait (&s->cnd_rx, &s->mtx); + chopstx_mutex_unlock (&s->mtx); +} + + +void +cdc_wait_connection (struct cdc *s) +{ + chopstx_mutex_lock (&s->mtx); + while (s->flag_connected == 0) + chopstx_cond_wait (&s->cnd_rx, &s->mtx); + s->flag_output_ready = 1; + s->flag_input_avail = 0; + s->input_len = 0; + usb_lld_rx_enable (s->endp3); /* Accept input for line */ + chopstx_mutex_unlock (&s->mtx); +} + +static int +check_tx (struct cdc *s) +{ + if (s->flag_output_ready) + /* TX done */ + return 1; + if (s->flag_connected == 0) + /* Disconnected */ + return -1; + return 0; +} + +int +cdc_send (struct cdc *s, const char *buf, int len) +{ + int r; + const char *p; + int count; + + p = buf; + count = len >= 64 ? 64 : len; + + while (1) + { + chopstx_mutex_lock (&s->mtx); + while ((r = check_tx (s)) == 0) + chopstx_cond_wait (&s->cnd_tx, &s->mtx); + if (r > 0) + { + usb_lld_txcpy (p, s->endp1, 0, count); + usb_lld_tx_enable (s->endp1, count); + s->flag_output_ready = 0; + } + chopstx_mutex_unlock (&s->mtx); + + len -= count; + p += count; + if (len == 0 && count != 64) + /* + * The size of the last packet should be != 0 + * If 64, send ZLP (zelo length packet) + */ + break; + count = len >= 64 ? 64 : len; + } + + return r; +} + + +static int +check_rx (void *arg) +{ + struct cdc *s = arg; + + if (s->flag_input_avail) + /* RX */ + return 1; + if (s->flag_connected == 0) + /* Disconnected */ + return 1; + return 0; +} + +/* + * Returns -1 on connection close + * 0 on timeout. + * >0 length of the input + * + */ +int +cdc_recv (struct cdc *s, char *buf, uint32_t *timeout) +{ + int r; + chopstx_poll_cond_t poll_desc; + + poll_desc.type = CHOPSTX_POLL_COND; + poll_desc.ready = 0; + poll_desc.cond = &s->cnd_rx; + poll_desc.mutex = &s->mtx; + poll_desc.check = check_rx; + poll_desc.arg = s; + + while (1) + { + struct chx_poll_head *pd_array[1] = { + (struct chx_poll_head *)&poll_desc + }; + chopstx_poll (timeout, 1, pd_array); + chopstx_mutex_lock (&s->mtx); + r = check_rx (s); + chopstx_mutex_unlock (&s->mtx); + if (r || (timeout != NULL && *timeout == 0)) + break; + } + + chopstx_mutex_lock (&s->mtx); + if (s->flag_connected == 0) + r = -1; + else if (s->flag_input_avail) + { + r = s->input_len; + memcpy (buf, s->input, r); + s->flag_input_avail = 0; + usb_lld_rx_enable (s->endp3); + s->input_len = 0; + } + else + r = 0; + chopstx_mutex_unlock (&s->mtx); + + return r; +} + + +int +cdc_ss_notify (struct cdc *s, uint16_t state_bits) +{ + int busy; + uint8_t notification[10]; + int interface; + + if (s == &cdc_table[0]) + interface = 0; + else + interface = 2; + + /* Little endian */ + notification[0] = REQUEST_DIR | CLASS_REQUEST | INTERFACE_RECIPIENT; + notification[1] = USB_CDC_NOTIFY_SERIAL_STATE; + notification[2] = notification[3] = 0; + notification[4] = interface; + notification[5] = 0; + notification[6] = 2; + notification[7] = 0; + notification[8] = (state_bits & 0xff); + notification[9] = (state_bits >> 8); + + chopstx_mutex_lock (&s->mtx); + busy = s->flag_notify_busy; + if (!busy) + { + usb_lld_write (s->endp2, notification, sizeof notification); + s->flag_notify_busy = 1; + } + chopstx_mutex_unlock (&s->mtx); + + return busy; +} diff --git a/gnuk/chopstx/mcu/ABOUT-SYS b/gnuk/chopstx/mcu/ABOUT-SYS new file mode 100644 index 0000000000..36b4713570 --- /dev/null +++ b/gnuk/chopstx/mcu/ABOUT-SYS @@ -0,0 +1,239 @@ +Consideration about SYS and the first pages of flash ROM +======================================================== + +Now, I'm developing something like SYS for Kinetis L MCU, so, I write +this document. + +* Compatibility + + SYS 1.0: The first verson + SYS 2.0: Added clock_init, gpio_init + SYS 2.1: Added sys_board_id, sys_board_name + SYS 3.0: Don't setup NVIC priority by usb_lld_sys_init + + +* Macro definition by DEFS in Makefile + + - USE_SYS_CLOCK_GPIO_SETTING + + Define this macro to ask chopstx/entry.c (the runtime code before + MAIN function) to use function entries in SYS for clock_init and + gpio_init. + + If not defined, entry.c includes the code for clock_init and + gpio_init which might be different to a board, and use them (entries + in SYS will not be used). This works well with the ROM of SYS + 1.0. + + Note that SYS entries of clock_init and gpio_init were introduced + in SYS 2.0. So, enable this macro only if the ROM is SYS 2.0 or + later. + + - USE_SYS_BOARD_ID + + Define this macro in a driver to get "sys_board_id" in SYS, so + that the driver can support various boards at runtime by changing + the settings according to the board. + + A simple driver could only support a single board, by the compile + time (BOARD_ID in board-*.h) choice of of a settings. + + Note that SYS entries of sys_board_id and sys_board_name were + introduced in SYS 2.1. So, enable this macro only if the ROM is + SYS 2.1 or later. + + - USE_SYS3 + + By defining this, it will have same effect of defining both of + USE_SYS_CLOCK_GPIO_SETTING and USE_SYS_BOARD_ID internally. + + +About SYS on STM32F103 +====================== + +In the development of Gnuk, we developed: + + SYS: The system predefined routines for STM32F103 + +It is now maintained as example-cdc/sys.c. + +There is another version in example-led/sys.c, which also supports +STM32F030, as well as STM32F103. But, it wouldn't be useful for +STM32F030. In fact, the file example-fsm-55/sys.c has name sys.c +but it doesn't include any system routines. + +The original issue was: + + (1) When it's protected, STM32F103 can't change the first 4KiB of + flash ROM at run time. + + (2) We want to support firmware upgrade through its USB. + +Later on, we add another point. + + (3) It is good if the executable of Gnuk could be shared among + different boards. + +For (1) and (2), we decided put some useful routines and data which are +not need to be changed. + +Now, the first 4KiB of flash ROM consists of: + + 1KiB: SYS + 3KiB: 3/4 of AES forward tables + +SYS consists of: + + Internal: reset entry, address of the end of RAM + Data: board identification + Routines: board specific + board independent + +and here is the list of all. + +* Internal routines + + reset entry + address of the end of RAM + +* Board identification + + sys_version + sys_board_id + sys_board_name + +* Board specific routines + + * led + set_led + + * mcu/board lower level + clock_init + gpio_init + + * usb + usb_lld_sys_init + usb_lld_sys_shutdown + +* Board independent routines + + * flash ROM access routines + + unlock + write halfword + erase page + brank check + write page + protect + erase_all & exec to ram + + * system reset routine + + nvic_system_reset + +The routines of clock_init and gpio_init are here because of some +historical reasons. (We could design a system with no such exported +routines: by defining: those things done internally after reset and +before calling the application.) + +Those are exported as entries of SYS, and it is the responsibility of +the application which do initialize clock and GPIO, calling those +routines. + +USB routines are needed because of hardware practice of STM32F103. +With STM32F103, each board has different way for handling the pull up +of USB D+ and how the device asks re-enumeration to host PC. In my +opinion, if it's defined as full speed device and it's OK for us not +to use high impedance (but asserting to LOW, instead) of D+ to ask +re-enumeration, we can just pull up D+ always. And we wouldn't need +such routines in SYS. + + + +About SYS on Kinetis L +====================== + +For Kinetis L, because it's ROM has the original firmware upgrade +support by the vendor (though USB HID), all that we needed for +firmware upgrade would be just erasing to factory settings. + +And it has no limitation like STM32F103's first 4KiB flash ROM. +All pages can be updated at run time. + +Nevertheless, the first two pages (2KiB) of KL27Z is still difficult +to use. + +So, I decide to introduce something like SYS for Kinetis L. + + +* Layout + +Three pages (3KiB) usage: + + ------------ The first page + End of RAM <-- not used but hardware defines this + Address of reset entry + sys_version + sys_board_info (id, name) + sys_vector + + other SYS routines and data... + + ------------ The second page + FLASH CONFIG: 16-byte + Reset entry function + flash routines + CRC-32 routines + CRC-32 table (768-byte of CRC-32 table) + + ------------ The third page + MAGIC 256-byte (256-byte of the last part of CRC-32 table) + ... + vectors (initial MSP, reset, ...) + ... + + +* data: Board identification + + sys_version + sys_board_id + sys_board_name ; null terminated + +* Board specific routines + + * mcu/board lower level + clock_init + gpio_init + + * led + set_led + +* data: Board independent routines + + * flash ROM access code to be loaded on to RAM + +* system reset routine??? + + nvic_system_reset + + +* data: vectors for routines and data + sys_version + sys_board_id + address of sys_board_name + address of set_led + address of clock_init + address of gpio_init + address of ... + + + +An Example of No-use of SYS +=========================== + +See example-fsm-55 for an example of no use of SYS. + +While chopstx/entry.c defines vectors in ROM and RAM, those are simply +discarded by example-fsm-55/hacker-emblem.ld. + +-- diff --git a/gnuk/chopstx/mcu/chx-gnu-linux.c b/gnuk/chopstx/mcu/chx-gnu-linux.c new file mode 100644 index 0000000000..51c8fc31d1 --- /dev/null +++ b/gnuk/chopstx/mcu/chx-gnu-linux.c @@ -0,0 +1,5 @@ +void +chx_sleep_mode (int enable_sleep) +{ + (void)enable_sleep; +} diff --git a/gnuk/chopstx/mcu/chx-mkl27z.c b/gnuk/chopstx/mcu/chx-mkl27z.c new file mode 100644 index 0000000000..48d683b99a --- /dev/null +++ b/gnuk/chopstx/mcu/chx-mkl27z.c @@ -0,0 +1,23 @@ +extern int chx_allow_sleep; + +void +chx_sleep_mode (int enable_sleep) +{ + (void)enable_sleep; +} + +void __attribute__((naked)) +chx_idle (void) +{ + int sleep_enabled; + + for (;;) + { + asm ("ldr %0, %1" : "=r" (sleep_enabled): "m" (chx_allow_sleep)); + if (sleep_enabled) + { + asm volatile ("wfi" : : : "memory"); + /* NOTE: it never comes here. Don't add lines after this. */ + } + } +} diff --git a/gnuk/chopstx/mcu/chx-stm32f0.c b/gnuk/chopstx/mcu/chx-stm32f0.c new file mode 100644 index 0000000000..00b77ee884 --- /dev/null +++ b/gnuk/chopstx/mcu/chx-stm32f0.c @@ -0,0 +1,42 @@ +#include +#include +#define MCU_STM32F0 +#include + +extern int chx_allow_sleep; + +void +chx_sleep_mode (int how) +{ + PWR->CR |= PWR_CR_CWUF; + PWR->CR &= ~(PWR_CR_PDDS|PWR_CR_LPDS); + + if (how == 0 || how == 1 /* Sleep only (not deepsleep) */) + SCB->SCR &= ~SCB_SCR_SLEEPDEEP; + else + { /* Deepsleep */ + /* how == 2: deepsleep but regulator ON */ + if (how == 3) + PWR->CR |= PWR_CR_LPDS; /* regulator low-power mode */ + else if (how == 4) + PWR->CR |= PWR_CR_PDDS; /* Power down: All OFF */ + + SCB->SCR |= SCB_SCR_SLEEPDEEP; + } +} + +void __attribute__((naked)) +chx_idle (void) +{ + int sleep_enabled; + + for (;;) + { + asm ("ldr %0, %1" : "=r" (sleep_enabled): "m" (chx_allow_sleep)); + if (sleep_enabled) + { + asm volatile ("wfi" : : : "memory"); + /* NOTE: it never comes here. Don't add lines after this. */ + } + } +} diff --git a/gnuk/chopstx/mcu/chx-stm32f103.c b/gnuk/chopstx/mcu/chx-stm32f103.c new file mode 100644 index 0000000000..0fbe8e523b --- /dev/null +++ b/gnuk/chopstx/mcu/chx-stm32f103.c @@ -0,0 +1,92 @@ +#include +#include +#include "board.h" + +extern int chx_allow_sleep; + +#define STM32_PLLSRC STM32_PLLSRC_HSE +#define STM32_PLLMUL ((STM32_PLLMUL_VALUE - 2) << 18) +#ifndef STM32_ADCPRE +#define STM32_ADCPRE STM32_ADCPRE_DIV6 +#endif +#ifndef STM32_USBPRE +#define STM32_USBPRE STM32_USBPRE_DIV1P5 +#endif + +static void +configure_clock (int high) +{ + uint32_t cfg_sw; + uint32_t cfg; + + if (high) + { + cfg = STM32_MCO_NOCLOCK | STM32_USBPRE + | STM32_PLLMUL | STM32_PLLXTPRE | STM32_PLLSRC + | STM32_ADCPRE | STM32_PPRE2_DIV1 + | STM32_PPRE1_DIV2 | STM32_HPRE_DIV1; + + cfg_sw = RCC_CFGR_SW_PLL; + } + else + { + cfg = STM32_MCO_NOCLOCK | STM32_USBPRE + | STM32_PLLMUL | STM32_PLLXTPRE | STM32_PLLSRC + | STM32_ADCPRE_DIV8 | STM32_PPRE2_DIV16 + | STM32_PPRE1_DIV16 | STM32_HPRE_DIV8; + + cfg_sw = RCC_CFGR_SW_HSI; + } + + RCC->CFGR = cfg | cfg_sw; + while ((RCC->CFGR & RCC_CFGR_SWS) != (cfg_sw << 2)) + ; +} + +/* + * When HOW=0 or HOW=1, SYSCLK is PLL (72MHz). + * When HOW=2, SYSCLK will be 1MHz with HSI (8MHz) on sleep. + * + * With lower clock, it can achieve lower power consumption. + * + * Implementation note: Deepsleep is only useful with RTC, Watch Dog, + * or WKUP pin. We can't use deepsleep for USB, it never wakes up. + * + */ +void +chx_sleep_mode (int how) +{ + if (how == 0 || how == 1) + configure_clock (1); + + /* how == 2: Defer setting to 8MHz clock to the idle function */ +} + +void __attribute__((naked)) +chx_idle (void) +{ + int sleep_enabled; + + for (;;) + { + asm ("ldr %0, %1" : "=r" (sleep_enabled): "m" (chx_allow_sleep)); + if (sleep_enabled) + { + asm volatile ("cpsid i" : : : "memory"); + if (sleep_enabled == 1) + { + /* Allow JTAG/SWD access on sleep. */ + DBGMCU->CR |= DBG_SLEEP; + } + else if (sleep_enabled == 2) + { + DBGMCU->CR &= ~DBG_SLEEP; /* Disable HCLK on sleep */ + configure_clock (0); + } + asm volatile ("cpsie i" : : : "memory"); + + asm volatile ("wfi" : : : "memory"); + /* NOTE: it never comes here. Don't add lines after this. */ + } + } +} diff --git a/gnuk/chopstx/mcu/clk_gpio_init-mkl27z.c b/gnuk/chopstx/mcu/clk_gpio_init-mkl27z.c new file mode 100644 index 0000000000..630fd0b62c --- /dev/null +++ b/gnuk/chopstx/mcu/clk_gpio_init-mkl27z.c @@ -0,0 +1,104 @@ +/* + * clk_gpio_init-mkl27z.c - Clock and GPIO initialization for Kinetis L. + * + * Copyright (C) 2016 Flying Stone Technology + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx 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. + * + * Chopstx 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 . + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +#include + +struct MCG { + volatile uint8_t C1; /* MCG Control Register 1 */ + volatile uint8_t C2; /* MCG Control Register 2 */ + uint8_t reserved0[4]; /* */ + volatile uint8_t S; /* MCG Status Register */ + uint8_t reserved1[1]; /* */ + volatile uint8_t SC; /* MCG Status and Control Register */ + uint8_t reserved2[15]; /* */ + volatile uint8_t MC; /* MCG Miscellaneous Control Register */ +}; +static struct MCG *const MCG = (struct MCG *)0x40064000; + +struct USB_CLK_RECOVER { + volatile uint8_t CTRL; /* USB Clock */ + uint8_t rsvd38[3]; /* recovery control */ + volatile uint8_t IRC_EN; /* IRC48M oscillator */ + uint8_t rsvd39[3]; /* enable register */ + volatile uint8_t INT_EN; /* Clock recovery */ + uint8_t rsvd40[3]; /* interrupt enable */ + volatile uint8_t INT_STATUS; /* Clock recovery */ + /* interrupt status */ +}; +static struct USB_CLK_RECOVER *const USB_CLK_RECOVER = + (struct USB_CLK_RECOVER *)0x40072140; + +static void __attribute__((used)) +clock_init (void) +{ + SIM->CLKDIV1 = (SIM->CLKDIV1 & 0xF0070000) + | (1 << 16) /* OUTDIV4 = 001: Divide-by-2 */ + ; + + MCG->MC = 0x80; /* HIRC Enable, LIRC_DIV2=000: Division factor=1 */ + MCG->C1 = 0x00; /* Select HIRC clock, LIRC disabled. */ + /* Make sure HIRC clock is selected. */ + while ((MCG->S & 0x0c) != 0) + ; + + /* TPMSRC=IRC48M, USBSRC=IRC48M, CLOKOUTSEL=LPO, RTC-clock */ + SIM->SOPT2 = 0x01040060; + + SIM->SCGC4 = (1 << 18); /* Enable USB FS clock */ + SIM->SCGC5 = (1 << 10); /* Enable Port B clock */ + SIM->SCGC6 = (1 << 25)|1; /* Enable TPM1 clock */ + SIM->COPC = 0; /* COP disabled */ + + /* Crystal-less USB setup. */ + USB_CLK_RECOVER->IRC_EN = 0x02; + USB_CLK_RECOVER->CTRL = 0x80; +} + + +static void __attribute__((used)) +gpio_init (void) +{ + PORTB->PCR0 = (1<<8) /* GPIO */ + | (0<<6) /* DriveStrengthEnable=0 */ + | (0<<4) /* PassiveFilterEnable=0 */ + | (1<<2) /* SlewRateEnable = slow */ + | (0<<1) /* pull enable = 0 */ + | (0<<0) /* pull up select= 0 */ + ; + PORTB->PCR1 = (1<<8) /* GPIO */ + | (0<<6) /* DriveStrengthEnable=0 */ + | (0<<4) /* PassiveFilterEnable=0 */ + | (1<<2) /* SlewRateEnable = slow */ + | (0<<1) /* pull enable = 0 */ + | (0<<0) /* pull up select= 0 */ + ; + + GPIOB->PDDR = (1 << 1) | (1 << 0); /* PTB0, PTB1 : Output */ + GPIOB->PSOR = (1 << 0); /* PTB0: Set : Light off */ + GPIOB->PCOR = (1 << 1); /* PTB1: Clear: Output 0 */ +} diff --git a/gnuk/chopstx/mcu/clk_gpio_init-stm32.c b/gnuk/chopstx/mcu/clk_gpio_init-stm32.c new file mode 100644 index 0000000000..9f1bab0fd6 --- /dev/null +++ b/gnuk/chopstx/mcu/clk_gpio_init-stm32.c @@ -0,0 +1,176 @@ +/* + * clk_gpio_init-stm32.c - Clock and GPIO initialization for STM32. + * + * Copyright (C) 2015, 2018 Flying Stone Technology + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx 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. + * + * Chopstx 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 . + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +#if defined(MCU_STM32F0) +#include +#else +#include +#endif + +#if defined(MCU_STM32F0) +#define STM32_PPRE1 STM32_PPRE1_DIV1 +#define STM32_PLLSRC STM32_PLLSRC_HSI +#define STM32_FLASHBITS 0x00000011 +#define STM32_PLLCLKIN (STM32_HSICLK / 2) +#else +#define STM32_PPRE1 STM32_PPRE1_DIV2 +#define STM32_PLLSRC STM32_PLLSRC_HSE +#define STM32_FLASHBITS 0x00000012 +#define STM32_PLLCLKIN (STM32_HSECLK / 1) +#endif + +#define STM32_SW STM32_SW_PLL +#define STM32_HPRE STM32_HPRE_DIV1 +#define STM32_PPRE2 STM32_PPRE2_DIV1 +#ifndef STM32_ADCPRE +#define STM32_ADCPRE STM32_ADCPRE_DIV6 +#endif +#define STM32_MCOSEL STM32_MCO_NOCLOCK +#ifndef STM32_USBPRE +#define STM32_USBPRE STM32_USBPRE_DIV1P5 +#endif + +#define STM32_PLLMUL ((STM32_PLLMUL_VALUE - 2) << 18) +#define STM32_PLLCLKOUT (STM32_PLLCLKIN * STM32_PLLMUL_VALUE) +#define STM32_SYSCLK STM32_PLLCLKOUT +#define STM32_HCLK (STM32_SYSCLK / 1) + + +static void __attribute__((used)) +clock_init (void) +{ + /* HSI setup */ + RCC->CR |= RCC_CR_HSION; + while (!(RCC->CR & RCC_CR_HSIRDY)) + ; + /* Reset HSEON, HSEBYP, CSSON, and PLLON, not touching RCC_CR_HSITRIM */ + RCC->CR &= (RCC_CR_HSITRIM | RCC_CR_HSION); + RCC->CFGR = 0; + while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI) + ; + +#if !defined(MCU_STM32F0) + /* HSE setup */ + RCC->CR |= RCC_CR_HSEON; + while (!(RCC->CR & RCC_CR_HSERDY)) + ; +#endif + + /* PLL setup */ + RCC->CFGR |= STM32_PLLMUL | STM32_PLLXTPRE | STM32_PLLSRC; + RCC->CR |= RCC_CR_PLLON; + while (!(RCC->CR & RCC_CR_PLLRDY)) + ; + + /* Clock settings */ + RCC->CFGR = STM32_MCOSEL | STM32_USBPRE | STM32_PLLMUL | STM32_PLLXTPRE + | STM32_PLLSRC | STM32_ADCPRE | STM32_PPRE2 | STM32_PPRE1 | STM32_HPRE; + + /* + * We don't touch RCC->CR2, RCC->CFGR2, RCC->CFGR3, and RCC->CIR. + */ + + /* Flash setup */ + FLASH->ACR = STM32_FLASHBITS; + + /* Switching on the configured clock source. */ + RCC->CFGR |= STM32_SW; + while ((RCC->CFGR & RCC_CFGR_SWS) != (STM32_SW << 2)) + ; + +#if defined(MCU_STM32F0) + RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; + RCC->APB2RSTR = RCC_APB2RSTR_SYSCFGRST; + RCC->APB2RSTR = 0; + +# if defined(STM32F0_USE_VECTOR_ON_RAM) + /* Use vectors on RAM */ + SYSCFG->CFGR1 = (SYSCFG->CFGR1 & ~SYSCFG_CFGR1_MEM_MODE) | 3; +# endif +#endif +} + + +static struct GPIO *const GPIO_LED = (struct GPIO *)GPIO_LED_BASE; +#ifdef GPIO_USB_BASE +static struct GPIO *const GPIO_USB = (struct GPIO *)GPIO_USB_BASE; +#endif +#ifdef GPIO_OTHER_BASE +static struct GPIO *const GPIO_OTHER = (struct GPIO *)GPIO_OTHER_BASE; +#endif + +static void __attribute__((used)) +gpio_init (void) +{ + /* Enable GPIO clock. */ +#if defined(MCU_STM32F0) + RCC->AHBENR |= RCC_ENR_IOP_EN; + RCC->AHBRSTR = RCC_RSTR_IOP_RST; + RCC->AHBRSTR = 0; +#else + RCC->APB2ENR |= RCC_ENR_IOP_EN; + RCC->APB2RSTR = RCC_RSTR_IOP_RST; + RCC->APB2RSTR = 0; +#endif + +#if defined(MCU_STM32F0) + GPIO_LED->OSPEEDR = VAL_GPIO_LED_OSPEEDR; + GPIO_LED->OTYPER = VAL_GPIO_LED_OTYPER; + GPIO_LED->MODER = VAL_GPIO_LED_MODER; + GPIO_LED->PUPDR = VAL_GPIO_LED_PUPDR; + +#ifdef GPIO_OTHER_BASE + GPIO_OTHER->OSPEEDR = VAL_GPIO_OTHER_OSPEEDR; + GPIO_OTHER->OTYPER = VAL_GPIO_OTHER_OTYPER; + GPIO_OTHER->MODER = VAL_GPIO_OTHER_MODER; + GPIO_OTHER->PUPDR = VAL_GPIO_OTHER_PUPDR; +#endif +#else +#ifdef AFIO_MAPR_SOMETHING + AFIO->MAPR |= AFIO_MAPR_SOMETHING; +#endif + + /* LED is mandatory. We configure it always. */ + GPIO_LED->ODR = VAL_GPIO_LED_ODR; + GPIO_LED->CRH = VAL_GPIO_LED_CRH; + GPIO_LED->CRL = VAL_GPIO_LED_CRL; + + /* If there is USB enabler pin and it's independent, we configure it. */ +#if defined(GPIO_USB_BASE) && GPIO_USB_BASE != GPIO_LED_BASE + GPIO_USB->ODR = VAL_GPIO_USB_ODR; + GPIO_USB->CRH = VAL_GPIO_USB_CRH; + GPIO_USB->CRL = VAL_GPIO_USB_CRL; +#endif + +#ifdef GPIO_OTHER_BASE + GPIO_OTHER->ODR = VAL_GPIO_OTHER_ODR; + GPIO_OTHER->CRH = VAL_GPIO_OTHER_CRH; + GPIO_OTHER->CRL = VAL_GPIO_OTHER_CRL; +#endif +#endif +} diff --git a/gnuk/chopstx/mcu/cortex-m.h b/gnuk/chopstx/mcu/cortex-m.h new file mode 100644 index 0000000000..dbae9c6c46 --- /dev/null +++ b/gnuk/chopstx/mcu/cortex-m.h @@ -0,0 +1,29 @@ +/* System Control Block */ +struct SCB +{ + volatile uint32_t CPUID; + volatile uint32_t ICSR; + volatile uint32_t VTOR; + volatile uint32_t AIRCR; + volatile uint32_t SCR; + volatile uint32_t CCR; + volatile uint8_t SHP[12]; + volatile uint32_t SHCSR; + volatile uint32_t CFSR; + volatile uint32_t HFSR; + volatile uint32_t DFSR; + volatile uint32_t MMAR; + volatile uint32_t BFAR; + volatile uint32_t AFSR; + volatile uint32_t PFR[2]; + volatile uint32_t DFR; + volatile uint32_t AFR; + volatile uint32_t MMFR[4]; + volatile uint32_t ISAR[5]; +}; + +#define SCS_BASE 0xE000E000 +#define SCB_BASE (SCS_BASE + 0x0D00) +static struct SCB *const SCB = (struct SCB *)SCB_BASE; +#define SCB_SCR_SLEEPDEEP (1 << 2) +#define SCB_AIRCR_SYSRESETREQ 0x04 diff --git a/gnuk/chopstx/mcu/mkl27z.h b/gnuk/chopstx/mcu/mkl27z.h new file mode 100644 index 0000000000..1ede77f354 --- /dev/null +++ b/gnuk/chopstx/mcu/mkl27z.h @@ -0,0 +1,70 @@ +/* System Integration Module. */ +struct SIM { + volatile uint32_t SOPT1; /* System Options Register 1 */ + volatile uint32_t SOPT1CFG; /* SOPT1 Configuration Register */ + uint32_t reserved0[1023]; /* */ + volatile uint32_t SOPT2; /* System Options Register 2 */ + uint32_t reserved1[1]; /* */ + volatile uint32_t SOPT4; /* System Options Register 4 */ + volatile uint32_t SOPT5; /* System Options Register 5 */ + uint32_t reserved2[1]; /* */ + volatile uint32_t SOPT7; /* System Options Register 7 */ + uint32_t reserved3[2]; /* */ + volatile uint32_t SDID; /* System Device Identification Register */ + uint32_t reserved4[3]; /* */ + volatile uint32_t SCGC4; /* System Clock Gating Control Register 4 */ + volatile uint32_t SCGC5; /* System Clock Gating Control Register 5 */ + volatile uint32_t SCGC6; /* System Clock Gating Control Register 6 */ + volatile uint32_t SCGC7; /* System Clock Gating Control Register 7 */ + volatile uint32_t CLKDIV1; /* System Clock Divider Register 1 */ + uint32_t reserved5[1]; /* */ + volatile uint32_t FCFG1; /* Flash Configuration Register 1 */ + volatile uint32_t FCFG2; /* Flash Configuration Register 2 */ + uint32_t reserved6[1]; /* */ + volatile uint32_t UIDMH; /* Unique Identification Register Mid-High */ + volatile uint32_t UIDML; /* Unique Identification Register Mid Low */ + volatile uint32_t UIDL; /* Unique Identification Register Low */ + uint32_t reserved7[39]; /* */ + volatile uint32_t COPC; /* COP Control Register */ + volatile uint32_t SRVCOP; /* Service COP */ +}; + +/* Port control. */ +struct PORT { + volatile uint32_t PCR0; volatile uint32_t PCR1; + volatile uint32_t PCR2; volatile uint32_t PCR3; + volatile uint32_t PCR4; volatile uint32_t PCR5; + volatile uint32_t PCR6; volatile uint32_t PCR7; + volatile uint32_t PCR8; volatile uint32_t PCR9; + volatile uint32_t PCR10; volatile uint32_t PCR11; + volatile uint32_t PCR12; volatile uint32_t PCR13; + volatile uint32_t PCR14; volatile uint32_t PCR15; + volatile uint32_t PCR16; volatile uint32_t PCR17; + volatile uint32_t PCR18; volatile uint32_t PCR19; + volatile uint32_t PCR20; volatile uint32_t PCR21; + volatile uint32_t PCR22; volatile uint32_t PCR23; + volatile uint32_t PCR24; volatile uint32_t PCR25; + volatile uint32_t PCR26; volatile uint32_t PCR27; + volatile uint32_t PCR28; volatile uint32_t PCR29; + volatile uint32_t PCR30; volatile uint32_t PCR31; + volatile uint32_t GPCLR; volatile uint32_t GPCHR; + uint32_t reserved[6]; + volatile uint32_t ISFR; +}; + +struct GPIO { + volatile uint32_t PDOR; /* Port Data Output Register */ + volatile uint32_t PSOR; /* Port Set Output Register */ + volatile uint32_t PCOR; /* Port Clear Output Register */ + volatile uint32_t PTOR; /* Port Toggle Output Register */ + volatile uint32_t PDIR; /* Port Data Input Register */ + volatile uint32_t PDDR; /* Port Data Direction Register */ +}; + +static struct SIM *const SIM = (struct SIM *)0x40047000; +static struct PORT *const PORTB = (struct PORT *)0x4004A000; +static struct PORT *const PORTD = (struct PORT *)0x4004C000; +static struct PORT *const PORTE = (struct PORT *)0x4004D000; +static struct GPIO *const GPIOB = (struct GPIO *)0x400FF040; +static struct GPIO *const GPIOD = (struct GPIO *)0x400FF0C0; +static struct GPIO *const GPIOE = (struct GPIO *)0x400FF100; diff --git a/gnuk/chopstx/mcu/stm32.h b/gnuk/chopstx/mcu/stm32.h new file mode 100644 index 0000000000..5a183658b7 --- /dev/null +++ b/gnuk/chopstx/mcu/stm32.h @@ -0,0 +1,224 @@ +#define PERIPH_BASE 0x40000000 +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) +#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000) +#define AHB2PERIPH_BASE (PERIPH_BASE + 0x08000000) + +struct RCC { + volatile uint32_t CR; + volatile uint32_t CFGR; + volatile uint32_t CIR; + volatile uint32_t APB2RSTR; + volatile uint32_t APB1RSTR; + volatile uint32_t AHBENR; + volatile uint32_t APB2ENR; + volatile uint32_t APB1ENR; + volatile uint32_t BDCR; + volatile uint32_t CSR; +#if defined(MCU_STM32F0) + volatile uint32_t AHBRSTR; + volatile uint32_t CFGR2; + volatile uint32_t CFGR3; + volatile uint32_t CR2; +#endif +}; + +#define RCC_BASE (AHBPERIPH_BASE + 0x1000) +static struct RCC *const RCC = (struct RCC *)RCC_BASE; + +#define RCC_APB1ENR_USBEN 0x00800000 +#define RCC_APB1RSTR_USBRST 0x00800000 + +#define RCC_CR_HSION 0x00000001 +#define RCC_CR_HSIRDY 0x00000002 +#define RCC_CR_HSITRIM 0x000000F8 +#define RCC_CR_HSEON 0x00010000 +#define RCC_CR_HSERDY 0x00020000 +#define RCC_CR_PLLON 0x01000000 +#define RCC_CR_PLLRDY 0x02000000 + +#define RCC_CFGR_SWS 0x0000000C +#define RCC_CFGR_SWS_HSI 0x00000000 +#define RCC_CFGR_SW_HSI (0 << 0) +#define RCC_CFGR_SW_HSE (1 << 0) +#define RCC_CFGR_SW_PLL (2 << 0) +#define RCC_CFGR_SW_MASK (3 << 0) + +#define RCC_AHBENR_DMA1EN 0x00000001 +#define RCC_AHBENR_CRCEN 0x00000040 + +#if defined(MCU_STM32F0) +#define RCC_AHBRSTR_IOPARST 0x00020000 +#define RCC_AHBRSTR_IOPBRST 0x00040000 +#define RCC_AHBRSTR_IOPCRST 0x00080000 +#define RCC_AHBRSTR_IOPDRST 0x00100000 +#define RCC_AHBRSTR_IOPFRST 0x00400000 + +#define RCC_AHBENR_IOPAEN 0x00020000 +#define RCC_AHBENR_IOPBEN 0x00040000 +#define RCC_AHBENR_IOPCEN 0x00080000 +#define RCC_AHBENR_IOPDEN 0x00100000 +#define RCC_AHBENR_IOPFEN 0x00400000 + +#define RCC_APB2RSTR_SYSCFGRST 0x00000001 +#define RCC_APB2ENR_SYSCFGEN 0x00000001 +#else +#define RCC_APB2ENR_ADC1EN 0x00000200 +#define RCC_APB2ENR_ADC2EN 0x00000400 +#define RCC_APB2ENR_TIM1EN 0x00000800 +#define RCC_APB1ENR_TIM2EN 0x00000001 +#define RCC_APB1ENR_TIM3EN 0x00000002 +#define RCC_APB1ENR_TIM4EN 0x00000004 + +#define RCC_APB2RSTR_ADC1RST 0x00000200 +#define RCC_APB2RSTR_ADC2RST 0x00000400 +#define RCC_APB2RSTR_TIM1RST 0x00000800 +#define RCC_APB1RSTR_TIM2RST 0x00000001 +#define RCC_APB1RSTR_TIM3RST 0x00000002 +#define RCC_APB1RSTR_TIM4RST 0x00000004 + +#define RCC_APB2RSTR_AFIORST 0x00000001 +#define RCC_APB2RSTR_IOPARST 0x00000004 +#define RCC_APB2RSTR_IOPBRST 0x00000008 +#define RCC_APB2RSTR_IOPCRST 0x00000010 +#define RCC_APB2RSTR_IOPDRST 0x00000020 +#define RCC_APB2RSTR_IOPERST 0x00000040 +#define RCC_APB2RSTR_IOPFRST 0x00000080 +#define RCC_APB2RSTR_IOPGRST 0x00000100 + +#define RCC_APB2ENR_AFIOEN 0x00000001 +#define RCC_APB2ENR_IOPAEN 0x00000004 +#define RCC_APB2ENR_IOPBEN 0x00000008 +#define RCC_APB2ENR_IOPCEN 0x00000010 +#define RCC_APB2ENR_IOPDEN 0x00000020 +#define RCC_APB2ENR_IOPEEN 0x00000040 +#define RCC_APB2ENR_IOPFEN 0x00000080 +#define RCC_APB2ENR_IOPGEN 0x00000100 +#endif + + +/* Clock setting values. + * Due to historical reason, it has the prefix of STM32_. + */ +#define STM32_SW_HSI (0 << 0) +#define STM32_SW_HSE (1 << 0) +#define STM32_SW_PLL (2 << 0) + +#define STM32_PLLSRC_HSI (0 << 16) +#define STM32_PLLSRC_HSE (1 << 16) + +#define STM32_PLLXTPRE_DIV1 (0 << 17) +#define STM32_PLLXTPRE_DIV2 (1 << 17) + +#define STM32_HPRE_DIV1 (0 << 4) +#define STM32_HPRE_DIV8 (10 << 4) +#define STM32_HPRE_DIV16 (11 << 4) + +#define STM32_PPRE1_DIV1 (0 << 8) +#define STM32_PPRE1_DIV2 (4 << 8) +#define STM32_PPRE1_DIV16 (7 << 8) + +#define STM32_PPRE2_DIV1 (0 << 11) +#define STM32_PPRE2_DIV2 (4 << 11) +#define STM32_PPRE2_DIV16 (7 << 11) + +#define STM32_ADCPRE_DIV4 (1 << 14) +#define STM32_ADCPRE_DIV6 (2 << 14) +#define STM32_ADCPRE_DIV8 (3 << 14) + +#define STM32_USBPRE_DIV1P5 (0 << 22) +#define STM32_USBPRE_DIV2 (3 << 22) /* Not for STM32, but GD32F103 */ + +#define STM32_MCO_NOCLOCK (0 << 24) + +struct PWR +{ + volatile uint32_t CR; + volatile uint32_t CSR; +}; +static struct PWR *const PWR = ((struct PWR *)0x40007000); +#define PWR_CR_LPDS 0x0001 /* Low-power deepsleep */ +#define PWR_CR_PDDS 0x0002 /* Power down deepsleep */ +#define PWR_CR_CWUF 0x0004 /* Clear wakeup flag */ + + +#if defined(MCU_STM32F0) +struct GPIO { + volatile uint32_t MODER; + volatile uint16_t OTYPER; + uint16_t dummy0; + volatile uint32_t OSPEEDR; + volatile uint32_t PUPDR; + volatile uint16_t IDR; + uint16_t dummy1; + volatile uint16_t ODR; + uint16_t dummy2; + volatile uint16_t BSRR; + uint16_t dummy3; + volatile uint32_t LCKR; + volatile uint32_t AFR[2]; + volatile uint16_t BRR; + uint16_t dummy4; +}; + +#define GPIOA_BASE (AHB2PERIPH_BASE + 0x0000) +#define GPIOA ((struct GPIO *) GPIOA_BASE) +#define GPIOB_BASE (AHB2PERIPH_BASE + 0x0400) +#define GPIOB ((struct GPIO *) GPIOB_BASE) +#define GPIOC_BASE (AHB2PERIPH_BASE + 0x0800) +#define GPIOC ((struct GPIO *) GPIOC_BASE) +#define GPIOD_BASE (AHB2PERIPH_BASE + 0x0C00) +#define GPIOD ((struct GPIO *) GPIOD_BASE) +#define GPIOF_BASE (AHB2PERIPH_BASE + 0x1400) +#define GPIOF ((struct GPIO *) GPIOF_BASE) +#else +struct GPIO { + volatile uint32_t CRL; + volatile uint32_t CRH; + volatile uint32_t IDR; + volatile uint32_t ODR; + volatile uint32_t BSRR; + volatile uint32_t BRR; + volatile uint32_t LCKR; +}; + +#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800) +#define GPIOA ((struct GPIO *) GPIOA_BASE) +#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00) +#define GPIOB ((struct GPIO *) GPIOB_BASE) +#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000) +#define GPIOC ((struct GPIO *) GPIOC_BASE) +#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400) +#define GPIOD ((struct GPIO *) GPIOD_BASE) +#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800) +#define GPIOE ((struct GPIO *) GPIOE_BASE) +#endif + + +#if defined(MCU_STM32F0) +struct SYSCFG { + volatile uint32_t CFGR1; + uint32_t dummy0; + volatile uint32_t EXTICR[4]; + volatile uint32_t CFGR2; +}; +#define SYSCFG_CFGR1_MEM_MODE 0x03 + +#define SYSCFG_BASE (APB1PERIPH_BASE + 0x00010000) +static struct SYSCFG *const SYSCFG = (struct SYSCFG *)SYSCFG_BASE; +#endif + +struct FLASH { + volatile uint32_t ACR; + volatile uint32_t KEYR; + volatile uint32_t OPTKEYR; + volatile uint32_t SR; + volatile uint32_t CR; + volatile uint32_t AR; + volatile uint32_t RESERVED; + volatile uint32_t OBR; + volatile uint32_t WRPR; +}; + +#define FLASH_R_BASE (AHBPERIPH_BASE + 0x2000) +static struct FLASH *const FLASH = (struct FLASH *)FLASH_R_BASE; diff --git a/gnuk/chopstx/mcu/stm32f103.h b/gnuk/chopstx/mcu/stm32f103.h new file mode 100644 index 0000000000..12cec0d022 --- /dev/null +++ b/gnuk/chopstx/mcu/stm32f103.h @@ -0,0 +1,647 @@ +#include +#include + +#define CRC_CR_RESET 0x00000001 + +struct CRC { + volatile uint32_t DR; + volatile uint8_t IDR; + uint8_t RESERVED0; + uint16_t RESERVED1; + volatile uint32_t CR; +}; + +#define CRC_BASE (AHBPERIPH_BASE + 0x3000) +static struct CRC *const CRC = (struct CRC *)CRC_BASE; + + +struct ADC { + volatile uint32_t SR; + volatile uint32_t CR1; + volatile uint32_t CR2; + volatile uint32_t SMPR1; + volatile uint32_t SMPR2; + volatile uint32_t JOFR1; + volatile uint32_t JOFR2; + volatile uint32_t JOFR3; + volatile uint32_t JOFR4; + volatile uint32_t HTR; + volatile uint32_t LTR; + volatile uint32_t SQR1; + volatile uint32_t SQR2; + volatile uint32_t SQR3; + volatile uint32_t JSQR; + volatile uint32_t JDR1; + volatile uint32_t JDR2; + volatile uint32_t JDR3; + volatile uint32_t JDR4; + volatile uint32_t DR; +}; + +#define ADC1_BASE (APB2PERIPH_BASE + 0x2400) +#define ADC2_BASE (APB2PERIPH_BASE + 0x2800) + +static struct ADC *const ADC1 = (struct ADC *)ADC1_BASE; +static struct ADC *const ADC2 = (struct ADC *)ADC2_BASE; + +#define ADC_CR1_DUALMOD_0 0x00010000 +#define ADC_CR1_DUALMOD_1 0x00020000 +#define ADC_CR1_DUALMOD_2 0x00040000 +#define ADC_CR1_DUALMOD_3 0x00080000 + +#define ADC_CR1_SCAN 0x00000100 + +#define ADC_CR2_ADON 0x00000001 +#define ADC_CR2_CONT 0x00000002 +#define ADC_CR2_CAL 0x00000004 +#define ADC_CR2_RSTCAL 0x00000008 +#define ADC_CR2_DMA 0x00000100 +#define ADC_CR2_ALIGN 0x00000800 +#define ADC_CR2_EXTSEL 0x000E0000 +#define ADC_CR2_EXTSEL_0 0x00020000 +#define ADC_CR2_EXTSEL_1 0x00040000 +#define ADC_CR2_EXTSEL_2 0x00080000 +#define ADC_CR2_EXTTRIG 0x00100000 +#define ADC_CR2_SWSTART 0x00400000 +#define ADC_CR2_TSVREFE 0x00800000 + +struct DMA_Channel { + volatile uint32_t CCR; + volatile uint32_t CNDTR; + volatile uint32_t CPAR; + volatile uint32_t CMAR; +}; + +struct DMA { + volatile uint32_t ISR; + volatile uint32_t IFCR; +}; + +#define STM32_DMA_CR_MINC DMA_CCR1_MINC +#define STM32_DMA_CR_MSIZE_WORD DMA_CCR1_MSIZE_1 +#define STM32_DMA_CR_PSIZE_WORD DMA_CCR1_PSIZE_1 +#define STM32_DMA_CR_TCIE DMA_CCR1_TCIE +#define STM32_DMA_CR_TEIE DMA_CCR1_TEIE +#define STM32_DMA_CR_HTIE DMA_CCR1_HTIE +#define STM32_DMA_ISR_TEIF DMA_ISR_TEIF1 +#define STM32_DMA_ISR_HTIF DMA_ISR_HTIF1 +#define STM32_DMA_ISR_TCIF DMA_ISR_TCIF1 + +#define STM32_DMA_ISR_MASK 0x0F +#define STM32_DMA_CCR_RESET_VALUE 0x00000000 +#define STM32_DMA_CR_PL_MASK DMA_CCR1_PL +#define STM32_DMA_CR_PL(n) ((n) << 12) + +#define DMA_CCR1_EN 0x00000001 +#define DMA_CCR1_TCIE 0x00000002 +#define DMA_CCR1_HTIE 0x00000004 +#define DMA_CCR1_TEIE 0x00000008 +#define DMA_CCR1_DIR 0x00000010 +#define DMA_CCR1_CIRC 0x00000020 +#define DMA_CCR1_PINC 0x00000040 +#define DMA_CCR1_MINC 0x00000080 +#define DMA_CCR1_PSIZE 0x00000300 +#define DMA_CCR1_PSIZE_0 0x00000100 +#define DMA_CCR1_PSIZE_1 0x00000200 +#define DMA_CCR1_MSIZE 0x00000C00 +#define DMA_CCR1_MSIZE_0 0x00000400 +#define DMA_CCR1_MSIZE_1 0x00000800 +#define DMA_CCR1_PL 0x00003000 +#define DMA_CCR1_PL_0 0x00001000 +#define DMA_CCR1_PL_1 0x00002000 +#define DMA_CCR1_MEM2MEM 0x00004000 + +#define DMA_ISR_GIF1 0x00000001 +#define DMA_ISR_TCIF1 0x00000002 +#define DMA_ISR_HTIF1 0x00000004 +#define DMA_ISR_TEIF1 0x00000008 +#define DMA_ISR_GIF2 0x00000010 +#define DMA_ISR_TCIF2 0x00000020 +#define DMA_ISR_HTIF2 0x00000040 +#define DMA_ISR_TEIF2 0x00000080 +#define DMA_ISR_GIF3 0x00000100 +#define DMA_ISR_TCIF3 0x00000200 +#define DMA_ISR_HTIF3 0x00000400 +#define DMA_ISR_TEIF3 0x00000800 +#define DMA_ISR_GIF4 0x00001000 +#define DMA_ISR_TCIF4 0x00002000 +#define DMA_ISR_HTIF4 0x00004000 +#define DMA_ISR_TEIF4 0x00008000 +#define DMA_ISR_GIF5 0x00010000 +#define DMA_ISR_TCIF5 0x00020000 +#define DMA_ISR_HTIF5 0x00040000 +#define DMA_ISR_TEIF5 0x00080000 +#define DMA_ISR_GIF6 0x00100000 +#define DMA_ISR_TCIF6 0x00200000 +#define DMA_ISR_HTIF6 0x00400000 +#define DMA_ISR_TEIF6 0x00800000 +#define DMA_ISR_GIF7 0x01000000 +#define DMA_ISR_TCIF7 0x02000000 +#define DMA_ISR_HTIF7 0x04000000 +#define DMA_ISR_TEIF7 0x08000000 + +#define DMA1_BASE (AHBPERIPH_BASE + 0x0000) +static struct DMA *const DMA1 = (struct DMA *)DMA1_BASE; + +#define DMA1_Channel1_BASE (AHBPERIPH_BASE + 0x0008) +static struct DMA_Channel *const DMA1_Channel1 = + (struct DMA_Channel *)DMA1_Channel1_BASE; + +/* Timer */ +struct TIM +{ + volatile uint16_t CR1; uint16_t RESERVED0; + volatile uint16_t CR2; uint16_t RESERVED1; + volatile uint16_t SMCR; uint16_t RESERVED2; + volatile uint16_t DIER; uint16_t RESERVED3; + volatile uint16_t SR; uint16_t RESERVED4; + volatile uint16_t EGR; uint16_t RESERVED5; + volatile uint16_t CCMR1; uint16_t RESERVED6; + volatile uint16_t CCMR2; uint16_t RESERVED7; + volatile uint16_t CCER; uint16_t RESERVED8; + volatile uint16_t CNT; uint16_t RESERVED9; + volatile uint16_t PSC; uint16_t RESERVED10; + volatile uint16_t ARR; uint16_t RESERVED11; + volatile uint16_t RCR; uint16_t RESERVED12; + volatile uint16_t CCR1; uint16_t RESERVED13; + volatile uint16_t CCR2; uint16_t RESERVED14; + volatile uint16_t CCR3; uint16_t RESERVED15; + volatile uint16_t CCR4; uint16_t RESERVED16; + volatile uint16_t BDTR; uint16_t RESERVED17; + volatile uint16_t DCR; uint16_t RESERVED18; + volatile uint16_t DMAR; uint16_t RESERVED19; +}; + +#define TIM2_BASE 0x40000000 +#define TIM3_BASE 0x40000400 +#define TIM4_BASE 0x40000800 +static struct TIM *const TIM2 = (struct TIM *)TIM2_BASE; +static struct TIM *const TIM3 = (struct TIM *)TIM3_BASE; +static struct TIM *const TIM4 = (struct TIM *)TIM4_BASE; + +#define TIM_CR1_CEN 0x0001 +#define TIM_CR1_UDIS 0x0002 +#define TIM_CR1_URS 0x0004 +#define TIM_CR1_OPM 0x0008 +#define TIM_CR1_DIR 0x0010 +#define TIM_CR1_CMS 0x0060 +#define TIM_CR1_CMS_0 0x0020 +#define TIM_CR1_CMS_1 0x0040 +#define TIM_CR1_ARPE 0x0080 +#define TIM_CR1_CKD 0x0300 +#define TIM_CR1_CKD_0 0x0100 +#define TIM_CR1_CKD_1 0x0200 + +#define TIM_CR2_CCPC 0x0001 +#define TIM_CR2_CCUS 0x0004 +#define TIM_CR2_CCDS 0x0008 +#define TIM_CR2_MMS 0x0070 +#define TIM_CR2_MMS_0 0x0010 +#define TIM_CR2_MMS_1 0x0020 +#define TIM_CR2_MMS_2 0x0040 +#define TIM_CR2_TI1S 0x0080 +#define TIM_CR2_OIS1 0x0100 +#define TIM_CR2_OIS1N 0x0200 +#define TIM_CR2_OIS2 0x0400 +#define TIM_CR2_OIS2N 0x0800 +#define TIM_CR2_OIS3 0x1000 +#define TIM_CR2_OIS3N 0x2000 +#define TIM_CR2_OIS4 0x4000 + +#define TIM_SMCR_SMS 0x0007 +#define TIM_SMCR_SMS_0 0x0001 +#define TIM_SMCR_SMS_1 0x0002 +#define TIM_SMCR_SMS_2 0x0004 +#define TIM_SMCR_TS 0x0070 +#define TIM_SMCR_TS_0 0x0010 +#define TIM_SMCR_TS_1 0x0020 +#define TIM_SMCR_TS_2 0x0040 +#define TIM_SMCR_MSM 0x0080 + +#define TIM_SMCR_ETF 0x0F00 +#define TIM_SMCR_ETF_0 0x0100 +#define TIM_SMCR_ETF_1 0x0200 +#define TIM_SMCR_ETF_2 0x0400 +#define TIM_SMCR_ETF_3 0x0800 + +#define TIM_SMCR_ETPS 0x3000 +#define TIM_SMCR_ETPS_0 0x1000 +#define TIM_SMCR_ETPS_1 0x2000 + +#define TIM_SMCR_ECE 0x4000 +#define TIM_SMCR_ETP 0x8000 + +#define TIM_DIER_UIE 0x0001 +#define TIM_DIER_CC1IE 0x0002 +#define TIM_DIER_CC2IE 0x0004 +#define TIM_DIER_CC3IE 0x0008 +#define TIM_DIER_CC4IE 0x0010 +#define TIM_DIER_COMIE 0x0020 +#define TIM_DIER_TIE 0x0040 +#define TIM_DIER_BIE 0x0080 +#define TIM_DIER_UDE 0x0100 +#define TIM_DIER_CC1DE 0x0200 +#define TIM_DIER_CC2DE 0x0400 +#define TIM_DIER_CC3DE 0x0800 +#define TIM_DIER_CC4DE 0x1000 +#define TIM_DIER_COMDE 0x2000 +#define TIM_DIER_TDE 0x4000 + +#define TIM_SR_UIF 0x0001 +#define TIM_SR_CC1IF 0x0002 +#define TIM_SR_CC2IF 0x0004 +#define TIM_SR_CC3IF 0x0008 +#define TIM_SR_CC4IF 0x0010 +#define TIM_SR_COMIF 0x0020 +#define TIM_SR_TIF 0x0040 +#define TIM_SR_BIF 0x0080 +#define TIM_SR_CC1OF 0x0200 +#define TIM_SR_CC2OF 0x0400 +#define TIM_SR_CC3OF 0x0800 +#define TIM_SR_CC4OF 0x1000 + +#define TIM_EGR_UG 0x01 +#define TIM_EGR_CC1G 0x02 +#define TIM_EGR_CC2G 0x04 +#define TIM_EGR_CC3G 0x08 +#define TIM_EGR_CC4G 0x10 +#define TIM_EGR_COMG 0x20 +#define TIM_EGR_TG 0x40 +#define TIM_EGR_BG 0x80 + +#define TIM_CCMR1_CC1S 0x0003 +#define TIM_CCMR1_CC1S_0 0x0001 +#define TIM_CCMR1_CC1S_1 0x0002 + +#define TIM_CCMR1_OC1FE 0x0004 +#define TIM_CCMR1_OC1PE 0x0008 + +#define TIM_CCMR1_OC1M 0x0070 +#define TIM_CCMR1_OC1M_0 0x0010 +#define TIM_CCMR1_OC1M_1 0x0020 +#define TIM_CCMR1_OC1M_2 0x0040 + +#define TIM_CCMR1_OC1CE 0x0080 + +#define TIM_CCMR1_CC2S 0x0300 +#define TIM_CCMR1_CC2S_0 0x0100 +#define TIM_CCMR1_CC2S_1 0x0200 + +#define TIM_CCMR1_OC2FE 0x0400 +#define TIM_CCMR1_OC2PE 0x0800 + +#define TIM_CCMR1_OC2M 0x7000 +#define TIM_CCMR1_OC2M_0 0x1000 +#define TIM_CCMR1_OC2M_1 0x2000 +#define TIM_CCMR1_OC2M_2 0x4000 + +#define TIM_CCMR1_OC2CE 0x8000 + + +#define TIM_CCMR1_IC1PSC 0x000C +#define TIM_CCMR1_IC1PSC_0 0x0004 +#define TIM_CCMR1_IC1PSC_1 0x0008 + +#define TIM_CCMR1_IC1F 0x00F0 +#define TIM_CCMR1_IC1F_0 0x0010 +#define TIM_CCMR1_IC1F_1 0x0020 +#define TIM_CCMR1_IC1F_2 0x0040 +#define TIM_CCMR1_IC1F_3 0x0080 + +#define TIM_CCMR1_IC2PSC 0x0C00 +#define TIM_CCMR1_IC2PSC_0 0x0400 +#define TIM_CCMR1_IC2PSC_1 0x0800 + +#define TIM_CCMR1_IC2F 0xF000 +#define TIM_CCMR1_IC2F_0 0x1000 +#define TIM_CCMR1_IC2F_1 0x2000 +#define TIM_CCMR1_IC2F_2 0x4000 +#define TIM_CCMR1_IC2F_3 0x8000 + +#define TIM_CCMR2_CC3S 0x0003 +#define TIM_CCMR2_CC3S_0 0x0001 +#define TIM_CCMR2_CC3S_1 0x0002 + +#define TIM_CCMR2_OC3FE 0x0004 +#define TIM_CCMR2_OC3PE 0x0008 + +#define TIM_CCMR2_OC3M 0x0070 +#define TIM_CCMR2_OC3M_0 0x0010 +#define TIM_CCMR2_OC3M_1 0x0020 +#define TIM_CCMR2_OC3M_2 0x0040 + +#define TIM_CCMR2_OC3CE 0x0080 + +#define TIM_CCMR2_CC4S 0x0300 +#define TIM_CCMR2_CC4S_0 0x0100 +#define TIM_CCMR2_CC4S_1 0x0200 + +#define TIM_CCMR2_OC4FE 0x0400 +#define TIM_CCMR2_OC4PE 0x0800 + +#define TIM_CCMR2_OC4M 0x7000 +#define TIM_CCMR2_OC4M_0 0x1000 +#define TIM_CCMR2_OC4M_1 0x2000 +#define TIM_CCMR2_OC4M_2 0x4000 + +#define TIM_CCMR2_OC4CE 0x8000 + + +#define TIM_CCMR2_IC3PSC 0x000C +#define TIM_CCMR2_IC3PSC_0 0x0004 +#define TIM_CCMR2_IC3PSC_1 0x0008 + +#define TIM_CCMR2_IC3F 0x00F0 +#define TIM_CCMR2_IC3F_0 0x0010 +#define TIM_CCMR2_IC3F_1 0x0020 +#define TIM_CCMR2_IC3F_2 0x0040 +#define TIM_CCMR2_IC3F_3 0x0080 + +#define TIM_CCMR2_IC4PSC 0x0C00 +#define TIM_CCMR2_IC4PSC_0 0x0400 +#define TIM_CCMR2_IC4PSC_1 0x0800 + +#define TIM_CCMR2_IC4F 0xF000 +#define TIM_CCMR2_IC4F_0 0x1000 +#define TIM_CCMR2_IC4F_1 0x2000 +#define TIM_CCMR2_IC4F_2 0x4000 +#define TIM_CCMR2_IC4F_3 0x8000 + +#define TIM_CCER_CC1E 0x0001 +#define TIM_CCER_CC1P 0x0002 +#define TIM_CCER_CC1NE 0x0004 +#define TIM_CCER_CC1NP 0x0008 +#define TIM_CCER_CC2E 0x0010 +#define TIM_CCER_CC2P 0x0020 +#define TIM_CCER_CC2NE 0x0040 +#define TIM_CCER_CC2NP 0x0080 +#define TIM_CCER_CC3E 0x0100 +#define TIM_CCER_CC3P 0x0200 +#define TIM_CCER_CC3NE 0x0400 +#define TIM_CCER_CC3NP 0x0800 +#define TIM_CCER_CC4E 0x1000 +#define TIM_CCER_CC4P 0x2000 + +#define TIM_CNT_CNT 0xFFFF + +#define TIM_PSC_PSC 0xFFFF + +#define TIM_ARR_ARR 0xFFFF + +#define TIM_RCR_REP 0xFF + +#define TIM_CCR1_CCR1 0xFFFF +#define TIM_CCR2_CCR2 0xFFFF +#define TIM_CCR3_CCR3 0xFFFF +#define TIM_CCR4_CCR4 0xFFFF + +#define TIM_BDTR_DTG 0x00FF +#define TIM_BDTR_DTG_0 0x0001 +#define TIM_BDTR_DTG_1 0x0002 +#define TIM_BDTR_DTG_2 0x0004 +#define TIM_BDTR_DTG_3 0x0008 +#define TIM_BDTR_DTG_4 0x0010 +#define TIM_BDTR_DTG_5 0x0020 +#define TIM_BDTR_DTG_6 0x0040 +#define TIM_BDTR_DTG_7 0x0080 + +#define TIM_BDTR_LOCK 0x0300 +#define TIM_BDTR_LOCK_0 0x0100 +#define TIM_BDTR_LOCK_1 0x0200 + +#define TIM_BDTR_OSSI 0x0400 +#define TIM_BDTR_OSSR 0x0800 +#define TIM_BDTR_BKE 0x1000 +#define TIM_BDTR_BKP 0x2000 +#define TIM_BDTR_AOE 0x4000 +#define TIM_BDTR_MOE 0x8000 + +#define TIM_DCR_DBA 0x001F +#define TIM_DCR_DBA_0 0x0001 +#define TIM_DCR_DBA_1 0x0002 +#define TIM_DCR_DBA_2 0x0004 +#define TIM_DCR_DBA_3 0x0008 +#define TIM_DCR_DBA_4 0x0010 + +#define TIM_DCR_DBL 0x1F00 +#define TIM_DCR_DBL_0 0x0100 +#define TIM_DCR_DBL_1 0x0200 +#define TIM_DCR_DBL_2 0x0400 +#define TIM_DCR_DBL_3 0x0800 +#define TIM_DCR_DBL_4 0x1000 + +#define TIM_DMAR_DMAB 0xFFFF + +struct EXTI +{ + volatile uint32_t IMR; + volatile uint32_t EMR; + volatile uint32_t RTSR; + volatile uint32_t FTSR; + volatile uint32_t SWIER; + volatile uint32_t PR; +}; + +#define EXTI_BASE 0x40010400 +static struct EXTI *const EXTI = (struct EXTI *)EXTI_BASE; + +#define EXTI_IMR_MR0 0x00000001 +#define EXTI_IMR_MR1 0x00000002 +#define EXTI_IMR_MR2 0x00000004 +#define EXTI_IMR_MR3 0x00000008 +#define EXTI_IMR_MR4 0x00000010 +#define EXTI_IMR_MR5 0x00000020 +#define EXTI_IMR_MR6 0x00000040 +#define EXTI_IMR_MR7 0x00000080 +#define EXTI_IMR_MR8 0x00000100 +#define EXTI_IMR_MR9 0x00000200 +#define EXTI_IMR_MR10 0x00000400 +#define EXTI_IMR_MR11 0x00000800 +#define EXTI_IMR_MR12 0x00001000 +#define EXTI_IMR_MR13 0x00002000 +#define EXTI_IMR_MR14 0x00004000 +#define EXTI_IMR_MR15 0x00008000 +#define EXTI_IMR_MR16 0x00010000 +#define EXTI_IMR_MR17 0x00020000 +#define EXTI_IMR_MR18 0x00040000 +#define EXTI_IMR_MR19 0x00080000 + +#define EXTI_EMR_MR0 0x00000001 +#define EXTI_EMR_MR1 0x00000002 +#define EXTI_EMR_MR2 0x00000004 +#define EXTI_EMR_MR3 0x00000008 +#define EXTI_EMR_MR4 0x00000010 +#define EXTI_EMR_MR5 0x00000020 +#define EXTI_EMR_MR6 0x00000040 +#define EXTI_EMR_MR7 0x00000080 +#define EXTI_EMR_MR8 0x00000100 +#define EXTI_EMR_MR9 0x00000200 +#define EXTI_EMR_MR10 0x00000400 +#define EXTI_EMR_MR11 0x00000800 +#define EXTI_EMR_MR12 0x00001000 +#define EXTI_EMR_MR13 0x00002000 +#define EXTI_EMR_MR14 0x00004000 +#define EXTI_EMR_MR15 0x00008000 +#define EXTI_EMR_MR16 0x00010000 +#define EXTI_EMR_MR17 0x00020000 +#define EXTI_EMR_MR18 0x00040000 +#define EXTI_EMR_MR19 0x00080000 + +#define EXTI_RTSR_TR0 0x00000001 +#define EXTI_RTSR_TR1 0x00000002 +#define EXTI_RTSR_TR2 0x00000004 +#define EXTI_RTSR_TR3 0x00000008 +#define EXTI_RTSR_TR4 0x00000010 +#define EXTI_RTSR_TR5 0x00000020 +#define EXTI_RTSR_TR6 0x00000040 +#define EXTI_RTSR_TR7 0x00000080 +#define EXTI_RTSR_TR8 0x00000100 +#define EXTI_RTSR_TR9 0x00000200 +#define EXTI_RTSR_TR10 0x00000400 +#define EXTI_RTSR_TR11 0x00000800 +#define EXTI_RTSR_TR12 0x00001000 +#define EXTI_RTSR_TR13 0x00002000 +#define EXTI_RTSR_TR14 0x00004000 +#define EXTI_RTSR_TR15 0x00008000 +#define EXTI_RTSR_TR16 0x00010000 +#define EXTI_RTSR_TR17 0x00020000 +#define EXTI_RTSR_TR18 0x00040000 +#define EXTI_RTSR_TR19 0x00080000 + +#define EXTI_FTSR_TR0 0x00000001 +#define EXTI_FTSR_TR1 0x00000002 +#define EXTI_FTSR_TR2 0x00000004 +#define EXTI_FTSR_TR3 0x00000008 +#define EXTI_FTSR_TR4 0x00000010 +#define EXTI_FTSR_TR5 0x00000020 +#define EXTI_FTSR_TR6 0x00000040 +#define EXTI_FTSR_TR7 0x00000080 +#define EXTI_FTSR_TR8 0x00000100 +#define EXTI_FTSR_TR9 0x00000200 +#define EXTI_FTSR_TR10 0x00000400 +#define EXTI_FTSR_TR11 0x00000800 +#define EXTI_FTSR_TR12 0x00001000 +#define EXTI_FTSR_TR13 0x00002000 +#define EXTI_FTSR_TR14 0x00004000 +#define EXTI_FTSR_TR15 0x00008000 +#define EXTI_FTSR_TR16 0x00010000 +#define EXTI_FTSR_TR17 0x00020000 +#define EXTI_FTSR_TR18 0x00040000 +#define EXTI_FTSR_TR19 0x00080000 + +#define EXTI_SWIER_SWIER0 0x00000001 +#define EXTI_SWIER_SWIER1 0x00000002 +#define EXTI_SWIER_SWIER2 0x00000004 +#define EXTI_SWIER_SWIER3 0x00000008 +#define EXTI_SWIER_SWIER4 0x00000010 +#define EXTI_SWIER_SWIER5 0x00000020 +#define EXTI_SWIER_SWIER6 0x00000040 +#define EXTI_SWIER_SWIER7 0x00000080 +#define EXTI_SWIER_SWIER8 0x00000100 +#define EXTI_SWIER_SWIER9 0x00000200 +#define EXTI_SWIER_SWIER10 0x00000400 +#define EXTI_SWIER_SWIER11 0x00000800 +#define EXTI_SWIER_SWIER12 0x00001000 +#define EXTI_SWIER_SWIER13 0x00002000 +#define EXTI_SWIER_SWIER14 0x00004000 +#define EXTI_SWIER_SWIER15 0x00008000 +#define EXTI_SWIER_SWIER16 0x00010000 +#define EXTI_SWIER_SWIER17 0x00020000 +#define EXTI_SWIER_SWIER18 0x00040000 +#define EXTI_SWIER_SWIER19 0x00080000 + +#define EXTI_PR_PR0 0x00000001 +#define EXTI_PR_PR1 0x00000002 +#define EXTI_PR_PR2 0x00000004 +#define EXTI_PR_PR3 0x00000008 +#define EXTI_PR_PR4 0x00000010 +#define EXTI_PR_PR5 0x00000020 +#define EXTI_PR_PR6 0x00000040 +#define EXTI_PR_PR7 0x00000080 +#define EXTI_PR_PR8 0x00000100 +#define EXTI_PR_PR9 0x00000200 +#define EXTI_PR_PR10 0x00000400 +#define EXTI_PR_PR11 0x00000800 +#define EXTI_PR_PR12 0x00001000 +#define EXTI_PR_PR13 0x00002000 +#define EXTI_PR_PR14 0x00004000 +#define EXTI_PR_PR15 0x00008000 +#define EXTI_PR_PR16 0x00010000 +#define EXTI_PR_PR17 0x00020000 +#define EXTI_PR_PR18 0x00040000 +#define EXTI_PR_PR19 0x00080000 + +#define EXTI0_IRQ 6 +#define EXTI1_IRQ 7 +#define EXTI2_IRQ 8 +#define EXTI3_IRQ 9 +#define EXTI4_IRQ 10 +#define EXTI9_5_IRQ 23 +#define TIM2_IRQ 28 +#define TIM3_IRQ 29 +#define TIM4_IRQ 30 + +struct AFIO +{ + volatile uint32_t EVCR; + volatile uint32_t MAPR; + volatile uint32_t EXTICR[4]; + uint32_t RESERVED0; + volatile uint32_t MAPR2; +}; + +#define AFIO_BASE 0x40010000 +static struct AFIO *const AFIO = (struct AFIO *)AFIO_BASE; + +#define AFIO_EXTICR1_EXTI0_PA 0x0000 +#define AFIO_EXTICR1_EXTI0_PB 0x0001 +#define AFIO_EXTICR1_EXTI0_PC 0x0002 +#define AFIO_EXTICR1_EXTI0_PD 0x0003 + +#define AFIO_EXTICR1_EXTI1_PA 0x0000 +#define AFIO_EXTICR1_EXTI1_PB 0x0010 +#define AFIO_EXTICR1_EXTI1_PC 0x0020 +#define AFIO_EXTICR1_EXTI1_PD 0x0030 + +#define AFIO_EXTICR1_EXTI2_PA 0x0000 +#define AFIO_EXTICR1_EXTI2_PB 0x0100 +#define AFIO_EXTICR1_EXTI2_PC 0x0200 +#define AFIO_EXTICR1_EXTI2_PD 0x0300 + +#define AFIO_EXTICR1_EXTI3_PA 0x0000 +#define AFIO_EXTICR1_EXTI3_PB 0x1000 +#define AFIO_EXTICR1_EXTI3_PC 0x2000 +#define AFIO_EXTICR1_EXTI3_PD 0x3000 + +#define AFIO_EXTICR2_EXTI4_PA 0x0000 +#define AFIO_EXTICR2_EXTI4_PB 0x0001 +#define AFIO_EXTICR2_EXTI4_PC 0x0002 +#define AFIO_EXTICR2_EXTI4_PD 0x0003 + +#define AFIO_EXTICR2_EXTI5_PA 0x0000 +#define AFIO_EXTICR2_EXTI5_PB 0x0010 +#define AFIO_EXTICR2_EXTI5_PC 0x0020 +#define AFIO_EXTICR2_EXTI5_PD 0x0030 + +#define AFIO_EXTICR2_EXTI6_PA 0x0000 +#define AFIO_EXTICR2_EXTI6_PB 0x0100 +#define AFIO_EXTICR2_EXTI6_PC 0x0200 +#define AFIO_EXTICR2_EXTI6_PD 0x0300 + +#define AFIO_EXTICR2_EXTI7_PA 0x0000 +#define AFIO_EXTICR2_EXTI7_PB 0x1000 +#define AFIO_EXTICR2_EXTI7_PC 0x2000 +#define AFIO_EXTICR2_EXTI7_PD 0x3000 + +#define AFIO_MAPR_TIM3_REMAP_PARTIALREMAP 0x00000800 +#define AFIO_MAPR_SWJ_CFG_DISABLE 0x04000000 +#define AFIO_MAPR_SWJ_CFG_JTAGDISABLE 0x02000000 + + +struct DBGMCU { + volatile uint32_t CR; +}; + +#define DBGMCU_BASE 0xE0042004 +#define DBG_SLEEP 1 +static struct DBGMCU *const DBGMCU = (struct DBGMCU *)DBGMCU_BASE; diff --git a/gnuk/chopstx/mcu/sys-gnu-linux.c b/gnuk/chopstx/mcu/sys-gnu-linux.c new file mode 100644 index 0000000000..332003e011 --- /dev/null +++ b/gnuk/chopstx/mcu/sys-gnu-linux.c @@ -0,0 +1,251 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "board.h" +#include "sys.h" + +const uint8_t sys_version[8] = { + 3*2+2, /* bLength */ + 0x03, /* bDescriptorType = USB_STRING_DESCRIPTOR_TYPE */ + /* sys version: "3.0" */ + '3', 0, '.', 0, '0', 0, +}; + +#if defined(USE_SYS3) || defined(USE_SYS_BOARD_ID) +const uint32_t sys_board_id = BOARD_ID; +const uint8_t sys_board_name[] = BOARD_NAME; +#endif + +int debug; + +void +set_led (int on) +{ + if ((debug & DEBUG_LED)) + puts (on ? "*": ""); +} + +static const char *flash_path; +static size_t flash_size; +static void * flash_addr; +static int flash_fd; + +uintptr_t +flash_init (const char *f_name) +{ + int fd; + struct stat sb; + void *addr; + + fd = open (f_name, O_RDONLY); + if (fd < 0) + { + perror ("flash_init: open"); + exit (1); + } + + if (fstat (fd, &sb) < 0) + { + perror ("flash_init: fstat"); + exit (1); + } + + addr = mmap (NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) + { + perror ("flash_init: mmap"); + exit (1); + } + + if (close (fd) < 0) + { + perror ("flash_init: close"); + exit (1); + } + + flash_path = f_name; + flash_addr = addr; + flash_size = sb.st_size; + + return (uintptr_t)addr; +} + +void +flash_unlock (void) +{ + int fd; + + fd = open (flash_path, O_WRONLY); + if (fd < 0) + { + perror ("flash_unlock: open"); + exit (1); + } + flash_fd = fd; +} + +int +flash_program_halfword (uintptr_t addr, uint16_t data) +{ + off_t offset; + char buf[2]; + + if ((debug & DEBUG_FLASH)) + fprintf (stderr, "flash_program_halfword: addr=%016lx, data=%04x\n", + addr, data); + offset = (off_t)(addr - (uintptr_t)flash_addr); + if (offset < 0 || offset >= (off_t)flash_size) + { + perror ("flash_program_halfword"); + return 1; + } + + offset = lseek (flash_fd, offset, SEEK_SET); + if (offset == (off_t)-1) + { + perror ("flash_program_halfword"); + return 1; + } + buf[0] = (data & 0xff); + buf[1] = (data >> 8); + if (write (flash_fd, buf, 2) != 2) + { + perror ("flash_program_halfword"); + return 2; + } + return 0; +} + +static const uint8_t erased[] = { [0 ... 1023 ] = 0xff }; + +int +flash_erase_page (uintptr_t addr) +{ + off_t offset; + + if ((debug & DEBUG_FLASH)) + fprintf (stderr, "flash_erase_page: addr=%016lx\n", addr); + + offset = (off_t)(addr - (uintptr_t)flash_addr); + if (offset < 0 || offset >= (off_t)flash_size) + { + perror ("flash_erase_page"); + return 1; + } + + offset = lseek (flash_fd, offset, SEEK_SET); + if (offset == (off_t)-1) + { + perror ("flash_erase_page"); + return 1; + } + + if (write (flash_fd, erased, sizeof (erased)) != sizeof (erased)) + { + perror ("flash_erase_page"); + return 2; + } + return 0; +} + +int +flash_check_blank (const uint8_t *p_start, size_t size) +{ + const uint8_t *p; + + if (p_start < (const uint8_t *)flash_addr + || p_start + size > (const uint8_t *)flash_addr + flash_size) + { + perror ("flash_check_blank"); + return 0; + } + + for (p = p_start; p < p_start + size; p++) + if (*p != 0xff) + return 0; + + return 1; +} + +int +flash_write (uintptr_t dst_addr, const uint8_t *src, size_t len) +{ + off_t offset; + + if ((debug & DEBUG_FLASH)) + fprintf (stderr, "flash_write: addr=%016lx, %p, %zd\n", + dst_addr, src, len); + + offset = (off_t)(dst_addr - (uintptr_t)flash_addr); + if (offset < 0 || offset >= (off_t)flash_size) + { + perror ("flash_write"); + return 1; + } + + offset = lseek (flash_fd, offset, SEEK_SET); + if (offset == (off_t)-1) + { + perror ("flash_write"); + return 1; + } + + if (write (flash_fd, src, len) != (ssize_t)len) + { + perror ("flash_write"); + return 2; + } + return 0; +} + +int +flash_protect (void) +{ + if ((debug & DEBUG_FLASH)) + fprintf (stderr, "flash_protect\n"); + return 0; +} + +void __attribute__((noreturn)) +flash_erase_all_and_exec (void (*entry)(void)) +{ + (void)entry; + exit (1); +} + +void +usb_lld_sys_init (void) +{ +} + +void +usb_lld_sys_shutdown (void) +{ +} + + +void __attribute__((noreturn)) +nvic_system_reset (void) +{ + exit (0); +} + + +void +clock_init (void) +{ +} + +void +gpio_init (void) +{ +} diff --git a/gnuk/chopstx/mcu/sys-gnu-linux.h b/gnuk/chopstx/mcu/sys-gnu-linux.h new file mode 100644 index 0000000000..ea8a64dfc7 --- /dev/null +++ b/gnuk/chopstx/mcu/sys-gnu-linux.h @@ -0,0 +1,52 @@ +extern const uint8_t sys_version[8]; +#if defined(USE_SYS3) || defined(USE_SYS_BOARD_ID) +extern const uint32_t sys_board_id; +extern const uint8_t sys_board_name[]; +# define SYS_BOARD_ID sys_board_id +#else +# define SYS_BOARD_ID BOARD_ID +#endif + +#define DEBUG_LED (1 << 1) +#define DEBUG_FLASH (1 << 2) +#define DEBUG_USB (1 << 3) + +extern int debug; + +static inline const uint8_t * +unique_device_id (void) +{ + /* + * STM32F103 has 96-bit unique device identifier. + * This routine mimics that. + */ + + static const uint8_t id[] = { /* My RSA fingerprint */ + 0x12, 0x41, 0x24, 0xBD, 0x3B, 0x48, 0x62, 0xAF, + 0x7A, 0x0A, 0x42, 0xF1, 0x00, 0xB4, 0x5E, 0xBD, + 0x4C, 0xA7, 0xBA, 0xBE + }; + + return id; +} + +void set_led (int on); + +uintptr_t flash_init (const char *f_name); +void flash_unlock (void); +int flash_program_halfword (uintptr_t addr, uint16_t data); +int flash_erase_page (uintptr_t addr); +int flash_check_blank (const uint8_t *p_start, size_t size); +int flash_write (uintptr_t dst_addr, const uint8_t *src, size_t len); +int flash_protect (void); +void __attribute__((noreturn)) +flash_erase_all_and_exec (void (*entry)(void)); + +void usb_lld_sys_init (void); +void usb_lld_sys_shutdown (void); + +void __attribute__((noreturn)) +nvic_system_reset (void); + +void clock_init (void); +void gpio_init (void); diff --git a/gnuk/chopstx/mcu/sys-mkl27z.c b/gnuk/chopstx/mcu/sys-mkl27z.c new file mode 100644 index 0000000000..cef68988a1 --- /dev/null +++ b/gnuk/chopstx/mcu/sys-mkl27z.c @@ -0,0 +1,414 @@ +/* + * sys.c - First pages for MKL27Z256. + * + * Copyright (C) 2016 Flying Stone Technology + * Author: NIIBE Yutaka + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. This file is offered as-is, + * without any warranty. + * + * + * First two pages of Flash ROM is difficult to use because of + * predefined purposes. It's defined as a default vector page and + * a flash configuration page. + * + * We put something useful to those two pages, together with the + * data for predefined purposes. + */ + +#include +#include +#include "board.h" + +#define ADDR_VECTORS (0x00000900) +#define ADDR_SCB_VTOR 0xe000ed08 + +static void __attribute__ ((naked,section(".fixed_function.reset"))) +reset (void) +{ + uint32_t r3 = ADDR_SCB_VTOR; + + asm volatile ("str %2, [%0]\n\t" /* Set SCB->VTOR */ + "ldr %0, [%2]\n\t" /* Stack address */ + "msr MSP, %0\n\t" /* Exception handler stack. */ + "ldr %0, [%2, #4]\n\t" /* The entry address */ + "bx %0\n\t" /* Jump to the entry */ + ".align 2" + : "=r" (r3) + : "0" (r3), "r" (ADDR_VECTORS) + : "memory"); + + /* Never reach here. */ +} + + +static uint32_t +stack_entry[] __attribute__ ((section(".first_page.first_words"),used)) = { + /* Since MSP are soon modified in RESET, we put 0 here. */ + 0, + (uint32_t)reset, +}; + +#include "mcu/clk_gpio_init-mkl27z.c" + +static void +set_led (int on) +{ + if (on) + GPIOB->PCOR = (1 << 0); /* PTB0: Clear: Light on */ + else + GPIOB->PSOR = (1 << 0); /* PTB0: Set : Light off */ +} + +/* + * Here comes other SYS routines and data. + */ + +const uint8_t sys_version[8] __attribute__((section(".sys.version"))) = { + 3*2+2, /* bLength */ + 0x03, /* bDescriptorType = STRING_DESCRIPTOR */ + /* sys version: "3.0" */ + '3', 0, '.', 0, '0', 0, +}; + +static const uint8_t board_name_string[] = BOARD_NAME; + +const uint8_t __attribute__((section(".sys.board_info"))) +*const sys_board_name = board_name_string; + +const uint32_t __attribute__((section(".sys.board_info"))) +sys_board_id = BOARD_ID; + +typedef void (*handler)(void); + +handler sys_vector[] __attribute__ ((section(".sys.vectors"))) = { + clock_init, + gpio_init, + (handler)set_led, + NULL, +}; + +static uint32_t +flash_config[] __attribute__ ((section(".flash_config"),used)) = { + 0xffffffff, 0xffffffff, /* Comparison Key */ + 0xffffffff, /* Protection bytes */ + 0xffff3ffe, /* FSEC=0xfe, FOPT=0x3f */ + /* FOPT=0x3f: + * BOOTSRC_SEL=00: Boot from flash + */ + /* FSEC=0xfe: + * unsecure + */ +}; + + +/* + * Flash memory routine + */ +struct FTFA { + volatile uint8_t FSTAT; + volatile uint8_t FCNFG; + volatile uint8_t FSEC; + volatile uint8_t FOPT; + /* Note: addressing (3,2,1,0, 7,6,5,4, B,A,9,8) */ + /* Use Bx macro. */ + volatile uint8_t FCCO[12]; + /* Note: addressing (3,2,1,0). Use Bx macro. */ + volatile uint8_t FPROT[4]; +}; +static struct FTFA *const FTFA = (struct FTFA *)0x40020000; + +#define FSTAT_CCIF 0x80 +#define B3 0 +#define B2 1 +#define B1 2 +#define B0 3 +#define B7 4 +#define B6 5 +#define B5 6 +#define B4 7 +#define BB 8 +#define BA 9 +#define B9 10 +#define B8 11 + +uint32_t __attribute__ ((naked,section(".fixed_function.flash_do_internal"))) +flash_do_internal (void) +{ +#ifdef ORIGINAL_IN_C + uint8_t r; + + asm volatile ("cpsid i" : : : "memory"); + FTFA->FSTAT |= FSTAT_CCIF; + while (((r = FTFA->FSTAT) & FSTAT_CCIF) == 0) + ; + r &= ~FSTAT_CCIF; + asm volatile ("cpsie i" : : : "memory"); + + return (uint32_t)r; +#else + register unsigned int r0 asm ("r0"); + register unsigned int r1 asm ("r1") = (unsigned int)FTFA; + register unsigned int r2 asm ("r2") = FSTAT_CCIF; + + asm volatile ("cpsid i\n\t" + "ldrb %0, [%1]\n\t" + "orr %0, %2\n\t" + "strb %0, [%1]\n" + "0:\t" + "ldrb %0, [%1]\n\t" + "uxtb %0, %0\n\t" + "tst %0, %2\n\t" + "beq 0b\n\t" + "cpsie i\n\t" + "bic %0, %2\n\t" + "bx lr" + : "=r" (r0) + : "r" (r1), "r" (r2) + : "memory"); + return r0; +#endif +} + +/* + * Let execute flash command. + * Since the code should be on RAM, we copy the code onto stack + * and let it go. + */ +uint32_t __attribute__ ((naked,section(".fixed_function.flash_do"))) +flash_do (void) +{ + register unsigned int r0 asm ("r0"); + register unsigned int r1 asm ("r1"); + register unsigned int r2 asm ("r2"); + register unsigned int r3 asm ("r3") = (unsigned int)flash_do_internal&~3; + /* Address of Thumb code &1 == 1, so, we clear the last bits. ------^ */ + + asm volatile ("sub sp, #32\n\t" + "mov %1, sp\n\t" + "mov %2, #0\n" + "0:\t" + "cmp %2, #32\n\t" + "beq 1f\n\t" + "ldr %0, [%3, %2]\n\t" + "str %0, [%1, %2]\n\t" + "add %2, #4\n\t" + "b 0b\n" + "1:\t" + "add %1, #1\n\t" /* Thumb code requires LSB=1. */ + "mov %3, lr\n\t" + "blx %1\n\t" + "add sp, #32\n\t" + "bx %3" + : "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3) + : "3" (r3)); +} + +#define FLASH_COMMAND_PROGRAM_LONGWORD 0x06 +#define FLASH_COMMAND_ERASE_FLASH_SECTOR 0x09 + +int __attribute__ ((naked,section(".fixed_function.flash_erase_page"))) +flash_erase_page (uint32_t addr) +{ +#ifdef ORIGINAL_IN_C + FTFA->FCCO[B0] = FLASH_COMMAND_ERASE_FLASH_SECTOR; + FTFA->FCCO[B3] = (addr >> 0) & 0xff; + FTFA->FCCO[B2] = (addr >> 8) & 0xff; + FTFA->FCCO[B1] = (addr >> 16) & 0xff; + flash_do (); +#else + register unsigned int r0 asm ("r0") = addr; + register unsigned int r1 asm ("r1"); + register unsigned int r2 asm ("r2") = FLASH_COMMAND_ERASE_FLASH_SECTOR; + register unsigned int r3 asm ("r3") = (unsigned int)FTFA; + + asm volatile ("strb %2, [%3, #7]\n\t" + "strb %0, [%3, #4]\n\t" + "lsr %0, #8\n\t" + "strb %0, [%3, #5]\n\t" + "lsr %0, #8\n\t" + "strb %0, [%3, #6]\n\t" + "b flash_do" + : "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3) + : "0" (r0), "2" (r2), "3" (r3)); +#endif +} + +int __attribute__ ((naked,section(".fixed_function.flash_program_word"))) +flash_program_word (uint32_t addr, uint32_t word) +{ +#ifdef ORIGINAL_IN_C + FTFA->FCCO[B0] = FLASH_COMMAND_PROGRAM_LONGWORD; + FTFA->FCCO[B3] = (addr >> 0) & 0xff; + FTFA->FCCO[B2] = (addr >> 8) & 0xff; + FTFA->FCCO[B1] = (addr >> 16) & 0xff; + FTFA->FCCO[B4] = (word >> 0) & 0xff; + FTFA->FCCO[B5] = (word >> 8) & 0xff; + FTFA->FCCO[B6] = (word >> 16) & 0xff; + FTFA->FCCO[B7] = (word >> 24) & 0xff; + flash_do (); +#else + register unsigned int r0 asm ("r0") = addr; + register unsigned int r1 asm ("r1") = word; + register unsigned int r2 asm ("r2") = FLASH_COMMAND_PROGRAM_LONGWORD; + register unsigned int r3 asm ("r3") = (unsigned int)FTFA; + + asm volatile ("strb %2, [%3, #7]\n\t" + "strb %0, [%3, #4]\n\t" + "lsr %0, #8\n\t" + "strb %0, [%3, #5]\n\t" + "lsr %0, #8\n\t" + "strb %0, [%3, #6]\n\t" + "strb %1, [%3, #11]\n\t" + "lsr %1, #8\n\t" + "strb %1, [%3, #10]\n\t" + "lsr %1, #8\n\t" + "strb %1, [%3, #9]\n\t" + "lsr %1, #8\n\t" + "strb %1, [%3, #8]\n\t" + "b flash_do" + : "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3) + : "0" (r0), "1" (r1), "2" (r2), "3" (r3)); +#endif +} + + +/* + * CRC32 calculation routines. + */ +void __attribute__ ((naked,section(".fixed_function.crc32_init"))) +crc32_init (unsigned int *p) +{ +#ifdef ORIGINAL_IN_C + *p = 0xffffffff; +#else + register unsigned int r3 asm ("r3"); + + asm volatile ("mov %0, #1\n\t" + "neg %0, %0\n\t" + "str %0, [%1]\n\t" + "bx lr" + : "=r" (r3) + : "r" (p) + : "memory"); +#endif +} + +#ifdef ORIGINAL_IN_C +const unsigned int *const crc32_table= (const unsigned int *)0x00000500; +#endif + +void __attribute__ ((naked,section(".fixed_function.crc32_u8"))) +crc32_u8 (unsigned int *p, unsigned char v) +{ +#ifdef ORIGINAL_IN_C + *p = crc32_table[(*p & 0xff) ^ v] ^ (*p >> 8); +#else + register unsigned int r2 asm ("r2"); + register unsigned int r3 asm ("r3"); + + asm volatile ("ldrb %2, [%4]\n\t" + "eor %0, %2\n\t" + "mov %2, #0xa0\n\t" /* (0x0500 >> 3) */ + "lsl %0, %0, #2\n\t" + "lsl %2, %2, #3\n\t" + "add %0, %0, %2\n\t" + "ldr %2, [%4]\n\t" + "ldr %1, [%0]\n\t" + "lsr %2, %2, #8\n\t" + "eor %2, %1\n\t" + "str %2, [%4]\n\t" + "bx lr" + : "=r" (v), "=r" (r2), "=r" (r3) + : "0" (v), "r" (p) + : "memory"); +#endif +} + +void __attribute__ ((naked,section(".fixed_function.crc32_u32"))) +crc32_u32 (unsigned int *p, unsigned int u) +{ +#ifdef ORIGINAL_IN_C + crc32_u8 (p, u & 0xff); + crc32_u8 (p, (u >> 8)& 0xff); + crc32_u8 (p, (u >> 16)& 0xff); + crc32_u8 (p, (u >> 24)& 0xff); +#else + register unsigned int r3 asm ("r3"); + register unsigned int r4 asm ("r4"); + register unsigned int r5 asm ("r5"); + + asm volatile ("push {%1, %2, %3, lr}\n\t" + "mov %2, %0\n\t" + "mov %3, %5\n\t" + "uxtb %0, %0\n\t" + "bl crc32_u8\n\t" + "lsr %0, %2, #8\n\t" + "mov %5, %3\n\t" + "uxtb %0, %0\n\t" + "bl crc32_u8\n\t" + "lsr %0, %2, #16\n\t" + "mov %5, %3\n\t" + "uxtb %0, %0\n\t" + "bl crc32_u8\n\t" + "mov %5, %3\n\t" + "lsr %0, %2, #24\n\t" + "bl crc32_u8\n\t" + "pop {%1, %2, %3, pc}" + : "=r" (u), "=r" (r3), "=r" (r4), "=r" (r5) + : "0" (u), "r" (p) + : "memory"); +#endif +} + +/* + * Table of CRC32, generated by gen_crc_table.py + */ +const unsigned int +crc32_table[256] __attribute__ ((section(".crc32_table"))) = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; diff --git a/gnuk/chopstx/mcu/sys-mkl27z.h b/gnuk/chopstx/mcu/sys-mkl27z.h new file mode 100644 index 0000000000..855cc62c94 --- /dev/null +++ b/gnuk/chopstx/mcu/sys-mkl27z.h @@ -0,0 +1,39 @@ +extern const uint8_t sys_version[8]; +#if defined(USE_SYS3) || defined(USE_SYS_BOARD_ID) +extern const uint32_t sys_board_id; +extern const char *const sys_board_name; +#endif + +typedef void (*handler)(void); +extern handler sys_vector[16]; + +static inline void +set_led (int on) +{ + void (*func) (int) = (void (*)(int))sys_vector[2]; + + return (*func) (on); +} + +void crc32_init (unsigned int *); +void crc32_u8 (unsigned int *, unsigned char); +void crc32_u32 (unsigned int *, unsigned int); + +int flash_erase_page (uint32_t addr); +int flash_program_word (uint32_t addr, uint32_t word); + +#ifdef REQUIRE_CLOCK_GPIO_SETTING_IN_SYS +/* Provide the function entries. */ + +static void __attribute__ ((used)) +clock_init (void) +{ + (*sys_vector[0]) (); +} + +static void __attribute__ ((used)) +gpio_init (void) +{ + (*sys_vector[1]) (); +} +#endif diff --git a/gnuk/chopstx/mcu/sys-stm32f0.c b/gnuk/chopstx/mcu/sys-stm32f0.c new file mode 100644 index 0000000000..cd3f60edca --- /dev/null +++ b/gnuk/chopstx/mcu/sys-stm32f0.c @@ -0,0 +1,397 @@ +/* + * sys-stm32f0.c - system routines for the initial page for STM32F030. + * + * Copyright (C) 2013, 2014, 2015, 2016, 2017 + * Flying Stone Technology + * Author: NIIBE Yutaka + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. This file is offered as-is, + * without any warranty. + * + * When the flash ROM is protected, we cannot modify the initial page. + * We put some system routines (which is useful for any program) here. + */ + +#include +#include +#include "board.h" + +#define STM32F0_USE_VECTOR_ON_RAM +#include "mcu/cortex-m.h" +#include "mcu/clk_gpio_init-stm32.c" + + +static void +usb_cable_config (int enable) +{ +#if defined(GPIO_USB_SET_TO_ENABLE) + if (enable) + GPIO_USB->BSRR = (1 << GPIO_USB_SET_TO_ENABLE); + else + GPIO_USB->BRR = (1 << GPIO_USB_SET_TO_ENABLE); +#elif defined(GPIO_USB_CLEAR_TO_ENABLE) + if (enable) + GPIO_USB->BRR = (1 << GPIO_USB_CLEAR_TO_ENABLE); + else + GPIO_USB->BSRR = (1 << GPIO_USB_CLEAR_TO_ENABLE); +#else + (void)enable; +#endif +} + +void +set_led (int on) +{ +#if defined(GPIO_LED_CLEAR_TO_EMIT) + if (on) + GPIO_LED->BRR = (1 << GPIO_LED_CLEAR_TO_EMIT); + else + GPIO_LED->BSRR = (1 << GPIO_LED_CLEAR_TO_EMIT); +#else + if (on) + GPIO_LED->BSRR = (1 << GPIO_LED_SET_TO_EMIT); + else + GPIO_LED->BRR = (1 << GPIO_LED_SET_TO_EMIT); +#endif +} + +static void wait (int count) +{ + int i; + + for (i = 0; i < count; i++) + asm volatile ("" : : "r" (i) : "memory"); +} + + +static void +usb_lld_sys_shutdown (void) +{ + RCC->APB1ENR &= ~RCC_APB1ENR_USBEN; + RCC->APB1RSTR = RCC_APB1RSTR_USBRST; + usb_cable_config (0); +} + +static void +usb_lld_sys_init (void) +{ + if ((RCC->APB1ENR & RCC_APB1ENR_USBEN) + && (RCC->APB1RSTR & RCC_APB1RSTR_USBRST) == 0) + /* Make sure the device is disconnected, even after core reset. */ + { + usb_lld_sys_shutdown (); + /* Disconnect requires SE0 (>= 2.5uS). */ + wait (300); + } + + usb_cable_config (1); + RCC->APB1ENR |= RCC_APB1ENR_USBEN; + RCC->APB1RSTR = RCC_APB1RSTR_USBRST; + RCC->APB1RSTR = 0; +} + +#define FLASH_KEY1 0x45670123UL +#define FLASH_KEY2 0xCDEF89ABUL + +enum flash_status +{ + FLASH_BUSY = 1, + FLASH_ERROR_PG, + FLASH_ERROR_WRP, + FLASH_COMPLETE, + FLASH_TIMEOUT +}; + +static void __attribute__ ((used)) +flash_unlock (void) +{ + FLASH->KEYR = FLASH_KEY1; + FLASH->KEYR = FLASH_KEY2; +} + + +#define intr_disable() asm volatile ("cpsid i" : : : "memory") +#define intr_enable() asm volatile ("cpsie i" : : : "memory") + +#define FLASH_SR_BSY 0x01 +#define FLASH_SR_PGERR 0x04 +#define FLASH_SR_WRPRTERR 0x10 +#define FLASH_SR_EOP 0x20 + +#define FLASH_CR_PG 0x0001 +#define FLASH_CR_PER 0x0002 +#define FLASH_CR_MER 0x0004 +#define FLASH_CR_OPTPG 0x0010 +#define FLASH_CR_OPTER 0x0020 +#define FLASH_CR_STRT 0x0040 +#define FLASH_CR_LOCK 0x0080 +#define FLASH_CR_OPTWRE 0x0200 +#define FLASH_CR_ERRIE 0x0400 +#define FLASH_CR_EOPIE 0x1000 + +static int +flash_wait_for_last_operation (uint32_t timeout) +{ + int status; + + do + { + status = FLASH->SR; + if (--timeout == 0) + break; + } + while ((status & FLASH_SR_BSY) != 0); + + return status & (FLASH_SR_BSY|FLASH_SR_PGERR|FLASH_SR_WRPRTERR); +} + +#define FLASH_PROGRAM_TIMEOUT 0x00010000 +#define FLASH_ERASE_TIMEOUT 0x01000000 + +static int +flash_program_halfword (uint32_t addr, uint16_t data) +{ + int status; + + status = flash_wait_for_last_operation (FLASH_PROGRAM_TIMEOUT); + + intr_disable (); + if (status == 0) + { + FLASH->CR |= FLASH_CR_PG; + + *(volatile uint16_t *)addr = data; + + status = flash_wait_for_last_operation (FLASH_PROGRAM_TIMEOUT); + FLASH->CR &= ~FLASH_CR_PG; + } + intr_enable (); + + return status; +} + +static int +flash_erase_page (uint32_t addr) +{ + int status; + + status = flash_wait_for_last_operation (FLASH_ERASE_TIMEOUT); + + intr_disable (); + if (status == 0) + { + FLASH->CR |= FLASH_CR_PER; + FLASH->AR = addr; + FLASH->CR |= FLASH_CR_STRT; + + status = flash_wait_for_last_operation (FLASH_ERASE_TIMEOUT); + FLASH->CR &= ~FLASH_CR_PER; + } + intr_enable (); + + return status; +} + +static int +flash_check_blank (const uint8_t *p_start, size_t size) +{ + const uint8_t *p; + + for (p = p_start; p < p_start + size; p++) + if (*p != 0xff) + return 0; + + return 1; +} + +#define FLASH_START_ADDR 0x08000000 /* Fixed for all STM32F0/F1. */ +#define FLASH_OFFSET 0x1000 /* First pages are not-writable + when protected. */ +#if defined(__ARM_ARCH_6M__) +#define FLASH_SIZE_REG ((uint16_t *)0x1ffff7cc) +#define CHIP_ID_REG ((uint32_t *)0x40015800) +#else +#define FLASH_SIZE_REG ((uint16_t *)0x1ffff7e0) +#define CHIP_ID_REG ((uint32_t *)0xe0042000) +#endif +#define FLASH_START (FLASH_START_ADDR+FLASH_OFFSET) + +static int +flash_write (uint32_t dst_addr, const uint8_t *src, size_t len) +{ + int status; + uint32_t flash_end = FLASH_START_ADDR + (*FLASH_SIZE_REG)*1024; + + if (dst_addr < FLASH_START || dst_addr + len > flash_end) + return 0; + + while (len) + { + uint16_t hw = *src++; + + hw |= (*src++ << 8); + status = flash_program_halfword (dst_addr, hw); + if (status != 0) + return 0; /* error return */ + + dst_addr += 2; + len -= 2; + } + + return 1; +} + +#define OPTION_BYTES_ADDR 0x1ffff800 + +static int +flash_protect (void) +{ + int status; + uint32_t option_bytes_value; + + status = flash_wait_for_last_operation (FLASH_ERASE_TIMEOUT); + + intr_disable (); + if (status == 0) + { + FLASH->OPTKEYR = FLASH_KEY1; + FLASH->OPTKEYR = FLASH_KEY2; + + FLASH->CR |= FLASH_CR_OPTER; + FLASH->CR |= FLASH_CR_STRT; + + status = flash_wait_for_last_operation (FLASH_ERASE_TIMEOUT); + FLASH->CR &= ~FLASH_CR_OPTER; + } + intr_enable (); + + if (status != 0) + return 0; + + option_bytes_value = *(uint32_t *)OPTION_BYTES_ADDR; + return (option_bytes_value & 0xff) == 0xff ? 1 : 0; +} + +static void __attribute__((naked)) +flash_erase_all_and_exec (void (*entry)(void)) +{ + uint32_t addr = FLASH_START; + uint32_t end = FLASH_START_ADDR + (*FLASH_SIZE_REG)*1024; + uint32_t page_size = 1024; + int r; + + if (((*CHIP_ID_REG) & 0xfff) == 0x0414) + page_size = 2048; + + while (addr < end) + { + r = flash_erase_page (addr); + if (r != 0) + break; + + addr += page_size; + } + + if (addr >= end) + (*entry) (); + + for (;;); +} + + +static void +nvic_system_reset (void) +{ + SCB->AIRCR = (0x05FA0000 | (SCB->AIRCR & 0x70) | SCB_AIRCR_SYSRESETREQ); + asm volatile ("dsb"); + for (;;); +} + +static void __attribute__ ((naked)) +reset (void) +{ + /* + * This code may not be at the start of flash ROM, because of DFU. + * So, we take the address from PC. + */ +#if defined(__ARM_ARCH_6M__) + asm volatile ("cpsid i\n\t" /* Mask all interrupts. */ + "ldr r0, 1f\n\t" /* r0 = RAM start */ + "mov r1, pc\n\t" /* r1 = (PC + 0x1000) & ~0x0fff */ + "mov r2, #0x10\n\t" + "lsl r2, #8\n\t" + "add r1, r1, r2\n\t" + "sub r2, r2, #1\n\t" + "bic r1, r1, r2\n\t" + "mov r2, #188\n" + "2:\n\t" /* Copy vectors. It will be enabled later by clock_init. */ + "ldr r3, [r1, r2]\n\t" + "str r3, [r0, r2]\n\t" + "sub r2, #4\n\t" + "bcs 2b\n\t" + "msr MSP, r3\n\t" /* Main (exception handler) stack. */ + "ldr r0, [r1, #4]\n\t" /* Reset handler. */ + "bx r0\n\t" + ".align 2\n" + "1: .word 0x20000000" + : /* no output */ : /* no input */ : "memory"); +#else + extern const uint32_t FT0[256], FT1[256], FT2[256]; + asm volatile ("cpsid i\n\t" /* Mask all interrupts. */ + "ldr r0, 1f\n\t" /* r0 = SCB start */ + "mov r1, pc\n\t" /* r1 = (PC + 0x1000) & ~0x0fff */ + "mov r2, #0x1000\n\t" + "add r1, r1, r2\n\t" + "sub r2, r2, #1\n\t" + "bic r1, r1, r2\n\t" + "str r1, [r0, #8]\n\t" /* Set SCB->VTOR */ + "ldr r0, [r1], #4\n\t" + "msr MSP, r0\n\t" /* Main (exception handler) stack. */ + "ldr r0, [r1]\n\t" /* Reset handler. */ + "bx r0\n\t" + ".align 2\n" + "1: .word 0xe000ed00" + : /* no output */ : /* no input */ : "memory"); + /* Artificial entry to refer FT0, FT1, and FT2. */ + asm volatile ("" + : : "r" (FT0), "r" (FT1), "r" (FT2)); +#endif + /* Never reach here. */ +} + +typedef void (*handler)(void); +extern uint8_t __ram_end__; + +handler vector[] __attribute__ ((section(".vectors"))) = { + (handler)&__ram_end__, + reset, + (handler)set_led, + flash_unlock, + (handler)flash_program_halfword, + (handler)flash_erase_page, + (handler)flash_check_blank, + (handler)flash_write, + (handler)flash_protect, + (handler)flash_erase_all_and_exec, + usb_lld_sys_init, + usb_lld_sys_shutdown, + nvic_system_reset, + clock_init, + gpio_init, + NULL, +}; + +const uint8_t sys_version[8] __attribute__((section(".sys.version"))) = { + 3*2+2, /* bLength */ + 0x03, /* bDescriptorType = USB_STRING_DESCRIPTOR_TYPE */ + /* sys version: "3.0" */ + '3', 0, '.', 0, '0', 0, +}; + +const uint32_t __attribute__((section(".sys.board_id"))) +sys_board_id = BOARD_ID; + +const uint8_t __attribute__((section(".sys.board_name"))) +sys_board_name[] = BOARD_NAME; diff --git a/gnuk/chopstx/mcu/sys-stm32f0.h b/gnuk/chopstx/mcu/sys-stm32f0.h new file mode 100644 index 0000000000..a08de8d91f --- /dev/null +++ b/gnuk/chopstx/mcu/sys-stm32f0.h @@ -0,0 +1,134 @@ +#if defined(__ARM_ARCH_6M__) +#define BOARD_ID_STM32F0_DISCOVERY 0xde4b4bc1 +#define BOARD_ID_FSM_55 0x83433c76 +#else +#define BOARD_ID_CQ_STARM 0xc5480875 +#define BOARD_ID_FST_01_00 0x613870a9 +#define BOARD_ID_FST_01 0x696886af +#define BOARD_ID_MAPLE_MINI 0x7a445272 +#define BOARD_ID_OLIMEX_STM32_H103 0xf92bb594 +#define BOARD_ID_STBEE_MINI 0x1f341961 +#define BOARD_ID_STBEE 0x945c37e8 +#define BOARD_ID_STM32_PRIMER2 0x21e5798d +#define BOARD_ID_STM8S_DISCOVERY 0x2f0976bb +#define BOARD_ID_ST_DONGLE 0x2cd4e471 +#define BOARD_ID_ST_NUCLEO_F103 0x9b87c16d +#endif + +extern const uint8_t sys_version[8]; +#if defined(USE_SYS3) || defined(USE_SYS_BOARD_ID) +extern const uint32_t sys_board_id; +extern const uint8_t sys_board_name[]; +#else +# define SYS_BOARD_ID BOARD_ID +#endif + +typedef void (*handler)(void); +extern handler vector[16]; + +static inline const uint8_t * +unique_device_id (void) +{ + /* STM32F103 has 96-bit unique device identifier */ + const uint8_t *addr = (const uint8_t *)0x1ffff7e8; + + return addr; +} + +static inline void +set_led (int on) +{ + void (*func) (int) = (void (*)(int))vector[2]; + + return (*func) (on); +} + +static inline void +flash_unlock (void) +{ + (*vector[3]) (); +} + +static inline int +flash_program_halfword (uint32_t addr, uint16_t data) +{ + int (*func) (uint32_t, uint16_t) = (int (*)(uint32_t, uint16_t))vector[4]; + + return (*func) (addr, data); +} + +static inline int +flash_erase_page (uint32_t addr) +{ + int (*func) (uint32_t) = (int (*)(uint32_t))vector[5]; + + return (*func) (addr); +} + +static inline int +flash_check_blank (const uint8_t *p_start, size_t size) +{ + int (*func) (const uint8_t *, int) = (int (*)(const uint8_t *, int))vector[6]; + + return (*func) (p_start, size); +} + +static inline int +flash_write (uint32_t dst_addr, const uint8_t *src, size_t len) +{ + int (*func) (uint32_t, const uint8_t *, size_t) + = (int (*)(uint32_t, const uint8_t *, size_t))vector[7]; + + return (*func) (dst_addr, src, len); +} + +static inline int +flash_protect (void) +{ + int (*func) (void) = (int (*)(void))vector[8]; + + return (*func) (); +} + +static inline void __attribute__((noreturn)) +flash_erase_all_and_exec (void (*entry)(void)) +{ + void (*func) (void (*)(void)) = (void (*)(void (*)(void)))vector[9]; + + (*func) (entry); + for (;;); +} + +static inline void +usb_lld_sys_init (void) +{ + (*vector[10]) (); +} + +static inline void +usb_lld_sys_shutdown (void) +{ + (*vector[11]) (); +} + +static inline void __attribute__((noreturn)) +nvic_system_reset (void) +{ + (*vector[12]) (); +} + +#ifdef REQUIRE_CLOCK_GPIO_SETTING_IN_SYS +/* Provide the function entries. */ + +static void __attribute__ ((used)) +clock_init (void) +{ + (*vector[13]) (); +} + +static void __attribute__ ((used)) +gpio_init (void) +{ + (*vector[14]) (); +} +#endif diff --git a/gnuk/chopstx/mcu/sys-stm32f103.c b/gnuk/chopstx/mcu/sys-stm32f103.c new file mode 100644 index 0000000000..2884385de3 --- /dev/null +++ b/gnuk/chopstx/mcu/sys-stm32f103.c @@ -0,0 +1,524 @@ +/* + * sys-stm32f103.c - system routines for the initial page for STM32F103. + * + * Copyright (C) 2013, 2014, 2015, 2016, 2017, 2018 + * Flying Stone Technology + * Author: NIIBE Yutaka + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. This file is offered as-is, + * without any warranty. + * + * When the flash ROM is protected, we cannot modify the initial page. + * We put some system routines (which is useful for any program) here. + */ + +#include +#include +#include "board.h" + +#include "mcu/clk_gpio_init-stm32.c" + + +static void +usb_cable_config (int enable) +{ +#if defined(GPIO_USB_SET_TO_ENABLE) + if (enable) + GPIO_USB->BSRR = (1 << GPIO_USB_SET_TO_ENABLE); + else + GPIO_USB->BRR = (1 << GPIO_USB_SET_TO_ENABLE); +#elif defined(GPIO_USB_CLEAR_TO_ENABLE) + if (enable) + GPIO_USB->BRR = (1 << GPIO_USB_CLEAR_TO_ENABLE); + else + GPIO_USB->BSRR = (1 << GPIO_USB_CLEAR_TO_ENABLE); +#else + (void)enable; +#endif +} + +void +set_led (int on) +{ +#if defined(GPIO_LED_CLEAR_TO_EMIT) + if (on) + GPIO_LED->BRR = (1 << GPIO_LED_CLEAR_TO_EMIT); + else + GPIO_LED->BSRR = (1 << GPIO_LED_CLEAR_TO_EMIT); +#else + if (on) + GPIO_LED->BSRR = (1 << GPIO_LED_SET_TO_EMIT); + else + GPIO_LED->BRR = (1 << GPIO_LED_SET_TO_EMIT); +#endif +} + +static void wait (int count) +{ + int i; + + for (i = 0; i < count; i++) + asm volatile ("" : : "r" (i) : "memory"); +} + + +static void +usb_lld_sys_shutdown (void) +{ + RCC->APB1ENR &= ~RCC_APB1ENR_USBEN; + RCC->APB1RSTR = RCC_APB1RSTR_USBRST; + usb_cable_config (0); +} + +static void +usb_lld_sys_init (void) +{ + if ((RCC->APB1ENR & RCC_APB1ENR_USBEN) + && (RCC->APB1RSTR & RCC_APB1RSTR_USBRST) == 0) + /* Make sure the device is disconnected, even after core reset. */ + { + usb_lld_sys_shutdown (); + /* Disconnect requires SE0 (>= 2.5uS). */ + wait (5*MHZ); + } + + usb_cable_config (1); + RCC->APB1ENR |= RCC_APB1ENR_USBEN; + RCC->APB1RSTR = RCC_APB1RSTR_USBRST; + RCC->APB1RSTR = 0; +} + +#define FLASH_KEY1 0x45670123UL +#define FLASH_KEY2 0xCDEF89ABUL + +enum flash_status +{ + FLASH_BUSY = 1, + FLASH_ERROR_PG, + FLASH_ERROR_WRP, + FLASH_COMPLETE, + FLASH_TIMEOUT +}; + +static void __attribute__ ((used)) +flash_unlock (void) +{ + FLASH->KEYR = FLASH_KEY1; + FLASH->KEYR = FLASH_KEY2; +} + + +#define intr_disable() asm volatile ("cpsid i" : : : "memory") +#define intr_enable() asm volatile ("cpsie i" : : : "memory") + +#define FLASH_SR_BSY 0x01 +#define FLASH_SR_PGERR 0x04 +#define FLASH_SR_WRPRTERR 0x10 +#define FLASH_SR_EOP 0x20 + +#define FLASH_CR_PG 0x0001 +#define FLASH_CR_PER 0x0002 +#define FLASH_CR_MER 0x0004 +#define FLASH_CR_OPTPG 0x0010 +#define FLASH_CR_OPTER 0x0020 +#define FLASH_CR_STRT 0x0040 +#define FLASH_CR_LOCK 0x0080 +#define FLASH_CR_OPTWRE 0x0200 +#define FLASH_CR_ERRIE 0x0400 +#define FLASH_CR_EOPIE 0x1000 + +static int +flash_wait_for_last_operation (uint32_t timeout) +{ + int status; + + do + { + status = FLASH->SR; + if (--timeout == 0) + break; + } + while ((status & FLASH_SR_BSY) != 0); + + return status & (FLASH_SR_BSY|FLASH_SR_PGERR|FLASH_SR_WRPRTERR); +} + +#define FLASH_PROGRAM_TIMEOUT 0x00010000 +#define FLASH_ERASE_TIMEOUT 0x01000000 + +static int +flash_program_halfword (uintptr_t addr, uint16_t data) +{ + int status; + + status = flash_wait_for_last_operation (FLASH_PROGRAM_TIMEOUT); + + intr_disable (); + if (status == 0) + { + FLASH->CR |= FLASH_CR_PG; + + *(volatile uint16_t *)addr = data; + + status = flash_wait_for_last_operation (FLASH_PROGRAM_TIMEOUT); + FLASH->CR &= ~FLASH_CR_PG; + } + intr_enable (); + + return status; +} + +static int +flash_erase_page (uintptr_t addr) +{ + int status; + + status = flash_wait_for_last_operation (FLASH_ERASE_TIMEOUT); + + intr_disable (); + if (status == 0) + { + FLASH->CR |= FLASH_CR_PER; + FLASH->AR = addr; + FLASH->CR |= FLASH_CR_STRT; + + status = flash_wait_for_last_operation (FLASH_ERASE_TIMEOUT); + FLASH->CR &= ~FLASH_CR_PER; + } + intr_enable (); + + return status; +} + +static int +flash_check_blank (const uint8_t *p_start, size_t size) +{ + const uint8_t *p; + + for (p = p_start; p < p_start + size; p++) + if (*p != 0xff) + return 0; + + return 1; +} + +#define FLASH_START_ADDR 0x08000000 /* Fixed for all STM32F1. */ +#define FLASH_OFFSET 0x1000 /* First pages are not-writable. */ +#define FLASH_START (FLASH_START_ADDR+FLASH_OFFSET) +#define CHIP_ID_REG ((uint32_t *)0xe0042000) +#define FLASH_SIZE_REG ((uint16_t *)0x1ffff7e0) + +static int +flash_write (uintptr_t dst_addr, const uint8_t *src, size_t len) +{ + int status; +#if defined(STM32F103_OVERRIDE_FLASH_SIZE_KB) + uintptr_t flash_end = FLASH_START_ADDR + STM32F103_OVERRIDE_FLASH_SIZE_KB*1024; +#else + uintptr_t flash_end = FLASH_START_ADDR + (*FLASH_SIZE_REG)*1024; +#endif + + if (dst_addr < FLASH_START || dst_addr + len > flash_end) + return 0; + + while (len) + { + uint16_t hw = *src++; + + hw |= (*src++ << 8); + status = flash_program_halfword (dst_addr, hw); + if (status != 0) + return 0; /* error return */ + + dst_addr += 2; + len -= 2; + } + + return 1; +} + +#define OPTION_BYTES_ADDR 0x1ffff800 + +static int +flash_protect (void) +{ + int status; + uint32_t option_bytes_value; + + status = flash_wait_for_last_operation (FLASH_ERASE_TIMEOUT); + + intr_disable (); + if (status == 0) + { + FLASH->OPTKEYR = FLASH_KEY1; + FLASH->OPTKEYR = FLASH_KEY2; + + while (!(FLASH->CR & FLASH_CR_OPTWRE)) + ; + + FLASH->CR |= FLASH_CR_OPTER; + FLASH->CR |= FLASH_CR_STRT; + + status = flash_wait_for_last_operation (FLASH_ERASE_TIMEOUT); + FLASH->CR &= ~FLASH_CR_OPTER; + } + intr_enable (); + + if (status != 0) + return 0; + + option_bytes_value = *(uint32_t *)OPTION_BYTES_ADDR; + return (option_bytes_value & 0xff) == 0xff ? 1 : 0; +} + +static void __attribute__((naked)) +flash_erase_all_and_exec (void (*entry)(void)) +{ + uintptr_t addr = FLASH_START; +#if defined(STM32F103_OVERRIDE_FLASH_SIZE_KB) + uintptr_t end = FLASH_START_ADDR + STM32F103_OVERRIDE_FLASH_SIZE_KB*1024; +#else + uintptr_t end = FLASH_START_ADDR + (*FLASH_SIZE_REG)*1024; +#endif + uint32_t page_size = 1024; + int r; + + if (((*CHIP_ID_REG) & 0xfff) == 0x0414) + page_size = 2048; + + while (addr < end) + { + r = flash_erase_page (addr); + if (r != 0) + break; + + addr += page_size; + } + + if (addr >= end) + (*entry) (); + + for (;;); +} + +static void +nvic_system_reset (void) +{ + SCB->AIRCR = (0x05FA0000 | (SCB->AIRCR & 0x70) | SCB_AIRCR_SYSRESETREQ); + asm volatile ("dsb"); + for (;;); +} + +static void __attribute__ ((naked)) +reset (void) +{ + extern const uint32_t FT0[256], FT1[256], FT2[256]; + + /* + * This code may not be at the start of flash ROM, because of DFU. + * So, we take the address from PC. + */ + asm volatile ("cpsid i\n\t" /* Mask all interrupts. */ + "ldr r0, 1f\n\t" /* r0 = SCB start */ + "mov r1, pc\n\t" /* r1 = (PC + 0x1000) & ~0x0fff */ + "mov r2, #0x1000\n\t" + "add r1, r1, r2\n\t" + "sub r2, r2, #1\n\t" + "bic r1, r1, r2\n\t" + "str r1, [r0, #8]\n\t" /* Set SCB->VTOR */ + "ldr r0, [r1], #4\n\t" + "msr MSP, r0\n\t" /* Main (exception handler) stack. */ + "ldr r0, [r1]\n\t" /* Reset handler. */ + "bx r0\n\t" + ".align 2\n" + "1: .word 0xe000ed00" + : /* no output */ : /* no input */ : "memory"); + + /* Never reach here. */ + /* Artificial entry to refer FT0, FT1, and FT2. */ + asm volatile ("" + : : "r" (FT0), "r" (FT1), "r" (FT2)); +} + +typedef void (*handler)(void); +extern uint8_t __ram_end__; + +handler vector[] __attribute__ ((section(".vectors"))) = { + (handler)&__ram_end__, + reset, + (handler)set_led, + flash_unlock, + (handler)flash_program_halfword, + (handler)flash_erase_page, + (handler)flash_check_blank, + (handler)flash_write, + (handler)flash_protect, + (handler)flash_erase_all_and_exec, + usb_lld_sys_init, + usb_lld_sys_shutdown, + nvic_system_reset, + clock_init, + gpio_init, + NULL, +}; + +const uint8_t sys_version[8] __attribute__((section(".sys.version"))) = { + 3*2+2, /* bLength */ + 0x03, /* bDescriptorType = USB_STRING_DESCRIPTOR_TYPE */ + /* sys version: "3.0" */ + '3', 0, '.', 0, '0', 0, +}; + +const uint32_t __attribute__((section(".sys.board_id"))) +sys_board_id = BOARD_ID; + +const uint8_t __attribute__((section(".sys.board_name"))) +sys_board_name[] = BOARD_NAME; + +/* + * aes-constant-ft.c - AES forward tables. + * + * We need something useful for the initial flash ROM page (4 Ki + * bytes), which cannot be modified after installation. Even after + * upgrade of the firmware, it stays intact. + * + * We decide to put 3/4 of AES forward tables to fill 3 Ki bytes, as + * its useful and it won't change. + * + * The code was taken from aes.c of PolarSSL version 0.14, and then, + * modified to add section names. + * + * Since this is just a data, it wouldn't be copyright-able, but the + * original auther would claim so. Thus, we put original copyright + * notice here. It is highly likely that there will be no such a + * thing for copyright. Nevertheless, we think that PolarSSL is good + * software to address here, and encourage people using it. + * + */ + +#include + +/* + * Original copyright notice is below: + */ + +/* + * FIPS-197 compliant AES implementation + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The AES block cipher was designed by Vincent Rijmen and Joan Daemen. + * + * http://csrc.nist.gov/encryption/aes/rijndael/Rijndael.pdf + * http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + */ + +/* + * Forward tables + */ +#define FT \ +\ + V(A5,63,63,C6), V(84,7C,7C,F8), V(99,77,77,EE), V(8D,7B,7B,F6), \ + V(0D,F2,F2,FF), V(BD,6B,6B,D6), V(B1,6F,6F,DE), V(54,C5,C5,91), \ + V(50,30,30,60), V(03,01,01,02), V(A9,67,67,CE), V(7D,2B,2B,56), \ + V(19,FE,FE,E7), V(62,D7,D7,B5), V(E6,AB,AB,4D), V(9A,76,76,EC), \ + V(45,CA,CA,8F), V(9D,82,82,1F), V(40,C9,C9,89), V(87,7D,7D,FA), \ + V(15,FA,FA,EF), V(EB,59,59,B2), V(C9,47,47,8E), V(0B,F0,F0,FB), \ + V(EC,AD,AD,41), V(67,D4,D4,B3), V(FD,A2,A2,5F), V(EA,AF,AF,45), \ + V(BF,9C,9C,23), V(F7,A4,A4,53), V(96,72,72,E4), V(5B,C0,C0,9B), \ + V(C2,B7,B7,75), V(1C,FD,FD,E1), V(AE,93,93,3D), V(6A,26,26,4C), \ + V(5A,36,36,6C), V(41,3F,3F,7E), V(02,F7,F7,F5), V(4F,CC,CC,83), \ + V(5C,34,34,68), V(F4,A5,A5,51), V(34,E5,E5,D1), V(08,F1,F1,F9), \ + V(93,71,71,E2), V(73,D8,D8,AB), V(53,31,31,62), V(3F,15,15,2A), \ + V(0C,04,04,08), V(52,C7,C7,95), V(65,23,23,46), V(5E,C3,C3,9D), \ + V(28,18,18,30), V(A1,96,96,37), V(0F,05,05,0A), V(B5,9A,9A,2F), \ + V(09,07,07,0E), V(36,12,12,24), V(9B,80,80,1B), V(3D,E2,E2,DF), \ + V(26,EB,EB,CD), V(69,27,27,4E), V(CD,B2,B2,7F), V(9F,75,75,EA), \ + V(1B,09,09,12), V(9E,83,83,1D), V(74,2C,2C,58), V(2E,1A,1A,34), \ + V(2D,1B,1B,36), V(B2,6E,6E,DC), V(EE,5A,5A,B4), V(FB,A0,A0,5B), \ + V(F6,52,52,A4), V(4D,3B,3B,76), V(61,D6,D6,B7), V(CE,B3,B3,7D), \ + V(7B,29,29,52), V(3E,E3,E3,DD), V(71,2F,2F,5E), V(97,84,84,13), \ + V(F5,53,53,A6), V(68,D1,D1,B9), V(00,00,00,00), V(2C,ED,ED,C1), \ + V(60,20,20,40), V(1F,FC,FC,E3), V(C8,B1,B1,79), V(ED,5B,5B,B6), \ + V(BE,6A,6A,D4), V(46,CB,CB,8D), V(D9,BE,BE,67), V(4B,39,39,72), \ + V(DE,4A,4A,94), V(D4,4C,4C,98), V(E8,58,58,B0), V(4A,CF,CF,85), \ + V(6B,D0,D0,BB), V(2A,EF,EF,C5), V(E5,AA,AA,4F), V(16,FB,FB,ED), \ + V(C5,43,43,86), V(D7,4D,4D,9A), V(55,33,33,66), V(94,85,85,11), \ + V(CF,45,45,8A), V(10,F9,F9,E9), V(06,02,02,04), V(81,7F,7F,FE), \ + V(F0,50,50,A0), V(44,3C,3C,78), V(BA,9F,9F,25), V(E3,A8,A8,4B), \ + V(F3,51,51,A2), V(FE,A3,A3,5D), V(C0,40,40,80), V(8A,8F,8F,05), \ + V(AD,92,92,3F), V(BC,9D,9D,21), V(48,38,38,70), V(04,F5,F5,F1), \ + V(DF,BC,BC,63), V(C1,B6,B6,77), V(75,DA,DA,AF), V(63,21,21,42), \ + V(30,10,10,20), V(1A,FF,FF,E5), V(0E,F3,F3,FD), V(6D,D2,D2,BF), \ + V(4C,CD,CD,81), V(14,0C,0C,18), V(35,13,13,26), V(2F,EC,EC,C3), \ + V(E1,5F,5F,BE), V(A2,97,97,35), V(CC,44,44,88), V(39,17,17,2E), \ + V(57,C4,C4,93), V(F2,A7,A7,55), V(82,7E,7E,FC), V(47,3D,3D,7A), \ + V(AC,64,64,C8), V(E7,5D,5D,BA), V(2B,19,19,32), V(95,73,73,E6), \ + V(A0,60,60,C0), V(98,81,81,19), V(D1,4F,4F,9E), V(7F,DC,DC,A3), \ + V(66,22,22,44), V(7E,2A,2A,54), V(AB,90,90,3B), V(83,88,88,0B), \ + V(CA,46,46,8C), V(29,EE,EE,C7), V(D3,B8,B8,6B), V(3C,14,14,28), \ + V(79,DE,DE,A7), V(E2,5E,5E,BC), V(1D,0B,0B,16), V(76,DB,DB,AD), \ + V(3B,E0,E0,DB), V(56,32,32,64), V(4E,3A,3A,74), V(1E,0A,0A,14), \ + V(DB,49,49,92), V(0A,06,06,0C), V(6C,24,24,48), V(E4,5C,5C,B8), \ + V(5D,C2,C2,9F), V(6E,D3,D3,BD), V(EF,AC,AC,43), V(A6,62,62,C4), \ + V(A8,91,91,39), V(A4,95,95,31), V(37,E4,E4,D3), V(8B,79,79,F2), \ + V(32,E7,E7,D5), V(43,C8,C8,8B), V(59,37,37,6E), V(B7,6D,6D,DA), \ + V(8C,8D,8D,01), V(64,D5,D5,B1), V(D2,4E,4E,9C), V(E0,A9,A9,49), \ + V(B4,6C,6C,D8), V(FA,56,56,AC), V(07,F4,F4,F3), V(25,EA,EA,CF), \ + V(AF,65,65,CA), V(8E,7A,7A,F4), V(E9,AE,AE,47), V(18,08,08,10), \ + V(D5,BA,BA,6F), V(88,78,78,F0), V(6F,25,25,4A), V(72,2E,2E,5C), \ + V(24,1C,1C,38), V(F1,A6,A6,57), V(C7,B4,B4,73), V(51,C6,C6,97), \ + V(23,E8,E8,CB), V(7C,DD,DD,A1), V(9C,74,74,E8), V(21,1F,1F,3E), \ + V(DD,4B,4B,96), V(DC,BD,BD,61), V(86,8B,8B,0D), V(85,8A,8A,0F), \ + V(90,70,70,E0), V(42,3E,3E,7C), V(C4,B5,B5,71), V(AA,66,66,CC), \ + V(D8,48,48,90), V(05,03,03,06), V(01,F6,F6,F7), V(12,0E,0E,1C), \ + V(A3,61,61,C2), V(5F,35,35,6A), V(F9,57,57,AE), V(D0,B9,B9,69), \ + V(91,86,86,17), V(58,C1,C1,99), V(27,1D,1D,3A), V(B9,9E,9E,27), \ + V(38,E1,E1,D9), V(13,F8,F8,EB), V(B3,98,98,2B), V(33,11,11,22), \ + V(BB,69,69,D2), V(70,D9,D9,A9), V(89,8E,8E,07), V(A7,94,94,33), \ + V(B6,9B,9B,2D), V(22,1E,1E,3C), V(92,87,87,15), V(20,E9,E9,C9), \ + V(49,CE,CE,87), V(FF,55,55,AA), V(78,28,28,50), V(7A,DF,DF,A5), \ + V(8F,8C,8C,03), V(F8,A1,A1,59), V(80,89,89,09), V(17,0D,0D,1A), \ + V(DA,BF,BF,65), V(31,E6,E6,D7), V(C6,42,42,84), V(B8,68,68,D0), \ + V(C3,41,41,82), V(B0,99,99,29), V(77,2D,2D,5A), V(11,0F,0F,1E), \ + V(CB,B0,B0,7B), V(FC,54,54,A8), V(D6,BB,BB,6D), V(3A,16,16,2C) + +#define V(a,b,c,d) 0x##a##b##c##d +const uint32_t FT0[256] __attribute__((section(".sys.0"))) = { FT }; +#undef V + +#define V(a,b,c,d) 0x##b##c##d##a +const uint32_t FT1[256] __attribute__((section(".sys.1"))) = { FT }; +#undef V + +#define V(a,b,c,d) 0x##c##d##a##b +const uint32_t FT2[256] __attribute__((section(".sys.2"))) = { FT }; +#undef V + +#ifdef ORIGINAL_IMPLEMENTATION +#define V(a,b,c,d) 0x##d##a##b##c +const uint32_t FT3[256] = { FT }; +#undef V +#endif diff --git a/gnuk/chopstx/mcu/sys-stm32f103.h b/gnuk/chopstx/mcu/sys-stm32f103.h new file mode 100644 index 0000000000..2492acc96e --- /dev/null +++ b/gnuk/chopstx/mcu/sys-stm32f103.h @@ -0,0 +1,136 @@ +#define BOARD_ID_CQ_STARM 0xc5480875 +#define BOARD_ID_FST_01_00 0x613870a9 +#define BOARD_ID_FST_01 0x696886af +#define BOARD_ID_FST_01G 0x8801277f +#define BOARD_ID_FST_01SZ 0x7e6fb084 +#define BOARD_ID_MAPLE_MINI 0x7a445272 +#define BOARD_ID_OLIMEX_STM32_H103 0xf92bb594 +#define BOARD_ID_STBEE_MINI 0x1f341961 +#define BOARD_ID_STBEE 0x945c37e8 +#define BOARD_ID_STM32_PRIMER2 0x21e5798d +#define BOARD_ID_STM8S_DISCOVERY 0x2f0976bb +#define BOARD_ID_ST_DONGLE 0x2cd4e471 +#define BOARD_ID_ST_NUCLEO_F103 0x9b87c16d +#define BOARD_ID_NITROKEY_START 0xad1e7ebd + +extern const uint8_t sys_version[8]; +#if defined(USE_SYS3) || defined(USE_SYS_BOARD_ID) +extern const uint32_t sys_board_id; +extern const uint8_t sys_board_name[]; +# define SYS_BOARD_ID sys_board_id +#else +# define SYS_BOARD_ID BOARD_ID +#endif + +typedef void (*handler)(void); +typedef void (*nonreturn_handler0)(void)__attribute__((noreturn)); +typedef void (*nonreturn_handler1)(void *)__attribute__((noreturn)); +extern handler vector[16]; + +static inline const uint8_t * +unique_device_id (void) +{ + /* STM32F103 has 96-bit unique device identifier */ + const uint8_t *addr = (const uint8_t *)0x1ffff7e8; + + return addr; +} + +static inline void +set_led (int on) +{ + void (*func) (int) = (void (*)(int))vector[2]; + + return (*func) (on); +} + +static inline void +flash_unlock (void) +{ + (*vector[3]) (); +} + +static inline int +flash_program_halfword (uintptr_t addr, uint16_t data) +{ + int (*func) (uintptr_t, uint16_t) = (int (*)(uintptr_t, uint16_t))vector[4]; + + return (*func) (addr, data); +} + +static inline int +flash_erase_page (uintptr_t addr) +{ + int (*func) (uintptr_t) = (int (*)(uintptr_t))vector[5]; + + return (*func) (addr); +} + +static inline int +flash_check_blank (const uint8_t *p_start, size_t size) +{ + int (*func) (const uint8_t *, int) = (int (*)(const uint8_t *, int))vector[6]; + + return (*func) (p_start, size); +} + +static inline int +flash_write (uintptr_t dst_addr, const uint8_t *src, size_t len) +{ + int (*func) (uintptr_t, const uint8_t *, size_t) + = (int (*)(uintptr_t, const uint8_t *, size_t))vector[7]; + + return (*func) (dst_addr, src, len); +} + +static inline int +flash_protect (void) +{ + int (*func) (void) = (int (*)(void))vector[8]; + + return (*func) (); +} + +static inline void __attribute__((noreturn)) +flash_erase_all_and_exec (void (*entry)(void)) +{ + nonreturn_handler1 func = (nonreturn_handler1) vector[9] ; + + (*func) (entry); +} + +static inline void +usb_lld_sys_init (void) +{ + (*vector[10]) (); +} + +static inline void +usb_lld_sys_shutdown (void) +{ + (*vector[11]) (); +} + +static inline void __attribute__((noreturn)) +nvic_system_reset (void) +{ + nonreturn_handler0 func = (nonreturn_handler0)vector[12]; + + (func) (); +} + +#ifdef REQUIRE_CLOCK_GPIO_SETTING_IN_SYS +/* Provide the function entries. */ + +static void __attribute__ ((used)) +clock_init (void) +{ + (*vector[13]) (); +} + +static void __attribute__ ((used)) +gpio_init (void) +{ + (*vector[14]) (); +} +#endif diff --git a/gnuk/chopstx/mcu/usb-mkl27z.c b/gnuk/chopstx/mcu/usb-mkl27z.c new file mode 100644 index 0000000000..d3fb2fe061 --- /dev/null +++ b/gnuk/chopstx/mcu/usb-mkl27z.c @@ -0,0 +1,995 @@ +/* + * usb-mkl27z.c - USB driver for MKL27Z + * + * Copyright (C) 2016, 2018 Flying Stone Technology + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx 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. + * + * Chopstx 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 . + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +#include +#include +#include + +#include "usb_lld.h" + +#define DATA0 0 +#define DATA1 1 + +struct endpoint_ctl { + uint32_t rx_odd: 1; + uint32_t tx_odd: 1; +}; +static struct endpoint_ctl ep[16]; + +#if 0 +struct USB_CONF { + const uint8_t PERID; /* Peripheral ID register */ + uint8_t rsvd0[3]; /* */ + const uint8_t IDCOMP; /* Peripheral ID Complement register */ + uint8_t rsvd1[3]; /* */ + const uint8_t REV; /* Peripheral Revision register */ + uint8_t rsvd2[3]; /* */ + volatile uint8_t ADDINFO; /* Peripheral Additional Info register */ +}; +static struct USB_CONF *const USB_CONF = (struct USB_CONF *)0x40072000; +#endif + +struct USB_CTRL0 { + volatile uint8_t OTGCTL; /* OTG Control register */ +}; +static struct USB_CTRL0 *const USB_CTRL0 = (struct USB_CTRL0 *)0x4007201c; + +struct USB_CTRL1 { + volatile uint8_t ISTAT; /* Interrupt Status register */ + uint8_t rsvd5[3]; /* */ + volatile uint8_t INTEN; /* Interrupt Enable register */ + uint8_t rsvd6[3]; /* */ + volatile uint8_t ERRSTAT; /* Error Interrupt Status register */ + uint8_t rsvd7[3]; /* */ + volatile uint8_t ERREN; /* Error Interrupt Enable register */ + uint8_t rsvd8[3]; /* */ + volatile uint8_t STAT; /* Status register */ + uint8_t rsvd9[3]; /* */ + volatile uint8_t CTL; /* Control register */ + uint8_t rsvd10[3]; /* */ + volatile uint8_t ADDR; /* Address register */ + uint8_t rsvd11[3]; /* */ + volatile uint8_t BDTPAGE1; /* BDT Page register 1 */ + uint8_t rsvd12[3]; /* */ + volatile uint8_t FRMNUML; /* Frame Number register Low */ + uint8_t rsvd13[3]; /* */ + volatile uint8_t FRMNUMH; /* Frame Number register High */ + uint8_t rsvd14[11]; /* */ + volatile uint8_t BDTPAGE2; /* BDT Page Register 2 */ + uint8_t rsvd15[3]; /* */ + volatile uint8_t BDTPAGE3; /* BDT Page Register 3 */ +}; +static struct USB_CTRL1 *const USB_CTRL1 = (struct USB_CTRL1 *)0x40072080; + +/* Interrupt source bits */ +#define USB_IS_STALL (1 << 7) +#define USB_IS_RESUME (1 << 5) +#define USB_IS_SLEEP (1 << 4) +#define USB_IS_TOKDNE (1 << 3) +#define USB_IS_SOFTOK (1 << 2) +#define USB_IS_ERROR (1 << 1) +#define USB_IS_USBRST (1 << 0) + + +struct USB_ENDPT { + volatile uint8_t EP; /* Endpoint Control register */ + uint8_t rsvd17[3]; +}; +static struct USB_ENDPT *const USB_ENDPT = (struct USB_ENDPT *)0x400720c0; + +struct USB_CTRL2 { + volatile uint8_t USBCTRL; /* USB Control register */ + uint8_t rsvd33[3]; /* */ + volatile uint8_t OBSERVE; /* USB OTG Observe register */ + uint8_t rsvd34[3]; /* */ + volatile uint8_t CONTROL; /* USB OTG Control register */ + uint8_t rsvd35[3]; /* */ + volatile uint8_t USBTRC0; /* USB Transceiver Control register 0 */ + uint8_t rsvd36[7]; /* */ + volatile uint8_t USBFRMADJUST; /* Frame Adjut Register */ +}; +static struct USB_CTRL2 *const USB_CTRL2 = (struct USB_CTRL2 *)0x40072100; + +/* Buffer Descriptor */ +struct BD { + volatile uint32_t ctrl; + volatile void *buf; +}; +/* + uint32_t rsvd0 : 2; + volatile uint32_t STALL: 1; + volatile uint32_t DTS: 1; + + volatile uint32_t NINC: 1; + volatile uint32_t KEEP: 1; + volatile uint32_t DATA01: 1; + volatile uint32_t OWN: 1; + + uint32_t rsvd1: 8; + volatile uint32_t BC: 10; + uint32_t rsvd2: 6; +*/ +#define TOK_PID(ctrl) ((ctrl >> 2) & 0x0f) + +extern uint8_t __usb_bdt__; + +static struct BD *const BD_table = (struct BD *)&__usb_bdt__; + +static void +kl27z_usb_init (void) +{ + int i; + + memset (ep, 0, sizeof (ep)); + memset (BD_table, 0, 16 * 2 * 2 * sizeof (struct BD)); + + /* D+ pull up */ + USB_CTRL0->OTGCTL = 0x80; + + USB_CTRL1->ERREN = 0xff; + + USB_CTRL1->BDTPAGE1 = ((uint32_t)BD_table) >> 8; + USB_CTRL1->BDTPAGE2 = ((uint32_t)BD_table) >> 16; + USB_CTRL1->BDTPAGE3 = ((uint32_t)BD_table) >> 24; + + /* Not suspended, Pull-down disabled. */ + USB_CTRL2->USBCTRL = 0x00; + /* DP Pullup in non-OTG device mode. */ + USB_CTRL2->CONTROL = 0x10; + + /* Disable all endpoints. */ + for (i = 0; i < 16; i++) + USB_ENDPT[i].EP = 0; + + /* + * Enable USB FS communication module, clearing all ODD-bits + * for BDT. + */ + USB_CTRL1->CTL = 0x03; + + /* ??? How we can ask re-enumeration? Is only hard RESET enough? */ +} + +static void +kl27z_set_daddr (uint8_t daddr) +{ + USB_CTRL1->ADDR = daddr; +} + +static void +kl27z_prepare_ep0_setup (struct usb_dev *dev) +{ + /* Endpoint 0, TX=0. */ + BD_table[ep[0].rx_odd].ctrl = 0x00080088; /* Len=8, OWN=1, DATA01=0, DTS=1 */ + BD_table[ep[0].rx_odd].buf = &dev->dev_req; + + BD_table[!ep[0].rx_odd].ctrl = 0x0000; /* OWN=0 */ + BD_table[!ep[0].rx_odd].buf = NULL; +} + +static void +kl27z_prepare_ep0_in (const void *buf, uint8_t len, int data01) +{ + /* Endpoint 0, TX=1 *//* OWN=1, DTS=1 */ + BD_table[2+ep[0].tx_odd].ctrl = (len << 16) | 0x0088 | (data01 << 6); + BD_table[2+ep[0].tx_odd].buf = (void *)buf; +} + +static void +kl27z_prepare_ep0_out (void *buf, uint8_t len, int data01) +{ + /* Endpoint 0, TX=0 *//* OWN=1, DTS=1 */ + BD_table[ep[0].rx_odd].ctrl = (len << 16) | 0x0088 | (data01 << 6); + BD_table[ep[0].rx_odd].buf = buf; +} + +static int +kl27z_ep_is_disabled (uint8_t n) +{ + return (USB_ENDPT[n].EP == 0); +} + +static int +kl27z_ep_is_stall (uint8_t n) +{ + return (USB_ENDPT[n].EP & 0x02) >> 1; +} + +static void +kl27z_ep_stall (uint8_t n) +{ + USB_ENDPT[n].EP |= 0x02; +} + +static void +kl27z_ep_clear_stall (uint8_t n) +{ + USB_ENDPT[n].EP &= ~0x02; +} + +static void +kl27z_ep_clear_dtog (int rx, uint8_t n) +{ + uint32_t config; + + if (!kl27z_ep_is_stall (n)) + /* Just in case, when the endpoint is active */ + kl27z_ep_stall (n); + + if (rx) + { + config = BD_table[4*n+ep[n].rx_odd].ctrl; + + BD_table[4*n+!ep[n].rx_odd].ctrl &= ~(1 << 6); + if ((config & 0x0080)) /* OWN already? */ + { + /* + * How to update BDT entry which is owned by USBFS seems to + * be not clearly documented. It would be just OK to update + * it as long as the endpoint is stalled (BDT entry is + * actually not in use). We write 0 at first and then write + * value with OWN, to avoid possible failure. + */ + BD_table[4*n+ep[n].rx_odd].ctrl = 0; + BD_table[4*n+ep[n].rx_odd].ctrl = (config & ~(1 << 6)); + } + } + else + { + config = BD_table[4*n+2+ep[n].tx_odd].ctrl; + + BD_table[4*n+2+!ep[n].tx_odd].ctrl &= ~(1 << 6); + if ((config & 0x0080)) /* OWN already? */ + { + BD_table[4*n+2+ep[n].tx_odd].ctrl = 0; + BD_table[4*n+2+ep[n].tx_odd].ctrl = (config & ~(1 << 6)); + } + } + + kl27z_ep_clear_stall (n); +} + +#include "usb_lld_driver.h" + +static int handle_transaction (struct usb_dev *dev, uint8_t stat); + +void +usb_lld_stall (int n) +{ + kl27z_ep_stall (n); +} + + +void +usb_lld_ctrl_error (struct usb_dev *dev) +{ + dev->state = STALLED; + kl27z_ep_stall (ENDP0); +} + +int +usb_lld_ctrl_ack (struct usb_dev *dev) +{ + /* Zero length packet for ACK. */ + dev->state = WAIT_STATUS_IN; + kl27z_prepare_ep0_in (&dev->dev_req, 0, DATA1); + return USB_EVENT_OK; +} + + +void +usb_lld_init (struct usb_dev *dev, uint8_t feature) +{ + usb_lld_set_configuration (dev, 0); + dev->feature = feature; + dev->state = WAIT_SETUP; + + kl27z_set_daddr (0); + kl27z_usb_init (); + + /* Enable the endpoint 0. */ + USB_ENDPT[0].EP = 0x0d; + + /* Clear Interrupt Status Register, and enable interrupt for USB */ + USB_CTRL1->ISTAT = 0xff; /* All clear */ + + USB_CTRL1->INTEN = USB_IS_STALL | USB_IS_TOKDNE + | USB_IS_ERROR | USB_IS_USBRST; +} + + +#define USB_MAKE_EV(event) (event<<24) +#define USB_MAKE_TXRX(ep_num,txrx,len) ((txrx? (1<<23):0)|(ep_num<<16)|len) + +int +usb_lld_event_handler (struct usb_dev *dev) +{ + uint8_t istat_value = USB_CTRL1->ISTAT; + uint8_t stat = USB_CTRL1->STAT; + + if ((istat_value & USB_IS_USBRST)) + { + USB_CTRL1->ISTAT = USB_IS_USBRST; + return USB_MAKE_EV (USB_EVENT_DEVICE_RESET); + } + else if ((istat_value & USB_IS_TOKDNE)) + return handle_transaction (dev, stat); + else if ((istat_value & USB_IS_ERROR)) + { /* Clear Errors. */ + USB_CTRL1->ERRSTAT = USB_CTRL1->ERRSTAT; + USB_CTRL1->ISTAT = USB_IS_ERROR; + } + else if ((istat_value & USB_IS_STALL)) + { + /* Does STAT have ENDPOINT info in this case?: No, it doesn't. */ + + if (kl27z_ep_is_stall (0)) + { /* It's endpoint 0, recover from erorr. */ + dev->state = WAIT_SETUP; + kl27z_ep_clear_stall (0); + kl27z_prepare_ep0_setup (dev); + } + + USB_CTRL1->ISTAT = USB_IS_STALL; + } + + return USB_EVENT_OK; +} + + +static void +handle_datastage_out (struct usb_dev *dev, uint8_t stat) +{ + struct ctrl_data *data_p = &dev->ctrl_data; + int odd = (stat >> 2)&1; + int data01 = !((BD_table[odd].ctrl >> 6)&1); + uint32_t len = (BD_table[odd].ctrl >> 16)&0x3ff; + + data_p->len -= len; + data_p->addr += len; + + len = data_p->len; + if (len > USB_MAX_PACKET_SIZE) + len = USB_MAX_PACKET_SIZE; + + if (data_p->len == 0) + { + /* No more data to receive, proceed to send acknowledge for IN. */ + dev->state = WAIT_STATUS_IN; + kl27z_prepare_ep0_in (&dev->dev_req, 0, DATA1); + } + else + { + dev->state = OUT_DATA; + kl27z_prepare_ep0_out (data_p->addr, len, data01); + } +} + +static void +handle_datastage_in (struct usb_dev *dev, uint8_t stat) +{ + struct ctrl_data *data_p = &dev->ctrl_data; + int odd = (stat >> 2)&1; + int data01 = !((BD_table[2+odd].ctrl >> 6)&1); + uint32_t len = USB_MAX_PACKET_SIZE; + + if ((data_p->len == 0) && (dev->state == LAST_IN_DATA)) + { + if (data_p->require_zlp) + { + data_p->require_zlp = 0; + + /* No more data to send. Send empty packet */ + kl27z_prepare_ep0_in (&dev->dev_req, 0, data01); + } + else + { + /* No more data to send, proceed to receive OUT acknowledge. */ + dev->state = WAIT_STATUS_OUT; + kl27z_prepare_ep0_out (&dev->dev_req, 0, DATA1); + } + + return; + } + + dev->state = (data_p->len <= len) ? LAST_IN_DATA : IN_DATA; + + if (len > data_p->len) + len = data_p->len; + + kl27z_prepare_ep0_in (data_p->addr, len, data01); + data_p->len -= len; + data_p->addr += len; +} + +typedef int (*HANDLER) (struct usb_dev *dev); + +static int +std_none (struct usb_dev *dev) +{ + (void)dev; + return -1; +} + +static uint16_t status_info; + +static int +std_get_status (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (arg->value != 0 || arg->len != 2 || (arg->index >> 8) != 0 + || USB_SETUP_SET (arg->type)) + return -1; + + if (rcp == DEVICE_RECIPIENT) + { + if (arg->index == 0) + { + /* Get Device Status */ + uint8_t feature = dev->feature; + + /* Remote Wakeup enabled */ + if ((feature & (1 << 5))) + status_info |= 2; + else + status_info &= ~2; + + /* Bus-powered */ + if ((feature & (1 << 6))) + status_info |= 1; + else /* Self-powered */ + status_info &= ~1; + + return usb_lld_ctrl_send (dev, &status_info, 2); + } + } + else if (rcp == INTERFACE_RECIPIENT) + { + if (dev->configuration == 0) + return -1; + + return USB_EVENT_GET_STATUS_INTERFACE; + } + else if (rcp == ENDPOINT_RECIPIENT) + { + uint8_t n = (arg->index & 0x0f); + + if ((arg->index & 0x70) || n == ENDP0) + return -1; + + if (kl27z_ep_is_disabled (n)) + return -1; + + status_info = kl27z_ep_is_stall (n); + return usb_lld_ctrl_send (dev, &status_info, 2); + } + + return -1; +} + +static int +std_clear_feature (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type)) + return -1; + + if (rcp == DEVICE_RECIPIENT) + { + if (arg->len != 0 || arg->index != 0) + return -1; + + if (arg->value == FEATURE_DEVICE_REMOTE_WAKEUP) + { + dev->feature &= ~(1 << 5); + return USB_EVENT_CLEAR_FEATURE_DEVICE; + } + } + else if (rcp == ENDPOINT_RECIPIENT) + { + uint8_t n = (arg->index & 0x0f); + + if (dev->configuration == 0) + return -1; + + if (arg->len != 0 || (arg->index >> 8) != 0 + || arg->value != FEATURE_ENDPOINT_HALT || n == ENDP0) + return -1; + + if (kl27z_ep_is_disabled (n)) + return -1; + + kl27z_ep_clear_dtog ((arg->index & 0x80) == 0, n); + + return USB_EVENT_CLEAR_FEATURE_ENDPOINT; + } + + return -1; +} + +static int +std_set_feature (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type)) + return -1; + + if (rcp == DEVICE_RECIPIENT) + { + if (arg->len != 0 || arg->index != 0) + return -1; + + if (arg->value == FEATURE_DEVICE_REMOTE_WAKEUP) + { + dev->feature |= 1 << 5; + return USB_EVENT_SET_FEATURE_DEVICE; + } + } + else if (rcp == ENDPOINT_RECIPIENT) + { + uint8_t n = (arg->index & 0x0f); + + if (dev->configuration == 0) + return -1; + + if (arg->len != 0 || (arg->index >> 8) != 0 + || arg->value != FEATURE_ENDPOINT_HALT || n == ENDP0) + return -1; + + if (kl27z_ep_is_disabled (n)) + return -1; + + kl27z_ep_stall (n); + + return USB_EVENT_SET_FEATURE_ENDPOINT; + } + + return -1; +} + +static int +std_set_address (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type)) + return -1; + + if (rcp == DEVICE_RECIPIENT && arg->len == 0 && arg->value <= 127 + && arg->index == 0 && dev->configuration == 0) + return usb_lld_ctrl_ack (dev); + + return -1; +} + +static int +std_get_descriptor (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + if (USB_SETUP_SET (arg->type)) + return -1; + + return USB_EVENT_GET_DESCRIPTOR; +} + +static int +std_get_configuration (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_SET (arg->type)) + return -1; + + if (arg->value != 0 || arg->index != 0 || arg->len != 1) + return -1; + + if (rcp == DEVICE_RECIPIENT) + return usb_lld_ctrl_send (dev, &dev->configuration, 1); + + return -1; +} + +static int +std_set_configuration (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type)) + return -1; + + if (rcp == DEVICE_RECIPIENT && arg->index == 0 && arg->len == 0) + return USB_EVENT_SET_CONFIGURATION; + + return -1; +} + +static int +std_get_interface (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_SET (arg->type)) + return -1; + + if (arg->value != 0 || (arg->index >> 8) != 0 || arg->len != 1) + return -1; + + if (dev->configuration == 0) + return -1; + + if (rcp == INTERFACE_RECIPIENT) + return USB_EVENT_GET_INTERFACE; + + return -1; +} + +static int +std_set_interface (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type) || rcp != INTERFACE_RECIPIENT + || arg->len != 0 || (arg->index >> 8) != 0 + || (arg->value >> 8) != 0 || dev->configuration == 0) + return -1; + + return USB_EVENT_SET_INTERFACE; +} + + +static int +handle_setup0 (struct usb_dev *dev) +{ + struct ctrl_data *data_p = &dev->ctrl_data; + int r = -1; + HANDLER handler; + + data_p->addr = NULL; + data_p->len = 0; + data_p->require_zlp = 0; + + if ((dev->dev_req.type & REQUEST_TYPE) == STANDARD_REQUEST) + { + switch (dev->dev_req.request) + { + case 0: handler = std_get_status; break; + case 1: handler = std_clear_feature; break; + case 3: handler = std_set_feature; break; + case 5: handler = std_set_address; break; + case 6: handler = std_get_descriptor; break; + case 8: handler = std_get_configuration; break; + case 9: handler = std_set_configuration; break; + case 10: handler = std_get_interface; break; + case 11: handler = std_set_interface; break; + default: handler = std_none; break; + } + + if ((r = (*handler) (dev)) < 0) + { + usb_lld_ctrl_error (dev); + return USB_EVENT_OK; + } + else + return r; + } + else + return USB_EVENT_CTRL_REQUEST; +} + +static int +handle_in0 (struct usb_dev *dev, uint8_t stat) +{ + int r = 0; + + if (dev->state == IN_DATA || dev->state == LAST_IN_DATA) + handle_datastage_in (dev, stat); + else if (dev->state == WAIT_STATUS_IN) + { /* Control WRITE transfer done successfully. */ + uint16_t value = dev->dev_req.value; + + if ((dev->dev_req.request == SET_ADDRESS) && + ((dev->dev_req.type & (REQUEST_TYPE | RECIPIENT)) + == (STANDARD_REQUEST | DEVICE_RECIPIENT))) + { + kl27z_set_daddr (value); + ep[0].rx_odd = 0; + r = USB_EVENT_DEVICE_ADDRESSED; + } + else + r = USB_EVENT_CTRL_WRITE_FINISH; + dev->state = WAIT_SETUP; + kl27z_prepare_ep0_setup (dev); + } + else + { + dev->state = STALLED; + kl27z_ep_stall (ENDP0); + } + + return r; +} + +static void +handle_out0 (struct usb_dev *dev, uint8_t stat) +{ + if (dev->state == OUT_DATA) + /* It's normal control WRITE transfer. */ + handle_datastage_out (dev, stat); + else if (dev->state == WAIT_STATUS_OUT) + { /* Control READ transfer done successfully. */ + dev->state = WAIT_SETUP; + kl27z_prepare_ep0_setup (dev); + } + else + { + /* + * dev->state == IN_DATA || dev->state == LAST_IN_DATA + * (Host aborts the transfer before finish) + * Or else, unexpected state. + * STALL the endpoint, until we receive the next SETUP token. + */ + dev->state = STALLED; + kl27z_ep_stall (ENDP0); + } +} + +#define USB_TOKEN_ACK 0x02 +#define USB_TOKEN_IN 0x09 +#define USB_TOKEN_SETUP 0x0d + +static int +handle_transaction (struct usb_dev *dev, uint8_t stat) +{ + int odd = (stat >> 2)&1; + uint8_t ep_num = (stat >> 4); + int r; + + if (ep_num == 0) + { + if ((stat & 0x08) == 0) + { + ep[0].rx_odd ^= 1; + if (TOK_PID (BD_table[odd].ctrl) == USB_TOKEN_SETUP) + { + r = handle_setup0 (dev); + USB_CTRL1->ISTAT = USB_IS_TOKDNE; + USB_CTRL1->CTL = 0x01; /* Clear TXSUSPENDTOKENBUSY. */ + return USB_MAKE_EV (r); + } + else + { + handle_out0 (dev, stat); + USB_CTRL1->ISTAT = USB_IS_TOKDNE; + return USB_EVENT_OK; + } + } + else + { + ep[0].tx_odd ^= 1; + r = handle_in0 (dev, stat); + USB_CTRL1->ISTAT = USB_IS_TOKDNE; + return USB_MAKE_EV (r); + } + } + else + { + uint16_t len; + + if ((stat & 0x08) == 0) + { + len = (BD_table[4*ep_num+odd].ctrl >> 16)&0x3ff; + ep[ep_num].rx_odd ^= 1; + USB_CTRL1->ISTAT = USB_IS_TOKDNE; + return USB_MAKE_TXRX (ep_num, 0, len); + } + else + { + /* + * IN transaction is usually in a sequence like: + * + * -----time------> + * host: IN ACK + * device: DATA0/1 + * + * Although it is not described in the specification (it's + * ambiguous), it is actually possible for some host + * implementation to send back a NAK on erroneous case like + * a device sent oversized data. + * + * -----time------> + * host: IN NAK + * device: DATA0/1 + * + * We do our best to distinguish successful tx and tx with + * failure. + * + */ + uint32_t dmaerr = (USB_CTRL1->ERRSTAT & (1 << 5)); + int success = (dmaerr == 0); + + len = (BD_table[4*ep_num+2+odd].ctrl >> 16)&0x3ff; + if (!success) + USB_CTRL1->ERRSTAT = dmaerr; /* Clear error. */ + + ep[ep_num].tx_odd ^= 1; + USB_CTRL1->ISTAT = USB_IS_TOKDNE; + return USB_MAKE_TXRX (ep_num, 1, len); + } + } +} + +void +usb_lld_reset (struct usb_dev *dev, uint8_t feature) +{ + usb_lld_set_configuration (dev, 0); + dev->feature = feature; + dev->state = WAIT_SETUP; + + /* Reset USB */ + USB_CTRL2->USBTRC0 = 0xc0; + + USB_CTRL1->CTL = 0x00; /* Disable USB FS communication module */ + + kl27z_set_daddr (0); + kl27z_usb_init (); + + /* Clear Interrupt Status Register, and enable interrupt for USB */ + USB_CTRL1->ISTAT = 0xff; /* All clear */ + + USB_CTRL1->INTEN = USB_IS_STALL | USB_IS_TOKDNE + | USB_IS_ERROR | USB_IS_USBRST; +} + +void +usb_lld_setup_endp (struct usb_dev *dev, int n, int rx_en, int tx_en) +{ + if (n == 0) + { + /* Enable the endpoint 0. */ + USB_ENDPT[0].EP = 0x0d; + kl27z_prepare_ep0_setup (dev); + } + else + { + /* Enable the endpoint. */ + USB_ENDPT[n].EP = (rx_en << 3)|(tx_en << 2)|0x11; + + /* Configure BDT entry so that it starts with DATA0. */ + + /* RX */ + BD_table[4*n+ep[n].rx_odd].ctrl = 0x0000; + BD_table[4*n+ep[n].rx_odd].buf = NULL; + BD_table[4*n+!ep[n].rx_odd].ctrl = 0x0040; + BD_table[4*n+!ep[n].rx_odd].buf = NULL; + + /* TX */ + BD_table[4*n+2+ep[n].tx_odd].ctrl = 0x0000; + BD_table[4*n+2+ep[n].tx_odd].buf = NULL; + BD_table[4*n+2+!ep[n].tx_odd].ctrl = 0x0040; + BD_table[4*n+2+!ep[n].tx_odd].buf = NULL; + } +} + + +void +usb_lld_set_configuration (struct usb_dev *dev, uint8_t config) +{ + dev->configuration = config; +} + +uint8_t +usb_lld_current_configuration (struct usb_dev *dev) +{ + return dev->configuration; +} + +int +usb_lld_ctrl_recv (struct usb_dev *dev, void *p, size_t len) +{ + struct ctrl_data *data_p = &dev->ctrl_data; + data_p->addr = (uint8_t *)p; + data_p->len = len; + if (len > USB_MAX_PACKET_SIZE) + len = USB_MAX_PACKET_SIZE; + + kl27z_prepare_ep0_out (p, len, DATA1); + dev->state = OUT_DATA; + return USB_EVENT_OK; +} + +/* + * BUF: Pointer to data memory. Data memory should not be allocated + * on stack when BUFLEN > USB_MAX_PACKET_SIZE. + * + * BUFLEN: size of the data. + */ +int +usb_lld_ctrl_send (struct usb_dev *dev, const void *buf, size_t buflen) +{ + struct ctrl_data *data_p = &dev->ctrl_data; + uint32_t len_asked = dev->dev_req.len; + uint32_t len; + + data_p->addr = (void *)buf; + data_p->len = buflen; + + /* Restrict the data length to be the one host asks for */ + if (data_p->len >= len_asked) + data_p->len = len_asked; + /* ZLP is only required when host doesn't expect the end of packets. */ + else if (data_p->len != 0 && (data_p->len % USB_MAX_PACKET_SIZE) == 0) + data_p->require_zlp = 1; + + if (data_p->len < USB_MAX_PACKET_SIZE) + { + len = data_p->len; + dev->state = LAST_IN_DATA; + } + else + { + len = USB_MAX_PACKET_SIZE; + dev->state = IN_DATA; + } + + kl27z_prepare_ep0_in (data_p->addr, len, DATA1); + + data_p->len -= len; + data_p->addr += len; + + return USB_EVENT_OK; +} + +void +usb_lld_rx_enable_buf (int n, void *buf, size_t len) +{ + int data01 = !((BD_table[4*n+!ep[n].rx_odd].ctrl >> 6)&1); + + BD_table[4*n+ep[n].rx_odd].ctrl = (len << 16) | 0x0088 | (data01 << 6); + BD_table[4*n+ep[n].rx_odd].buf = buf; +} + + +void +usb_lld_tx_enable_buf (int n, const void *buf, size_t len) +{ + int data01 = !((BD_table[4*n+2+!ep[n].tx_odd].ctrl >> 6)&1); + + BD_table[4*n+2+ep[n].tx_odd].ctrl = (len << 16) | 0x0088 | (data01 << 6); + BD_table[4*n+2+ep[n].tx_odd].buf = (void *)buf; +} diff --git a/gnuk/chopstx/mcu/usb-stm32f103.c b/gnuk/chopstx/mcu/usb-stm32f103.c new file mode 100644 index 0000000000..79a7bfaa56 --- /dev/null +++ b/gnuk/chopstx/mcu/usb-stm32f103.c @@ -0,0 +1,1135 @@ +/* + * usb-stm32f103.c - USB driver for STM32F103 + * + * Copyright (C) 2016, 2017, 2018 Flying Stone Technology + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx 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. + * + * Chopstx 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 . + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +#include +#include + +#include "sys-stm32f103.h" +#include "usb_lld.h" +#include "usb_lld_driver.h" + +#define REG_BASE (0x40005C00UL) /* USB_IP Peripheral Registers base address */ +#define PMA_ADDR (0x40006000UL) /* USB_IP Packet Memory Area base address */ + +/* Control register */ +#define CNTR ((volatile uint16_t *)(REG_BASE + 0x40)) +/* Interrupt status register */ +#define ISTR ((volatile uint16_t *)(REG_BASE + 0x44)) +/* Frame number register */ +#define FNR ((volatile uint16_t *)(REG_BASE + 0x48)) +/* Device address register */ +#define DADDR ((volatile uint16_t *)(REG_BASE + 0x4C)) +/* Buffer Table address register */ +#define BTABLE ((volatile uint16_t *)(REG_BASE + 0x50)) + +#define ISTR_CTR (0x8000) /* Correct TRansfer (read-only bit) */ +#define ISTR_OVR (0x4000) /* OVeR/underrun (clear-only bit) */ +#define ISTR_ERR (0x2000) /* ERRor (clear-only bit) */ +#define ISTR_WKUP (0x1000) /* WaKe UP (clear-only bit) */ +#define ISTR_SUSP (0x0800) /* SUSPend (clear-only bit) */ +#define ISTR_RESET (0x0400) /* RESET (clear-only bit) */ +#define ISTR_SOF (0x0200) /* Start Of Frame (clear-only bit) */ +#define ISTR_ESOF (0x0100) /* Expected Start Of Frame (clear-only bit) */ + +#define ISTR_DIR (0x0010) /* DIRection of transaction (read-only bit) */ +#define ISTR_EP_ID (0x000F) /* EndPoint IDentifier (read-only bit) */ + +#define CLR_OVR (~ISTR_OVR) /* clear OVeR/underrun bit*/ +#define CLR_ERR (~ISTR_ERR) /* clear ERRor bit */ +#define CLR_WKUP (~ISTR_WKUP) /* clear WaKe UP bit */ +#define CLR_SUSP (~ISTR_SUSP) /* clear SUSPend bit */ +#define CLR_RESET (~ISTR_RESET) /* clear RESET bit */ +#define CLR_SOF (~ISTR_SOF) /* clear Start Of Frame bit */ +#define CLR_ESOF (~ISTR_ESOF) /* clear Expected Start Of Frame bit */ + +#define CNTR_CTRM (0x8000) /* Correct TRansfer Mask */ +#define CNTR_OVRM (0x4000) /* OVeR/underrun Mask */ +#define CNTR_ERRM (0x2000) /* ERRor Mask */ +#define CNTR_WKUPM (0x1000) /* WaKe UP Mask */ +#define CNTR_SUSPM (0x0800) /* SUSPend Mask */ +#define CNTR_RESETM (0x0400) /* RESET Mask */ +#define CNTR_SOFM (0x0200) /* Start Of Frame Mask */ +#define CNTR_ESOFM (0x0100) /* Expected Start Of Frame Mask */ + +#define CNTR_RESUME (0x0010) /* RESUME request */ +#define CNTR_FSUSP (0x0008) /* Force SUSPend */ +#define CNTR_LPMODE (0x0004) /* Low-power MODE */ +#define CNTR_PDWN (0x0002) /* Power DoWN */ +#define CNTR_FRES (0x0001) /* Force USB RESet */ + +#define DADDR_EF (0x80) +#define DADDR_ADD (0x7F) + +#define EP_CTR_RX (0x8000) /* EndPoint Correct TRansfer RX */ +#define EP_DTOG_RX (0x4000) /* EndPoint Data TOGGLE RX */ +#define EPRX_STAT (0x3000) /* EndPoint RX STATus bit field */ +#define EP_SETUP (0x0800) /* EndPoint SETUP */ +#define EP_T_FIELD (0x0600) /* EndPoint TYPE */ +#define EP_KIND (0x0100) /* EndPoint KIND */ +#define EP_CTR_TX (0x0080) /* EndPoint Correct TRansfer TX */ +#define EP_DTOG_TX (0x0040) /* EndPoint Data TOGGLE TX */ +#define EPTX_STAT (0x0030) /* EndPoint TX STATus bit field */ +#define EPADDR_FIELD (0x000F) /* EndPoint ADDRess FIELD */ + +#define EPREG_MASK (EP_CTR_RX|EP_SETUP|EP_T_FIELD|EP_KIND|EP_CTR_TX|EPADDR_FIELD) + +/* STAT_TX[1:0] STATus for TX transfer */ +#define EP_TX_DIS (0x0000) /* EndPoint TX DISabled */ +#define EP_TX_STALL (0x0010) /* EndPoint TX STALLed */ +#define EP_TX_NAK (0x0020) /* EndPoint TX NAKed */ +#define EP_TX_VALID (0x0030) /* EndPoint TX VALID */ +#define EPTX_DTOG1 (0x0010) /* EndPoint TX Data TOGgle bit1 */ +#define EPTX_DTOG2 (0x0020) /* EndPoint TX Data TOGgle bit2 */ + +/* STAT_RX[1:0] STATus for RX transfer */ +#define EP_RX_DIS (0x0000) /* EndPoint RX DISabled */ +#define EP_RX_STALL (0x1000) /* EndPoint RX STALLed */ +#define EP_RX_NAK (0x2000) /* EndPoint RX NAKed */ +#define EP_RX_VALID (0x3000) /* EndPoint RX VALID */ +#define EPRX_DTOG1 (0x1000) /* EndPoint RX Data TOGgle bit1 */ +#define EPRX_DTOG2 (0x2000) /* EndPoint RX Data TOGgle bit1 */ + +static int usb_handle_transfer (struct usb_dev *dev, uint16_t istr_value); + +static void st103_set_btable (void) +{ + *BTABLE = 0; +} + +static uint16_t st103_get_istr (void) +{ + return *ISTR; +} + +static void st103_set_istr (uint16_t istr) +{ + *ISTR = istr; +} + +static void st103_set_cntr (uint16_t cntr) +{ + *CNTR = cntr; +} + +static void st103_set_daddr (uint16_t daddr) +{ + *DADDR = daddr | DADDR_EF; +} + +static void st103_set_epreg (uint8_t ep_num, uint16_t value) +{ + uint16_t *reg_p = (uint16_t *)(REG_BASE + ep_num*4); + + *reg_p = value; +} + +static uint16_t st103_get_epreg (uint8_t ep_num) +{ + uint16_t *reg_p = (uint16_t *)(REG_BASE + ep_num*4); + + return *reg_p; +} + +static void st103_set_tx_addr (uint8_t ep_num, uint16_t addr) +{ + uint16_t *reg_p = (uint16_t *)(PMA_ADDR + (ep_num*8+0)*2); + + *reg_p = addr; +} + +static uint16_t st103_get_tx_addr (uint8_t ep_num) +{ + uint16_t *reg_p = (uint16_t *)(PMA_ADDR + (ep_num*8+0)*2); + + return *reg_p; +} + + +static void st103_set_tx_count (uint8_t ep_num, uint16_t size) +{ + uint16_t *reg_p = (uint16_t *)(PMA_ADDR + (ep_num*8+2)*2); + + *reg_p = size; +} + +static uint16_t st103_get_tx_count (uint8_t ep_num) +{ + uint16_t *reg_p = (uint16_t *)(PMA_ADDR + (ep_num*8+2)*2); + + return *reg_p & 0x03ff; +} + + +static void st103_set_rx_addr (uint8_t ep_num, uint16_t addr) +{ + uint16_t *reg_p = (uint16_t *)(PMA_ADDR + (ep_num*8+4)*2); + + *reg_p = addr; +} + +static uint16_t st103_get_rx_addr (uint8_t ep_num) +{ + uint16_t *reg_p = (uint16_t *)(PMA_ADDR + (ep_num*8+4)*2); + + return *reg_p; +} + + +static void st103_set_rx_buf_size (uint8_t ep_num, uint16_t size) +{ /* Assume size is even */ + uint16_t *reg_p = (uint16_t *)(PMA_ADDR + (ep_num*8+6)*2); + uint16_t value; + + if (size <= 62) + value = (size & 0x3e) << 9; + else + value = 0x8000 | (((size >> 5) - 1) << 10); + + *reg_p = value; +} + +static uint16_t st103_get_rx_count (uint8_t ep_num) +{ + uint16_t *reg_p = (uint16_t *)(PMA_ADDR + (ep_num*8+6)*2); + + return *reg_p & 0x03ff; +} + + +static void st103_ep_clear_ctr_rx (uint8_t ep_num) +{ + uint16_t value = st103_get_epreg (ep_num) & ~EP_CTR_RX & EPREG_MASK; + + st103_set_epreg (ep_num, value); +} + +static void st103_ep_clear_ctr_tx (uint8_t ep_num) +{ + uint16_t value = st103_get_epreg (ep_num) & ~EP_CTR_TX & EPREG_MASK; + + st103_set_epreg (ep_num, value); +} + +static void st103_ep_set_rxtx_status (uint8_t ep_num, uint16_t st_rx, + uint16_t st_tx) +{ + uint16_t value = st103_get_epreg (ep_num); + + value &= (EPREG_MASK|EPRX_STAT|EPTX_STAT); + value ^= (EPRX_DTOG1 & st_rx); + value ^= (EPRX_DTOG2 & st_rx); + value ^= (EPTX_DTOG1 & st_tx); + value ^= (EPTX_DTOG2 & st_tx); + value |= EP_CTR_RX | EP_CTR_TX; + st103_set_epreg (ep_num, value); +} + +static void st103_ep_set_rx_status (uint8_t ep_num, uint16_t st_rx) +{ + uint16_t value = st103_get_epreg (ep_num); + + value &= (EPREG_MASK|EPRX_STAT); + value ^= (EPRX_DTOG1 & st_rx); + value ^= (EPRX_DTOG2 & st_rx); + value |= EP_CTR_RX | EP_CTR_TX; + st103_set_epreg (ep_num, value); +} + +static uint16_t st103_ep_get_rx_status (uint8_t ep_num) +{ + uint16_t value = st103_get_epreg (ep_num); + + return value & EPRX_STAT; +} + +static void st103_ep_set_tx_status (uint8_t ep_num, uint16_t st_tx) +{ + uint16_t value = st103_get_epreg (ep_num); + + value &= (EPREG_MASK|EPTX_STAT); + value ^= (EPTX_DTOG1 & st_tx); + value ^= (EPTX_DTOG2 & st_tx); + value |= EP_CTR_RX | EP_CTR_TX; + st103_set_epreg (ep_num, value); +} + +static uint16_t st103_ep_get_tx_status (uint8_t ep_num) +{ + uint16_t value = st103_get_epreg (ep_num); + + return value & EPTX_STAT; +} + +static void st103_ep_clear_dtog_rx (uint8_t ep_num) +{ + uint16_t value = st103_get_epreg (ep_num); + + if ((value & EP_DTOG_RX)) + { + value &= EPREG_MASK; + value |= EP_CTR_RX | EP_CTR_TX | EP_DTOG_RX; + st103_set_epreg (ep_num, value); + } +} + +static void st103_ep_clear_dtog_tx (uint8_t ep_num) +{ + uint16_t value = st103_get_epreg (ep_num); + + if ((value & EP_DTOG_TX)) + { + value &= EPREG_MASK; + value |= EP_CTR_RX | EP_CTR_TX | EP_DTOG_TX; + st103_set_epreg (ep_num, value); + } +} + +void +usb_lld_ctrl_error (struct usb_dev *dev) +{ + dev->state = STALLED; + st103_ep_set_rxtx_status (ENDP0, EP_RX_STALL, EP_TX_STALL); +} + +int +usb_lld_ctrl_ack (struct usb_dev *dev) +{ + dev->state = WAIT_STATUS_IN; + st103_set_tx_count (ENDP0, 0); + st103_ep_set_rxtx_status (ENDP0, EP_RX_NAK, EP_TX_VALID); + return USB_EVENT_OK; +} + +void usb_lld_init (struct usb_dev *dev, uint8_t feature) +{ + usb_lld_sys_init (); + + dev->configuration = 0; + dev->feature = feature; + dev->state = WAIT_SETUP; + + /* Reset USB */ + st103_set_cntr (CNTR_FRES); + st103_set_cntr (0); + + /* Clear Interrupt Status Register, and enable interrupt for USB */ + st103_set_istr (0); + + st103_set_btable (); + + st103_set_cntr (CNTR_CTRM | CNTR_OVRM | CNTR_ERRM + | CNTR_WKUPM | CNTR_SUSPM | CNTR_RESETM); + +#if 0 +/* + * Since stop mode makes PLL, HSI & HES oscillators stop, USB clock is + * not supplied in stop mode. Thus, USB wakeup can't occur. + * + * So, only sleep mode can be supported with USB, which doesn't + * require use of EXTI. + */ +#include "mcu/stm32f103.h" + /* Setting of EXTI wakeup event to break stop mode. */ + EXTI->EMR |= (1 << 18); /* Event mask cleared */ + EXTI->RTSR |= (1 << 18); /* Rising trigger selection */ +#endif +} + +void usb_lld_prepare_shutdown (void) +{ + st103_set_istr (0); + st103_set_cntr (0); +} + +void usb_lld_shutdown (void) +{ + st103_set_cntr (CNTR_PDWN); + usb_lld_sys_shutdown (); +} + +#define USB_MAKE_EV(event) (event<<24) +#define USB_MAKE_TXRX(ep_num,txrx,len) ((txrx? (1<<23):0)|(ep_num<<16)|len) + +int +usb_lld_event_handler (struct usb_dev *dev) +{ + uint16_t istr_value = st103_get_istr (); + + if ((istr_value & ISTR_RESET)) + { + st103_set_istr (CLR_RESET); + return USB_MAKE_EV (USB_EVENT_DEVICE_RESET); + } + else if ((istr_value & ISTR_WKUP)) + { + *CNTR &= ~CNTR_FSUSP; + st103_set_istr (CLR_WKUP); + return USB_MAKE_EV (USB_EVENT_DEVICE_WAKEUP); + } + else if ((istr_value & ISTR_SUSP)) + { + *CNTR |= CNTR_FSUSP; + st103_set_istr (CLR_SUSP); + *CNTR |= CNTR_LPMODE; + return USB_MAKE_EV (USB_EVENT_DEVICE_SUSPEND); + } + else + { + if ((istr_value & ISTR_OVR)) + st103_set_istr (CLR_OVR); + + if ((istr_value & ISTR_ERR)) + st103_set_istr (CLR_ERR); + + if ((istr_value & ISTR_CTR)) + return usb_handle_transfer (dev, istr_value); + } + + return USB_EVENT_OK; +} + +static void handle_datastage_out (struct usb_dev *dev) +{ + if (dev->ctrl_data.addr && dev->ctrl_data.len) + { + uint32_t len = st103_get_rx_count (ENDP0); + + if (len > dev->ctrl_data.len) + len = dev->ctrl_data.len; + + usb_lld_from_pmabuf (dev->ctrl_data.addr, st103_get_rx_addr (ENDP0), len); + dev->ctrl_data.len -= len; + dev->ctrl_data.addr += len; + } + + if (dev->ctrl_data.len == 0) + { + dev->state = WAIT_STATUS_IN; + st103_set_tx_count (ENDP0, 0); + st103_ep_set_tx_status (ENDP0, EP_TX_VALID); + } + else + { + dev->state = OUT_DATA; + st103_ep_set_rx_status (ENDP0, EP_RX_VALID); + } +} + +static void handle_datastage_in (struct usb_dev *dev) +{ + uint32_t len = USB_MAX_PACKET_SIZE;; + struct ctrl_data *data_p = &dev->ctrl_data; + + if ((data_p->len == 0) && (dev->state == LAST_IN_DATA)) + { + if (data_p->require_zlp) + { + data_p->require_zlp = 0; + + /* No more data to send. Send empty packet */ + st103_set_tx_count (ENDP0, 0); + st103_ep_set_tx_status (ENDP0, EP_TX_VALID); + } + else + { + /* No more data to send, proceed to receive OUT acknowledge. */ + dev->state = WAIT_STATUS_OUT; + st103_ep_set_rx_status (ENDP0, EP_RX_VALID); + } + + return; + } + + dev->state = (data_p->len <= len) ? LAST_IN_DATA : IN_DATA; + + if (len > data_p->len) + len = data_p->len; + + usb_lld_to_pmabuf (data_p->addr, st103_get_tx_addr (ENDP0), len); + data_p->len -= len; + data_p->addr += len; + st103_set_tx_count (ENDP0, len); + st103_ep_set_tx_status (ENDP0, EP_TX_VALID); +} + +typedef int (*HANDLER) (struct usb_dev *dev); + +static int std_none (struct usb_dev *dev) +{ + (void)dev; + return -1; +} + +static int std_get_status (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = (arg->type & RECIPIENT); + uint16_t status_info = 0; + + if (arg->value != 0 || arg->len != 2 || (arg->index >> 8) != 0 + || USB_SETUP_SET (arg->type)) + return -1; + + if (rcp == DEVICE_RECIPIENT) + { + if (arg->index == 0) + { + /* Get Device Status */ + uint8_t feature = dev->feature; + + /* Remote Wakeup enabled */ + if ((feature & (1 << 5))) + status_info |= 2; + else + status_info &= ~2; + + /* Bus-powered */ + if ((feature & (1 << 6))) + status_info |= 1; + else /* Self-powered */ + status_info &= ~1; + + return usb_lld_ctrl_send (dev, &status_info, 2); + } + } + else if (rcp == INTERFACE_RECIPIENT) + { + if (dev->configuration == 0) + return -1; + + return USB_EVENT_GET_STATUS_INTERFACE; + } + else if (rcp == ENDPOINT_RECIPIENT) + { + uint8_t endpoint = (arg->index & 0x0f); + uint16_t status; + + if ((arg->index & 0x70) || endpoint == ENDP0) + return -1; + + if ((arg->index & 0x80)) + { + status = st103_ep_get_tx_status (endpoint); + if (status == 0) /* Disabled */ + return -1; + else if (status == EP_TX_STALL) + status_info |= 1; /* IN Endpoint stalled */ + } + else + { + status = st103_ep_get_rx_status (endpoint); + if (status == 0) /* Disabled */ + return -1; + else if (status == EP_RX_STALL) + status_info |= 1; /* OUT Endpoint stalled */ + } + + return usb_lld_ctrl_send (dev, &status_info, 2); + } + + return -1; +} + +static int std_clear_feature (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type)) + return -1; + + if (rcp == DEVICE_RECIPIENT) + { + if (arg->len != 0 || arg->index != 0) + return -1; + + if (arg->value == FEATURE_DEVICE_REMOTE_WAKEUP) + { + dev->feature &= ~(1 << 5); + return USB_EVENT_CLEAR_FEATURE_DEVICE; + } + } + else if (rcp == ENDPOINT_RECIPIENT) + { + uint8_t endpoint = (arg->index & 0x0f); + uint16_t status; + + if (dev->configuration == 0) + return -1; + + if (arg->len != 0 || (arg->index >> 8) != 0 + || arg->value != FEATURE_ENDPOINT_HALT || endpoint == ENDP0) + return -1; + + if ((arg->index & 0x80)) + status = st103_ep_get_tx_status (endpoint); + else + status = st103_ep_get_rx_status (endpoint); + + if (status == 0) /* It's disabled endpoint. */ + return -1; + + if (arg->index & 0x80) /* IN endpoint */ + st103_ep_clear_dtog_tx (endpoint); + else /* OUT endpoint */ + st103_ep_clear_dtog_rx (endpoint); + + return USB_EVENT_CLEAR_FEATURE_ENDPOINT; + } + + return -1; +} + +static int std_set_feature (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type)) + return -1; + + if (rcp == DEVICE_RECIPIENT) + { + if (arg->len != 0 || arg->index != 0) + return -1; + + if (arg->value == FEATURE_DEVICE_REMOTE_WAKEUP) + { + dev->feature |= 1 << 5; + return USB_EVENT_SET_FEATURE_DEVICE; + } + } + else if (rcp == ENDPOINT_RECIPIENT) + { + uint8_t endpoint = (arg->index & 0x0f); + uint32_t status; + + if (dev->configuration == 0) + return -1; + + if (arg->len != 0 || (arg->index >> 8) != 0 + || arg->value != FEATURE_ENDPOINT_HALT || endpoint == ENDP0) + return -1; + + if ((arg->index & 0x80)) + status = st103_ep_get_tx_status (endpoint); + else + status = st103_ep_get_rx_status (endpoint); + + if (status == 0) /* It's disabled endpoint. */ + return -1; + + if (arg->index & 0x80) /* IN endpoint */ + st103_ep_set_tx_status (endpoint, EP_TX_STALL); + else /* OUT endpoint */ + st103_ep_set_rx_status (endpoint, EP_RX_STALL); + + return USB_EVENT_SET_FEATURE_ENDPOINT; + } + + return -1; +} + +static int std_set_address (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type)) + return -1; + + if (rcp == DEVICE_RECIPIENT && arg->len == 0 && arg->value <= 127 + && arg->index == 0 && dev->configuration == 0) + return usb_lld_ctrl_ack (dev); + + return -1; +} + +static int std_get_descriptor (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + if (USB_SETUP_SET (arg->type)) + return -1; + + return USB_EVENT_GET_DESCRIPTOR; +} + +static int std_get_configuration (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_SET (arg->type)) + return -1; + + if (arg->value != 0 || arg->index != 0 || arg->len != 1) + return -1; + + if (rcp == DEVICE_RECIPIENT) + return usb_lld_ctrl_send (dev, &dev->configuration, 1); + + return -1; +} + +static int std_set_configuration (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type)) + return -1; + + if (arg->index != 0 || arg->len != 0) + return -1; + + if (rcp == DEVICE_RECIPIENT) + return USB_EVENT_SET_CONFIGURATION; + + return -1; +} + +static int std_get_interface (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_SET (arg->type)) + return -1; + + if (arg->value != 0 || (arg->index >> 8) != 0 || arg->len != 1) + return -1; + + if (dev->configuration == 0) + return -1; + + if (rcp == INTERFACE_RECIPIENT) + return USB_EVENT_GET_INTERFACE; + + return -1; +} + +static int std_set_interface (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type) || rcp != INTERFACE_RECIPIENT + || arg->len != 0 || (arg->index >> 8) != 0 + || (arg->value >> 8) != 0 || dev->configuration == 0) + return -1; + + return USB_EVENT_SET_INTERFACE; +} + + +static int handle_setup0 (struct usb_dev *dev) +{ + const uint16_t *pw; + uint16_t w; + uint8_t req_no; + HANDLER handler; + + pw = (uint16_t *)(PMA_ADDR + (uint8_t *)(st103_get_rx_addr (ENDP0) * 2)); + w = *pw++; + + dev->dev_req.type = (w & 0xff); + dev->dev_req.request = req_no = (w >> 8); + pw++; + dev->dev_req.value = *pw++; + pw++; + dev->dev_req.index = *pw++; + pw++; + dev->dev_req.len = *pw; + + dev->ctrl_data.addr = NULL; + dev->ctrl_data.len = 0; + dev->ctrl_data.require_zlp = 0; + + if ((dev->dev_req.type & REQUEST_TYPE) == STANDARD_REQUEST) + { + int r; + + switch (req_no) + { + case 0: handler = std_get_status; break; + case 1: handler = std_clear_feature; break; + case 3: handler = std_set_feature; break; + case 5: handler = std_set_address; break; + case 6: handler = std_get_descriptor; break; + case 8: handler = std_get_configuration; break; + case 9: handler = std_set_configuration; break; + case 10: handler = std_get_interface; break; + case 11: handler = std_set_interface; break; + default: handler = std_none; break; + } + + if ((r = (*handler) (dev)) < 0) + { + usb_lld_ctrl_error (dev); + return USB_EVENT_OK; + } + else + return r; + } + else + return USB_EVENT_CTRL_REQUEST; +} + +static int handle_in0 (struct usb_dev *dev) +{ + int r = 0; + + if (dev->state == IN_DATA || dev->state == LAST_IN_DATA) + handle_datastage_in (dev); + else if (dev->state == WAIT_STATUS_IN) + { + dev->state = WAIT_SETUP; + + if ((dev->dev_req.request == SET_ADDRESS) && + ((dev->dev_req.type & (REQUEST_TYPE | RECIPIENT)) + == (STANDARD_REQUEST | DEVICE_RECIPIENT))) + { + st103_set_daddr (dev->dev_req.value); + r = USB_EVENT_DEVICE_ADDRESSED; + } + else + r = USB_EVENT_CTRL_WRITE_FINISH; + } + else + { + dev->state = STALLED; + st103_ep_set_rxtx_status (ENDP0, EP_RX_STALL, EP_TX_STALL); + } + + return r; +} + +static void handle_out0 (struct usb_dev *dev) +{ + if (dev->state == OUT_DATA) + /* Usual case. */ + handle_datastage_out (dev); + else if (dev->state == WAIT_STATUS_OUT) + /* + * Control READ transfer finished by ZLP. + * Leave ENDP0 status RX_NAK, TX_NAK. + */ + dev->state = WAIT_SETUP; + else + { + /* + * dev->state == IN_DATA || dev->state == LAST_IN_DATA + * (Host aborts the transfer before finish) + * Or else, unexpected state. + * STALL the endpoint, until we receive the next SETUP token. + */ + dev->state = STALLED; + st103_ep_set_rxtx_status (ENDP0, EP_RX_STALL, EP_TX_STALL); + } +} + + +static int +usb_handle_transfer (struct usb_dev *dev, uint16_t istr_value) +{ + uint16_t ep_value = 0; + uint8_t ep_num = (istr_value & ISTR_EP_ID); + + ep_value = st103_get_epreg (ep_num); + + if (ep_num == 0) + { + if ((ep_value & EP_CTR_TX)) + { + st103_ep_clear_ctr_tx (ep_num); + return USB_MAKE_EV (handle_in0 (dev)); + } + + if ((ep_value & EP_CTR_RX)) + { + st103_ep_clear_ctr_rx (ep_num); + + if ((ep_value & EP_SETUP)) + return USB_MAKE_EV (handle_setup0 (dev)); + else + { + handle_out0 (dev); + return USB_EVENT_OK; + } + } + } + else + { + uint16_t len; + + if ((ep_value & EP_CTR_RX)) + { + len = st103_get_rx_count (ep_num); + st103_ep_clear_ctr_rx (ep_num); + return USB_MAKE_TXRX (ep_num, 0, len); + } + + if ((ep_value & EP_CTR_TX)) + { + len = st103_get_tx_count (ep_num); + st103_ep_clear_ctr_tx (ep_num); + return USB_MAKE_TXRX (ep_num, 1, len); + } + } + + return USB_EVENT_OK; +} + +void usb_lld_reset (struct usb_dev *dev, uint8_t feature) +{ + usb_lld_set_configuration (dev, 0); + dev->feature = feature; + st103_set_daddr (0); +} + +void usb_lld_txcpy (const void *src, + int ep_num, int offset, size_t len) +{ + usb_lld_to_pmabuf (src, st103_get_tx_addr (ep_num) + offset, len); +} + +void usb_lld_write (uint8_t ep_num, const void *buf, size_t len) +{ + usb_lld_to_pmabuf (buf, st103_get_tx_addr (ep_num), len); + st103_set_tx_count (ep_num, len); + st103_ep_set_tx_status (ep_num, EP_TX_VALID); +} + +void usb_lld_rxcpy (uint8_t *dst, + int ep_num, int offset, size_t len) +{ + usb_lld_from_pmabuf (dst, st103_get_rx_addr (ep_num) + offset, len); +} + +void usb_lld_tx_enable (int ep_num, size_t len) +{ + st103_set_tx_count (ep_num, len); + st103_ep_set_tx_status (ep_num, EP_TX_VALID); +} + +void usb_lld_stall_tx (int ep_num) +{ + st103_ep_set_tx_status (ep_num, EP_TX_STALL); +} + +void usb_lld_stall_rx (int ep_num) +{ + st103_ep_set_rx_status (ep_num, EP_RX_STALL); +} + +void usb_lld_rx_enable (int ep_num) +{ + st103_ep_set_rx_status (ep_num, EP_RX_VALID); +} + +void usb_lld_setup_endpoint (int ep_num, int ep_type, int ep_kind, + int ep_rx_addr, int ep_tx_addr, + int ep_rx_buf_size) +{ + uint16_t epreg_value = st103_get_epreg (ep_num); + uint16_t ep_rxtx_status = 0; /* Both disabled */ + + /* Clear: Write 1 if 1: EP_DTOG_RX, EP_DTOG_TX */ + /* Set: Write: EP_T_FIELD, EP_KIND, EPADDR_FIELD */ + /* Set: Toggle: EPRX_STAT, EPTX_STAT */ + epreg_value &= (EPRX_STAT | EP_SETUP | EPTX_STAT | EP_DTOG_RX | EP_DTOG_TX); +#if USB_KEEP_CORRECT_TRANSFER_FLAGS + /* Keep: Write 1: EP_CTR_RX, EP_CTR_TX */ + epreg_value |= (EP_CTR_RX|EP_CTR_TX); +#else + /* Clear: Write 0: EP_CTR_RX, EP_CTR_TX */ +#endif + epreg_value |= ep_type; + epreg_value |= ep_kind; + epreg_value |= ep_num; + + if (ep_rx_addr) + { + ep_rxtx_status |= EP_RX_NAK; + st103_set_rx_addr (ep_num, ep_rx_addr); + st103_set_rx_buf_size (ep_num, ep_rx_buf_size); + } + + if (ep_tx_addr) + { + ep_rxtx_status |= EP_TX_NAK; + st103_set_tx_addr (ep_num, ep_tx_addr); + } + + epreg_value ^= (EPRX_DTOG1 & ep_rxtx_status); + epreg_value ^= (EPRX_DTOG2 & ep_rxtx_status); + epreg_value ^= (EPTX_DTOG1 & ep_rxtx_status); + epreg_value ^= (EPTX_DTOG2 & ep_rxtx_status); + + st103_set_epreg (ep_num, epreg_value); +} + +void usb_lld_set_configuration (struct usb_dev *dev, uint8_t config) +{ + dev->configuration = config; +} + +uint8_t usb_lld_current_configuration (struct usb_dev *dev) +{ + return dev->configuration; +} + +int usb_lld_ctrl_recv (struct usb_dev *dev, void *p, size_t len) +{ + struct ctrl_data *data_p = &dev->ctrl_data; + data_p->addr = p; + data_p->len = len; + dev->state = OUT_DATA; + st103_ep_set_rx_status (ENDP0, EP_RX_VALID); + return USB_EVENT_OK; +} + +void usb_lld_to_pmabuf (const void *src, uint16_t addr, size_t n) +{ + const uint8_t *s = (const uint8_t *)src; + uint16_t *p; + uint16_t w; + + if (n == 0) + return; + + if ((addr & 1)) + { + p = (uint16_t *)(PMA_ADDR + (addr - 1) * 2); + w = *p; + w = (w & 0xff) | (*s++) << 8; + *p = w; + p += 2; + n--; + } + else + p = (uint16_t *)(PMA_ADDR + addr * 2); + + while (n >= 2) + { + w = *s++; + w |= (*s++) << 8; + *p = w; + p += 2; + n -= 2; + } + + if (n > 0) + { + w = *s; + *p = w; + } +} + +void usb_lld_from_pmabuf (void *dst, uint16_t addr, size_t n) +{ + uint8_t *d = (uint8_t *)dst; + uint16_t *p; + uint16_t w; + + if (n == 0) + return; + + if ((addr & 1)) + { + p = (uint16_t *)(PMA_ADDR + (addr - 1) * 2); + w = *p; + *d++ = (w >> 8); + p += 2; + n--; + } + else + p = (uint16_t *)(PMA_ADDR + addr * 2); + + while (n >= 2) + { + w = *p; + *d++ = (w & 0xff); + *d++ = (w >> 8); + p += 2; + n -= 2; + } + + if (n > 0) + { + w = *p; + *d = (w & 0xff); + } +} + + +/* + * BUF: Pointer to data memory. Data memory should not be allocated + * on stack when BUFLEN > USB_MAX_PACKET_SIZE. + * + * BUFLEN: size of the data. + */ +int +usb_lld_ctrl_send (struct usb_dev *dev, const void *buf, size_t buflen) +{ + struct ctrl_data *data_p = &dev->ctrl_data; + uint32_t len_asked = dev->dev_req.len; + uint32_t len; + + data_p->addr = (void *)buf; + data_p->len = buflen; + + /* Restrict the data length to be the one host asks for */ + if (data_p->len >= len_asked) + data_p->len = len_asked; + /* ZLP is only required when host doesn't expect the end of packets. */ + else if (data_p->len != 0 && (data_p->len % USB_MAX_PACKET_SIZE) == 0) + data_p->require_zlp = 1; + + if (data_p->len < USB_MAX_PACKET_SIZE) + { + len = data_p->len; + dev->state = LAST_IN_DATA; + } + else + { + len = USB_MAX_PACKET_SIZE; + dev->state = IN_DATA; + } + + if (len) + { + usb_lld_to_pmabuf (data_p->addr, st103_get_tx_addr (ENDP0), len); + data_p->len -= len; + data_p->addr += len; + } + + st103_set_tx_count (ENDP0, len); + st103_ep_set_rxtx_status (ENDP0, EP_RX_NAK, EP_TX_VALID); + return USB_EVENT_OK; +} diff --git a/gnuk/chopstx/mcu/usb-usbip.c b/gnuk/chopstx/mcu/usb-usbip.c new file mode 100644 index 0000000000..ed8402827d --- /dev/null +++ b/gnuk/chopstx/mcu/usb-usbip.c @@ -0,0 +1,2317 @@ +/* + * usb-usbip.c - USB Device Emulation (server side) by USBIP + * + * Copyright (C) 2017, 2018 g10 Code GmbH + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx 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. + * + * Chopstx 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 . + * + */ + +/* + FIXME: + RESET handling + USB Shutdown + Use reply structure of its own + */ + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include "sys.h" /* for debug */ + +static pthread_t tid_main; +static pthread_t tid_usbip; + +#define USBIP_PORT 3240 + +#define CMD_REQ_LIST 0x01118005 +#define CMD_REQ_ATTACH 0x01118003 +#define CMD_URB_SUBMIT 0x00000001 +#define CMD_URB_UNLINK 0x00000002 + +#define REP_URB_SUBMIT 0x00000003 +#define REP_URB_UNLINK 0x00000004 + +struct usbip_msg_head { + uint32_t cmd; + uint32_t seq; +}; + +struct usbip_usb_device { + char path[256]; + char busid[32]; + + uint32_t busnum; + uint32_t devnum; + uint32_t speed; + + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + + uint8_t bConfigurationValue; + uint8_t bNumConfigurations; + uint8_t bNumInterfaces; +} __attribute__((packed)); + +struct urb { + struct urb *next; + struct urb *prev; + + uint16_t remain; + char *data_p; + + pthread_t tid; + uint32_t seq; + uint32_t devid; + uint32_t dir; + uint32_t ep; + uint32_t len; + uint8_t setup[8]; + char data[0]; +}; + +/* + * Only support a single device. + */ +static struct usbip_usb_device usbip_usb_device; + +static struct urb *issue_get_desc (void); + +#define MY_BUS_ID "1-1\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" + +#define USBIP_DIR_OUT 0 +#define USBIP_DIR_IN 1 + +static void +refresh_usb_device (void) +{ + struct urb *urb = issue_get_desc (); + char *desc = urb->data; + + memset (usbip_usb_device.path, 0, 256); + strcpy (usbip_usb_device.path, + "/sys/devices/pci0000:00/0000:00:01.1/usb1/1-1"); + memcpy (usbip_usb_device.busid, MY_BUS_ID, 32); + + usbip_usb_device.busnum = 0; + usbip_usb_device.devnum = 0; + usbip_usb_device.speed = htonl (2); /* Full speed. */ + + /* USB descriptors are little endian. USBIP is network order. */ + + /* 0: size=18 (or follows more desc) */ + /* 1: DEVICE_DESCRIPTOR */ + /* 2: bcdUSB: ignore or use for speed? */ + /* 4: bDeviceClass */ + /* 5: bDeviceSubClass */ + /* 6: bDeviceProtocol */ + /* 7: bMaxPacketSize: ignore */ + /* 8: idVendor */ + /* 10: idProduct */ + /* 12: bcdDevice */ + /* 14: iManufacturer: ignore */ + /* 15: iProduct: ignore */ + /* 16: iSerialNumber: ignore */ + /* 17: bNumConfigurations */ + /* ... */ + usbip_usb_device.idVendor = htons (((desc[9] << 8)|desc[8])); + usbip_usb_device.idProduct = htons (((desc[11] << 8)|desc[10])); + usbip_usb_device.bcdDevice = htons (((desc[13] << 8)|desc[12])); + + usbip_usb_device.bDeviceClass = desc[4]; + usbip_usb_device.bDeviceSubClass = desc[5]; + usbip_usb_device.bDeviceProtocol = desc[6]; + + usbip_usb_device.bConfigurationValue = 0; + usbip_usb_device.bNumConfigurations = desc[17]; + usbip_usb_device.bNumInterfaces = 0; + free (urb); +} + +#define USBIP_REPLY_HEADER_SIZE 8 +#define DEVICE_INFO_SIZE (256+32+12+6+6) +#define INTERFACE_INFO_SIZE 4 +#define DEVICE_LIST_SIZE (DEVICE_INFO_SIZE*1) + +#define USBIP_REPLY_DEVICE_LIST "\x01\x11\x00\x05" +#define USBIP_REPLY_ATTACH "\x01\x11\x00\x03" + +#define NETWORK_UINT32_ZERO "\x00\x00\x00\x00" +#define NETWORK_UINT32_ONE "\x00\x00\x00\x01" +#define NETWORK_UINT32_TWO "\x00\x00\x00\x02" +#define NETWORK_UINT16_ZERO "\x00\x00" +#define NETWORK_UINT16_ONE_ONE "\x01\x01" + +enum { + USB_INTR_NONE = 0, + USB_INTR_SETUP, + USB_INTR_DATA_TRANSFER, + USB_INTR_RESET, + USB_INTR_SUSPEND, + USB_INTR_SHUTDOWN +}; + +struct usb_controller { + pthread_mutex_t mutex; + pthread_cond_t cond; + uint8_t intr; + uint8_t dir; + uint8_t ep_num; +}; + +static struct usb_controller usbc; + +static void +notify_device (uint8_t intr, uint8_t ep_num, uint8_t dir) +{ + pthread_mutex_lock (&usbc.mutex); + if (usbc.intr) + pthread_cond_wait (&usbc.cond, &usbc.mutex); + usbc.intr = intr; + usbc.dir = (dir == USBIP_DIR_IN); + usbc.ep_num = ep_num; + pthread_kill (tid_main, SIGUSR1); + pthread_mutex_unlock (&usbc.mutex); +} + + +static const char * +list_devices (size_t *len_p) +{ + refresh_usb_device (); + *len_p = sizeof (usbip_usb_device); + return (const char *)&usbip_usb_device; +} + +static const char * +attach_device (char busid[32], size_t *len_p) +{ + *len_p = 0; + if (memcmp (busid, MY_BUS_ID, 32) != 0) + return NULL; + + // notify_device (USB_INTR_RESET, 0, 0); + + refresh_usb_device (); + *len_p = sizeof (usbip_usb_device); + return (const char *)&usbip_usb_device; +} + +#define URB_DATA_SIZE 65535 + +struct usbip_msg_cmd { + uint32_t devid; + uint32_t dir; + uint32_t ep; + uint32_t flags; + uint32_t len; + uint32_t rsvd[3]; +}; + +struct usbip_msg_rep { + uint32_t devid; + uint32_t dir; + uint32_t ep; + uint32_t status; + uint32_t len; + uint32_t rsvd[5]; +}; + +static pthread_mutex_t urb_mutex; +static struct urb *urb_list; + +static uint8_t usb_setup[8]; + +static int control_setup_transaction (struct urb *urb); +static int control_write_data_transaction (char *buf, uint16_t count); +static int control_write_status_transaction (void); +static int control_read_data_transaction (char *buf, uint16_t count); +static int control_read_status_transaction (void); + +enum { + USB_STATE_DISABLED = 0, + USB_STATE_STALL, + USB_STATE_NAK, + USB_STATE_READY, +}; + +struct usb_control { + pthread_mutex_t mutex; + int eventfd; + + /* Device side: state, buf, len */ + uint8_t state; + uint8_t *buf; + uint16_t len; + + /* Host controller side: urb */ + struct urb *urb; +}; + +static struct usb_control usbc_ep_in[8]; +static struct usb_control usbc_ep_out[8]; +#define usbc_ep0 usbc_ep_out[0] +/* + * usbc_ep_in[0] not used. + */ + +static int write_data_transaction (struct usb_control *usbc_p, + int ep_num, char *buf, uint16_t count); +static int read_data_transaction (struct usb_control *usbc_p, + int ep_num, char *buf, uint16_t count); + +static int +hc_handle_control_urb (struct urb *urb) +{ + int r; + uint16_t count; + uint16_t remain = urb->len; + uint64_t l; + + if ((debug & DEBUG_USB)) + puts ("hcu 0"); + + usbc_ep0.urb = urb; + r = control_setup_transaction (urb); + if (r < 0) + goto error; + + if ((debug & DEBUG_USB)) + puts ("hcu 1"); + + if (urb->dir == USBIP_DIR_OUT) + { /* Output from host to device. */ + if ((debug & DEBUG_USB)) + printf ("hcu: %d\n", r); + + while (r == 0) + { + if (remain > 64) + count = 64; + else + count = remain; + + read (usbc_ep0.eventfd, &l, sizeof (l)); + r = control_write_data_transaction (urb->data_p, count); + if (r < 0) + break; + + urb->data_p += count; + remain -= count; + if (count < 64) + break; + } + if (r >= 0) + { + read (usbc_ep0.eventfd, &l, sizeof (l)); + r = control_write_status_transaction (); + } + } + else + { /* Input from device to host. */ + if ((debug & DEBUG_USB)) + puts ("hcu 2"); + + while (1) + { + if (remain > 64) + count = 64; + else + count = remain; + + read (usbc_ep0.eventfd, &l, sizeof (l)); + r = control_read_data_transaction (urb->data_p, count); + if (r < 0) + break; + + if ((debug & DEBUG_USB)) + puts ("hcu 3"); + + remain -= r; + urb->data_p += r; + if (r < 64) + break; + } + + if ((debug & DEBUG_USB)) + puts ("hcu 4"); + + if (r >= 0) + { + if ((debug & DEBUG_USB)) + puts ("hcu 5"); + + read (usbc_ep0.eventfd, &l, sizeof (l)); + r = control_read_status_transaction (); + if (r >= 0) + r = remain; + } + if ((debug & DEBUG_USB)) + puts ("hcu 6"); + } + + if (r < 0) + { + error: + if ((debug & DEBUG_USB)) + printf ("hcu 7 %d\n", r); + + /* recovery. */ + usbc_ep0.state = USB_STATE_READY; + } + else + /* Wait until the device is ready to accept the SETUP token. */ + read (usbc_ep0.eventfd, &l, sizeof (l)); + + if (urb->dir == USBIP_DIR_IN) + { + if (r >= 0) /* R>0 means buf remained as unused. */ + urb->len -= r; + else + urb->len = 0; + r = 0; + } + else + urb->len = 0; + + if ((debug & DEBUG_USB)) + printf ("hu-next: %d (%d)\n", urb->len, urb->seq); + + usbc_ep0.urb = NULL; + return r; +} + +static void usbip_finish_urb (struct urb *urb, int r); + +static void +usbip_handle_control_urb (struct urb *urb) +{ + int r = 0; + r = hc_handle_control_urb (urb); + usbip_finish_urb (urb, r); +} + +static int +usbip_handle_data_urb (struct urb *urb) +{ + int r; + struct usb_control *usbc_p; + + if (urb->dir == USBIP_DIR_OUT) + /* Output from host to device. */ + usbc_p = &usbc_ep_out[urb->ep]; + else + /* Input from device to host. */ + usbc_p = &usbc_ep_in[urb->ep]; + + pthread_mutex_lock (&usbc_p->mutex); + if (usbc_p->state == USB_STATE_DISABLED + || usbc_p->state == USB_STATE_STALL) + r = -EPIPE; + else /* nak or ready */ + { + if (usbc_p->urb == NULL) + usbc_p->urb = urb; + r = 0; + } + pthread_mutex_unlock (&usbc_p->mutex); + return r; +} + +static int fd; +static int shutdown_notify_fd; +static pthread_mutex_t fd_mutex; + +static void unlink_urb (struct urb *urb); + +static void +usbip_finish_urb (struct urb *urb, int r) +{ + struct usbip_msg_head msg; + struct usbip_msg_rep msg_rep; + + msg.cmd = htonl (REP_URB_SUBMIT); + msg.seq = htonl (urb->seq); + + memset (&msg_rep, 0, sizeof (msg_rep)); + msg_rep.len = htonl (urb->len); + + if ((debug & DEBUG_USB)) + printf ("ufu: %d (%d)\n", r, urb->seq); + + if (r < 0) + msg_rep.status = htonl (r); + + pthread_mutex_lock (&fd_mutex); + if ((size_t)send (fd, &msg, sizeof (msg), 0) != sizeof (msg)) + { + perror ("reply send"); + } + + if ((size_t)send (fd, &msg_rep, sizeof (msg_rep), 0) != sizeof (msg_rep)) + { + perror ("reply send"); + } + + if (urb->dir == USBIP_DIR_IN && urb->len) + { + if (send (fd, urb->data, urb->len, 0) != urb->len) + { + perror ("reply send"); + } + } + pthread_mutex_unlock (&fd_mutex); + + unlink_urb (urb); + free (urb); +} + + +static int +hc_handle_data_urb (struct usb_control *usbc_p) +{ + int r; + uint16_t count; + struct urb *urb = usbc_p->urb; + + if ((debug & DEBUG_USB)) + puts ("hc_hdu 0"); + + if (urb->remain > 64) + count = 64; + else + count = urb->remain; + + if (urb->dir == USBIP_DIR_OUT) + { /* Output from host to device. */ + if ((debug & DEBUG_USB)) + puts ("hc_hdu 1"); + + r = write_data_transaction (usbc_p, urb->ep, urb->data_p, count); + if (r < 0) + return r; + + urb->data_p += count; + urb->remain -= count; + + if (urb->remain == 0 || count < 64) + { + size_t len = urb->len - urb->remain; + + if ((debug & DEBUG_USB)) + printf ("->data: %lu\n", len); + + // successfully finished + if (len) + { + char *s = alloca (len + 1); + memcpy (s, urb->data, len); + s[len] = 0; + if ((debug & DEBUG_USB)) + printf (" : %s\n", s); + } + return 0; + } + + return 1; + } + else + { /* Input from device to host. */ + if ((debug & DEBUG_USB)) + puts ("hc_hdu 2"); + + r = read_data_transaction (usbc_p, urb->ep, urb->data_p, count); + if (r < 0) + return r; + + urb->remain -= r; + urb->data_p += r; + if (urb->remain == 0 || r < 64) + { + size_t len = urb->len - urb->remain; + + if ((debug & DEBUG_USB)) + printf ("<-data: %lu %d\n", len, r); + + // successfully finished + if (len) + { + char *s = alloca (len + 1); + memcpy (s, urb->data, len); + s[len] = 0; + if ((debug & DEBUG_USB)) + printf (" : %s\n", s); + } + urb->len = len; + return 0; + } + + return 1; + } +} + +#define USB_REQ_GET_DESCRIPTOR 0x06 + +static struct urb * +issue_get_desc (void) +{ + struct urb *urb; + + urb = malloc (sizeof (struct urb) + 64); + + urb->next = urb->prev = urb; + + urb->setup[0] = 0x80; /* Type: GET, Standard, DEVICE */ + urb->setup[1] = USB_REQ_GET_DESCRIPTOR; /* Request */ + urb->setup[2] = 0; /* Value L: desc_index */ + urb->setup[3] = 1; /* Value H: desc_type */ + urb->setup[4] = 0; /* Index */ + urb->setup[5] = 0; + urb->setup[6] = 64; /* Length */ + urb->setup[7] = 0; + urb->data_p = urb->data; + urb->seq = 0; + urb->devid = 0; + urb->dir = USBIP_DIR_IN; + urb->ep = 0; + urb->remain = urb->len = 64; + hc_handle_control_urb (urb); + return urb; +} + + +static void +unlink_urb (struct urb *urb) +{ + if (urb_list == urb) + { + if (urb->next == urb) + urb_list = NULL; + else + urb_list = urb->next; + } + + urb->next->prev = urb->prev; + urb->prev->next = urb->next; +} + +static void +usbip_handle_urb (uint32_t seq) +{ + int r = 0; + struct usbip_msg_head msg; + struct usbip_msg_cmd msg_cmd; + struct usbip_msg_rep msg_rep; + struct urb *urb = NULL; + + if (recv (fd, (char *)&msg_cmd, sizeof (msg_cmd), 0) != sizeof (msg_cmd)) + { + perror ("msg recv ctl"); + r = -EINVAL; + goto leave; + } + + if (ntohl (msg_cmd.len) > URB_DATA_SIZE) + { + perror ("msg len too long"); + r = -EINVAL; + goto leave; + } + + urb = malloc (sizeof (struct urb) + ntohl (msg_cmd.len)); + if (urb == NULL) + { + perror ("URB alloc"); + exit (1); + } + + pthread_mutex_lock (&urb_mutex); + if (urb_list == NULL) + { + urb_list = urb; + urb->next = urb->prev = urb; + } + else + { + urb->next = urb_list; + urb->prev = urb_list->prev; + urb_list->prev->next = urb; + urb_list->prev = urb; + urb_list = urb; + } + pthread_mutex_unlock (&urb_mutex); + + urb->tid = 0; + urb->seq = seq; + urb->devid = ntohl (msg_cmd.devid); + urb->dir = ntohl (msg_cmd.dir); + urb->ep = ntohl (msg_cmd.ep); + urb->remain = urb->len = ntohl (msg_cmd.len); + urb->data_p = urb->data; + + if ((debug & DEBUG_USB)) + printf ("URB: dir=%s, ep=%d, len=%d\n", urb->dir==USBIP_DIR_IN? "IN": "OUT", + urb->ep, urb->len); + + if (recv (fd, (char *)urb->setup, sizeof (urb->setup), 0) != sizeof (urb->setup)) + { + perror ("msg recv setup"); + r = -EINVAL; + goto leave; + } + + if (urb->ep == 0) + if ((debug & DEBUG_USB)) + printf ("URB: %02x %02x %02x %02x %02x %02x %02x %02x\n", + urb->setup[0], urb->setup[1], urb->setup[2], urb->setup[3], + urb->setup[4], urb->setup[5], urb->setup[6], urb->setup[7]); + + if (urb->dir == USBIP_DIR_OUT && urb->len) + { + if (recv (fd, urb->data, urb->len, 0) != urb->len) + { + perror ("msg recv data"); + r = -EINVAL; + goto leave; + } + } + + if (urb->ep == 0) + { + usbip_handle_control_urb (urb); + return; + } + else + { + r = usbip_handle_data_urb (urb); + if (r == 0) + return; + } + + leave: + msg.cmd = htonl (REP_URB_SUBMIT); + msg.seq = htonl (urb->seq); + + memset (&msg_rep, 0, sizeof (msg_rep)); + msg_rep.status = htonl (r); + + pthread_mutex_lock (&fd_mutex); + if ((size_t)send (fd, &msg, sizeof (msg), 0) != sizeof (msg)) + { + perror ("reply send"); + } + + if ((size_t)send (fd, &msg_rep, sizeof (msg_rep), 0) != sizeof (msg_rep)) + { + perror ("reply send"); + } + + pthread_mutex_unlock (&fd_mutex); + if (urb) + { + pthread_mutex_lock (&urb_mutex); + unlink_urb (urb); + pthread_mutex_unlock (&urb_mutex); + free (urb); + } +} + + +static void +usbip_send_reply (char *reply, int ok) +{ + char buf[8]; + char *p = buf; + + memcpy (p, reply, 4); + p += 4; + if (ok) + memcpy (p, NETWORK_UINT32_ZERO, 4); + else + memcpy (p, NETWORK_UINT32_ONE, 4); + p += 4; + + if ((size_t)send (fd, buf, 8, 0) != 8) + { + perror ("reply send"); + } +} + +static int attached = 0; + + +static void +unlink_urb_ep (struct urb *urb) +{ + struct usb_control *usbc_p; + + if (urb->dir == USBIP_DIR_OUT) + usbc_p = &usbc_ep_out[urb->ep]; + else + usbc_p = &usbc_ep_in[urb->ep]; + + pthread_mutex_lock (&usbc_p->mutex); + if (usbc_p->urb == urb) + usbc_p->urb = NULL; + pthread_mutex_unlock (&usbc_p->mutex); +/* FIXME: rescan the list and register??? */ +} + +static int +usbip_process_cmd (void) +{ + struct usbip_msg_head msg; + + if (recv (fd, (char *)&msg, sizeof (msg), 0) != sizeof (msg)) + { + if (errno) + perror ("msg recv"); + return -1; + } + + msg.cmd = ntohl (msg.cmd); + msg.seq = ntohl (msg.seq); + + if (msg.cmd == CMD_REQ_LIST) + { + const char *device_list; + size_t device_list_size; + + if ((debug & DEBUG_USB)) + printf ("Device List\n"); + + if (attached) + { + fprintf (stderr, "REQ list while attached\n"); + return -1; + } + + device_list = list_devices (&device_list_size); + + pthread_mutex_lock (&fd_mutex); + usbip_send_reply (USBIP_REPLY_DEVICE_LIST, !!device_list); + + if (send (fd, NETWORK_UINT32_ONE, 4, 0) == 4 + && (size_t)send (fd, device_list, device_list_size, 0) == device_list_size) + pthread_mutex_unlock (&fd_mutex); + else + { + pthread_mutex_unlock (&fd_mutex); + perror ("list send"); + return -1; + } + + close (fd); + return 1; + } + else if (msg.cmd == CMD_REQ_ATTACH) + { + const char *attach; + size_t attach_size; + char busid[32]; + + if ((debug & DEBUG_USB)) + printf ("Attach device\n"); + + if (attached) + { + fprintf (stderr, "REQ attach while attached\n"); + return -1; + } + + if (recv (fd, busid, 32, 0) != 32) + { + perror ("attach recv"); + return -1; + } + + attach = attach_device (busid, &attach_size); + + pthread_mutex_lock (&fd_mutex); + usbip_send_reply (USBIP_REPLY_ATTACH, !!attach); + if (attach + && (size_t)send (fd, attach, attach_size, 0) == attach_size) + { + if ((debug & DEBUG_USB)) + printf ("Attach device!\n"); + attached = 1; + pthread_mutex_unlock (&fd_mutex); + } + else + { + pthread_mutex_unlock (&fd_mutex); + perror ("attach send"); + return -1; + } + } + else if (msg.cmd == CMD_URB_SUBMIT) + { + if (!attached) + { + fprintf (stderr, "SUBMIT while not attached\n"); + return -1; + } + + if ((debug & DEBUG_USB)) + printf ("URB SUBMIT! %d\n", msg.seq); + usbip_handle_urb (msg.seq); + } + else if (msg.cmd == CMD_URB_UNLINK) + { + struct usbip_msg_cmd msg_cmd; + struct usbip_msg_rep msg_rep; + uint32_t seq; + struct urb *urb; + char buf[8]; + int found = 0; + + if (!attached) + { + fprintf (stderr, "UNLINK while not attached\n"); + return -1; + } + + if (recv (fd, (char *)&msg_cmd, sizeof (msg_cmd), 0) != sizeof (msg_cmd)) + { + perror ("msg recv ctl"); + return -1; + } + + if (recv (fd, buf, sizeof (buf), 0) != sizeof (buf)) + { + perror ("msg recv setup"); + return -1; + } + + seq = ntohl (msg_cmd.flags); + + pthread_mutex_lock (&urb_mutex); + if ((urb = urb_list)) + { + do + if (urb->seq == seq) + { + found = 1; + break; + } + else + urb = urb->next; + while (urb != urb_list); + + if (found) + { + unlink_urb (urb); + unlink_urb_ep (urb); + free (urb); + } + } + pthread_mutex_unlock (&urb_mutex); + + msg.cmd = htonl (REP_URB_UNLINK); + msg.seq = htonl (msg.seq); + + memset (&msg_rep, 0, sizeof (msg_rep)); + if (found) + msg_rep.status = htonl(-ECONNRESET); + + if ((debug & DEBUG_USB)) + printf ("URB UNLINK! %d: %s\n", seq, found?"o":"x"); + + pthread_mutex_lock (&fd_mutex); + if ((size_t)send (fd, &msg, sizeof (msg), 0) != sizeof (msg)) + { + perror ("reply send"); + } + + if ((size_t)send (fd, &msg_rep, sizeof (msg_rep), 0) != sizeof (msg_rep)) + { + perror ("reply send"); + } + + pthread_mutex_unlock (&fd_mutex); + } + else + { + fprintf (stderr, "Unknown command %08x, disconnecting.\n", msg.cmd); + return -1; + } + + return 0; +} + +static void +usbip_ep_ready (struct usb_control *usbc_p) +{ + uint64_t l; + int r; + + if (!usbc_p->urb) + { + if ((debug & DEBUG_USB)) + puts ("???usbip_ep_ready"); + + return; + } + + read (usbc_p->eventfd, &l, sizeof (l)); + pthread_mutex_lock (&usbc_p->mutex); + r = hc_handle_data_urb (usbc_p); + if (r <= 0) + { + struct urb *urb; + int found = 0; + + urb = usbc_p->urb->prev; + do + { + if (urb->ep == usbc_p->urb->ep + && urb->dir == usbc_p->urb->dir) + { + if (urb != usbc_p->urb) + found = 1; + break; + } + if (urb == urb_list) + break; + urb = urb->prev; + } + while (1); + + usbip_finish_urb (usbc_p->urb, r); + + if (found) + usbc_p->urb = urb; + else + usbc_p->urb = NULL; + } + pthread_mutex_unlock (&usbc_p->mutex); +} + +/* + * In the USBIP protocol, client sends URB (USB Request Block) to this + * server. + * + * This server acts/emulates as a USB host controller, and transforms + * URB into packets to device, transforms packets from device into + * URB. + */ +static void * +usbip_run_server (void *arg) +{ + int sock; + struct sockaddr_in v4addr; + const int one = 1; + struct pollfd pollfds[16]; + int i; + int r = 0; + + (void)arg; + + pthread_mutex_init (&usbc.mutex, NULL); + pthread_cond_init (&usbc.cond, NULL); + + pthread_mutex_init (&fd_mutex, NULL); + pthread_mutex_init (&urb_mutex, NULL); + + if ((sock = socket (PF_INET, SOCK_STREAM, 0)) < 0) + { + perror ("socket"); + exit (1); + } + + if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, + (const char*)&one, sizeof (int)) < 0) + perror ("WARN: setsockopt"); + + memset (&v4addr, 0, sizeof (v4addr)); + v4addr.sin_family = AF_INET; + v4addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + v4addr.sin_port = htons (USBIP_PORT); + + if (bind (sock, (const struct sockaddr *)&v4addr, sizeof (v4addr)) < 0) + { + perror ("bind"); + exit (1); + } + + /* We only accept a connection from a single client. */ + if (listen (sock, 1) < 0) + { + perror ("listen"); + exit (1); + } + + pollfds[1].fd = shutdown_notify_fd; + pollfds[1].events = POLLIN; + pollfds[1].revents = 0; + + for (i = 1; i < 8; i++) + { + pollfds[i*2].fd = usbc_ep_in[i].eventfd; + pollfds[i*2].revents = 0; + pollfds[i*2+1].fd = usbc_ep_out[i].eventfd; + pollfds[i*2+1].revents = 0; + } + + again: + /* We don't care who is connecting. */ + if ((fd = accept (sock, NULL, NULL)) < 0) + { + perror ("accept"); + exit (1); + } + + /* Since EP0 is handled synchronously, we don't poll on + * usbc_ep0.eventfd. We poll on socket and shutdown request. + */ + pollfds[0].fd = fd; + pollfds[0].events = POLLIN; + pollfds[0].revents = 0; + + for (;;) + { + for (i = 1; i < 8; i++) + { + if (usbc_ep_in[i].urb) + pollfds[i*2].events = POLLIN; + else + pollfds[i*2].events = 0; + + if (usbc_ep_out[i].urb) + pollfds[i*2+1].events = POLLIN; + else + pollfds[i*2+1].events = 0; + + pollfds[i*2].revents = 0; + pollfds[i*2+1].revents = 0; + } + + if (poll (pollfds, 16, -1) < 0) + { + if (errno == EINTR) + continue; + + perror ("poll"); + exit (1); + } + + if ((pollfds[0].revents & POLLNVAL) + || (pollfds[0].revents & POLLERR)) + { + fprintf (stderr, "Error on USBIP client socket: %d\n", + pollfds[0].revents); + exit (1); + } + if ((pollfds[0].revents & POLLIN)) + { + r = usbip_process_cmd (); + if (r < 0) + break; + if (r) + goto again; + } + + if ((pollfds[1].revents & POLLNVAL) + || (pollfds[1].revents & POLLERR)) + { + fprintf (stderr, "Error on USBIP client socket: %d\n", + pollfds[1].revents); + exit (1); + } + if ((pollfds[1].revents & POLLIN)) + { + /* Shutdown request */ + r = 0; + break; + } + + for (i = 1; i < 8; i++) + { + if ((pollfds[i*2].revents & POLLNVAL) + || (pollfds[i*2].revents & POLLERR)) + { + fprintf (stderr, "Error on ENDP_IN eventfd: %d\n", i); + exit (1); + } + if ((pollfds[i*2].revents & POLLIN)) + { + if ((debug & DEBUG_USB)) + puts ("poll in read"); + + usbip_ep_ready (&usbc_ep_in[i]); + } + + if ((pollfds[i*2+1].revents & POLLNVAL) + || (pollfds[i*2+1].revents & POLLERR)) + { + fprintf (stderr, "Error on ENDP_OUT eventfd: %d\n", i); + exit (1); + } + if ((pollfds[i*2+1].revents & POLLIN)) + { + if ((debug & DEBUG_USB)) + puts ("poll out read"); + + usbip_ep_ready (&usbc_ep_out[i]); + } + } + } + + { + struct urb *urb; + + pthread_mutex_lock (&urb_mutex); + if ((urb = urb_list)) + { + do + { + unlink_urb (urb); + unlink_urb_ep (urb); + free (urb); + } + while ((urb = urb_list)); + } + pthread_mutex_unlock (&urb_mutex); + } + + close (fd); + close (sock); + + close (shutdown_notify_fd); + close (usbc_ep0.eventfd); + + for (i = 1; i < 8; i++) + { + close (usbc_ep_in[i].eventfd); + close (usbc_ep_out[i].eventfd); + } + + attached = 0; + + if (r < 0) + /* It was detach request from client. */ + exit (0); + + return NULL; +} + +/** Control setup transaction. + * + * -->[SETUP]-->[DATA0]-->{ACK}-> Success + * \-------> Error + */ +static int +control_setup_transaction (struct urb *urb) +{ + int r; + + pthread_mutex_lock (&usbc_ep0.mutex); + if (usbc_ep0.state == USB_STATE_READY) + { + if (urb->dir == USBIP_DIR_OUT + && urb->setup[6] == 0 && urb->setup[7] == 0) + /* Control Write Transfer with no data stage. */ + r = 1; + else + r = 0; + + usbc_ep0.state = USB_STATE_NAK; + memcpy (usb_setup, urb->setup, sizeof (usb_setup)); + notify_device (USB_INTR_SETUP, 0, urb->dir); + } + else if (usbc_ep0.state == USB_STATE_NAK) + /* something wrong. */ + r = -EAGAIN; + else + { + if ((debug & DEBUG_USB)) + printf ("cst error %d\n", usbc_ep0.state); + r = -EPIPE; + } + pthread_mutex_unlock (&usbc_ep0.mutex); + return r; +} + +/** Control WRITE transaction. + * + * -->[OUT]-->[DATA0]---->{ACK}---> Success + * \--{NAK}---> again + * \--{STALL}-> Error + * \----------> Error + */ +static int +control_write_data_transaction (char *buf, uint16_t count) +{ + int r; + + pthread_mutex_lock (&usbc_ep0.mutex); + if (usbc_ep0.state == USB_STATE_READY) + { + if (usbc_ep0.len < count) + { + if ((debug & DEBUG_USB)) + printf ("*** usbc_ep0.len < count"); + r = -EPIPE; + usbc_ep0.state = USB_STATE_STALL; + } + else + { + r = 0; + usbc_ep0.state = USB_STATE_NAK; + memcpy (usbc_ep0.buf, buf, count); + usbc_ep0.len = count; + notify_device (USB_INTR_DATA_TRANSFER, 0, USBIP_DIR_OUT); + } + } + else if (usbc_ep0.state == USB_STATE_NAK) + /* something wrong. */ + r = -EAGAIN; + else + r = -EPIPE; + pthread_mutex_unlock (&usbc_ep0.mutex); + return r; +} + +/** Status transaction for control WRITE. + * zero-len + * -->[IN]--->{DATA0}->[ACK]---> Success + * \--{NAK}---> again + * \--{STALL}-> Error + * \----------> Error + */ +static int +control_write_status_transaction (void) +{ + int r; + + pthread_mutex_lock (&usbc_ep0.mutex); + if (usbc_ep0.state == USB_STATE_READY) + { + if ((debug & DEBUG_USB)) + puts ("control_write_status_transaction"); + + if (usbc_ep0.len != 0) + if ((debug & DEBUG_USB)) + printf ("*** ACK length %d\n", usbc_ep0.len); + usbc_ep0.state = USB_STATE_NAK; + notify_device (USB_INTR_DATA_TRANSFER, 0, USBIP_DIR_IN); + r = 0; + } + else if (usbc_ep0.state == USB_STATE_NAK) + r = -EAGAIN; + else + r = -EPIPE; + pthread_mutex_unlock (&usbc_ep0.mutex); + return r; +} + +/** Control READ transaction. + * + * -->[IN]-->{DATAx}---->[ACK]---> Success + * | \---------> Error + * \-{STALL}------------> Error + * \-{NAK}--------------> again + */ +static int +control_read_data_transaction (char *buf, uint16_t count) +{ + int r; + + pthread_mutex_lock (&usbc_ep0.mutex); + if (usbc_ep0.state == USB_STATE_READY) + { + if (usbc_ep0.len > count) + { + if ((debug & DEBUG_USB)) + printf ("***c read: length %d > %d\n", usbc_ep0.len, count); + } + else + count = usbc_ep0.len; + + memcpy (buf, usbc_ep0.buf, count); + usbc_ep0.state = USB_STATE_NAK; + notify_device (USB_INTR_DATA_TRANSFER, 0, USBIP_DIR_IN); + r = count; + } + else if (usbc_ep0.state == USB_STATE_NAK) + r = -EAGAIN; + else + r = -EPIPE; + pthread_mutex_unlock (&usbc_ep0.mutex); + return r; +} + +/* Status transaction for control READ. + * zero-len + * -->[OUT]--->[DATA0]--->{ACK}---> Success + * \--{NAK}---> again + * \--{STALL}-> Error + * \----------> Error + */ +static int +control_read_status_transaction (void) +{ + int r; + + pthread_mutex_lock (&usbc_ep0.mutex); + if (usbc_ep0.state == USB_STATE_READY) + { + usbc_ep0.len = 0; + usbc_ep0.state = USB_STATE_NAK; + notify_device (USB_INTR_DATA_TRANSFER, 0, USBIP_DIR_OUT); + r = 0; + } + else if (usbc_ep0.state == USB_STATE_NAK) + r = -EAGAIN; + else + r = -EPIPE; + pthread_mutex_unlock (&usbc_ep0.mutex); + return r; +} + +#define USB_URB_TIMEOUT 1000000 /* nanosecond */ + +/* WRITE transaction + * -->[OUT]--->[DATAx]--->{ACK}---> Success + * \--{NAK}---> again + * \--{STALL}-> Error + * \----------> Error + */ +static int +write_data_transaction (struct usb_control *usbc_p, + int ep_num, char *buf, uint16_t count) +{ + if (usbc_p->len < count) + { + usbc_p->state = USB_STATE_STALL; + return -EPIPE; + } + + usbc_p->state = USB_STATE_NAK; + memcpy (usbc_p->buf, buf, count); + usbc_p->len = count; + notify_device (USB_INTR_DATA_TRANSFER, ep_num, USBIP_DIR_OUT); + return count; +} + +/** READ transaction. + * + * -->[IN]-->{DATAx}---->[ACK]---> Success + * | \---------> Error + * \-{STALL}------------> Error + * \-{NAK}--------------> again + */ +static int +read_data_transaction (struct usb_control *usbc_p, + int ep_num, char *buf, uint16_t count) +{ + if (usbc_p->len > count) + { + if ((debug & DEBUG_USB)) + printf ("*** length %d > %d\n", usbc_p->len, count); + } + else + count = usbc_p->len; + + usbc_p->state = USB_STATE_NAK; + memcpy (buf, usbc_p->buf, count); + notify_device (USB_INTR_DATA_TRANSFER, ep_num, USBIP_DIR_IN); + return count; +} + +extern void chx_handle_intr (uint32_t irq_num); + + +static void +usb_intr (int signum, siginfo_t *siginfo, void *arg) +{ + extern void chx_sigmask (ucontext_t *uc); + + ucontext_t *uc = arg; + (void)signum; + (void)siginfo; + chx_handle_intr (INTR_REQ_USB); + chx_sigmask (uc); +} + + +void +usb_lld_init (struct usb_dev *dev, uint8_t feature) +{ + int r; + sigset_t sigset; + struct sigaction act; + uint8_t ep; + + sigemptyset (&sigset); + sigaddset (&sigset, SIGUSR1); + sigaddset (&sigset, SIGALRM); + + /* + * Launch the thread for USBIP. This maps to usb_lld_sys_init where + * we initialize USB controller on MCU. + */ + tid_main = pthread_self (); + + pthread_sigmask (SIG_BLOCK, &sigset, NULL); + + shutdown_notify_fd = eventfd (0, EFD_CLOEXEC); + if (shutdown_notify_fd < 0) + { + perror ("eventfd"); + exit (1); + } + + pthread_mutex_init (&usbc_ep0.mutex, NULL); + usbc_ep0.urb = NULL; + usbc_ep0.eventfd = eventfd (0, EFD_CLOEXEC); + if (usbc_ep0.eventfd < 0) + { + perror ("eventfd"); + exit (1); + } + + for (ep = 1; ep < 8; ep++) + { + pthread_mutex_init (&usbc_ep_in[ep].mutex, NULL); + pthread_mutex_init (&usbc_ep_out[ep].mutex, NULL); + + usbc_ep_in[ep].urb = NULL; + usbc_ep_in[ep].eventfd = eventfd (0, EFD_CLOEXEC); + if (usbc_ep_in[ep].eventfd < 0) + { + perror ("eventfd"); + exit (1); + } + + usbc_ep_out[ep].urb = NULL; + usbc_ep_out[ep].eventfd = eventfd (0, EFD_CLOEXEC); + if (usbc_ep_out[ep].eventfd < 0) + { + perror ("eventfd"); + exit (1); + } + } + + r = pthread_create (&tid_usbip, NULL, usbip_run_server, NULL); + if (r) + { + fprintf (stderr, "usb_lld_init: %s\n", strerror (r)); + exit (1); + } + + act.sa_sigaction = usb_intr; + sigfillset (&act.sa_mask); + act.sa_flags = SA_SIGINFO|SA_RESTART; + sigaction (SIGUSR1, &act, NULL); + + pthread_sigmask (SIG_UNBLOCK, &sigset, NULL); + + dev->configuration = 0; + dev->feature = feature; + dev->state = WAIT_SETUP; + + usbc_ep0.state = USB_STATE_READY; +} + + +void +usb_lld_prepare_shutdown (void) +{ +} + +void +usb_lld_shutdown (void) +{ + const uint64_t l = 1; + + /* + * Tell USBIP server thread about shutdown. + */ + write (shutdown_notify_fd, &l, sizeof (l)); + pthread_join (tid_usbip, NULL); + notify_device (USB_INTR_SHUTDOWN, 0, 0); +} + +static void +notify_hostcontroller (struct usb_control *usbc_p) +{ + const uint64_t l = 1; + + write (usbc_p->eventfd, &l, sizeof (l)); +} + + +#define USB_MAKE_EV(event) (event<<24) +#define USB_MAKE_TXRX(ep_num,txrx,len) ((txrx? (1<<23):0)|(ep_num<<16)|len) + +static int handle_setup0 (struct usb_dev *dev); +static int usb_handle_transfer (struct usb_dev *dev, uint8_t dir, uint8_t ep_num); + +int +usb_lld_event_handler (struct usb_dev *dev) +{ + uint8_t intr; + uint8_t dir; + uint8_t ep_num; + + pthread_mutex_lock (&usbc.mutex); + intr = usbc.intr; + dir = usbc.dir; + ep_num = usbc.ep_num; + usbc.intr = USB_INTR_NONE; + pthread_cond_signal (&usbc.cond); + pthread_mutex_unlock (&usbc.mutex); + + if (intr == USB_INTR_RESET) + return USB_MAKE_EV (USB_EVENT_DEVICE_RESET); + else if (intr == USB_INTR_SETUP) + return USB_MAKE_EV (handle_setup0 (dev)); + else if (intr == USB_INTR_DATA_TRANSFER) + return usb_handle_transfer (dev, dir, ep_num); + else if (intr == USB_INTR_SHUTDOWN) + { + if ((debug & DEBUG_USB)) + puts ("USB shutdown"); + } + + return USB_EVENT_OK; +} + +static void handle_datastage_out (struct usb_dev *dev) +{ + struct ctrl_data *data_p = &dev->ctrl_data; + uint32_t len; + + pthread_mutex_lock (&usbc_ep0.mutex); + len = usbc_ep0.len; + data_p->len -= len; + data_p->addr += len; + + len = data_p->len; + if (len > USB_MAX_PACKET_SIZE) + len = USB_MAX_PACKET_SIZE; + + if (dev->ctrl_data.len == 0) + { + dev->state = WAIT_STATUS_IN; + usbc_ep0.buf = usb_setup; + usbc_ep0.len = 0; + usbc_ep0.state = USB_STATE_READY; + } + else + { + dev->state = OUT_DATA; + usbc_ep0.buf = data_p->addr; + usbc_ep0.len = len; + usbc_ep0.state = USB_STATE_READY; + } + + notify_hostcontroller (&usbc_ep0); + pthread_mutex_unlock (&usbc_ep0.mutex); +} + +static void handle_datastage_in (struct usb_dev *dev) +{ + struct ctrl_data *data_p = &dev->ctrl_data; + uint32_t len = USB_MAX_PACKET_SIZE; + + if ((data_p->len == 0) && (dev->state == LAST_IN_DATA)) + { + pthread_mutex_lock (&usbc_ep0.mutex); + + if (data_p->require_zlp) + { + data_p->require_zlp = 0; + + /* No more data to send. Send empty packet */ + usbc_ep0.buf = usb_setup; + usbc_ep0.len = 0; + usbc_ep0.state = USB_STATE_READY; + } + else + { + /* No more data to send, proceed to receive OUT acknowledge. */ + dev->state = WAIT_STATUS_OUT; + usbc_ep0.buf = usb_setup; + usbc_ep0.len = 0; + usbc_ep0.state = USB_STATE_READY; + } + + notify_hostcontroller (&usbc_ep0); + pthread_mutex_unlock (&usbc_ep0.mutex); + return; + } + + dev->state = (data_p->len <= len) ? LAST_IN_DATA : IN_DATA; + + if (len > data_p->len) + len = data_p->len; + + pthread_mutex_lock (&usbc_ep0.mutex); + usbc_ep0.buf = data_p->addr; + usbc_ep0.len = len; + usbc_ep0.state = USB_STATE_READY; + data_p->len -= len; + data_p->addr += len; + notify_hostcontroller (&usbc_ep0); + pthread_mutex_unlock (&usbc_ep0.mutex); +} + +typedef int (*HANDLER) (struct usb_dev *dev); + +static int std_none (struct usb_dev *dev) +{ + (void)dev; + return -1; +} + +static uint16_t status_info; + +static int std_get_status (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = (arg->type & RECIPIENT); + + if (arg->value != 0 || arg->len != 2 || (arg->index >> 8) != 0 + || USB_SETUP_SET (arg->type)) + return -1; + + if (rcp == DEVICE_RECIPIENT) + { + if (arg->index == 0) + { + /* Get Device Status */ + uint8_t feature = dev->feature; + + /* Remote Wakeup enabled */ + if ((feature & (1 << 5))) + status_info |= 2; + else + status_info &= ~2; + + /* Bus-powered */ + if ((feature & (1 << 6))) + status_info |= 1; + else /* Self-powered */ + status_info &= ~1; + + return usb_lld_ctrl_send (dev, &status_info, 2); + } + } + else if (rcp == INTERFACE_RECIPIENT) + { + if (dev->configuration == 0) + return -1; + + return USB_EVENT_GET_STATUS_INTERFACE; + } + else if (rcp == ENDPOINT_RECIPIENT) + { + uint8_t ep_num = (arg->index & 0x0f); + + if ((arg->index & 0x70) || ep_num == ENDP0) + return -1; + + if ((arg->index & 0x80)) + { + if (usbc_ep_in[ep_num].state == USB_STATE_DISABLED) + return -1; + else if (usbc_ep_in[ep_num].state == USB_STATE_STALL) + status_info |= 1; + } + else + { + if (usbc_ep_out[ep_num].state == USB_STATE_DISABLED) + return -1; + else if (usbc_ep_out[ep_num].state == USB_STATE_STALL) + status_info |= 1; + } + + return usb_lld_ctrl_send (dev, &status_info, 2); + } + + return -1; +} + +static int std_clear_feature (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type)) + return -1; + + if (rcp == DEVICE_RECIPIENT) + { + if (arg->len != 0 || arg->index != 0) + return -1; + + if (arg->value == FEATURE_DEVICE_REMOTE_WAKEUP) + { + dev->feature &= ~(1 << 5); + return USB_EVENT_CLEAR_FEATURE_DEVICE; + } + } + else if (rcp == ENDPOINT_RECIPIENT) + { + uint8_t ep_num = (arg->index & 0x0f); + + if (dev->configuration == 0) + return -1; + + if (arg->len != 0 || (arg->index >> 8) != 0 + || arg->value != FEATURE_ENDPOINT_HALT || ep_num == ENDP0) + return -1; + + if ((arg->index & 0x80)) + { + if (usbc_ep_in[ep_num].state == USB_STATE_DISABLED) + return -1; + + usbc_ep_in[ep_num].state = USB_STATE_NAK; + } + else + { + if (usbc_ep_out[ep_num].state == USB_STATE_DISABLED) + return -1; + + usbc_ep_out[ep_num].state = USB_STATE_NAK; + } + + return USB_EVENT_CLEAR_FEATURE_ENDPOINT; + } + + return -1; +} + +static int std_set_feature (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type)) + return -1; + + if (rcp == DEVICE_RECIPIENT) + { + if (arg->len != 0 || arg->index != 0) + return -1; + + if (arg->value == FEATURE_DEVICE_REMOTE_WAKEUP) + { + dev->feature |= 1 << 5; + return USB_EVENT_SET_FEATURE_DEVICE; + } + } + else if (rcp == ENDPOINT_RECIPIENT) + { + uint8_t ep_num = (arg->index & 0x0f); + + if (dev->configuration == 0) + return -1; + + if (arg->len != 0 || (arg->index >> 8) != 0 + || arg->value != FEATURE_ENDPOINT_HALT || ep_num == ENDP0) + return -1; + + if ((arg->index & 0x80)) + { + if (usbc_ep_in[ep_num].state == USB_STATE_DISABLED) + return -1; + + usbc_ep_in[ep_num].state = USB_STATE_STALL; + } + else + { + if (usbc_ep_out[ep_num].state == USB_STATE_DISABLED) + return -1; + + usbc_ep_out[ep_num].state = USB_STATE_STALL; + } + + return USB_EVENT_SET_FEATURE_ENDPOINT; + } + + return -1; +} + +static int std_set_address (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type)) + return -1; + + if (rcp == DEVICE_RECIPIENT && arg->len == 0 && arg->value <= 127 + && arg->index == 0 && dev->configuration == 0) + return usb_lld_ctrl_ack (dev); + + return -1; +} + +static int std_get_descriptor (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + if (USB_SETUP_SET (arg->type)) + return -1; + + return USB_EVENT_GET_DESCRIPTOR; +} + +static int std_get_configuration (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_SET (arg->type)) + return -1; + + if (arg->value != 0 || arg->index != 0 || arg->len != 1) + return -1; + + if (rcp == DEVICE_RECIPIENT) + return usb_lld_ctrl_send (dev, &dev->configuration, 1); + + return -1; +} + +static int std_set_configuration (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type)) + return -1; + + if (arg->index != 0 || arg->len != 0) + return -1; + + if (rcp == DEVICE_RECIPIENT) + return USB_EVENT_SET_CONFIGURATION; + + return -1; +} + +static int std_get_interface (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_SET (arg->type)) + return -1; + + if (arg->value != 0 || (arg->index >> 8) != 0 || arg->len != 1) + return -1; + + if (dev->configuration == 0) + return -1; + + if (rcp == INTERFACE_RECIPIENT) + return USB_EVENT_GET_INTERFACE; + + return -1; +} + +static int std_set_interface (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type) || rcp != INTERFACE_RECIPIENT + || arg->len != 0 || (arg->index >> 8) != 0 + || (arg->value >> 8) != 0 || dev->configuration == 0) + return -1; + + return USB_EVENT_SET_INTERFACE; +} + + +static int +handle_setup0 (struct usb_dev *dev) +{ + uint8_t req_no; + HANDLER handler; + + dev->dev_req.type = usb_setup[0]; + dev->dev_req.request = req_no = usb_setup[1]; + dev->dev_req.value = (usb_setup[3] << 8) + usb_setup[2]; + dev->dev_req.index = (usb_setup[5] << 8) + usb_setup[4]; + dev->dev_req.len = (usb_setup[7] << 8) + usb_setup[6]; + + dev->ctrl_data.addr = NULL; + dev->ctrl_data.len = 0; + dev->ctrl_data.require_zlp = 0; + + if ((dev->dev_req.type & REQUEST_TYPE) == STANDARD_REQUEST) + { + int r; + + switch (req_no) + { + case 0: handler = std_get_status; break; + case 1: handler = std_clear_feature; break; + case 3: handler = std_set_feature; break; + case 5: handler = std_set_address; break; + case 6: handler = std_get_descriptor; break; + case 8: handler = std_get_configuration; break; + case 9: handler = std_set_configuration; break; + case 10: handler = std_get_interface; break; + case 11: handler = std_set_interface; break; + default: handler = std_none; break; + } + + if ((r = (*handler) (dev)) < 0) + { + usb_lld_ctrl_error (dev); + return USB_EVENT_OK; + } + else + return r; + } + else + return USB_EVENT_CTRL_REQUEST; +} + +static int handle_in0 (struct usb_dev *dev) +{ + int r = 0; + + if (dev->state == IN_DATA || dev->state == LAST_IN_DATA) + handle_datastage_in (dev); + else if (dev->state == WAIT_STATUS_IN) + { + if ((dev->dev_req.request == SET_ADDRESS) && + ((dev->dev_req.type & (REQUEST_TYPE | RECIPIENT)) + == (STANDARD_REQUEST | DEVICE_RECIPIENT))) + { + /* XXX: record the assigned address of this device??? */ + if ((debug & DEBUG_USB)) + printf ("Set Address: %d\n", dev->dev_req.value); + + r = USB_EVENT_DEVICE_ADDRESSED; + } + else + r = USB_EVENT_CTRL_WRITE_FINISH; + dev->state = WAIT_SETUP; + pthread_mutex_lock (&usbc_ep0.mutex); + usbc_ep0.buf = usb_setup; + usbc_ep0.len = 8; + usbc_ep0.state = USB_STATE_READY; + notify_hostcontroller (&usbc_ep0); + pthread_mutex_unlock (&usbc_ep0.mutex); + } + else + { + if ((debug & DEBUG_USB)) + puts ("handle_in0 error"); + + dev->state = STALLED; + pthread_mutex_lock (&usbc_ep0.mutex); + usbc_ep0.state = USB_STATE_STALL; + notify_hostcontroller (&usbc_ep0); + pthread_mutex_unlock (&usbc_ep0.mutex); + } + + return r; +} + +static void handle_out0 (struct usb_dev *dev) +{ + if (dev->state == OUT_DATA) + /* Usual case. */ + handle_datastage_out (dev); + else if (dev->state == WAIT_STATUS_OUT) + { + /* + * Control READ transfer finished by ZLP. + * Leave ENDP0 status RX_NAK, TX_NAK. + */ + dev->state = WAIT_SETUP; + pthread_mutex_lock (&usbc_ep0.mutex); + usbc_ep0.buf = usb_setup; + usbc_ep0.len = 8; + usbc_ep0.state = USB_STATE_READY; + notify_hostcontroller (&usbc_ep0); + pthread_mutex_unlock (&usbc_ep0.mutex); + } + else + { + /* + * dev->state == IN_DATA || dev->state == LAST_IN_DATA + * (Host aborts the transfer before finish) + * Or else, unexpected state. + * STALL the endpoint, until we receive the next SETUP token. + */ + if ((debug & DEBUG_USB)) + puts ("handle_out0 error"); + + dev->state = STALLED; + pthread_mutex_lock (&usbc_ep0.mutex); + usbc_ep0.state = USB_STATE_STALL; + notify_hostcontroller (&usbc_ep0); + pthread_mutex_unlock (&usbc_ep0.mutex); + } +} + + +static int +usb_handle_transfer (struct usb_dev *dev, uint8_t dir, uint8_t ep_num) +{ + if (ep_num == 0) + { + if (dir) + return USB_MAKE_EV (handle_in0 (dev)); + else + { + handle_out0 (dev); + return USB_EVENT_OK; + } + } + else + { + uint16_t len; + + if (dir) + { + len = usbc_ep_in[ep_num].len; + return USB_MAKE_TXRX (ep_num, 1, len); + } + else + { + len = usbc_ep_out[ep_num].len; + return USB_MAKE_TXRX (ep_num, 0, len); + } + } + + return USB_EVENT_OK; +} + +int +usb_lld_ctrl_ack (struct usb_dev *dev) +{ + dev->state = WAIT_STATUS_IN; + pthread_mutex_lock (&usbc_ep0.mutex); + usbc_ep0.buf = usb_setup; + usbc_ep0.len = 0; + usbc_ep0.state = USB_STATE_READY; + notify_hostcontroller (&usbc_ep0); + pthread_mutex_unlock (&usbc_ep0.mutex); + return USB_EVENT_OK; +} + +int +usb_lld_ctrl_recv (struct usb_dev *dev, void *p, size_t len) +{ + struct ctrl_data *data_p = &dev->ctrl_data; + data_p->addr = p; + data_p->len = len; + dev->state = OUT_DATA; + if (len > USB_MAX_PACKET_SIZE) + len = USB_MAX_PACKET_SIZE; + pthread_mutex_lock (&usbc_ep0.mutex); + usbc_ep0.state = USB_STATE_READY; + usbc_ep0.buf = p; + usbc_ep0.len = len; + notify_hostcontroller (&usbc_ep0); + pthread_mutex_unlock (&usbc_ep0.mutex); + return USB_EVENT_OK; +} + +int +usb_lld_ctrl_send (struct usb_dev *dev, const void *buf, size_t buflen) +{ + struct ctrl_data *data_p = &dev->ctrl_data; + uint32_t len_asked = dev->dev_req.len; + uint32_t len; + + data_p->addr = (void *)buf; + data_p->len = buflen; + + /* Restrict the data length to be the one host asks for */ + if (data_p->len >= len_asked) + data_p->len = len_asked; + /* ZLP is only required when host doesn't expect the end of packets. */ + else if (data_p->len != 0 && (data_p->len % USB_MAX_PACKET_SIZE) == 0) + data_p->require_zlp = 1; + + if (data_p->len < USB_MAX_PACKET_SIZE) + { + len = data_p->len; + dev->state = LAST_IN_DATA; + } + else + { + len = USB_MAX_PACKET_SIZE; + dev->state = IN_DATA; + } + + pthread_mutex_lock (&usbc_ep0.mutex); + usbc_ep0.buf = data_p->addr; + usbc_ep0.len = len; + usbc_ep0.state = USB_STATE_READY; + data_p->len -= len; + data_p->addr += len; + notify_hostcontroller (&usbc_ep0); + pthread_mutex_unlock (&usbc_ep0.mutex); + return USB_EVENT_OK; +} + +uint8_t +usb_lld_current_configuration (struct usb_dev *dev) +{ + return dev->configuration; +} + + +void +usb_lld_ctrl_error (struct usb_dev *dev) +{ + if ((debug & DEBUG_USB)) + puts ("ctrl_error"); + + dev->state = STALLED; + pthread_mutex_lock (&usbc_ep0.mutex); + usbc_ep0.state = USB_STATE_STALL; + notify_hostcontroller (&usbc_ep0); + pthread_mutex_unlock (&usbc_ep0.mutex); +} + +/* FIXME: ??? */ +void +usb_lld_reset (struct usb_dev *dev, uint8_t feature) +{ + usb_lld_set_configuration (dev, 0); + dev->feature = feature; + usbc_ep0.state = USB_STATE_READY; +} + +void +usb_lld_set_configuration (struct usb_dev *dev, uint8_t config) +{ + dev->configuration = config; +} + + +void +usb_lld_setup_endp (struct usb_dev *dev, int ep_num, int rx_en, int tx_en) +{ + (void)dev; + + if (ep_num == 0) + return; + + if (rx_en) + { + usbc_ep_out[ep_num].buf = NULL; + usbc_ep_out[ep_num].len = 0; + usbc_ep_out[ep_num].state = USB_STATE_NAK; + } + + if (tx_en) + { + usbc_ep_in[ep_num].buf = NULL; + usbc_ep_in[ep_num].len = 0; + usbc_ep_in[ep_num].state = USB_STATE_NAK; + } +} + + +void +usb_lld_stall_tx (int ep_num) +{ + struct usb_control *usbc_p = &usbc_ep_in[ep_num]; + + pthread_mutex_lock (&usbc_p->mutex); + usbc_p->state = USB_STATE_STALL; + notify_hostcontroller (usbc_p); + pthread_mutex_unlock (&usbc_p->mutex); + + if ((debug & DEBUG_USB)) + printf ("stall tx %d\n", ep_num); +} + +void +usb_lld_stall_rx (int ep_num) +{ + struct usb_control *usbc_p = &usbc_ep_out[ep_num]; + + pthread_mutex_lock (&usbc_p->mutex); + usbc_p->state = USB_STATE_STALL; + notify_hostcontroller (usbc_p); + pthread_mutex_unlock (&usbc_p->mutex); + if ((debug & DEBUG_USB)) + printf ("stall rx %d\n", ep_num); +} + +void +usb_lld_rx_enable_buf (int ep_num, void *buf, size_t len) +{ + struct usb_control *usbc_p = &usbc_ep_out[ep_num]; + + pthread_mutex_lock (&usbc_p->mutex); + usbc_p->state = USB_STATE_READY; + usbc_p->buf = buf; + usbc_p->len = len; + + notify_hostcontroller (usbc_p); + pthread_mutex_unlock (&usbc_p->mutex); + if ((debug & DEBUG_USB)) + printf ("usb_lld_rx_enable_buf: %d\n", ep_num); +} + + +void +usb_lld_tx_enable_buf (int ep_num, const void *buf, size_t len) +{ + struct usb_control *usbc_p = &usbc_ep_in[ep_num]; + + pthread_mutex_lock (&usbc_p->mutex); + usbc_p->state = USB_STATE_READY; + usbc_p->buf = (void *)buf; + usbc_p->len = len; + notify_hostcontroller (usbc_p); + pthread_mutex_unlock (&usbc_p->mutex); + if ((debug & DEBUG_USB)) + printf ("usb_lld_tx_enable_buf: %d %ld\n", ep_num, len); +} diff --git a/gnuk/chopstx/rules.mk b/gnuk/chopstx/rules.mk new file mode 100644 index 0000000000..d7197c2f6e --- /dev/null +++ b/gnuk/chopstx/rules.mk @@ -0,0 +1,108 @@ +# Chopstx make rules. + +CSRC += $(CHOPSTX)/entry.c $(CHOPSTX)/chopstx.c + +ifneq ($(USE_EVENTFLAG),) +CSRC += $(CHOPSTX)/eventflag.c +endif + +ifeq ($(EMULATION),) +CSRC += $(CHOPSTX)/mcu/chx-$(CHIP).c +else +CSRC += $(CHOPSTX)/mcu/chx-gnu-linux.c +endif + +ifneq ($(USE_SYS),) +CSRC += $(CHOPSTX)/mcu/sys-$(CHIP).c +endif +ifneq ($(USE_USB),) +ifeq ($(EMULATION),) +CSRC += $(CHOPSTX)/mcu/usb-$(CHIP).c +else +CSRC += $(CHOPSTX)/mcu/usb-usbip.c +endif +endif +ifneq ($(USE_ADC),) +CSRC += $(CHOPSTX)/contrib/adc-$(CHIP).c +endif +ifneq ($(USE_USART),) +CSRC += $(CHOPSTX)/contrib/usart-$(CHIP).c +endif +ifneq ($(USE_ACKBTN),) +CSRC += $(CHOPSTX)/contrib/ackbtn-$(CHIP).c +endif + +INCDIR += $(CHOPSTX) + +BUILDDIR = build +ifeq ($(EMULATION),) +OUTFILES = $(BUILDDIR)/$(PROJECT).elf $(BUILDDIR)/$(PROJECT).bin +ifneq ($(ENABLE_OUTPUT_HEX),) +OUTFILES += $(BUILDDIR)/$(PROJECT).hex +endif +else +OUTFILES = $(BUILDDIR)/$(PROJECT) +endif + + +OPT += -ffunction-sections -fdata-sections -fno-common + +OBJS = $(addprefix $(BUILDDIR)/, $(notdir $(CSRC:.c=.o))) + +IINCDIR = $(patsubst %,-I%,$(INCDIR)) +LLIBDIR = $(patsubst %,-L%,$(LIBDIR)) + +VPATH = $(sort $(dir $(CSRC))) +### +ifeq ($(EMULATION),) +MCFLAGS = -mcpu=$(MCU) +LDFLAGS = $(MCFLAGS) -nostartfiles -T$(LDSCRIPT) \ + -Wl,-Map=$(BUILDDIR)/$(PROJECT).map,--cref,--no-warn-mismatch,--gc-sections +else +MCFLAGS = +LDFLAGS = +DEFS += -D_GNU_SOURCE +endif + +CFLAGS = $(MCFLAGS) $(OPT) $(CWARN) -Wa,-alms=$(BUILDDIR)/$(notdir $(<:.c=.lst)) $(DEFS) +LDFLAGS += $(LLIBDIR) + +ifeq ($(EMULATION),) +CFLAGS += -mthumb -mno-thumb-interwork -DTHUMB +LDFLAGS += -mthumb -mno-thumb-interwork +endif + +CFLAGS += -MD -MP -MF .dep/$(@F).d + +all: $(OUTFILES) + +$(OBJS): | $(BUILDDIR) + +$(BUILDDIR): + mkdir -p $(BUILDDIR) + +$(OBJS) : $(BUILDDIR)/%.o : %.c Makefile + @echo + $(CC) -c $(CFLAGS) -I. $(IINCDIR) $< -o $@ + +ifeq ($(EMULATION),) +%.elf: $(OBJS) $(OBJS_ADD) $(LDSCRIPT) + @echo + $(LD) $(OBJS) $(OBJS_ADD) $(LDFLAGS) $(LIBS) -o $@ + +%.bin: %.elf $(LDSCRIPT) + $(OBJCOPY) -O binary $< $@ + +%.hex: %.elf $(LDSCRIPT) + $(OBJCOPY) -O ihex $< $@ +else +$(BUILDDIR)/$(PROJECT): $(OBJS) $(OBJS_ADD) + @echo + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(OBJS_ADD) $(LIBS) +endif + +clean: + -rm -f -r .dep $(BUILDDIR) + +# Include dependency files. +-include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*) diff --git a/gnuk/chopstx/sys.h b/gnuk/chopstx/sys.h new file mode 100644 index 0000000000..38a69c9ddf --- /dev/null +++ b/gnuk/chopstx/sys.h @@ -0,0 +1,9 @@ +#if defined(GNU_LINUX_EMULATION) +#include "mcu/sys-gnu-linux.h" +#elif defined(MCU_KINETIS_L) +#include "mcu/sys-mkl27z.h" +#elif defined(MCU_STM32F0) +#include "mcu/sys-stm32f0.h" +#else +#include "mcu/sys-stm32f103.h" +#endif diff --git a/gnuk/chopstx/usb_lld.h b/gnuk/chopstx/usb_lld.h new file mode 100644 index 0000000000..ce08f73132 --- /dev/null +++ b/gnuk/chopstx/usb_lld.h @@ -0,0 +1,177 @@ +#define STANDARD_ENDPOINT_DESC_SIZE 0x09 + +/* endpoints enumeration */ +#define ENDP0 ((uint8_t)0) +#define ENDP1 ((uint8_t)1) +#define ENDP2 ((uint8_t)2) +#define ENDP3 ((uint8_t)3) +#define ENDP4 ((uint8_t)4) +#define ENDP5 ((uint8_t)5) +#define ENDP6 ((uint8_t)6) +#define ENDP7 ((uint8_t)7) + +enum RECIPIENT_TYPE +{ + DEVICE_RECIPIENT = 0, /* Recipient device */ + INTERFACE_RECIPIENT, /* Recipient interface */ + ENDPOINT_RECIPIENT, /* Recipient endpoint */ + OTHER_RECIPIENT +}; + +enum DESCRIPTOR_TYPE +{ + DEVICE_DESCRIPTOR = 1, + CONFIG_DESCRIPTOR, + STRING_DESCRIPTOR, + INTERFACE_DESCRIPTOR, + ENDPOINT_DESCRIPTOR +}; + +#define REQUEST_DIR 0x80 /* Mask to get request dir */ +#define REQUEST_TYPE 0x60 /* Mask to get request type */ +#define STANDARD_REQUEST 0x00 /* Standard request */ +#define CLASS_REQUEST 0x20 /* Class request */ +#define VENDOR_REQUEST 0x40 /* Vendor request */ +#define RECIPIENT 0x1F /* Mask to get recipient */ + +#define USB_SETUP_SET(req) ((req & REQUEST_DIR) == 0) +#define USB_SETUP_GET(req) ((req & REQUEST_DIR) != 0) + +struct device_req { + uint8_t type; + uint8_t request; + uint16_t value; + uint16_t index; + uint16_t len; +}; + +struct ctrl_data { + uint8_t *addr; + uint16_t len; + uint8_t require_zlp; +}; + +struct usb_dev { + uint8_t configuration; + uint8_t feature; + uint8_t state; + struct device_req dev_req; + struct ctrl_data ctrl_data; +}; + +enum { + USB_EVENT_OK=0, /* Processed in lower layer. */ + /* Device reset and suspend. */ + USB_EVENT_DEVICE_RESET, + USB_EVENT_DEVICE_SUSPEND, + USB_EVENT_DEVICE_WAKEUP, + /* Device Requests (Control WRITE Transfer): Standard */ + USB_EVENT_SET_CONFIGURATION, + USB_EVENT_SET_INTERFACE, + USB_EVENT_SET_FEATURE_DEVICE, + USB_EVENT_SET_FEATURE_ENDPOINT, + USB_EVENT_CLEAR_FEATURE_DEVICE, + USB_EVENT_CLEAR_FEATURE_ENDPOINT, + /* Device Requests (Control READ Transfer): Standard */ + USB_EVENT_GET_STATUS_INTERFACE, + USB_EVENT_GET_DESCRIPTOR, + USB_EVENT_GET_INTERFACE, + /* Device Requests (Control READ/WRITE Transfer): Non-Standard */ + USB_EVENT_CTRL_REQUEST, + USB_EVENT_CTRL_WRITE_FINISH, + /* Device addressed. */ + USB_EVENT_DEVICE_ADDRESSED, +}; + +enum DEVICE_STATE { + USB_DEVICE_STATE_UNCONNECTED = 0, /* No USB */ + USB_DEVICE_STATE_ATTACHED = 1, + USB_DEVICE_STATE_POWERED = 2, + USB_DEVICE_STATE_DEFAULT = 3, + USB_DEVICE_STATE_ADDRESSED = 4, + USB_DEVICE_STATE_CONFIGURED = 5, + USB_DEVICE_STATE_SUSPEND = 128 /* Or-ed to other states */ +}; + +void usb_lld_init (struct usb_dev *dev, uint8_t feature); + +/* + * Return value is encoded integer: + * event-no: 8-bit, 0 if TX/RX + * tx/rx-flag: 1-bit, 0 if rx, 1 if tx + * endpoint no: 7-bit + * length: 16-bit + */ +#define USB_EVENT_TXRX(e) ((e >> 23) & 1) +#define USB_EVENT_LEN(e) (e & 0xffff) +#define USB_EVENT_ENDP(e) ((e >> 16) & 0x7f) +#define USB_EVENT_ID(e) ((e >> 24)) +int usb_lld_event_handler (struct usb_dev *dev); + +/* + * Control Endpoint ENDP0 does device requests handling. + * In response to an event of + * USB_EVENT_SET_CONFIGURATION + * USB_EVENT_SET_INTERFACE + * USB_EVENT_SET_FEATURE_DEVICE + * USB_EVENT_SET_FEATURE_ENDPOINT + * USB_EVENT_CLEAR_FEATURE_DEVICE + * USB_EVENT_CLEAR_FEATURE_ENDPOINT + * USB_EVENT_GET_STATUS_INTERFACE + * USB_EVENT_GET_DESCRIPTOR + * USB_EVENT_GET_INTERFACE + * USB_EVENT_CTRL_REQUEST + * a single action should be done, which is SEND, RECV, or, + * ACKNOWLEDGE (no data to be sent, or to be received). + * Otherwise, it's an error. + */ +int usb_lld_ctrl_send (struct usb_dev *dev, const void *buf, size_t buflen); +int usb_lld_ctrl_recv (struct usb_dev *dev, void *p, size_t len); +int usb_lld_ctrl_ack (struct usb_dev *dev); +void usb_lld_ctrl_error (struct usb_dev *dev); + +void usb_lld_reset (struct usb_dev *dev, uint8_t feature); +void usb_lld_set_configuration (struct usb_dev *dev, uint8_t config); +uint8_t usb_lld_current_configuration (struct usb_dev *dev); +void usb_lld_prepare_shutdown (void); +void usb_lld_shutdown (void); + +#if defined(MCU_KINETIS_L) +#define INTR_REQ_USB 24 +void usb_lld_tx_enable_buf (int ep_num, const void *buf, size_t len); +void usb_lld_rx_enable_buf (int ep_num, void *buf, size_t len); + +void usb_lld_setup_endp (struct usb_dev *dev, int ep_num, int rx_en, int tx_en); +void usb_lld_stall (int ep_num); +#elif defined(GNU_LINUX_EMULATION) +#include +#define INTR_REQ_USB SIGUSR1 +void usb_lld_tx_enable_buf (int ep_num, const void *buf, size_t len); +void usb_lld_rx_enable_buf (int ep_num, void *buf, size_t len); + +void usb_lld_setup_endp (struct usb_dev *dev, int ep_num, int rx_en, int tx_en); +void usb_lld_stall_tx (int ep_num); +void usb_lld_stall_rx (int ep_num); +#else +#define INTR_REQ_USB 20 +/* EP_TYPE[1:0] EndPoint TYPE */ +#define EP_BULK (0x0000) /* EndPoint BULK */ +#define EP_CONTROL (0x0200) /* EndPoint CONTROL */ +#define EP_ISOCHRONOUS (0x0400) /* EndPoint ISOCHRONOUS */ +#define EP_INTERRUPT (0x0600) /* EndPoint INTERRUPT */ + +void usb_lld_tx_enable (int ep_num, size_t len); +void usb_lld_rx_enable (int ep_num); + +void usb_lld_setup_endpoint (int ep_num, int ep_type, int ep_kind, + int ep_rx_addr, int ep_tx_addr, + int ep_rx_memory_size); +void usb_lld_stall_tx (int ep_num); +void usb_lld_stall_rx (int ep_num); + +void usb_lld_txcpy (const void *src, int ep_num, int offset, size_t len); +void usb_lld_write (uint8_t ep_num, const void *buf, size_t len); +void usb_lld_rxcpy (uint8_t *dst, int ep_num, int offset, size_t len); +void usb_lld_to_pmabuf (const void *src, uint16_t addr, size_t n); +void usb_lld_from_pmabuf (void *dst, uint16_t addr, size_t n); +#endif diff --git a/gnuk/chopstx/usb_lld_driver.h b/gnuk/chopstx/usb_lld_driver.h new file mode 100644 index 0000000000..6a4c95ff15 --- /dev/null +++ b/gnuk/chopstx/usb_lld_driver.h @@ -0,0 +1,37 @@ +#define USB_MAX_PACKET_SIZE 64 /* For FS device */ + +enum STANDARD_REQUESTS +{ + GET_STATUS = 0, + CLEAR_FEATURE, + RESERVED1, + SET_FEATURE, + RESERVED2, + SET_ADDRESS, + GET_DESCRIPTOR, + SET_DESCRIPTOR, + GET_CONFIGURATION, + SET_CONFIGURATION, + GET_INTERFACE, + SET_INTERFACE, + SYNCH_FRAME, + TOTAL_REQUEST /* Total number of Standard request */ +}; + +/* The state machine states of a control pipe */ +enum CONTROL_STATE +{ + WAIT_SETUP, + IN_DATA, + OUT_DATA, + LAST_IN_DATA, + WAIT_STATUS_IN, + WAIT_STATUS_OUT, + STALLED, +}; + +enum FEATURE_SELECTOR +{ + FEATURE_ENDPOINT_HALT=0, + FEATURE_DEVICE_REMOTE_WAKEUP=1 +}; diff --git a/gnuk/doc/Makefile b/gnuk/doc/Makefile new file mode 100644 index 0000000000..14d3b22095 --- /dev/null +++ b/gnuk/doc/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = a4 +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/GnukDocumentation.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/GnukDocumentation.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/GnukDocumentation" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/GnukDocumentation" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/gnuk/doc/__update_web b/gnuk/doc/__update_web new file mode 100644 index 0000000000..c52612d0a4 --- /dev/null +++ b/gnuk/doc/__update_web @@ -0,0 +1,3 @@ +cd _build +rsync -rntpv html/ atom.fsij.org:/home/fsij/gnuk-doc-html/ +rsync -rtpv html/ atom.fsij.org:/home/fsij/gnuk-doc-html/ diff --git a/gnuk/doc/conf.py b/gnuk/doc/conf.py new file mode 100644 index 0000000000..64e740a733 --- /dev/null +++ b/gnuk/doc/conf.py @@ -0,0 +1,246 @@ +# -*- coding: utf-8 -*- +# +# Gnuk Documentation documentation build configuration file, created by +# sphinx-quickstart on Wed Jul 4 15:29:05 2012. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.viewcode'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Gnuk Documentation' +copyright = u'2012, NIIBE Yutaka' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '1.0' +# The full version, including alpha/beta/rc tags. +release = '1.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'GnukDocumentationdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'GnukDocumentation.tex', u'Gnuk Documentation Documentation', + u'NIIBE Yutaka', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'gnukdocumentation', u'Gnuk Documentation Documentation', + [u'NIIBE Yutaka'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'GnukDocumentation', u'Gnuk Documentation Documentation', + u'NIIBE Yutaka', 'GnukDocumentation', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'http://docs.python.org/': None} diff --git a/gnuk/doc/development.rst b/gnuk/doc/development.rst new file mode 100644 index 0000000000..0e9f994b8f --- /dev/null +++ b/gnuk/doc/development.rst @@ -0,0 +1,59 @@ +Development Environment +======================= + + +Hardware +-------- + +For development, it is highly recommended to have JTAG/SWD debugger. + +For boards with DFU (Device Firmware Upgrade) feature (such as DfuSe), +it is possible to develop with that. But it should be considered +*experimental* environment, and it should not be used for usual +purpose. That's because it is basically impossible for DfuSe +implementations to disable reading-out from flash ROM. It means +that your secrets will be readily extracted by DfuSe. + +For JTAG debugger, Olimex JTAG-Tiny is good and supported well. For +SWD debugger, ST-Link/V2 would be good, and it is supported by +tool/stlinkv2.py. + + +OpenOCD +------- + +For JTAG/SWD debugger, we can use OpenOCD. + + +GNU Toolchain +------------- + +You need GNU toolchain and newlib for 'arm-none-eabi' target. +In Debian, we can just apt-get packages of: gcc-arm-none-eabi, binutils-arm-none-eabi, gdb-arm-none-eabi and libnewlib-arm-none-eabi. + +For other distributiions, there is "gcc-arm-embedded" project. See: +https://launchpad.net/gcc-arm-embedded/ + +We are using "-O3 -Os" for compiler option. + + +Building Gnuk +------------- + +Change directory to ``src``: + + $ cd gnuk-VERSION/src + +Then, run ``configure``: + + $ ./configure --vidpid= + +Here, you need to specify USB vendor ID and product ID. For FSIJ's, +it's: --vidpid=234b:0000 . Please read the section 'USB vendor ID and +product ID' in README. + +Type: + + $ make + +Then, we will have "gnuk.elf" under src/build directory. diff --git a/gnuk/doc/generating-key.rst b/gnuk/doc/generating-key.rst new file mode 100644 index 0000000000..ede0b6e710 --- /dev/null +++ b/gnuk/doc/generating-key.rst @@ -0,0 +1,487 @@ +==================== +Generating key pairs +==================== + +In this section, we describe how to generate 2048-bit RSA keys. + +You would like to use newer ECC keys instead of RSA keys. It is also described. + + +Key length of RSA +================= + +In 2005, NIST (National Institute of Standards and Technology, USA) +issued the first revision of NIST Special Publication 800-57, +"Recommendation for Key Management". + +In 800-57, NIST advises that 1024-bit RSA keys will no longer be +viable after 2010 and advises moving to 2048-bit RSA keys. NIST +advises that 2048-bit keys should be viable until 2030. + +As of 2016, GnuPG's default for generating RSA key is 2048-bit. + +Some people have preference on RSA 4096-bit keys, considering "longer is better". + +However, "longer is better" is not always true. When it's long, it +requires more computational resource, memory, and storage. Further, +it consumes more power for nomal usages. These days, many people has +enough computational resource, that would be true, but less is better +for power consumption, isn't it? + +For security, the key length is just a single factor. We had and will have +algorithm issues, too. It is true that it's difficult to update +our public keys, but this problem wouldn't be solved by just having +longer keys. + +We deliberately recommend use of RSA 2048-bit keys for Gnuk, +considering device computation power and host software constraints. + +Thus, the key size is 2048-bit in the examples below. + +When/If your environment allows use of newer ECC keys, newer ECC keys are recommended. + + +Generating RSA keys on host PC +============================== + +Here is the example session to generate main key and a subkey for encryption. + +I invoke GnuPG with ``--quick-gen-key`` option. :: + + $ gpg --quick-gen-key "Niibe Yutaka " + About to create a key for: + "Niibe Yutaka " + + Continue? (Y/n) y + +It askes passphrase for this **key on host PC**. +Note that this is a passphrase for the key on host PC. +It is different thing to the passphrase of Gnuk Token. +We enter two same inputs two times +(once for passphrase input, and another for confirmation), +. + +Then, GnuPG generate keys. It takes some time. :: + + We need to generate a lot of random bytes. It is a good idea to perform + some other action (type on the keyboard, move the mouse, utilize the + disks) during the prime generation; this gives the random number + generator a better chance to gain enough entropy. + gpg: key 76A9392B02CD15D1 marked as ultimately trusted + gpg: revocation certificate stored as '/home/gniibe.gnupg/openpgp-revocs.d/36CE0B8408CFE5CD07F94ACF76A9392B02CD15D1.rev' + public and secret key created and signed. + + gpg: checking the trustdb + gpg: marginals needed: 3 completes needed: 1 trust model: pgp + gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u + pub rsa2048 2016-06-20 [S] + 36CE0B8408CFE5CD07F94ACF76A9392B02CD15D1 + uid [ultimate] Niibe Yutaka + sub rsa2048 2016-06-20 [] + +Done. + +Then, we create authentication subkey. +Authentication subkey is not that common, +but very useful (for SSH authentication). +As it is not that common, we need ``--expert`` option for GnuPG. :: + + gpg (GnuPG) 2.1.13; Copyright (C) 2016 Free Software Foundation, Inc. + This is free software: you are free to change and redistribute it. + There is NO WARRANTY, to the extent permitted by law. + + Secret key is available. + + sec rsa2048/76A9392B02CD15D1 + created: 2016-06-20 expires: never usage: SC + trust: ultimate validity: ultimate + ssb rsa2048/4BD1EB26F0E607E6 + created: 2016-06-20 expires: never usage: E + [ultimate] (1). Niibe Yutaka + + gpg> + +Here, it displays that there are main key and a subkey. +It prompts sub-command with ``gpg>`` . + +Here, we enter ``addkey`` sub-command. + + gpg> addkey + +GnuPG asks kind of key. We select ``RSA (set your own capabilities)``. :: + + Please select what kind of key you want: + (3) DSA (sign only) + (4) RSA (sign only) + (5) Elgamal (encrypt only) + (6) RSA (encrypt only) + (7) DSA (set your own capabilities) + (8) RSA (set your own capabilities) + (10) ECC (sign only) + (11) ECC (set your own capabilities) + (12) ECC (encrypt only) + (13) Existing key + Your selection? 8 + +And select ``Authenticate`` for the capabilities for this key. +Initially, it's ``Sign`` and ``Encrypt``. +I need to deselect ``Sign`` and ``Encrypt``, and select ``Authenticate``. +To do that, I enter ``s``, ``e``, and ``a``. :: + + Possible actions for a RSA key: Sign Encrypt Authenticate + Current allowed actions: Sign Encrypt + + (S) Toggle the sign capability + (E) Toggle the encrypt capability + (A) Toggle the authenticate capability + (Q) Finished + + Your selection? s + + Possible actions for a RSA key: Sign Encrypt Authenticate + Current allowed actions: Encrypt + + (S) Toggle the sign capability + (E) Toggle the encrypt capability + (A) Toggle the authenticate capability + (Q) Finished + + Your selection? e + + Possible actions for a RSA key: Sign Encrypt Authenticate + Current allowed actions: + + (S) Toggle the sign capability + (E) Toggle the encrypt capability + (A) Toggle the authenticate capability + (Q) Finished + + Your selection? a + + Possible actions for a RSA key: Sign Encrypt Authenticate + Current allowed actions: Authenticate + + (S) Toggle the sign capability + (E) Toggle the encrypt capability + (A) Toggle the authenticate capability + (Q) Finished + +OK, we set the capability of ``Authenticate``. +We enter ``q`` to finish setting capabilities. :: + + Your selection? q + +GnuPG asks bitsize and expiration, we enter 2048 for bitsize and no expiration. +Then, we confirm that we really create the key. :: + + RSA keys may be between 1024 and 4096 bits long. + What keysize do you want? (2048) + Requested keysize is 2048 bits + Please specify how long the key should be valid. + 0 = key does not expire + = key expires in n days + w = key expires in n weeks + m = key expires in n months + y = key expires in n years + Key is valid for? (0) 0 + Key does not expire at all + Is this correct? (y/N) y + Really create? (y/N) y + +Then, it askes the passphrase, it is the passphrase of **key on host PC**. +It's the one we entered above as . + +Then, GnuPG generate the key. :: + + We need to generate a lot of random bytes. It is a good idea to perform + some other action (type on the keyboard, move the mouse, utilize the + disks) during the prime generation; this gives the random number + generator a better chance to gain enough entropy. + + sec rsa2048/76A9392B02CD15D1 + created: 2016-06-20 expires: never usage: SC + trust: ultimate validity: ultimate + ssb rsa2048/4BD1EB26F0E607E6 + created: 2016-06-20 expires: never usage: E + ssb rsa2048/F3BA52C64012198D + created: 2016-06-20 expires: never usage: A + [ultimate] (1). Niibe Yutaka + + gpg> + +We save the key (to the storage of the host PC). :: + + gpg> save + $ + +Now, we have three keys (one primary key for signature and certification, +subkey for encryption, and another subkey for authentication). + + +Publishing public key +===================== + +We make a file for the public key by ``--export`` option of GnuPG. :: + + $ gpg --armor --output .asc --export + +We can publish the file by web server. Or we can publish the key +to a keyserver, by invoking GnuPG with ``--send-keys`` option. :: + + $ gpg --keyserver pool.sks-keyservers.net --send-keys + +Here, pool.sks-keyservers.net is a keyserver, which is widely used. + + +Backup the private key +====================== + +There are some ways to back up private key, such that backup .gnupg +directory entirely, or use of paperkey, etc. +Here, we describe backup by ASCII file. +ASCII file is good, because it has less risk on transfer. +Binary file has a risk to be modified on transfer. + +Note that the key on host PC is protected by passphrase (which +is in the example above). Using the key +from the backup needs this passphrase. It is common that +people will forget passphrase for backup. Never forget it. +You have been warned. + +To make ASCII backup for private key, +invokde GnuPG with ``--armor`` option and ``--export-secret-keys`` +specifying the key identifier. :: + + $ gpg --armor --output .asc --export-secret-keys + +From the backup, +we can recover privet key by invoking GnuPG with ``--import`` option. :: + + $ gpg --import .asc + + +Generating ECC keys on host PC +============================== + +Here is an example session log to create newer ECC keys. You need +libgcrypt 1.7 or newer and GnuPG 2.1.8 or newer. + +Next, we invoke gpg frontend with ``--expert`` and ``--full-gen-key`` option. :: + + $ gpg --expert --full-gen-key + gpg (GnuPG) 2.1.13; Copyright (C) 2016 Free Software Foundation, Inc. + This is free software: you are free to change and redistribute it. + There is NO WARRANTY, to the extent permitted by law. + +Then, we input ``9`` to select ECC primary key and ECC encryption subkey. :: + + Please select what kind of key you want: + (1) RSA and RSA (default) + (2) DSA and Elgamal + (3) DSA (sign only) + (4) RSA (sign only) + (7) DSA (set your own capabilities) + (8) RSA (set your own capabilities) + (9) ECC and ECC + (10) ECC (sign only) + (11) ECC (set your own capabilities) + Your selection? 9 + +Next is the important selection. We input ``1`` to select "Curve25519". :: + + Please select which elliptic curve you want: + (1) Curve 25519 + (2) NIST P-256 + (3) NIST P-384 + (4) NIST P-521 + (5) Brainpool P-256 + (6) Brainpool P-384 + (7) Brainpool P-512 + (8) secp256k1 + Your selection? 1 + +You may see WARNING (it depends on version of GnuPG) and may been asked. Since it is what you want, please answer with 'y'. :: + + gpg: WARNING: Curve25519 is not yet part of the OpenPGP standard. + Use this curve anyway? (y/N) y + +It asks about expiration of key. :: + + Please specify how long the key should be valid. + 0 = key does not expire + = key expires in n days + w = key expires in n weeks + m = key expires in n months + y = key expires in n years + Key is valid for? (0) + Key does not expire at all + Is this correct? (y/N) y + +Then, it asks about a user ID. :: + + GnuPG needs to construct a user ID to identify your key. + + Real name: Kunisada Chuji + Email address: chuji@gniibe.org + Comment: + You selected this USER-ID: + "Kunisada Chuji " + +Lastly, it asks confirmation. :: + + Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o + +Then, it goes like this. :: + + We need to generate a lot of random bytes. It is a good idea to perform + some other action (type on the keyboard, move the mouse, utilize the + disks) during the prime generation; this gives the random number + generator a better chance to gain enough entropy. + We need to generate a lot of random bytes. It is a good idea to perform + some other action (type on the keyboard, move the mouse, utilize the + disks) during the prime generation; this gives the random number + generator a better chance to gain enough entropy. + +It asks the passphrase for keys by pop-up window, and then, finishes. :: + + gpg: key 17174C1A7C406DB5 marked as ultimately trusted + gpg: revocation certificate stored as '/home/gniibe.gnupg/openpgp-revocs.d/1719874a4fe5a1d8c465277d5a1bb27e3000f4ff.rev' + public and secret key created and signed. + + gpg: checking the trustdb + gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model + gpg: depth: 0 valid: 6 signed: 67 trust: 0-, 0q, 0n, 0m, 0f, 6u + gpg: depth: 1 valid: 67 signed: 40 trust: 67-, 0q, 0n, 0m, 0f, 0u + gpg: next trustdb check due at 2016-10-05 + pub ed25519 2016-07-08 + F478770235B60A230BE78005006A236C292C31D7 + uid [ultimate] Kunisada Chuji + sub cv25519 2016-07-08 + + $ + +We have the primary key with ed25519, and encryption subkey with cv25519. + + +Next, we add authentication subkey which can be used with OpenSSH. +We invoke gpg frontend with ``--edit-key`` and the key ID. :: + + $ gpg2 --expert --edit-key 17174C1A7C406DB5 + gpg (GnuPG) 2.1.13; Copyright (C) 2016 Free Software Foundation, Inc. + This is free software: you are free to change and redistribute it. + There is NO WARRANTY, to the extent permitted by law. + + Secret key is available. + + sec ed25519/17174C1A7C406DB5 + created: 2016-07-08 expires: never usage: SC + trust: ultimate validity: ultimate + ssb cv25519/37A03183DF7B31B1 + created: 2016-07-08 expires: never usage: E + [ultimate] (1). Kunisada Chuji + +We invoke ``addkey`` subcommand. :: + + gpg> addkey + +It asks a kind of key, we input ``11`` to select ECC for authentication. :: + + Please select what kind of key you want: + (3) DSA (sign only) + (4) RSA (sign only) + (5) Elgamal (encrypt only) + (6) RSA (encrypt only) + (7) DSA (set your own capabilities) + (8) RSA (set your own capabilities) + (10) ECC (sign only) + (11) ECC (set your own capabilities) + (12) ECC (encrypt only) + (13) Existing key + Your selection? 11 + +and then, we specify "Authenticate" capability. :: + + Possible actions for a ECDSA/EdDSA key: Sign Authenticate + Current allowed actions: Sign + + (S) Toggle the sign capability + (A) Toggle the authenticate capability + (Q) Finished + + Your selection? a + + Possible actions for a ECDSA/EdDSA key: Sign Authenticate + Current allowed actions: Sign Authenticate + + (S) Toggle the sign capability + (A) Toggle the authenticate capability + (Q) Finished + + Your selection? s + + Possible actions for a ECDSA/EdDSA key: Sign Authenticate + Current allowed actions: Authenticate + + (S) Toggle the sign capability + (A) Toggle the authenticate capability + (Q) Finished + + Your selection? q + +Then, it asks which curve. We input ``1`` for "Curve25519". :: + + Please select which elliptic curve you want: + (1) Curve 25519 + (2) NIST P-256 + (3) NIST P-384 + (4) NIST P-521 + (5) Brainpool P-256 + (6) Brainpool P-384 + (7) Brainpool P-512 + (8) secp256k1 + Your selection? 1 + +It may ask confirmation with WARNING (depends on version). We say ``y``. :: + + gpg: WARNING: Curve25519 is not yet part of the OpenPGP standard. + Use this curve anyway? (y/N) y + +It asks expiration of the key. :: + + Please specify how long the key should be valid. + 0 = key does not expire + = key expires in n days + w = key expires in n weeks + m = key expires in n months + y = key expires in n years + Key is valid for? (0) + Key does not expire at all + Is this correct? (y/N) y + +And the confirmation. :: + + Really create? (y/N) y + +It goes. :: + + We need to generate a lot of random bytes. It is a good idea to perform + some other action (type on the keyboard, move the mouse, utilize the + disks) during the prime generation; this gives the random number + generator a better chance to gain enough entropy. + +It asks the passphrase. And done. :: + + sec ed25519/17174C1A7C406DB5 + created: 2016-09-08 expires: never usage: SC + trust: ultimate validity: ultimate + ssb cv25519/37A03183DF7B31B1 + created: 2016-09-08 expires: never usage: E + ssb ed25519/4AD7D2428679DF5F + created: 2016-09-08 expires: never usage: A + [ultimate] (1). Kunisada Chuji + +We type ``save`` to exit form gpg. :: + + gpg> save + $ + diff --git a/gnuk/doc/gnuk-keytocard-noremoval.rst b/gnuk/doc/gnuk-keytocard-noremoval.rst new file mode 100644 index 0000000000..b2e72eca26 --- /dev/null +++ b/gnuk/doc/gnuk-keytocard-noremoval.rst @@ -0,0 +1,25 @@ +============================================= +Key import from PC to Gnuk Token (no removal) +============================================= + +This document describes how I put my **keys on PC** to the Token +without removing keys from PC. + +The difference is only the last step. +I don't save changes on PC after keytocard. + +For the steps before the last step, please see `keytocard with removing keys on PC`_. + +.. _keytocard with removing keys on PC: gnuk-keytocard + +Here is the session log of the last step. + +Lastly, I quit GnuPG. Note that I **don't** save changes. :: + + gpg> quit + Save changes? (y/N) n + Quit without saving? (y/N) y + $ + +All keys are imported to Gnuk Token now. +Still, secret keys are available on PC, too. diff --git a/gnuk/doc/gnuk-keytocard.rst b/gnuk/doc/gnuk-keytocard.rst new file mode 100644 index 0000000000..13ebb30789 --- /dev/null +++ b/gnuk/doc/gnuk-keytocard.rst @@ -0,0 +1,188 @@ +================================ +Key import from PC to Gnuk Token +================================ + +This document describes how I put my **keys on PC** to the Token, +and remove secret keys from PC. + +Note that there is **no ways** to export keys from the Gnuk Token, +so please be careful. + + +If you want to import same keys to multiple Tokens, +please copy ``.gnupg`` directory beforehand. + +In my case, I do something like following: :: + + $ cp -a .gnupg tmp/gnuk-testing-dir + +See `another document`_ to import keys to the Token from copied directory. + +.. _another document: gnuk-keytocard-noremoval + +After personalization, I put my keys into the Token. + +Here is the session log. + +I invoke GnuPG with my key (249CB3771750745D5CDD323CE267B052364F028D). :: + + $ gpg --edit-key 249CB3771750745D5CDD323CE267B052364F028D + gpg (GnuPG) 2.1.13; Copyright (C) 2016 Free Software Foundation, Inc. + This is free software: you are free to change and redistribute it. + There is NO WARRANTY, to the extent permitted by law. + + Secret key is available. + + sec ed25519/E267B052364F028D + created: 2015-08-12 expires: never usage: SC + trust: ultimate validity: ultimate + ssb cv25519/850AF040D619F240 + created: 2015-08-12 expires: never usage: E + ssb ed25519/5F910521FAA805B1 + created: 2015-08-12 expires: never usage: A + [ultimate] (1). NIIBE Yutaka + [ultimate] (2) NIIBE Yutaka + + gpg> + + +Then, GnuPG enters its own command interaction mode. The prompt is ``gpg>``. + +Firstly, I import my primary key into Gnuk Token. +I type ``keytocard`` command, answer ``y`` to confirm keyimport, +and type ``1`` to say it's signature key. :: + + gpg> keytocard + Really move the primary key? (y/N) y + Please select where to store the key: + (1) Signature key + (3) Authentication key + Your selection? 1 + +Then, GnuPG asks two kinds of passphrases. One is the passphrase of **keys on PC** +and another is the passphrase of **Gnuk Token**. Note that the passphrase of +the token and the passphrase of the keys on PC are different things, +although they can be same. + +Here, I assume that Gnuk Token's admin passphrase of factory setting (12345678). + +I enter these passphrases. :: + + Please enter your passphrase, so that the secret key can be unlocked for this session + + + Please enter the Admin PIN + Enter Admin PIN: 12345678 + + sec ed25519/E267B052364F028D + created: 2015-08-12 expires: never usage: SC + trust: ultimate validity: ultimate + ssb cv25519/850AF040D619F240 + created: 2015-08-12 expires: never usage: E + ssb ed25519/5F910521FAA805B1 + created: 2015-08-12 expires: never usage: A + [ultimate] (1). NIIBE Yutaka + [ultimate] (2) NIIBE Yutaka + +Secondly, I import my subkey of encryption. I select key number '1'. :: + + gpg> key 1 + + sec ed25519/E267B052364F028D + created: 2015-08-12 expires: never usage: SC + trust: ultimate validity: ultimate + ssb* cv25519/850AF040D619F240 + created: 2015-08-12 expires: never usage: E + ssb ed25519/5F910521FAA805B1 + created: 2015-08-12 expires: never usage: A + [ultimate] (1). NIIBE Yutaka + [ultimate] (2) NIIBE Yutaka + +You can see that the subkey is marked by '*'. +I type ``keytocard`` command to import this subkey to Gnuk Token. +I select ``2`` as it's encryption key. :: + + gpg> keytocard + Please select where to store the key: + (2) Encryption key + Your selection? 2 + +Then, GnuPG asks the passphrase of **keys on PC** again. I enter. :: + + Please enter your passphrase, so that the secret key can be unlocked for this session + + + sec ed25519/E267B052364F028D + created: 2015-08-12 expires: never usage: SC + trust: ultimate validity: ultimate + ssb* cv25519/850AF040D619F240 + created: 2015-08-12 expires: never usage: E + ssb ed25519/5F910521FAA805B1 + created: 2015-08-12 expires: never usage: A + [ultimate] (1). NIIBE Yutaka + [ultimate] (2) NIIBE Yutaka + +The sub key is now on the Token. + +I type ``key 1`` to deselect key number '1'. :: + + gpg> key 1 + + sec ed25519/E267B052364F028D + created: 2015-08-12 expires: never usage: SC + trust: ultimate validity: ultimate + ssb cv25519/850AF040D619F240 + created: 2015-08-12 expires: never usage: E + ssb ed25519/5F910521FAA805B1 + created: 2015-08-12 expires: never usage: A + [ultimate] (1). NIIBE Yutaka + [ultimate] (2) NIIBE Yutaka + +Thirdly, I select sub key of authentication which has key number '2'. :: + + gpg> key 2 + + sec ed25519/E267B052364F028D + created: 2015-08-12 expires: never usage: SC + trust: ultimate validity: ultimate + ssb cv25519/850AF040D619F240 + created: 2015-08-12 expires: never usage: E + ssb* ed25519/5F910521FAA805B1 + created: 2015-08-12 expires: never usage: A + [ultimate] (1). NIIBE Yutaka + [ultimate] (2) NIIBE Yutaka + +You can see that the subkey number '2' is marked by '*'. +I type ``keytocard`` command to import this subkey to Gnuk Token. +I select ``3`` as it's authentication key. :: + + gpg> keytocard + Please select where to store the key: + (3) Authentication key + Your selection? 3 + +Then, GnuPG asks the passphrase of **keys on PC** again. I enter. :: + + Please enter your passphrase, so that the secret key can be unlocked for this session + + + sec ed25519/E267B052364F028D + created: 2015-08-12 expires: never usage: SC + trust: ultimate validity: ultimate + ssb cv25519/850AF040D619F240 + created: 2015-08-12 expires: never usage: E + ssb* ed25519/5F910521FAA805B1 + created: 2015-08-12 expires: never usage: A + [ultimate] (1). NIIBE Yutaka + [ultimate] (2) NIIBE Yutaka + +The sub key is now on the Token. + +Lastly, I save changes of **keys on PC** and quit GnuPG. :: + + gpg> save + $ + +All secret keys are imported to Gnuk Token now. +On PC, only references (card-no) to the Token remain +and secrets have been removed. diff --git a/gnuk/doc/gnuk-passphrase-setting.rst b/gnuk/doc/gnuk-passphrase-setting.rst new file mode 100644 index 0000000000..66027e10bd --- /dev/null +++ b/gnuk/doc/gnuk-passphrase-setting.rst @@ -0,0 +1,146 @@ +========================================== +Set up your passphrase for your Gnuk Token +========================================== + +Terminology +=========== + +In the OpenPGPcard specification, there are two passwords: one is +user-password and another is admin-password. In the specification, +user-password is refered as PW1, and admin-password is refered as PW3. +Besides, there is reset code, which enable a user to reset PW1. + +Note that people sometimes use different words than "password" to +refer same thing, in GnuPG and its applications. For example, the +output explained above includes the word "PIN" (Personal +Identification Number), and the helper program for input is named +"pinentry". Note that it is OK (and recommended) to include +characters other than digits for the case of OpenPGPcard. + +Besides, some people sometimes prefer the word "passphrase" to +"password", as it can encourage to have longer string, but it means +same thing and it just refer user-password or admin-password. + + +Set up PW1 and PW3 +================== + +Invoke GnuPG with the option ``--card-edit``. :: + + Reader ...........: 234B:0000:FSIJ-1.2.0-87193059:0 + Application ID ...: D276000124010200FFFE871930590000 + Version ..........: 2.0 + Manufacturer .....: unmanaged S/N range + Serial number ....: 87193059 + Name of cardholder: Yutaka Niibe + Language prefs ...: ja + Sex ..............: male + URL of public key : http://www.gniibe.org/gniibe-20150813.asc + Login data .......: gniibe + Signature PIN ....: not forced + Key attributes ...: ed25519 cv25519 ed25519 + Max. PIN lengths .: 127 127 127 + PIN retry counter : 3 3 3 + Signature counter : 0 + Signature key ....: 249C B377 1750 745D 5CDD 323C E267 B052 364F 028D + created ....: 2015-08-12 07:10:48 + Encryption key....: E228 AB42 0F73 3B1D 712D E50C 850A F040 D619 F240 + created ....: 2015-08-12 07:10:48 + Authentication key: E63F 31E6 F203 20B5 D796 D266 5F91 0521 FAA8 05B1 + created ....: 2015-08-12 07:16:14 + General key info..: pub ed25519/E267B052364F028D 2015-08-12 NIIBE Yutaka + sec> ed25519/E267B052364F028D created: 2015-08-12 expires: never + card-no: FFFE 87193059 + ssb> cv25519/850AF040D619F240 created: 2015-08-12 expires: never + card-no: FFFE 87193059 + ssb> ed25519/5F910521FAA805B1 created: 2015-08-12 expires: never + card-no: FFFE 87193059 + + gpg/card> + +It shows the status of the card (as same as the output of ``gpg --card-status``). + +Then, GnuPG enters its own command interaction mode. The prompt is ``gpg/card>``. + +Firstly, I change PIN of card user from factory setting (of "123456"). +Note that, by only changing user's PIN, it enables "admin less mode" of Gnuk. +"Admin less mode" means that admin password will become same one of user's. +That is, PW1 = PW3. +Note that *the length of PIN should be more than (or equals to) 8* for +"admin less mode". :: + + gpg/card> passwd + gpg: OpenPGP card no. D276000124010200FFFE871930590000 detected + + Please enter the PIN + Enter PIN: 123456 + + New PIN + Enter New PIN: + + New PIN + Repeat this PIN: + PIN changed. + +The "admin less mode" is Gnuk only feature, not defined in the +OpenPGPcard specification. By using "admin less mode", it will be +only a sigle password for user to memorize, and it will be easier if a token +is used by an individual. + +(If you want normal way ("admin full mode" in Gnuk's term), +that is, user-password *and* admin-password independently, +please change admin-password at first. +Then, the token works as same as OpenPGPcard specification +with regards to PW1 and PW3.) + + +Set up of reset code (optional) +=============================== + +Lastly, we can setup reset code, entering admin mode. + +Having reset code, we can unblock the token when the token will be blocked +(by wrong attempts to entering passphrase). Note that this is optional step. + +When reset code is known to someone, that person can try to guess your passphrase of PW1 more times by unblocking the token. So, I don't use this feature by myself. + +If we do, here is the interaction. :: + + gpg/card> admin + Admin commands are allowed + + gpg/card> passwd + gpg: OpenPGP card no. D276000124010200FFFE871930590000 detected + + 1 - change PIN + 2 - unblock PIN + 3 - change Admin PIN + 4 - set the Reset Code + Q - quit + + Your selection? 4 + gpg: 3 Admin PIN attempts remaining before card is permanently locked + + Please enter the Admin PIN + Enter Admin PIN: + + New Reset Code + Enter New PIN: + + New Reset Code + Repeat this PIN: + Reset Code set. + + 1 - change PIN + 2 - unblock PIN + 3 - change Admin PIN + 4 - set the Reset Code + Q - quit + + Your selection? q + +Then, I quit. :: + + gpg/card> quit + +That's all in this step. diff --git a/gnuk/doc/gnuk-personalization.rst b/gnuk/doc/gnuk-personalization.rst new file mode 100644 index 0000000000..43117dbd5a --- /dev/null +++ b/gnuk/doc/gnuk-personalization.rst @@ -0,0 +1,77 @@ +============================= +Personalization of Gnuk Token +============================= + + +Personalize your Gnuk Token +=========================== + +Invoke GnuPG with the option ``--card-edit``. :: + + $ gpg --card-edit + + Reader ...........: 234B:0000:FSIJ-1.2.0-87193059:0 + Application ID ...: D276000124010200FFFE871930590000 + Version ..........: 2.0 + Manufacturer .....: unmanaged S/N range + Serial number ....: 87193059 + Name of cardholder: [not set] + Language prefs ...: [not set] + Sex ..............: unspecified + URL of public key : [not set] + Login data .......: [not set] + Signature PIN ....: forced + Key attributes ...: rsa2048 rsa2048 rsa2048 + Max. PIN lengths .: 127 127 127 + PIN retry counter : 3 3 3 + Signature counter : 0 + Signature key ....: [none] + Encryption key....: [none] + Authentication key: [none] + General key info..: [none] + + gpg/card> + +It shows the status of the card (as same as the output of ``gpg --card-status``). + +Then, GnuPG enters its own command interaction mode. The prompt is ``gpg/card>``. + +First, enabling admin command, I put name of mine. +Note that I input admin PIN of factory setting (12345678) here. :: + + gpg/card> admin + Admin commands are allowed + + gpg/card> name + Cardholder's surname: Niibe + Cardholder's given name: Yutaka + gpg: 3 Admin PIN attempts remaining before card is permanently locked + + Please enter the Admin PIN + Enter Admin PIN: 12345678 + +Secondly, I put some other informations, such as language, sex, +login, and URL. URL specifies the place where I put my public keys. :: + + gpg/card> lang + Language preferences: ja + + gpg/card> sex + Sex ((M)ale, (F)emale or space): m + + gpg/card> url + URL to retrieve public key: http://www.gniibe.org/gniibe-20150813.asc + + gpg/card> login + Login data (account name): gniibe + +Since I don't force PIN input everytime, +toggle it to non-force-pin-for-signature. :: + + gpg/card> forcesig + +Then, I quit. :: + + gpg/card> quit + +That's all in this step. diff --git a/gnuk/doc/gnuk-token-initial-configuration.rst b/gnuk/doc/gnuk-token-initial-configuration.rst new file mode 100644 index 0000000000..3a690ebfcc --- /dev/null +++ b/gnuk/doc/gnuk-token-initial-configuration.rst @@ -0,0 +1,41 @@ +=================================== +Initial Configuration of Gnuk Token +=================================== + +This is optional step. + +You don't need to setup the serial number of Gnuk Token, +as it comes with its default serial number based on MCU's chip ID. + +You can setup the serial number of Gnuk Token only once. + + +Conditions +========== + +I assume you are using GNU/Linux. + + +Preparation +=========== + +Make sure there is no ``scdaemon`` for configuring Gnuk Token. You can kill ``scdaemon`` by: :: + + $ gpg-connect-agent "SCD KILLSCD" "SCD BYE" /bye + + +Serial Number (optional) +======================== + +Note that this is completely optional step. I don't know anyone other than me, do this. Even for me, I only do that for a single device among multiple devices I use. I do that to test the feature. + +In the file ``GNUK_SERIAL_NUMBER``, each line has email and 6-byte serial number. The first two bytes are organization number (F5:17 is for FSIJ). Last four bytes are number for tokens. + +The tool ``../tool/gnuk_put_binary_libusb.py`` examines environment variable of ``EMAIL``, and writes corresponding serial number to Gnuk Token. :: + + $ ../tool/gnuk_put_binary_libusb.py -s ../GNUK_SERIAL_NUMBER + Writing serial number + Device: + Configuration: 1 + Interface: 0 + d2 76 00 01 24 01 02 00 f5 17 00 00 00 01 00 00 diff --git a/gnuk/doc/gpg-settings.rst b/gnuk/doc/gpg-settings.rst new file mode 100644 index 0000000000..aa1f78d97d --- /dev/null +++ b/gnuk/doc/gpg-settings.rst @@ -0,0 +1,55 @@ +.. -*- coding: utf-8 -*- + +============== +GnuPG settings +============== + +Here is my GnuPG settings. + +.gnupg/gpg.conf +=============== + +I create ``.gnupg/gpg.conf`` file with the following content. :: + + use-agent + default-key 0xE267B052364F028D + +In addition to the ``use-agent`` option, I specify my default key. + +The ``use-agent`` option is for GnuPG 1.4.x and it means using gpg-agent if available. +If no option, GnuPG 1.4.x directly connects to Gnuk Token by itself, instead of through scdaemon. When GnuPG 1.4.x tries to access Gnuk Token and scdaemon is running, there are conflicts. + +We recommend to specify the ``use-agent`` option for GnuPG 1.4.x to access Gnuk Token through gpg-agent and scdaemon. + +For GnuPG 2.0 and 2.1, gpg-agent is always used, so, there is no need to specify the ``use-agent`` option, but having this option is no harm, anyway. + + +Let gpg-agent manage SSH key +============================ + +I create ``.gnupg/gpg-agent.conf`` file with the following content. :: + + enable-ssh-support + +I edit the file /etc/X11/Xsession.options and comment out use-ssh-agent line, +so that Xsession doesn't invoke original ssh-agent. We use gpg-agent as ssh-agent. + +In the files /etc/xdg/autostart/gnome-keyring-ssh.desktop, +I have a line something like: :: + + OnlyShowIn=GNOME;Unity;MATE; + +I edit this line to: :: + + OnlyShowIn= + +So that no desktop environment enables gnome-keyring for ssh. + +References +========== + +* `Creating a new GPG key`_ +* `Use OpenPGP Keys for OpenSSH, how to use gpg with ssh`_ + +.. _Creating a new GPG key: http://keyring.debian.org/creating-key.html +.. _Use OpenPGP Keys for OpenSSH, how to use gpg with ssh: http://www.programmierecke.net/howto/gpg-ssh.html diff --git a/gnuk/doc/images/gnuk-sticker.png b/gnuk/doc/images/gnuk-sticker.png new file mode 100644 index 0000000000..511c783fd1 Binary files /dev/null and b/gnuk/doc/images/gnuk-sticker.png differ diff --git a/gnuk/doc/index.rst b/gnuk/doc/index.rst new file mode 100644 index 0000000000..df820383f9 --- /dev/null +++ b/gnuk/doc/index.rst @@ -0,0 +1,37 @@ +.. Gnuk Documentation documentation master file, created by + sphinx-quickstart on Wed Jul 4 15:29:05 2012. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + Copyright (C) 2012, 2013, 2016 NIIBE Yutaka + Copyright (C) 2012, 2013, 2016 Free Software Initiative of Japan + This document is licensed under a CC-BY-SA 3.0 Unported License + +Gnuk Documentation +================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + intro.rst + gpg-settings.rst + stop-scdaemon.rst + udev-rules.rst + gnuk-token-initial-configuration.rst + gnuk-personalization.rst + generating-key.rst + gnuk-keytocard.rst + gnuk-keytocard-noremoval.rst + gnuk-passphrase-setting.rst + using-gnuk-token-with-another-computer.rst + development.rst + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/gnuk/doc/intro.rst b/gnuk/doc/intro.rst new file mode 100644 index 0000000000..21b19e5052 --- /dev/null +++ b/gnuk/doc/intro.rst @@ -0,0 +1,65 @@ +Introduction +============ + + +What's Gnuk? +------------ + +Gnuk is an implementation of USB cryptographic token for GNU Privacy +Guard. Gnuk supports OpenPGP card protocol version 2, and it runs on +STM32F103 processor. + +This document explains about Gnuk 1.2, which comes with ECC algorithm. + + +Cryptographic token and feature of Gnuk +--------------------------------------- + +Cryptographic token is a store of private keys and it computes cryptographic +functions on the device. + +The idea is to separate important secrets to independent device, +from where nobody can extract them. + + +Development Environment +----------------------- + +See :doc:`development` for development environment for Gnuk. +Gnuk is developed on the environment where there are only Free Software. + + +Target boards for running Gnuk +------------------------------ + +Hardware requirement for Gnuk is the micro controller STM32F103. +In version 1.2, Gnuk supports following boards. + +* FST-01 (Flying Stone Tiny ZERO-ONE) + +* Olimex STM32-H103 + +* ST Nucleo F103 + +* Nitrokey Start + + +Host prerequisites for using Gnuk Token +--------------------------------------- + +* GNU Privacy Guard (GnuPG) + +* libusb + +* [Optional] SSH: openssh + +* [experimental] Web: scute, firefox + + +Usages +------ + +* Sign with GnuPG +* Decrypt with GnuPG +* Use with OpenSSH through gpg-agent (as ssh-agent) +* [experimental] Use with Firefox through Scute for X.509 client certificate authentication diff --git a/gnuk/doc/note/HACKING b/gnuk/doc/note/HACKING new file mode 100644 index 0000000000..6d54b5ed66 --- /dev/null +++ b/gnuk/doc/note/HACKING @@ -0,0 +1,53 @@ +* [DONE] 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). + + +* [Mostly DONE] RSA + +It would be good not to use malloc. + + +* Flash ROM recover from unexpected shutdown during write + + +* [DONE] configure support + +configure script would be good to select a board and to generate +random serial number. + + +* [DONE] Random number update + +Implemented using SECECT_FILE and UPDATE_BINARY command, which is not +in the OpenPGP card specification. + +Old description: Currently, Gnuk doesn't have random number generator, +but use random bytes calculated by hosts. After Gnuk uses random +number, the entry in Flash ROM will be cleared. Some scheme to update +random number bytes is needed. Possibly, private Data Objects, or by +another SELECT FILE. + + +* [DONE] Manufacture ID + +Get it from FSFE. + + +* [DONE] Serial number + +The AID of the card contains serial number. It should be unique. USB +serial number should be unique to identify different tokens, too. + + +* [DONE] Flash ROM garbage collection + + +* [DONE] Flash ROM protection + +Flash ROM can be protected with OpenOCD. DfuSe users should know that +the content can be accessible by DfuSe, even if we enable read +protection of flash ROM. For proper protection, don't use DfuSe but +use OpenOCD to write and protect. diff --git a/gnuk/doc/note/NOTES b/gnuk/doc/note/NOTES new file mode 100644 index 0000000000..2d10463e6a --- /dev/null +++ b/gnuk/doc/note/NOTES @@ -0,0 +1,88 @@ +USB communication (current) +=========================== + +* Get response, command chaining, short APDU only. + + +If it were only for token and no smartcard: + +* Get response, command chaining and short APDU would be considered + useless. + +* It would be wise to use extended APDU and CCID/ICCD block chaining. + +The question would be: When lower layer (CCID/ICCD layer) support +enough mechanism of block assembly, why not use one in the application +layer (ISO 7816)? + +For token implementation, CCID/ICCD would be lower layer and ISO 7816 +would be higher layer, but it's different in fact. The figure of +communication is like:: + + host <-----------> reader <---------> smartcard + CCID/ICCD ISO 7816 + + host <--> token + +Given the situation host side (GnuPG) already has the features of +handling get response, command chaining, and short APDU only, it is +rather better to do that on token side too. + +Besides, supporting extended APDU on host side, it would be needed to +prepare 64KB buffer (that's the maximum size), as there is no explicit +limitation for buffer size. 64KB would be large, and this could be +avoided when we use short APDU only. + + +USB communication (second attempt) +================================== + +* No get response, no command chaining, but extended APDU and extended + Lc and Le. I think that this keep the code simple. + +* The value of dwMaxCCIDMessageLength is 320, that's the size + of header of ICC block plus size of maximum APDU (by 64 + granularity). Still, some ccid implementation (ccid 1.3.11, for + example) sends ICC block using chaining unfortunately, so we keep + the code of ICC block chaining. + + +USB communication (initial attempt) +=================================== + +* Once in the past (version <= 0.4), the value of + dwMaxCCIDMessageLength was 64 and we supported CCID/ICCD block chaining, + so that we could not handle multple Bulk transactions. + + +OpenPGP card protocol implementation +==================================== + +I try to follow "no clear password(s)" policy, even if it is on +protected flash memory. Further, keystrings for user and reset code +are removed after key imports. Because of this, replacing keys are +not possible without password information. (Thus, replacing existing +keys are not supported.) + +Therefore, there is "no clear password" and "no keystrings" on flash +ROM when Gnuk is used by admin-less mode. When it is used with admin, +the keystring of admin is on flash ROM. + + +How a private key is stored +=========================== + +KEYPTR + ----> [ P ][ Q ][ N ] + <---encrypted----><--- plain ----> + +initial_vector (random) 16-byte +checksum_encrypted 16-byte +dek_encrypted_by_keystring_pw1 16-byte +dek_encrypted_by_keystring_rc 16-byte +dek_encrypted_by_keystring_pw3 16-byte + +... decrypted to + +[ P ][ Q ] +checksum 16-byte diff --git a/gnuk/doc/note/firmware-update b/gnuk/doc/note/firmware-update new file mode 100644 index 0000000000..386fe8ae38 --- /dev/null +++ b/gnuk/doc/note/firmware-update @@ -0,0 +1,100 @@ +Firmware update feature +======================= + +The firmware update feature of Gnuk is experimental. Please be +careful using that. + +Note that updating firmware, all data objects and keys will be +removed. There is _no way_ to preserve those data. + + +Preparation +=========== + +In addition to settings of Gnuk, I create a file +/etc/udev/rules.d/92-gnuk.rules:: + + # For updating firmware, permission settings are needed. + + SUBSYSTEMS=="usb", ATTRS{idVendor}=="234b", ATTRS{idProduct}=="0000", \ + ENV{ID_USB_INTERFACES}=="*:ff0000:*", GROUP="pcscd" + + +While I am a member of group "pcscd" in /etc/group. + +This is needed for reGNUal, the firmware update program. + + +Registering a public key for firmware update +============================================ + +You need to register a public key to update the firmware. It should +be RSA 2048-bit. + +One way to extract public key data is by using "gpg-connect-agent" +command connecting gpg-agent. + +We can examine key information of gpg-agent by "KEYINFO" command. +Here is my example:: + + $ gpg-connect-agent "KEYINFO --list" /bye + S KEYINFO 65F67E742101C7FE6D5B33FCEFCF4F65EAF0688C T D276000124010200F517000000010000 OPENPGP.2 - - - + S KEYINFO 101DE7B639FE29F4636BDEECF442A9273AFA6565 T D276000124010200F517000000010000 OPENPGP.1 - - - + S KEYINFO 5D6C89682D07CCFC034AF508420BF2276D8018ED T D276000124010200F517000000010000 OPENPGP.3 - - - + OK + +I have three keys in my token. + +With the script below, I extract public key of the keygrip +5D6C89682D07CCFC034AF508420BF2276D8018ED into the file: 5D6C8968.bin:: + + $ ./get_raw_public_key.py 5D6C89682D07CCFC034AF508420BF2276D8018ED + +(The script is available in the directory gnuk/tool. Please note that +it was written in the early stage of the development. The quality of +the code is somewhat questionable.) + + +Then, we can put the data of public key into token by:: + + $ tool/gnuk_put_binary_libusb.py -k 0 5D6C8968.bin + + +Invoking firmware update +======================== + +We specify reGNUal binary and Gnuk binary. + + $ ../tool/gnuk_upgrade.py ../regnual/regnual.bin gnuk.bin + + +Two or more tokens +================== + +Currently, GnuPG doesn't support multiple devices connected to the +host. + +In order to update the firmware of a TARGET token, we use GnuPG to +authenticate with public key. It is assumed that you have another +AUTH token for this. This situation is somewhat complicated. + +What I do is: +(1) Don't run PC/SC daemon:: + + # /etc/init.d/pcscd stop + +(2) To make sure, kill scdaemon:: + + $ killall -9 scdaemon + +(3) Insert the AUTH token to USB, and use it:: + + $ gpg --card-status + +(4) Insert the TARGET token to USB (after scdaemon communicates AUTH + token), and invoke gnuk_upgrade.py. + In this situation, gnuk_upgrade.py tries to connect one of tokens, + but a connection to the AUTH token will fail because scdaemon is + connecting to that device, and will be expected to connect to the + TARGET token succesufully, instead. +-- diff --git a/gnuk/doc/note/firmware-update-2 b/gnuk/doc/note/firmware-update-2 new file mode 100644 index 0000000000..6f78599343 --- /dev/null +++ b/gnuk/doc/note/firmware-update-2 @@ -0,0 +1,131 @@ +Please refer: + + How can I reflash FST-01 with SWD port?: + http://www.gniibe.org/FST-01/q_and_a/swd-debugger.html + + +Installing newer version of Gnuk onto FST-01 with Gnuk 1.0.1 +============================================================ + +Please note that the feature of firmware upgrade is somewhat +experimental. I haven't got any success reports yet, but it's only +used by me, so far. When you will get some failure during your +firmware installation, you will need SWD debugger. YOU HAVE BEEN +WARNED. It is best to try firmware upgrade after you get a SWD +debugger. + + +The firmare upgrade feature of Gnuk +------------------------------------ + +Gnuk supports firmware upgrade by reGNUal. It works in the following +steps. + +1. User registers RSA public key to Gnuk Token for firmware upgrade + +2. When User wants firmware upgrade, user sends + the GET_CHALLENGE command then the EXTERNAL_AUTHENTICATE command + to Gnuk Token from host PC to authenticate. + The EXTERNAL_AUTHENTICATE command message consists of + signature (of challenge) by corresponding RSA private key. + +3. When Gnuk Token receives the EXTERNAL_AUTHENTICATE command message + and validates signature successfully, Gnuk finishes its normal + operation and goes to enter mode of loading special program onto RAM. + +4. Host PC sends reflashing program (reGNUal) to Gnuk Token. + +5. Gnuk clears up all content of flash ROM (but first 4KiB of system) + at the end of receiving special program and transfers its control + to reGNUal. + +6. reGNUal on Gnuk Token receives new firmware image from host PC and writes + to each page. + +7. Done. + + +Host PC setting for Gnuk +------------------------ + +You need proper configuration for permission of Gnuk Token (udev +setting). It should have lines something like: :: + + # Gnuk Token by FSIJ + + SUBSYSTEMS=="usb", ACTION=="add", \ + ATTRS{idVendor}=="234b", ATTRS{idProduct}=="0000", \ + ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg" + +I have those lines in /etc/udev/rules.d/69-gnuk.rules. + + +Building another version (newer) of Gnuk +---------------------------------------- + +Please see README of Gnuk for detail, but it's like configure +and make: :: + + $ pwd + /home/user/src/gnuk + $ cd src + $ ./configure --vidpid=234b:0000 + $ make + +Please take care of configure options. The default target in 1.0.x +series is Olimex STM32 H103 (not FST-01). The default target in 1.1.8 +is FST-01. + + +Then you get build/gnuk.elf and build/gnuk.bin. + +Invoking configure with FSIJ's USB ID (234b:0000) means that you are +using FSIJ's USB ID (for reGNUal in this case). Please note that FSIJ +only allows use of its USB ID for specific situations. Please read +README of Gnuk about that. + + +Bulding reGNUal +--------------- + +You need to compile reGNUal. :: + + $ cd ../regnual + $ make + +Then, you should have regnual.bin. Note that 'configure' of Gnuk +itself is needed before compiling reGNUal. + + +upgrade_by_passwd.py +-------------------- + +In the source code distribution of 1.0.4 (or current development +version) of Gnuk, there is a tool named 'upgrade_by_passwd.py'. + +This is an easy tool to hide lengthy steps from user and to allow user +firmware upgrade only by password of Gnuk Token. + +Before running the script, you need to kill scdaemon: :: + + $ gpg-connect-agent "SCD KILLSCD" "SCD BYE" /bye + +The command line invokation above assumes that you properly configure +your environment for Gnuk Token. + + +How to run the script: :: + + $ cd tool + $ ./upgrade_by_passwd.py ../regnual/regnual.bin ../src/build/gnuk.bin + +Then, the script on your host PC invoke the steps described above, and +you will get new version of Gnuk installed. + +You can also specify -p option to enter your password (other than +factory setting). + +If you already have configured another upgrade key installed, you can +specify different slot by -k ```` option. SLOT_NO can be 0 +to 3. +-- diff --git a/gnuk/doc/note/settings-for-DnDpinentry b/gnuk/doc/note/settings-for-DnDpinentry new file mode 100644 index 0000000000..c0c52ad72a --- /dev/null +++ b/gnuk/doc/note/settings-for-DnDpinentry @@ -0,0 +1,34 @@ +On GNU/Linux Desktop, I use udisks-glue so that DnDpinentry folder can +be mounted with sync and noatime options. + +After installing udisks-glue, I invoke gnome-session-properties to +add udisks-glue to "Startup Program". + +Then, I have two files to configure udisks (disable udisks for +DnDpinentry) and udisks-glue (enable and specify options for DnDpinentry). + +/etc/udev/rules.d/88-udisks.rules +--------------- +ENV{ID_VENDOR}=="FSIJ", ENV{DEVTYPE}=="disk", ENV{ID_FS_LABEL}=="DnDpinentry", ENV{UDISKS_PRESENTATION_NOPOLICY}="1" +--------------- + +~/.udisks-glue.conf +--------------- +filter gone { + label = "DnDpinentry" + optical = false +} + +match gone { + automount = true + automount_options = { sync, noatime } +} +--------------- + +We need following setting for pinentry. Or else, you can't do +anything when pinentry grabs mouse focus. + +~/.gnupg/gpg-agent.conf +--------------- +no-grab +--------------- diff --git a/gnuk/doc/stop-scdaemon.rst b/gnuk/doc/stop-scdaemon.rst new file mode 100644 index 0000000000..fdd52a075e --- /dev/null +++ b/gnuk/doc/stop-scdaemon.rst @@ -0,0 +1,43 @@ +=========================== +Stopping/Resetting SCDAEMON +=========================== + +There is a daemon named ``scdaemon`` behind gpg-agent, which handles +communication to smartcard/token. + +Ideally, we don't need to care about ``scdaemon``, and it should +handle everything automatically. But, there are some cases (because +of bugs), where we need to talk to the daemon directly, in practice. + + +How to communicate SCDAEMON +=========================== + +We have a utility to communicate with a running gpg-agent, that's +gpg-connect-agent. We can use it to communicate with scdaemon, +as it supports sub-command "SCD", exactly for this purpose. + + +Stopping SCDAEMON +================= + +To stop SCDAEMON and let it exit, type:: + + $ gpg-connect-agent "SCD KILLSCD" "SCD BYE" /bye + +Then, you can confirm that there is no SCDAEMON any more by ``ps`` +command. + +Or, you can use ``gpgconf`` command. Type:: + + $ gpgconf --reload scdameon + +will do the samething. + + +Let GPG-AGENT/SCDAEMON learn +============================ + +To let gpg-agent/scdaemon "learn" from Gnuk Token, type:: + + $ gpg-connect-agent learn /bye diff --git a/gnuk/doc/udev-rules.rst b/gnuk/doc/udev-rules.rst new file mode 100644 index 0000000000..53541bd102 --- /dev/null +++ b/gnuk/doc/udev-rules.rst @@ -0,0 +1,59 @@ +=============================================== +Device Configuration for Gnuk Token with libusb +=============================================== + +In order to use Gnuk Token with libusb, configuration of device is +needed for permissions. Note that this is not needed for the case of +PC/SC Lite, as it has its own device configuration. + + +udev rules for Gnuk Token +========================= + +In case of Debian, there is a file /lib/udev/rules.d/60-gnupg.rules +(or /lib/udev/rules.d/60-scdamon.rules for newer version), +when you install "gnupg" package (or "scdaemon" package). +This is the place we need to +change, if your installation is older than jessie. Newer "gnupg" +package (1.4.15-1 or later) or "scdaemon" package has already +supported Gnuk Token. + +If needed, please add lines for Gnuk Token to give a desktop user the +permission to use the device. We specify USB ID of Gnuk Token (by +FSIJ):: + + --- /lib/udev/rules.d/60-gnupg.rules.orig 2012-06-24 21:51:26.000000000 +0900 + +++ /lib/udev/rules.d/60-gnupg.rules 2012-07-13 17:18:55.149587687 +0900 + @@ -10,4 +10,7 @@ + ATTR{idVendor}=="04e6", ATTR{idProduct}=="5115", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg" + ATTR{idVendor}=="20a0", ATTR{idProduct}=="4107", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg" + + +# Gnuk + +ATTR{idVendor}=="234b", ATTR{idProduct}=="0000", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg" + + + LABEL="gnupg_rules_end" + +When we only install "gnupg2" package for 2.0 (with no "gnupg" package), +there will be no udev rules (there is a bug report #543217 for this issue). +In this case, we need something like this in /etc/udev/rules.d/60-gnuk.rules:: + + SUBSYSTEMS=="usb", ATTRS{idVendor}=="234b", ATTRS{idProduct}=="0000", \ + ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg" + +Usually, udev daemon automatically handles for the changes of configuration +files. If not, please let the daemon reload rules:: + + # udevadm control --reload-rules + + + + +udev rules for ST-Link/V2 +========================= + +For development of Gnuk, we use ST-Link/V2 as JTAG/SWD debugger. +We need to have a udev rule for ST-Link/V2. It's like:: + + ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="3748", GROUP="tape", MODE="664", SYMLINK+="stlink" + +I have this in the file /etc/udev/rules.d/10-stlink.rules. diff --git a/gnuk/doc/using-gnuk-token-with-another-computer.rst b/gnuk/doc/using-gnuk-token-with-another-computer.rst new file mode 100644 index 0000000000..6a4a4b6172 --- /dev/null +++ b/gnuk/doc/using-gnuk-token-with-another-computer.rst @@ -0,0 +1,204 @@ +====================================== +Using Gnuk Token with another computer +====================================== + +This document describes how you can use Gnuk Token +on another PC (which is not the one you generate your keys). + +Note that the Token only brings your secret keys, +while ``.gnupg`` directory contains keyrings and trustdb, too. + + +Fetch the public key and connect it to the Token +================================================ + +In order to use the Token, we need to put the public key and the secret +key references (to the token) under ``.gnupg`` directory. + +To do that, invoke GnuPG with ``--card-edit`` option. :: + + Reader ...........: 234B:0000:FSIJ-1.2.0-87193059:0 + Application ID ...: D276000124010200FFFE871930590000 + Version ..........: 2.0 + Manufacturer .....: unmanaged S/N range + Serial number ....: 87193059 + Name of cardholder: Yutaka Niibe + Language prefs ...: ja + Sex ..............: male + URL of public key : http://www.gniibe.org/gniibe-20150813.asc + Login data .......: gniibe + Signature PIN ....: not forced + Key attributes ...: ed25519 cv25519 ed25519 + Max. PIN lengths .: 127 127 127 + PIN retry counter : 3 3 3 + Signature counter : 0 + Signature key ....: 249C B377 1750 745D 5CDD 323C E267 B052 364F 028D + created ....: 2015-08-12 07:10:48 + Encryption key....: E228 AB42 0F73 3B1D 712D E50C 850A F040 D619 F240 + created ....: 2015-08-12 07:10:48 + Authentication key: E63F 31E6 F203 20B5 D796 D266 5F91 0521 FAA8 05B1 + created ....: 2015-08-12 07:16:14 + General key info..: [none] + + gpg/card> + +Here, the secret key references (to the token) are created under ``.gnupg/private-keys-v1.d`` directory. It can be also created when I do ``--card-status`` by GnuPG. + +Still, it says that there is no key info related to this token on my PC (``[none]`` for General key info), because I don't have the public key on this PC yet. + +So, I fetch the public key from URL specified in the Token. :: + + gpg/card> fetch + gpg: requesting key E267B052364F028D from http server www.gniibe.org + gpg: key E267B052364F028D: public key "NIIBE Yutaka " imported + gpg: Total number processed: 1 + gpg: imported: 1 + gpg: marginals needed: 3 completes needed: 1 trust model: pgp + gpg: depth: 0 valid: 6 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 6u + + gpg/card> + +Good. The public key is now under ``.gnupg`` directory. We can examine by ``gpg --list-keys``. + +When I type return at the ``gpg/card>`` prompt, now, I can see: :: + + Reader ...........: 234B:0000:FSIJ-1.2.0-87193059:0 + Application ID ...: D276000124010200FFFE871930590000 + Version ..........: 2.0 + Manufacturer .....: unmanaged S/N range + Serial number ....: 87193059 + Name of cardholder: Yutaka Niibe + Language prefs ...: ja + Sex ..............: male + URL of public key : http://www.gniibe.org/gniibe-20150813.asc + Login data .......: gniibe + Signature PIN ....: not forced + Key attributes ...: ed25519 cv25519 ed25519 + Max. PIN lengths .: 127 127 127 + PIN retry counter : 3 3 3 + Signature counter : 0 + Signature key ....: 249C B377 1750 745D 5CDD 323C E267 B052 364F 028D + created ....: 2015-08-12 07:10:48 + Encryption key....: E228 AB42 0F73 3B1D 712D E50C 850A F040 D619 F240 + created ....: 2015-08-12 07:10:48 + Authentication key: E63F 31E6 F203 20B5 D796 D266 5F91 0521 FAA8 05B1 + created ....: 2015-08-12 07:16:14 + General key info..: pub ed25519/E267B052364F028D 2015-08-12 NIIBE Yutaka + sec> ed25519/E267B052364F028D created: 2015-08-12 expires: never + card-no: FFFE 87193059 + ssb> cv25519/850AF040D619F240 created: 2015-08-12 expires: never + card-no: FFFE 87193059 + ssb> ed25519/5F910521FAA805B1 created: 2015-08-12 expires: never + card-no: FFFE 87193059 + + + gpg/card> + +Note that, it displays the information about "General key info". + +OK, now I can use the Token on this computer. + + +Update trustdb for the key on Gnuk Token +======================================== + +Yes, I can use the Token by the public key and the secret +key references to the card. More, I need to update the trustdb. + +To do that, I do: :: + + $ ./gpg --edit-key E267B052364F028D + gpg (GnuPG) 2.1.13; Copyright (C) 2016 Free Software Foundation, Inc. + This is free software: you are free to change and redistribute it. + There is NO WARRANTY, to the extent permitted by law. + + Secret key is available. + + sec ed25519/E267B052364F028D + created: 2015-08-12 expires: never usage: SC + card-no: FFFE 87193059 + trust: unknown validity: unknown + ssb cv25519/850AF040D619F240 + created: 2015-08-12 expires: never usage: E + card-no: FFFE 87193059 + ssb ed25519/5F910521FAA805B1 + created: 2015-08-12 expires: never usage: A + card-no: FFFE 87193059 + [ unknown] (1). NIIBE Yutaka + [ unknown] (2) NIIBE Yutaka + +See, the key is ``unknown`` state. Add trust for that, because it's the key under my control. :: + + gpg> trust + sec ed25519/E267B052364F028D + created: 2015-08-12 expires: never usage: SC + card-no: FFFE 87193059 + trust: unknown validity: unknown + ssb cv25519/850AF040D619F240 + created: 2015-08-12 expires: never usage: E + card-no: FFFE 87193059 + ssb ed25519/5F910521FAA805B1 + created: 2015-08-12 expires: never usage: A + card-no: FFFE 87193059 + [ unknown] (1). NIIBE Yutaka + [ unknown] (2) NIIBE Yutaka + + Please decide how far you trust this user to correctly verify other users' keys + (by looking at passports, checking fingerprints from different sources, etc.) + + 1 = I don't know or won't say + 2 = I do NOT trust + 3 = I trust marginally + 4 = I trust fully + 5 = I trust ultimately + m = back to the main menu + + Your decision? 5 + Do you really want to set this key to ultimate trust? (y/N) y + + sec ed25519/E267B052364F028D + created: 2015-08-12 expires: never usage: SC + card-no: FFFE 87193059 + trust: ultimate validity: unknown + ssb cv25519/850AF040D619F240 + created: 2015-08-12 expires: never usage: E + card-no: FFFE 87193059 + ssb ed25519/5F910521FAA805B1 + created: 2015-08-12 expires: never usage: A + card-no: FFFE 87193059 + [ unknown] (1). NIIBE Yutaka + [ unknown] (2) NIIBE Yutaka + Please note that the shown key validity is not necessarily correct + unless you restart the program. + + gpg> + +And I quit from gpg. Then, when I invoke GnuPG, it will be ``ultimate`` key. Let's see: :: + + $ ./gpg --edit-key E267B052364F028D + gpg (GnuPG) 2.1.13; Copyright (C) 2016 Free Software Foundation, Inc. + This is free software: you are free to change and redistribute it. + There is NO WARRANTY, to the extent permitted by law. + + Secret key is available. + + gpg: checking the trustdb + gpg: marginals needed: 3 completes needed: 1 trust model: pgp + gpg: depth: 0 valid: 7 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 7u + sec ed25519/E267B052364F028D + created: 2015-08-12 expires: never usage: SC + card-no: FFFE 87193059 + trust: ultimate validity: ultimate + ssb cv25519/850AF040D619F240 + created: 2015-08-12 expires: never usage: E + card-no: FFFE 87193059 + ssb ed25519/5F910521FAA805B1 + created: 2015-08-12 expires: never usage: A + card-no: FFFE 87193059 + [ultimate] (1). NIIBE Yutaka + [ultimate] (2) NIIBE Yutaka + + gpg> quit + $ + +OK, all set. I'm ready to use my Gnuk Token on this PC. diff --git a/gnuk/docker/Dockerfile.check b/gnuk/docker/Dockerfile.check new file mode 100644 index 0000000000..5ee7acf454 --- /dev/null +++ b/gnuk/docker/Dockerfile.check @@ -0,0 +1,7 @@ +FROM gnuk:latest + +LABEL Description="Image for checking gnuK" + +RUN apt install -y shellcheck +RUN apt install -y clang libfindbin-libs-perl +RUN apt clean diff --git a/gnuk/docker/Dockerfile.debug b/gnuk/docker/Dockerfile.debug new file mode 100644 index 0000000000..5aa8a091fb --- /dev/null +++ b/gnuk/docker/Dockerfile.debug @@ -0,0 +1,4 @@ +FROM gnuk:latest +LABEL Description="Image for building gnuK with debugging" + +RUN apt install -y gdb-arm-none-eabi && apt clean diff --git a/gnuk/docker/Dockerfile.release b/gnuk/docker/Dockerfile.release new file mode 100644 index 0000000000..0c933f10ad --- /dev/null +++ b/gnuk/docker/Dockerfile.release @@ -0,0 +1,6 @@ +FROM debian:latest +LABEL Description="Image for building gnuK" + +RUN apt update -y && apt install -y make gcc-arm-none-eabi && apt clean + +CMD ["/bin/sh", "-c", "cd /gnuk/src && make clean && ./configure $GNUK_CONFIG && make"] diff --git a/gnuk/docker/Makefile b/gnuk/docker/Makefile new file mode 100644 index 0000000000..0924902ffd --- /dev/null +++ b/gnuk/docker/Makefile @@ -0,0 +1,36 @@ +ifndef GNUK_CONFIG +$(warning configuration flags not set in GNUK_CONFIG) +endif + +all: ../chopstx docker-build-release + docker run --user=`id -u` --env GNUK_CONFIG --rm -v `pwd`/..:/gnuk/ -t gnuk:latest + +clean: docker-build-release + docker run --user=`id -u` --env GNUK_CONFIG --rm -v `pwd`/..:/gnuk/ -w /gnuk/src -t gnuk:latest make clean + +gdb: docker-build-debug + docker run --net host --rm -i -v `pwd`/..:/gnuk/ -t gnuk:latest-debug arm-none-eabi-gdb /gnuk/src/build/gnuk.elf + +shellcheck: docker-build-check + docker run --rm -v `pwd`/..:/gnuk/ -t gnuk:latest-check shellcheck /gnuk/src/configure + +CHECKERS=security optin nullability core deadcode alpha.core alpha.security +scan-build: clean docker-build-check + docker run --user=`id -u` --rm -v `pwd`/..:/gnuk/ -w /gnuk/src -t gnuk:latest-check scan-build -o scan-build \ + -analyze-headers -stats $(addprefix -enable-checker ,$(CHECKERS)) -k \ + --use-cc=arm-none-eabi-gcc \ + make +../chopstx: + git submodule update --init + +docker-build-release: + docker build -t gnuk:latest -f `pwd`/Dockerfile.release .. + +docker-build-debug: docker-build-release + docker build -t gnuk:latest-debug -f `pwd`/Dockerfile.debug .. + +docker-build-check: docker-build-release + docker build -t gnuk:latest-check -f `pwd`/Dockerfile.check .. + +.PHONY: all clean gdb shellcheck scan-build \ + docker-build-release docker-build-debug docker-build-check diff --git a/gnuk/gnuk-sticker.svg b/gnuk/gnuk-sticker.svg new file mode 100644 index 0000000000..b3671648b4 --- /dev/null +++ b/gnuk/gnuk-sticker.svg @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + GNUK + + + + + + + + + + + + GnuPG USB Token + + diff --git a/gnuk/gnuk-stickers.svg b/gnuk/gnuk-stickers.svg new file mode 100644 index 0000000000..bd14205a07 --- /dev/null +++ b/gnuk/gnuk-stickers.svg @@ -0,0 +1,553 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + GNUK + + + + + + + + + + + + GnuPG USB Token + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gnuk/gnuk.png b/gnuk/gnuk.png new file mode 100644 index 0000000000..3510a00437 Binary files /dev/null and b/gnuk/gnuk.png differ diff --git a/gnuk/gnuk.svg b/gnuk/gnuk.svg new file mode 100644 index 0000000000..6463adb3f4 --- /dev/null +++ b/gnuk/gnuk.svg @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/gnuk/misc/debug-bn.c b/gnuk/misc/debug-bn.c new file mode 100644 index 0000000000..c9092da46f --- /dev/null +++ b/gnuk/misc/debug-bn.c @@ -0,0 +1,220 @@ +/* + * debug-bn.c - Debug Bignum + * Copyright (C) 2014 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + */ + +#include +#include +#include +#include +#include + +#include "bn.h" + +void +print_le_bn256 (const bn256 *X) +{ + int i; + const uint8_t *p = (const uint8_t *)X; + + for (i = 0; i < 32; i++) + printf ("%02x", p[i]); + puts (""); +} + +void +print_be_bn256 (const bn256 *X) +{ + int i; + + for (i = 7; i >= 0; i--) + printf ("%08x", X->word[i]); + puts (""); +} + +#define MAXLINE 4096 + +static int lineno; +static int test_no; +static bn256 sk[1]; +static bn256 pk[1]; +static unsigned char msg[MAXLINE]; +static size_t msglen; +static bn512 sig[1]; + +const char * +skip_white_space (const char *l) +{ + while (*l != '\n' && isspace (*l)) + l++; + + return l; +} + + +static int +read_hex_4bit (char c) +{ + int r; + + if (c >= '0' && c <= '9') + r = c - '0'; + else if (c >= 'a' && c <= 'f') + r = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + r = c - 'A' + 10; + else + r = -1; + return r; +} + +static int +read_hex_8bit (const char **l_p) +{ + const char *l = *l_p; + int r, v; + + r = read_hex_4bit (*l++); + if (r < 0) + return -1; + v = r*16; + r = read_hex_4bit (*l++); + if (r < 0) + return -1; + v += r; + + *l_p = l; + return v; +} + +static int +read_msg (unsigned char *msg, const char *l, int len) +{ + int i, r; + + for (i = 0; i < len; i++) + { + r = read_hex_8bit (&l); + if (r < 0) + return -1; + msg[i] = r; + } + + return 0; +} + + +static int +read_le_bn256 (bn256 *sk, const char *l) +{ + int i; + uint8_t *p = (uint8_t *)sk; + + for (i = 0; i < sizeof (bn256); i++) + { + int r; + + if (*l == '\n') + { + /* should support small input??? */ + return -1; + } + + r = read_hex_8bit (&l); + if (r < 0) + return -1; + + p[i] = r; + } + + return 0; +} + +static int +read_be_bn256 (bn256 *sk, const char *l) +{ + int i; + uint8_t *p = (uint8_t *)sk; + + for (i = 0; i < sizeof (bn256); i++) + { + int r; + + if (*l == '\n') + { + /* should support small input??? */ + return -1; + } + + r = read_hex_8bit (&l); + if (r < 0) + return -1; + + p[31 - i] = r; + } + + return 0; +} + + +static int +read_pk (bn256 *pk, const char *l, int len) +{ + int r; + + if (len == 64) /* 64 chars == 32-byte */ + { /* compressed form */ + r = read_le_bn256 (pk, l); + if (r < 0) + return -1; + return 0; + } + else + { + bn256 x[1]; + + r = read_hex_8bit (&l); + if (r < 0) + return -1; + if (r != 4) + return -1; + + r = read_be_bn256 (x, l); + if (r < 0) + return -1; + r = read_be_bn256 (pk, l+64); + if (r < 0) + return -1; + + pk->word[7] ^= (x->word[0] & 1) * 0x80000000; + return 0; + } +} + +static int +read_le_bn512 (bn512 *sig, const char *l) +{ + int i; + uint8_t *p = (uint8_t *)sig; + + for (i = 0; i < sizeof (bn512); i++) + { + int r; + + if (*l == '\n') + { + /* should support small input??? */ + return -1; + } + + r = read_hex_8bit (&l); + if (r < 0) + return -1; + + p[i] = r; + } + + return 0; +} diff --git a/gnuk/misc/t-eddsa.c b/gnuk/misc/t-eddsa.c new file mode 100644 index 0000000000..89670a484e --- /dev/null +++ b/gnuk/misc/t-eddsa.c @@ -0,0 +1,398 @@ +/* + * t-eddsa.c - testing EdDSA + * Copyright (C) 2014 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * Run following commands. The file t-ed25519.inp is available in GNU + * libgcrypt source code under 'tests' directory. + + gcc -Wall -c ecc-edwards.c + gcc -Wall -c -DBN256_NO_RANDOM -DBN256_C_IMPLEMENTATION bn.c + gcc -Wall -c mod.c + gcc -Wall -c -DBN256_C_IMPLEMENTATION mod25638.c + gcc -Wall -c sha512.c + gcc -Wall -c t-eddsa.c + gcc -o t-eddsa t-eddsa.o ecc-edwards.o bn.o mod.o mod25638.o sha512.o + ./t-eddsa < ./t-ed25519.inp + + * + */ + +#include +#include +#include +#include +#include + +#include "bn.h" +#include "affine.h" +#include "sha512.h" + +static void +print_le_bn256 (const bn256 *X) +{ + int i; + const uint8_t *p = (const uint8_t *)X; + + for (i = 0; i < 32; i++) + printf ("%02x", p[i]); + puts (""); +} + +static void +print_be_bn256 (const bn256 *X) +{ + int i; + + for (i = 7; i >= 0; i--) + printf ("%08x", X->word[i]); + puts (""); +} + +#define MAXLINE 4096 + +static int lineno; +static int test_no; +static bn256 sk[1]; +static bn256 pk[1]; +static unsigned char msg[MAXLINE]; +static size_t msglen; +static bn512 sig[1]; + +static const char * +skip_white_space (const char *l) +{ + while (*l != '\n' && isspace (*l)) + l++; + + return l; +} + + +static int +read_hex_4bit (char c) +{ + int r; + + if (c >= '0' && c <= '9') + r = c - '0'; + else if (c >= 'a' && c <= 'f') + r = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + r = c - 'A' + 10; + else + r = -1; + return r; +} + +static int +read_hex_8bit (const char **l_p) +{ + const char *l = *l_p; + int r, v; + + r = read_hex_4bit (*l++); + if (r < 0) + return -1; + v = r*16; + r = read_hex_4bit (*l++); + if (r < 0) + return -1; + v += r; + + *l_p = l; + return v; +} + +static int +read_msg (unsigned char *msg, const char *l, int len) +{ + int i, r; + + for (i = 0; i < len; i++) + { + r = read_hex_8bit (&l); + if (r < 0) + return -1; + msg[i] = r; + } + + return 0; +} + + +static int +read_le_bn256 (bn256 *sk, const char *l) +{ + int i; + uint8_t *p = (uint8_t *)sk; + + for (i = 0; i < sizeof (bn256); i++) + { + int r; + + if (*l == '\n') + { + /* should support small input??? */ + return -1; + } + + r = read_hex_8bit (&l); + if (r < 0) + return -1; + + p[i] = r; + } + + return 0; +} + +static int +read_be_bn256 (bn256 *sk, const char *l) +{ + int i; + uint8_t *p = (uint8_t *)sk; + + for (i = 0; i < sizeof (bn256); i++) + { + int r; + + if (*l == '\n') + { + /* should support small input??? */ + return -1; + } + + r = read_hex_8bit (&l); + if (r < 0) + return -1; + + p[31 - i] = r; + } + + return 0; +} + + +static int +read_pk (bn256 *pk, const char *l, int len) +{ + int r; + + if (len == 64) /* 64 chars == 32-byte */ + { /* compressed form */ + r = read_le_bn256 (pk, l); + if (r < 0) + return -1; + return 0; + } + else + { + bn256 x[1]; + + r = read_hex_8bit (&l); + if (r < 0) + return -1; + if (r != 4) + return -1; + + r = read_be_bn256 (x, l); + if (r < 0) + return -1; + r = read_be_bn256 (pk, l+64); + if (r < 0) + return -1; + + pk->word[7] ^= (x->word[0] & 1) * 0x80000000; + return 0; + } +} + +static int +read_le_bn512 (bn512 *sig, const char *l) +{ + int i; + uint8_t *p = (uint8_t *)sig; + + for (i = 0; i < sizeof (bn512); i++) + { + int r; + + if (*l == '\n') + { + /* should support small input??? */ + return -1; + } + + r = read_hex_8bit (&l); + if (r < 0) + return -1; + + p[i] = r; + } + + return 0; +} + +static int +read_testcase (void) +{ + ssize_t r; + size_t len = 0; + char *line = NULL; + int start = 0; + int err = 0; + + test_no = 0; + memset (sk, 0, sizeof (bn256)); + memset (pk, 0, sizeof (ac)); + msglen = 0; + memset (sig, 0, sizeof (bn512)); + + while (1) + { + lineno++; + r = getline (&line, &len, stdin); + if (r < 0) + { + /* EOF */ + if (!start) + err = -1; + break; + } + len = r; /* We don't need allocated size, but length. */ + if (len >= MAXLINE) + { + fprintf (stderr, "Line too long: %d: >= %d\n", lineno, MAXLINE); + err = -1; + break; + } + + if (r == 1 && *line == '\n') + { + if (start) + break; /* Done. */ + else + continue; /* Ignore blank line before start. */ + } + + if (r > 0 && *line == '#') /* Ignore comment line. */ + continue; + + start = 1; + if (r > 4 && strncmp (line, "TST:", 4) == 0) + test_no = strtol (line+4, NULL, 10); + else if (r > 3 && strncmp (line, "SK:", 3) == 0) + { + const char *l = skip_white_space (line+3); + if (read_le_bn256 (sk, l) < 0) + { + fprintf (stderr, "read_le_bn256: %d\n", lineno); + err = -1; + break; + } + } + else if (r > 3 && strncmp (line, "PK:", 3) == 0) + { + const char *l = skip_white_space (line+3); + if (read_pk (pk, l, line+len-1-l) < 0) + { + fprintf (stderr, "read_pk: %d\n", lineno); + err = -1; + break; + } + } + else if (r > 4 && strncmp (line, "MSG:", 4) == 0) + { + const char *l = skip_white_space (line+4); + msglen = (line+len-1-l)/2; + if (read_msg (msg, l, msglen) < 0) + { + fprintf (stderr, "read_msg: %d\n", lineno); + err = -1; + break; + } + } + else if (r > 4 && strncmp (line, "SIG:", 4) == 0) + { + const char *l = skip_white_space (line+4); + if (read_le_bn512 (sig, l) < 0) + { + fprintf (stderr, "read_le_bn512: %d\n", lineno); + err = -1; + break; + } + } + else + { + fprintf (stderr, "Garbage line: %d", lineno); + err = -1; + break; + } + } + + free (line); + return err; +} + + +int +main (int argc, char *argv[]) +{ + int all_good = 1; + int r; + bn256 pk_calculated[1]; + uint8_t hash[64]; + bn256 a[1]; + bn256 *R, *S; + uint8_t out[64]; + + extern void eddsa_sign_25519 (const uint8_t *input, size_t ilen, + uint8_t *output, + const bn256 *a, const uint8_t *seed, + const bn256 *pk); + extern void eddsa_public_key_25519 (bn256 *pk, const bn256 *a); + + R = (bn256 *)out; + S = (bn256 *)(out+32); + + while (1) + { + r = read_testcase (); + if (r < 0) + break; + + sha512 ((uint8_t *)sk, sizeof (bn256), hash); + hash[0] &= 248; + hash[31] &= 127; + hash[31] |= 64; + memcpy (a, hash, sizeof (bn256)); /* Lower half of hash */ + + eddsa_public_key_25519 (pk_calculated, a); + if (memcmp (pk, pk_calculated, sizeof (bn256)) != 0) + { + printf ("ERR PK: %d\n", test_no); + print_be_bn256 (sk); + print_be_bn256 (pk); + print_be_bn256 (pk_calculated); + all_good = 0; + continue; + } + + eddsa_sign_25519 (msg, msglen, out, a, hash+32, pk); + if (memcmp (sig, R, sizeof (bn256)) != 0 + || memcmp (((const uint8_t *)sig)+32, S, sizeof (bn256)) != 0) + { + printf ("ERR SIG: %d\n", test_no); + print_le_bn256 (R); + print_le_bn256 (S); + print_le_bn256 ((const bn256 *)sig); + print_le_bn256 ((const bn256 *)(((const uint8_t *)sig)+32)); + all_good = 0; + continue; + } + + printf ("%d\n", test_no); + } + return all_good == 1?0:1; +} diff --git a/gnuk/misc/t-mont.c b/gnuk/misc/t-mont.c new file mode 100644 index 0000000000..6a1b2f1f5f --- /dev/null +++ b/gnuk/misc/t-mont.c @@ -0,0 +1,92 @@ +/* + * t-eddsa.c - testing EdDSA + * Copyright (C) 2014 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * Run following commands. The file t-ed25519.inp is available in GNU + * libgcrypt source code under 'tests' directory. + + gcc -Wall -c -DBN256_C_IMPLEMENTATION ecc-mont.c + gcc -Wall -c -DBN256_NO_RANDOM -DBN256_C_IMPLEMENTATION bn.c + gcc -Wall -c mod.c + gcc -Wall -c -DBN256_C_IMPLEMENTATION mod25638.c + gcc -Wall -c t-mont.c + gcc -Wall -c debug-bn.c + gcc -o t-mont t-mont.o ecc-mont.o bn.o mod.o mod25638.o debug-bn.o + + + * + */ + +#include +#include +#include +#include +#include + +#include "bn.h" + +const uint8_t k[32] = { + 0x30, 0x01, 0x33, 0xE7, 0xDC, 0x52, 0xAD, 0x9F, + 0x89, 0xFE, 0xC0, 0x59, 0x4A, 0x6D, 0x65, 0xE5, + 0xF8, 0x7A, 0xD6, 0xA9, 0xA4, 0x89, 0x00, 0xB1, + 0x93, 0x7E, 0xD3, 0x6F, 0x09, 0x1E, 0xB7, 0x76, +}; + +int +main (int argc, char *argv[]) +{ + int all_good = 1; + int r; + bn256 *pk; + bn256 a[1]; + uint8_t out[32]; + + extern void ecdh_decrypt_curve25519 (const uint8_t *input, + uint8_t *output, + const bn256 *k); + extern uint8_t *ecdh_compute_public_25519 (const uint8_t*k); + extern void print_le_bn256 (const bn256 *X); + + while (1) + { +#if 0 + hash[0] &= 248; + hash[31] &= 127; + hash[31] |= 64; + memcpy (a, hash, sizeof (bn256)); /* Lower half of hash */ +#endif + + pk = ecdh_compute_public_25519 (k); + print_le_bn256 (pk); + return 0; + +#if 0 + if (memcmp (pk, pk_calculated, sizeof (bn256)) != 0) + { + printf ("ERR PK: %d\n", test_no); + print_be_bn256 (sk); + print_be_bn256 (pk); + print_be_bn256 (pk_calculated); + all_good = 0; + continue; + } + + ecdh_decrypt_25519 (msg, out, a); + if (memcmp (sig, R, sizeof (bn256)) != 0 + || memcmp (((const uint8_t *)sig)+32, S, sizeof (bn256)) != 0) + { + printf ("ERR SIG: %d\n", test_no); + print_le_bn256 (R); + print_le_bn256 (S); + print_le_bn256 ((const bn256 *)sig); + print_le_bn256 ((const bn256 *)(((const uint8_t *)sig)+32)); + all_good = 0; + continue; + } + + printf ("%d\n", test_no); +#endif + } + return all_good == 1?0:1; +} diff --git a/gnuk/polarssl/ChangeLog b/gnuk/polarssl/ChangeLog new file mode 100644 index 0000000000..86beaf70c0 --- /dev/null +++ b/gnuk/polarssl/ChangeLog @@ -0,0 +1,770 @@ +PolarSSL ChangeLog + += Version 1.2.10 released 2013-10-07 +Changes + * Changed RSA blinding to a slower but thread-safe version + +Bugfix + * Fixed memory leak in RSA as a result of introduction of blinding + * Fixed ssl_pkcs11_decrypt() prototype + * Fixed MSVC project files + += Version 1.2.9 released 2013-10-01 +Changes + * x509_verify() now case insensitive for cn (RFC 6125 6.4) + +Bugfix + * Fixed potential memory leak when failing to resume a session + * Fixed potential file descriptor leaks (found by Remi Gacogne) + * Minor fixes + +Security + * Fixed potential heap buffer overflow on large hostname setting + * Fixed potential negative value misinterpretation in load_file() + * RSA blinding on CRT operations to counter timing attacks + (found by Cyril Arnaud and Pierre-Alain Fouque) + += Version 1.2.8 released 2013-06-19 +Features + * Parsing of PKCS#8 encrypted private key files + * PKCS#12 PBE and derivation functions + * Centralized module option values in config.h to allow user-defined + settings without editing header files by using POLARSSL_CONFIG_OPTIONS + +Changes + * HAVEGE random generator disabled by default + * Internally split up x509parse_key() into a (PEM) handler function + and specific DER parser functions for the PKCS#1 and unencrypted + PKCS#8 private key formats + * Added mechanism to provide alternative implementations for all + symmetric cipher and hash algorithms (e.g. POLARSSL_AES_ALT in + config.h) + * PKCS#5 module added. Moved PBKDF2 functionality inside and deprecated + old PBKDF2 module + +Bugfix + * Secure renegotiation extension should only be sent in case client + supports secure renegotiation + * Fixed offset for cert_type list in ssl_parse_certificate_request() + * Fixed const correctness issues that have no impact on the ABI + * x509parse_crt() now better handles PEM error situations + * ssl_parse_certificate() now calls x509parse_crt_der() directly + instead of the x509parse_crt() wrapper that can also parse PEM + certificates + * x509parse_crtpath() is now reentrant and uses more portable stat() + * Fixed bignum.c and bn_mul.h to support Thumb2 and LLVM compiler + * Fixed values for 2-key Triple DES in cipher layer + * ssl_write_certificate_request() can handle empty ca_chain + +Security + * A possible DoS during the SSL Handshake, due to faulty parsing of + PEM-encoded certificates has been fixed (found by Jack Lloyd) + += Version 1.2.7 released 2013-04-13 +Features + * Ability to specify allowed ciphersuites based on the protocol version. + +Changes + * Default Blowfish keysize is now 128-bits + * Test suites made smaller to accommodate Raspberry Pi + +Bugfix + * Fix for MPI assembly for ARM + * GCM adapted to support sizes > 2^29 + += Version 1.2.6 released 2013-03-11 +Bugfix + * Fixed memory leak in ssl_free() and ssl_reset() for active session + * Corrected GCM counter incrementation to use only 32-bits instead of + 128-bits (found by Yawning Angel) + * Fixes for 64-bit compilation with MS Visual Studio + * Fixed net_bind() for specified IP addresses on little endian systems + * Fixed assembly code for ARM (Thumb and regular) for some compilers + +Changes + * Internally split up rsa_pkcs1_encrypt(), rsa_pkcs1_decrypt(), + rsa_pkcs1_sign() and rsa_pkcs1_verify() to separate PKCS#1 v1.5 and + PKCS#1 v2.1 functions + * Added support for custom labels when using rsa_rsaes_oaep_encrypt() + or rsa_rsaes_oaep_decrypt() + * Re-added handling for SSLv2 Client Hello when the define + POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO is set + * The SSL session cache module (ssl_cache) now also retains peer_cert + information (not the entire chain) + +Security + * Removed further timing differences during SSL message decryption in + ssl_decrypt_buf() + * Removed timing differences due to bad padding from + rsa_rsaes_pkcs1_v15_decrypt() and rsa_pkcs1_decrypt() for PKCS#1 v1.5 + operations + += Version 1.2.5 released 2013-02-02 +Changes + * Allow enabling of dummy error_strerror() to support some use-cases + * Debug messages about padding errors during SSL message decryption are + disabled by default and can be enabled with POLARSSL_SSL_DEBUG_ALL + * Sending of security-relevant alert messages that do not break + interoperability can be switched on/off with the flag + POLARSSL_SSL_ALL_ALERT_MESSAGES + +Security + * Removed timing differences during SSL message decryption in + ssl_decrypt_buf() due to badly formatted padding + += Version 1.2.4 released 2013-01-25 +Changes + * Added ssl_handshake_step() to allow single stepping the handshake process + +Bugfix + * Memory leak when using RSA_PKCS_V21 operations fixed + * Handle future version properly in ssl_write_certificate_request() + * Correctly handle CertificateRequest message in client for <= TLS 1.1 + without DN list + += Version 1.2.3 released 2012-11-26 +Bugfix + * Server not always sending correct CertificateRequest message + += Version 1.2.2 released 2012-11-24 +Changes + * Added p_hw_data to ssl_context for context specific hardware acceleration + data + * During verify trust-CA is only checked for expiration and CRL presence + +Bugfixes + * Fixed client authentication compatibility + * Fixed dependency on POLARSSL_SHA4_C in SSL modules + += Version 1.2.1 released 2012-11-20 +Changes + * Depth that the certificate verify callback receives is now numbered + bottom-up (Peer cert depth is 0) + +Bugfixes + * Fixes for MSVC6 + * Moved mpi_inv_mod() outside POLARSSL_GENPRIME + * Allow R and A to point to same mpi in mpi_div_mpi (found by Manuel + Pégourié-Gonnard) + * Fixed possible segfault in mpi_shift_r() (found by Manuel + Pégourié-Gonnard) + * Added max length check for rsa_pkcs1_sign with PKCS#1 v2.1 + += Version 1.2.0 released 2012-10-31 +Features + * Added support for NULL cipher (POLARSSL_CIPHER_NULL_CIPHER) and weak + ciphersuites (POLARSSL_ENABLE_WEAK_CIPHERSUITES). They are disabled by + default! + * Added support for wildcard certificates + * Added support for multi-domain certificates through the X509 Subject + Alternative Name extension + * Added preliminary ASN.1 buffer writing support + * Added preliminary X509 Certificate Request writing support + * Added key_app_writer example application + * Added cert_req example application + * Added base Galois Counter Mode (GCM) for AES + * Added TLS 1.2 support (RFC 5246) + * Added GCM suites to TLS 1.2 (RFC 5288) + * Added commandline error code convertor (util/strerror) + * Added support for Hardware Acceleration hooking in SSL/TLS + * Added OpenSSL / PolarSSL compatibility script (tests/compat.sh) and + example application (programs/ssl/o_p_test) (requires OpenSSL) + * Added X509 CA Path support + * Added Thumb assembly optimizations + * Added DEFLATE compression support as per RFC3749 (requires zlib) + * Added blowfish algorithm (Generic and cipher layer) + * Added PKCS#5 PBKDF2 key derivation function + * Added Secure Renegotiation (RFC 5746) + * Added predefined DHM groups from RFC 5114 + * Added simple SSL session cache implementation + * Added ServerName extension parsing (SNI) at server side + * Added option to add minimum accepted SSL/TLS protocol version + +Changes + * Removed redundant POLARSSL_DEBUG_MSG define + * AES code only check for Padlock once + * Fixed const-correctness mpi_get_bit() + * Documentation for mpi_lsb() and mpi_msb() + * Moved out_msg to out_hdr + 32 to support hardware acceleration + * Changed certificate verify behaviour to comply with RFC 6125 section 6.3 + to not match CN if subjectAltName extension is present (Closes ticket #56) + * Cipher layer cipher_mode_t POLARSSL_MODE_CFB128 is renamed to + POLARSSL_MODE_CFB, to also handle different block size CFB modes. + * Removed handling for SSLv2 Client Hello (as per RFC 5246 recommendation) + * Revamped session resumption handling + * Generalized external private key implementation handling (like PKCS#11) + in SSL/TLS + * Revamped x509_verify() and the SSL f_vrfy callback implementations + * Moved from unsigned long to fixed width uint32_t types throughout code + * Renamed ciphersuites naming scheme to IANA reserved names + +Bugfix + * Fixed handling error in mpi_cmp_mpi() on longer B values (found by + Hui Dong) + * Fixed potential heap corruption in x509_name allocation + * Fixed single RSA test that failed on Big Endian systems (Closes ticket #54) + * mpi_exp_mod() now correctly handles negative base numbers (Closes ticket + #52) + * Handle encryption with private key and decryption with public key as per + RFC 2313 + * Handle empty certificate subject names + * Prevent reading over buffer boundaries on X509 certificate parsing + * mpi_add_abs() now correctly handles adding short numbers to long numbers + with carry rollover (found by Ruslan Yushchenko) + * Handle existence of OpenSSL Trust Extensions at end of X.509 DER blob + * Fixed MPI assembly for SPARC64 platform + +Security + * Fixed potential memory zeroization on miscrafted RSA key (found by Eloi + Vanderbeken) + += Version 1.1.5 released on 2013-01-16 +Bugfix + * Fixed MPI assembly for SPARC64 platform + * Handle existence of OpenSSL Trust Extensions at end of X.509 DER blob + * mpi_add_abs() now correctly handles adding short numbers to long numbers + with carry rollover + * Moved mpi_inv_mod() outside POLARSSL_GENPRIME + * Prevent reading over buffer boundaries on X509 certificate parsing + * mpi_exp_mod() now correctly handles negative base numbers (Closes ticket + #52) + * Fixed possible segfault in mpi_shift_r() (found by Manuel + Pégourié-Gonnard) + * Allow R and A to point to same mpi in mpi_div_mpi (found by Manuel + Pégourié-Gonnard) + * Added max length check for rsa_pkcs1_sign with PKCS#1 v2.1 + * Memory leak when using RSA_PKCS_V21 operations fixed + * Handle encryption with private key and decryption with public key as per + RFC 2313 + * Fixes for MSVC6 + +Security + * Fixed potential memory zeroization on miscrafted RSA key (found by Eloi + Vanderbeken) + += Version 1.1.4 released on 2012-05-31 +Bugfix + * Correctly handle empty SSL/TLS packets (Found by James Yonan) + * Fixed potential heap corruption in x509_name allocation + * Fixed single RSA test that failed on Big Endian systems (Closes ticket #54) + += Version 1.1.3 released on 2012-04-29 +Bugfix + * Fixed random MPI generation to not generate more size than requested. + += Version 1.1.2 released on 2012-04-26 +Bugfix + * Fixed handling error in mpi_cmp_mpi() on longer B values (found by + Hui Dong) + +Security + * Fixed potential memory corruption on miscrafted client messages (found by + Frama-C team at CEA LIST) + * Fixed generation of DHM parameters to correct length (found by Ruslan + Yushchenko) + += Version 1.1.1 released on 2012-01-23 +Bugfix + * Check for failed malloc() in ssl_set_hostname() and x509_get_entries() + (Closes ticket #47, found by Hugo Leisink) + * Fixed issues with Intel compiler on 64-bit systems (Closes ticket #50) + * Fixed multiple compiler warnings for VS6 and armcc + * Fixed bug in CTR_CRBG selftest + += Version 1.1.0 released on 2011-12-22 +Features + * Added ssl_session_reset() to allow better multi-connection pools of + SSL contexts without needing to set all non-connection-specific + data and pointers again. Adapted ssl_server to use this functionality. + * Added ssl_set_max_version() to allow clients to offer a lower maximum + supported version to a server to help buggy server implementations. + (Closes ticket #36) + * Added cipher_get_cipher_mode() and cipher_get_cipher_operation() + introspection functions (Closes ticket #40) + * Added CTR_DRBG based on AES-256-CTR (NIST SP 800-90) random generator + * Added a generic entropy accumulator that provides support for adding + custom entropy sources and added some generic and platform dependent + entropy sources + +Changes + * Documentation for AES and Camellia in modes CTR and CFB128 clarified. + * Fixed rsa_encrypt and rsa_decrypt examples to use public key for + encryption and private key for decryption. (Closes ticket #34) + * Inceased maximum size of ASN1 length reads to 32-bits. + * Added an EXPLICIT tag number parameter to x509_get_ext() + * Added a separate CRL entry extension parsing function + * Separated the ASN.1 parsing code from the X.509 specific parsing code. + So now there is a module that is controlled with POLARSSL_ASN1_PARSE_C. + * Changed the defined key-length of DES ciphers in cipher.h to include the + parity bits, to prevent mistakes in copying data. (Closes ticket #33) + * Loads of minimal changes to better support WINCE as a build target + (Credits go to Marco Lizza) + * Added POLARSSL_MPI_WINDOW_SIZE definition to allow easier time to memory + trade-off + * Introduced POLARSSL_MPI_MAX_SIZE and POLARSSL_MPI_MAX_BITS for MPI size + management (Closes ticket #44) + * Changed the used random function pointer to more flexible format. Renamed + havege_rand() to havege_random() to prevent mistakes. Lots of changes as + a consequence in library code and programs + * Moved all examples programs to use the new entropy and CTR_DRBG + * Added permissive certificate parsing to x509parse_crt() and + x509parse_crtfile(). With permissive parsing the parsing does not stop on + encountering a parse-error. Beware that the meaning of return values has + changed! + * All error codes are now negative. Even on mermory failures and IO errors. + +Bugfix + * Fixed faulty HMAC-MD2 implementation. Found by dibac. (Closes + ticket #37) + * Fixed a bug where the CRL parser expected an EXPLICIT ASN.1 tag + before version numbers + * Allowed X509 key usage parsing to accept 4 byte values instead of the + standard 1 byte version sometimes used by Microsoft. (Closes ticket #38) + * Fixed incorrect behaviour in case of RSASSA-PSS with a salt length + smaller than the hash length. (Closes ticket #41) + * If certificate serial is longer than 32 octets, serial number is now + appended with '....' after first 28 octets + * Improved build support for s390x and sparc64 in bignum.h + * Fixed MS Visual C++ name clash with int64 in sha4.h + * Corrected removal of leading "00:" in printing serial numbers in + certificates and CRLs + += Version 1.0.0 released on 2011-07-27 +Features + * Expanded cipher layer with support for CFB128 and CTR mode + * Added rsa_encrypt and rsa_decrypt simple example programs. + +Changes + * The generic cipher and message digest layer now have normal error + codes instead of integers + +Bugfix + * Undid faulty bug fix in ssl_write() when flushing old data (Ticket + #18) + += Version 0.99-pre5 released on 2011-05-26 +Features + * Added additional Cipher Block Modes to symmetric ciphers + (AES CTR, Camellia CTR, XTEA CBC) including the option to + enable and disable individual modes when needed + * Functions requiring File System functions can now be disabled + by undefining POLARSSL_FS_IO + * A error_strerror function() has been added to translate between + error codes and their description. + * Added mpi_get_bit() and mpi_set_bit() individual bit setter/getter + functions. + * Added ssl_mail_client and ssl_fork_server as example programs. + +Changes + * Major argument / variable rewrite. Introduced use of size_t + instead of int for buffer lengths and loop variables for + better unsigned / signed use. Renamed internal bigint types + t_int and t_dbl to t_uint and t_udbl in the process + * mpi_init() and mpi_free() now only accept a single MPI + argument and do not accept variable argument lists anymore. + * The error codes have been remapped and combining error codes + is now done with a PLUS instead of an OR as error codes + used are negative. + * Changed behaviour of net_read(), ssl_fetch_input() and ssl_recv(). + net_recv() now returns 0 on EOF instead of + POLARSSL_ERR_NET_CONN_RESET. ssl_fetch_input() returns + POLARSSL_ERR_SSL_CONN_EOF on an EOF from its f_recv() function. + ssl_read() returns 0 if a POLARSSL_ERR_SSL_CONN_EOF is received + after the handshake. + * Network functions now return POLARSSL_ERR_NET_WANT_READ or + POLARSSL_ERR_NET_WANT_WRITE instead of the ambiguous + POLARSSL_ERR_NET_TRY_AGAIN + += Version 0.99-pre4 released on 2011-04-01 +Features + * Added support for PKCS#1 v2.1 encoding and thus support + for the RSAES-OAEP and RSASSA-PSS operations. + * Reading of Public Key files incorporated into default x509 + functionality as well. + * Added mpi_fill_random() for centralized filling of big numbers + with random data (Fixed ticket #10) + +Changes + * Debug print of MPI now removes leading zero octets and + displays actual bit size of the value. + * x509parse_key() (and as a consequence x509parse_keyfile()) + does not zeroize memory in advance anymore. Use rsa_init() + before parsing a key or keyfile! + +Bugfix + * Debug output of MPI's now the same independent of underlying + platform (32-bit / 64-bit) (Fixes ticket #19, found by Mads + Kiilerich and Mihai Militaru) + * Fixed bug in ssl_write() when flushing old data (Fixed ticket + #18, found by Nikolay Epifanov) + * Fixed proper handling of RSASSA-PSS verification with variable + length salt lengths + += Version 0.99-pre3 released on 2011-02-28 +This release replaces version 0.99-pre2 which had possible copyright issues. +Features + * Parsing PEM private keys encrypted with DES and AES + are now supported as well (Fixes ticket #5) + * Added crl_app program to allow easy reading and + printing of X509 CRLs from file + +Changes + * Parsing of PEM files moved to separate module (Fixes + ticket #13). Also possible to remove PEM support for + systems only using DER encoding + +Bugfixes + * Corrected parsing of UTCTime dates before 1990 and + after 1950 + * Support more exotic OID's when parsing certificates + (found by Mads Kiilerich) + * Support more exotic name representations when parsing + certificates (found by Mads Kiilerich) + * Replaced the expired test certificates + * Do not bail out if no client certificate specified. Try + to negotiate anonymous connection (Fixes ticket #12, + found by Boris Krasnovskiy) + +Security fixes + * Fixed a possible Man-in-the-Middle attack on the + Diffie Hellman key exchange (thanks to Larry Highsmith, + Subreption LLC) + += Version 0.99-pre1 released on 2011-01-30 +Features +Note: Most of these features have been donated by Fox-IT + * Added Doxygen source code documentation parts + * Added reading of DHM context from memory and file + * Improved X509 certificate parsing to include extended + certificate fields, including Key Usage + * Improved certificate verification and verification + against the available CRLs + * Detection for DES weak keys and parity bits added + * Improvements to support integration in other + applications: + + Added generic message digest and cipher wrapper + + Improved information about current capabilities, + status, objects and configuration + + Added verification callback on certificate chain + verification to allow external blacklisting + + Additional example programs to show usage + * Added support for PKCS#11 through the use of the + libpkcs11-helper library + +Changes + * x509parse_time_expired() checks time in addition to + the existing date check + * The ciphers member of ssl_context and the cipher member + of ssl_session have been renamed to ciphersuites and + ciphersuite respectively. This clarifies the difference + with the generic cipher layer and is better naming + altogether + += Version 0.14.0 released on 2010-08-16 +Features + * Added support for SSL_EDH_RSA_AES_128_SHA and + SSL_EDH_RSA_CAMELLIA_128_SHA ciphersuites + * Added compile-time and run-time version information + * Expanded ssl_client2 arguments for more flexibility + * Added support for TLS v1.1 + +Changes + * Made Makefile cleaner + * Removed dependency on rand() in rsa_pkcs1_encrypt(). + Now using random fuction provided to function and + changed the prototype of rsa_pkcs1_encrypt(), + rsa_init() and rsa_gen_key(). + * Some SSL defines were renamed in order to avoid + future confusion + +Bug fixes + * Fixed CMake out of source build for tests (found by + kkert) + * rsa_check_private() now supports PKCS1v2 keys as well + * Fixed deadlock in rsa_pkcs1_encrypt() on failing random + generator + += Version 0.13.1 released on 2010-03-24 +Bug fixes + * Fixed Makefile in library that was mistakenly merged + * Added missing const string fixes + += Version 0.13.0 released on 2010-03-21 +Features + * Added option parsing for host and port selection to + ssl_client2 + * Added support for GeneralizedTime in X509 parsing + * Added cert_app program to allow easy reading and + printing of X509 certificates from file or SSL + connection. + +Changes + * Added const correctness for main code base + * X509 signature algorithm determination is now + in a function to allow easy future expansion + * Changed symmetric cipher functions to + identical interface (returning int result values) + * Changed ARC4 to use seperate input/output buffer + * Added reset function for HMAC context as speed-up + for specific use-cases + +Bug fixes + * Fixed bug resulting in failure to send the last + certificate in the chain in ssl_write_certificate() and + ssl_write_certificate_request() (found by fatbob) + * Added small fixes for compiler warnings on a Mac + (found by Frank de Brabander) + * Fixed algorithmic bug in mpi_is_prime() (found by + Smbat Tonoyan) + += Version 0.12.1 released on 2009-10-04 +Changes + * Coverage test definitions now support 'depends_on' + tagging system. + * Tests requiring specific hashing algorithms now honor + the defines. + +Bug fixes + * Changed typo in #ifdef in x509parse.c (found + by Eduardo) + += Version 0.12.0 released on 2009-07-28 +Features + * Added CMake makefiles as alternative to regular Makefiles. + * Added preliminary Code Coverage tests for AES, ARC4, + Base64, MPI, SHA-family, MD-family, HMAC-SHA-family, + Camellia, DES, 3-DES, RSA PKCS#1, XTEA, Diffie-Hellman + and X509parse. + +Changes + * Error codes are not (necessarily) negative. Keep + this is mind when checking for errors. + * RSA_RAW renamed to SIG_RSA_RAW for consistency. + * Fixed typo in name of POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE. + * Changed interface for AES and Camellia setkey functions + to indicate invalid key lengths. + +Bug fixes + * Fixed include location of endian.h on FreeBSD (found by + Gabriel) + * Fixed include location of endian.h and name clash on + Apples (found by Martin van Hensbergen) + * Fixed HMAC-MD2 by modifying md2_starts(), so that the + required HMAC ipad and opad variables are not cleared. + (found by code coverage tests) + * Prevented use of long long in bignum if + POLARSSL_HAVE_LONGLONG not defined (found by Giles + Bathgate). + * Fixed incorrect handling of negative strings in + mpi_read_string() (found by code coverage tests). + * Fixed segfault on handling empty rsa_context in + rsa_check_pubkey() and rsa_check_privkey() (found by + code coverage tests). + * Fixed incorrect handling of one single negative input + value in mpi_add_abs() (found by code coverage tests). + * Fixed incorrect handling of negative first input + value in mpi_sub_abs() (found by code coverage tests). + * Fixed incorrect handling of negative first input + value in mpi_mod_mpi() and mpi_mod_int(). Resulting + change also affects mpi_write_string() (found by code + coverage tests). + * Corrected is_prime() results for 0, 1 and 2 (found by + code coverage tests). + * Fixed Camellia and XTEA for 64-bit Windows systems. + += Version 0.11.1 released on 2009-05-17 + * Fixed missing functionality for SHA-224, SHA-256, SHA384, + SHA-512 in rsa_pkcs1_sign() + += Version 0.11.0 released on 2009-05-03 + * Fixed a bug in mpi_gcd() so that it also works when both + input numbers are even and added testcases to check + (found by Pierre Habouzit). + * Added support for SHA-224, SHA-256, SHA-384 and SHA-512 + one way hash functions with the PKCS#1 v1.5 signing and + verification. + * Fixed minor bug regarding mpi_gcd located within the + POLARSSL_GENPRIME block. + * Fixed minor memory leak in x509parse_crt() and added better + handling of 'full' certificate chains (found by Mathias + Olsson). + * Centralized file opening and reading for x509 files into + load_file() + * Made definition of net_htons() endian-clean for big endian + systems (Found by Gernot). + * Undefining POLARSSL_HAVE_ASM now also handles prevents asm in + padlock and timing code. + * Fixed an off-by-one buffer allocation in ssl_set_hostname() + responsible for crashes and unwanted behaviour. + * Added support for Certificate Revocation List (CRL) parsing. + * Added support for CRL revocation to x509parse_verify() and + SSL/TLS code. + * Fixed compatibility of XTEA and Camellia on a 64-bit system + (found by Felix von Leitner). + += Version 0.10.0 released on 2009-01-12 + * Migrated XySSL to PolarSSL + * Added XTEA symmetric cipher + * Added Camellia symmetric cipher + * Added support for ciphersuites: SSL_RSA_CAMELLIA_128_SHA, + SSL_RSA_CAMELLIA_256_SHA and SSL_EDH_RSA_CAMELLIA_256_SHA + * Fixed dangerous bug that can cause a heap overflow in + rsa_pkcs1_decrypt (found by Christophe Devine) + +================================================================ +XySSL ChangeLog + += Version 0.9 released on 2008-03-16 + + * Added support for ciphersuite: SSL_RSA_AES_128_SHA + * Enabled support for large files by default in aescrypt2.c + * Preliminary openssl wrapper contributed by David Barrett + * Fixed a bug in ssl_write() that caused the same payload to + be sent twice in non-blocking mode when send returns EAGAIN + * Fixed ssl_parse_client_hello(): session id and challenge must + not be swapped in the SSLv2 ClientHello (found by Greg Robson) + * Added user-defined callback debug function (Krystian Kolodziej) + * Before freeing a certificate, properly zero out all cert. data + * Fixed the "mode" parameter so that encryption/decryption are + not swapped on PadLock; also fixed compilation on older versions + of gcc (bug reported by David Barrett) + * Correctly handle the case in padlock_xcryptcbc() when input or + ouput data is non-aligned by falling back to the software + implementation, as VIA Nehemiah cannot handle non-aligned buffers + * Fixed a memory leak in x509parse_crt() which was reported by Greg + Robson-Garth; some x509write.c fixes by Pascal Vizeli, thanks to + Matthew Page who reported several bugs + * Fixed x509_get_ext() to accept some rare certificates which have + an INTEGER instead of a BOOLEAN for BasicConstraints::cA. + * Added support on the client side for the TLS "hostname" extension + (patch contributed by David Patino) + * Make x509parse_verify() return BADCERT_CN_MISMATCH when an empty + string is passed as the CN (bug reported by spoofy) + * Added an option to enable/disable the BN assembly code + * Updated rsa_check_privkey() to verify that (D*E) = 1 % (P-1)*(Q-1) + * Disabled obsolete hash functions by default (MD2, MD4); updated + selftest and benchmark to not test ciphers that have been disabled + * Updated x509parse_cert_info() to correctly display byte 0 of the + serial number, setup correct server port in the ssl client example + * Fixed a critical denial-of-service with X.509 cert. verification: + peer may cause xyssl to loop indefinitely by sending a certificate + for which the RSA signature check fails (bug reported by Benoit) + * Added test vectors for: AES-CBC, AES-CFB, DES-CBC and 3DES-CBC, + HMAC-MD5, HMAC-SHA1, HMAC-SHA-256, HMAC-SHA-384, and HMAC-SHA-512 + * Fixed HMAC-SHA-384 and HMAC-SHA-512 (thanks to Josh Sinykin) + * Modified ssl_parse_client_key_exchange() to protect against + Daniel Bleichenbacher attack on PKCS#1 v1.5 padding, as well + as the Klima-Pokorny-Rosa extension of Bleichenbacher's attack + * Updated rsa_gen_key() so that ctx->N is always nbits in size + * Fixed assembly PPC compilation errors on Mac OS X, thanks to + David Barrett and Dusan Semen + += Version 0.8 released on 2007-10-20 + + * Modified the HMAC functions to handle keys larger + than 64 bytes, thanks to Stephane Desneux and gary ng + * Fixed ssl_read_record() to properly update the handshake + message digests, which fixes IE6/IE7 client authentication + * Cleaned up the XYSSL* #defines, suggested by Azriel Fasten + * Fixed net_recv(), thanks to Lorenz Schori and Egon Kocjan + * Added user-defined callbacks for handling I/O and sessions + * Added lots of debugging output in the SSL/TLS functions + * Added preliminary X.509 cert. writing by Pascal Vizeli + * Added preliminary support for the VIA PadLock routines + * Added AES-CFB mode of operation, contributed by chmike + * Added an SSL/TLS stress testing program (ssl_test.c) + * Updated the RSA PKCS#1 code to allow choosing between + RSA_PUBLIC and RSA_PRIVATE, as suggested by David Barrett + * Updated ssl_read() to skip 0-length records from OpenSSL + * Fixed the make install target to comply with *BSD make + * Fixed a bug in mpi_read_binary() on 64-bit platforms + * mpi_is_prime() speedups, thanks to Kevin McLaughlin + * Fixed a long standing memory leak in mpi_is_prime() + * Replaced realloc with malloc in mpi_grow(), and set + the sign of zero as positive in mpi_init() (reported + by Jonathan M. McCune) + += Version 0.7 released on 2007-07-07 + + * Added support for the MicroBlaze soft-core processor + * Fixed a bug in ssl_tls.c which sometimes prevented SSL + connections from being established with non-blocking I/O + * Fixed a couple bugs in the VS6 and UNIX Makefiles + * Fixed the "PIC register ebx clobbered in asm" bug + * Added HMAC starts/update/finish support functions + * Added the SHA-224, SHA-384 and SHA-512 hash functions + * Fixed the net_set_*block routines, thanks to Andreas + * Added a few demonstration programs: md5sum, sha1sum, + dh_client, dh_server, rsa_genkey, rsa_sign, rsa_verify + * Added new bignum import and export helper functions + * Rewrote README.txt in program/ssl/ca to better explain + how to create a test PKI + += Version 0.6 released on 2007-04-01 + + * Ciphers used in SSL/TLS can now be disabled at compile + time, to reduce the memory footprint on embedded systems + * Added multiply assembly code for the TriCore and modified + havege_struct for this processor, thanks to David Patiño + * Added multiply assembly code for 64-bit PowerPCs, + thanks to Peking University and the OSU Open Source Lab + * Added experimental support of Quantum Cryptography + * Added support for autoconf, contributed by Arnaud Cornet + * Fixed "long long" compilation issues on IA-64 and PPC64 + * Fixed a bug introduced in xyssl-0.5/timing.c: hardclock + was not being correctly defined on ARM and MIPS + += Version 0.5 released on 2007-03-01 + + * Added multiply assembly code for SPARC and Alpha + * Added (beta) support for non-blocking I/O operations + * Implemented session resuming and client authentication + * Fixed some portability issues on WinCE, MINIX 3, Plan9 + (thanks to Benjamin Newman), HP-UX, FreeBSD and Solaris + * Improved the performance of the EDH key exchange + * Fixed a bug that caused valid packets with a payload + size of 16384 bytes to be rejected + += Version 0.4 released on 2007-02-01 + + * Added support for Ephemeral Diffie-Hellman key exchange + * Added multiply asm code for SSE2, ARM, PPC, MIPS and M68K + * Various improvement to the modular exponentiation code + * Rewrote the headers to generate the API docs with doxygen + * Fixed a bug in ssl_encrypt_buf (incorrect padding was + generated) and in ssl_parse_client_hello (max. client + version was not properly set), thanks to Didier Rebeix + * Fixed another bug in ssl_parse_client_hello: clients with + cipherlists larger than 96 bytes were incorrectly rejected + * Fixed a couple memory leak in x509_read.c + += Version 0.3 released on 2007-01-01 + + * Added server-side SSLv3 and TLSv1.0 support + * Multiple fixes to enhance the compatibility with g++, + thanks to Xosé Antón Otero Ferreira + * Fixed a bug in the CBC code, thanks to dowst; also, + the bignum code is no longer dependant on long long + * Updated rsa_pkcs1_sign to handle arbitrary large inputs + * Updated timing.c for improved compatibility with i386 + and 486 processors, thanks to Arnaud Cornet + += Version 0.2 released on 2006-12-01 + + * Updated timing.c to support ARM and MIPS arch + * Updated the MPI code to support 8086 on MSVC 1.5 + * Added the copyright notice at the top of havege.h + * Fixed a bug in sha2_hmac, thanks to newsoft/Wenfang Zhang + * Fixed a bug reported by Adrian Rüegsegger in x509_read_key + * Fixed a bug reported by Torsten Lauter in ssl_read_record + * Fixed a bug in rsa_check_privkey that would wrongly cause + valid RSA keys to be dismissed (thanks to oldwolf) + * Fixed a bug in mpi_is_prime that caused some primes to fail + the Miller-Rabin primality test + + I'd also like to thank Younès Hafri for the CRUX linux port, + Khalil Petit who added XySSL into pkgsrc and Arnaud Cornet + who maintains the Debian package :-) + += Version 0.1 released on 2006-11-01 + diff --git a/gnuk/polarssl/LICENSE b/gnuk/polarssl/LICENSE new file mode 100644 index 0000000000..d511905c16 --- /dev/null +++ b/gnuk/polarssl/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/gnuk/polarssl/README b/gnuk/polarssl/README new file mode 100644 index 0000000000..7f7d0b66db --- /dev/null +++ b/gnuk/polarssl/README @@ -0,0 +1,40 @@ +README for PolarSSL + +-- COMPILING +There are currently three active build systems within the PolarSSL releases: + - Make + - CMake + - Microsoft Visual Studio + +The main system used for development is CMake. That system is always the most up-to-date. The others should reflect all changes present in the CMake build system, but some features are not ported there by default. + +--- Make +In order to build the source using Make, just enter at the command line: +make + +In order to run the tests, enter: +make check + +--- CMake +In order to build the source using CMake, just enter at the command line: +cmake . +make + +There are 3 different active build modes specified within the CMake buildsystem: + - Release + This generates the default code without any unnecessary information in the binary files. + - Debug + This generates debug information and disables optimization of the code. + - Coverage + This generates code coverage information in addition to debug information. + +Switching build modes in CMake is simple. For debug mode, enter at the command line: +cmake -D CMAKE_BUILD_TYPE:String="Debug" . + +In order to run the tests, enter: +make test + +--- Microsoft Visual Studio +The build files for Microsoft Visual Studio are generated for Visual Studio 6.0 all future Visual Studio's should be able to open and use this older version of the build files. + +The workspace 'polarssl.dsw' contains all the basic projects needed to build the library and all the programs. The files in tests are not generated and compiled, as these need a perl environment as well. diff --git a/gnuk/polarssl/include/polarssl/aes.h b/gnuk/polarssl/include/polarssl/aes.h new file mode 100644 index 0000000000..f83da34a3a --- /dev/null +++ b/gnuk/polarssl/include/polarssl/aes.h @@ -0,0 +1,204 @@ +/** + * \file aes.h + * + * \brief AES block cipher + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_AES_H +#define POLARSSL_AES_H + +#include "config.h" + +#include + +#ifdef _MSC_VER +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +#define AES_ENCRYPT 1 +#define AES_DECRYPT 0 + +#define POLARSSL_ERR_AES_INVALID_KEY_LENGTH -0x0020 /**< Invalid key length. */ +#define POLARSSL_ERR_AES_INVALID_INPUT_LENGTH -0x0022 /**< Invalid data input length. */ + +#if !defined(POLARSSL_AES_ALT) +// Regular implementation +// + +/** + * \brief AES context structure + */ +typedef struct +{ + int nr; /*!< number of rounds */ + uint32_t *rk; /*!< AES round keys */ + uint32_t buf[68]; /*!< unaligned data */ +} +aes_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief AES key schedule (encryption) + * + * \param ctx AES context to be initialized + * \param key encryption key + * \param keysize must be 128, 192 or 256 + * + * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH + */ +int aes_setkey_enc( aes_context *ctx, const unsigned char *key, unsigned int keysize ); + +/** + * \brief AES key schedule (decryption) + * + * \param ctx AES context to be initialized + * \param key decryption key + * \param keysize must be 128, 192 or 256 + * + * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH + */ +int aes_setkey_dec( aes_context *ctx, const unsigned char *key, unsigned int keysize ); + +/** + * \brief AES-ECB block encryption/decryption + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param input 16-byte input block + * \param output 16-byte output block + * + * \return 0 if successful + */ +int aes_crypt_ecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ); + +#if 0 +/** + * \brief AES-CBC buffer encryption/decryption + * Length should be a multiple of the block + * size (16 bytes) + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param length length of the input data + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_INPUT_LENGTH + */ +int aes_crypt_cbc( aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ); +#endif + +/** + * \brief AES-CFB128 buffer encryption/decryption. + * + * Note: Due to the nature of CFB you should use the same key schedule for + * both encryption and decryption. So a context initialized with + * aes_setkey_enc() for both AES_ENCRYPT and AES_DECRYPT. + * + * both + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param length length of the input data + * \param iv_off offset in IV (updated after use) + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if successful + */ +int aes_crypt_cfb128( aes_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ); + +/** + * \brief AES-CTR buffer encryption/decryption + * + * Warning: You have to keep the maximum use of your counter in mind! + * + * Note: Due to the nature of CTR you should use the same key schedule for + * both encryption and decryption. So a context initialized with + * aes_setkey_enc() for both AES_ENCRYPT and AES_DECRYPT. + * + * \param length The length of the data + * \param nc_off The offset in the current stream_block (for resuming + * within current cipher stream). The offset pointer to + * should be 0 at the start of a stream. + * \param nonce_counter The 128-bit nonce and counter. + * \param stream_block The saved stream-block for resuming. Is overwritten + * by the function. + * \param input The input data stream + * \param output The output data stream + * + * \return 0 if successful + */ +int aes_crypt_ctr( aes_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output ); + +#ifdef __cplusplus +} +#endif + +#else /* POLARSSL_AES_ALT */ +#include "aes_alt.h" +#endif /* POLARSSL_AES_ALT */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int aes_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* aes.h */ diff --git a/gnuk/polarssl/include/polarssl/bignum.h b/gnuk/polarssl/include/polarssl/bignum.h new file mode 100644 index 0000000000..7d4d2f202b --- /dev/null +++ b/gnuk/polarssl/include/polarssl/bignum.h @@ -0,0 +1,687 @@ +/** + * \file bignum.h + * + * \brief Multi-precision integer library + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_BIGNUM_H +#define POLARSSL_BIGNUM_H + +#include +#include + +#include "config.h" + +#ifdef _MSC_VER +#include +#if (_MSC_VER <= 1200) +typedef signed short int16_t; +typedef unsigned short uint16_t; +#else +typedef INT16 int16_t; +typedef UINT16 uint16_t; +#endif +typedef INT32 int32_t; +typedef INT64 int64_t; +typedef UINT32 uint32_t; +typedef UINT64 uint64_t; +#else +#include +#endif + +#define POLARSSL_ERR_MPI_FILE_IO_ERROR -0x0002 /**< An error occurred while reading from or writing to a file. */ +#define POLARSSL_ERR_MPI_BAD_INPUT_DATA -0x0004 /**< Bad input parameters to function. */ +#define POLARSSL_ERR_MPI_INVALID_CHARACTER -0x0006 /**< There is an invalid character in the digit string. */ +#define POLARSSL_ERR_MPI_BUFFER_TOO_SMALL -0x0008 /**< The buffer is too small to write to. */ +#define POLARSSL_ERR_MPI_NEGATIVE_VALUE -0x000A /**< The input arguments are negative or result in illegal output. */ +#define POLARSSL_ERR_MPI_DIVISION_BY_ZERO -0x000C /**< The input argument for division is zero, which is not allowed. */ +#define POLARSSL_ERR_MPI_NOT_ACCEPTABLE -0x000E /**< The input arguments are not acceptable. */ +#define POLARSSL_ERR_MPI_MALLOC_FAILED -0x0010 /**< Memory allocation failed. */ + +#define MPI_CHK(f) if( ( ret = f ) != 0 ) goto cleanup + +/* + * Maximum size MPIs are allowed to grow to in number of limbs. + */ +#define POLARSSL_MPI_MAX_LIMBS 10000 + +#if !defined(POLARSSL_CONFIG_OPTIONS) +/* + * Maximum window size used for modular exponentiation. Default: 6 + * Minimum value: 1. Maximum value: 6. + * + * Result is an array of ( 2 << POLARSSL_MPI_WINDOW_SIZE ) MPIs used + * for the sliding window calculation. (So 64 by default) + * + * Reduction in size, reduces speed. + */ +#define POLARSSL_MPI_WINDOW_SIZE 6 /**< Maximum windows size used. */ + +/* + * Maximum size of MPIs allowed in bits and bytes for user-MPIs. + * ( Default: 512 bytes => 4096 bits, Maximum tested: 2048 bytes => 16384 bits ) + * + * Note: Calculations can results temporarily in larger MPIs. So the number + * of limbs required (POLARSSL_MPI_MAX_LIMBS) is higher. + */ +#define POLARSSL_MPI_MAX_SIZE 256 /**< Maximum number of bytes for usable MPIs. */ + +#endif /* !POLARSSL_CONFIG_OPTIONS */ + +#define POLARSSL_MPI_MAX_BITS ( 8 * POLARSSL_MPI_MAX_SIZE ) /**< Maximum number of bits for usable MPIs. */ + +/* + * When reading from files with mpi_read_file() and writing to files with + * mpi_write_file() the buffer should have space + * for a (short) label, the MPI (in the provided radix), the newline + * characters and the '\0'. + * + * By default we assume at least a 10 char label, a minimum radix of 10 + * (decimal) and a maximum of 4096 bit numbers (1234 decimal chars). + * Autosized at compile time for at least a 10 char label, a minimum radix + * of 10 (decimal) for a number of POLARSSL_MPI_MAX_BITS size. + * + * This used to be statically sized to 1250 for a maximum of 4096 bit + * numbers (1234 decimal chars). + * + * Calculate using the formula: + * POLARSSL_MPI_RW_BUFFER_SIZE = ceil(POLARSSL_MPI_MAX_BITS / ln(10) * ln(2)) + + * LabelSize + 6 + */ +#define POLARSSL_MPI_MAX_BITS_SCALE100 ( 100 * POLARSSL_MPI_MAX_BITS ) +#define LN_2_DIV_LN_10_SCALE100 332 +#define POLARSSL_MPI_RW_BUFFER_SIZE ( ((POLARSSL_MPI_MAX_BITS_SCALE100 + LN_2_DIV_LN_10_SCALE100 - 1) / LN_2_DIV_LN_10_SCALE100) + 10 + 6 ) + +/* + * Define the base integer type, architecture-wise + */ +#if defined(POLARSSL_HAVE_INT8) +typedef signed char t_sint; +typedef unsigned char t_uint; +typedef uint16_t t_udbl; +#define POLARSSL_HAVE_UDBL +#else +#if defined(POLARSSL_HAVE_INT16) +typedef int16_t t_sint; +typedef uint16_t t_uint; +typedef uint32_t t_udbl; +#define POLARSSL_HAVE_UDBL +#else + #if ( defined(_MSC_VER) && defined(_M_AMD64) ) + typedef int64_t t_sint; + typedef uint64_t t_uint; + #else + #if ( defined(__GNUC__) && ( \ + defined(__amd64__) || defined(__x86_64__) || \ + defined(__ppc64__) || defined(__powerpc64__) || \ + defined(__ia64__) || defined(__alpha__) || \ + (defined(__sparc__) && defined(__arch64__)) || \ + defined(__s390x__) ) ) + typedef int64_t t_sint; + typedef uint64_t t_uint; + typedef unsigned int t_udbl __attribute__((mode(TI))); + #define POLARSSL_HAVE_UDBL + #else + typedef int32_t t_sint; + typedef uint32_t t_uint; + #if ( defined(_MSC_VER) && defined(_M_IX86) ) + typedef uint64_t t_udbl; + #define POLARSSL_HAVE_UDBL + #else + #if defined( POLARSSL_HAVE_LONGLONG ) + typedef unsigned long long t_udbl; + #define POLARSSL_HAVE_UDBL + #endif + #endif + #endif + #endif +#endif /* POLARSSL_HAVE_INT16 */ +#endif /* POLARSSL_HAVE_INT8 */ + +/** + * \brief MPI structure + */ +typedef struct +{ + int s; /*!< integer sign */ + size_t n; /*!< total # of limbs */ + t_uint *p; /*!< pointer to limbs */ +} +mpi; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initialize one MPI + * + * \param X One MPI to initialize. + */ +void mpi_init( mpi *X ); + +/** + * \brief Unallocate one MPI + * + * \param X One MPI to unallocate. + */ +void mpi_free( mpi *X ); + +/** + * \brief Enlarge to the specified number of limbs + * + * \param X MPI to grow + * \param nblimbs The target number of limbs + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_grow( mpi *X, size_t nblimbs ); + +/** + * \brief Copy the contents of Y into X + * + * \param X Destination MPI + * \param Y Source MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_copy( mpi *X, const mpi *Y ); + +/** + * \brief Swap the contents of X and Y + * + * \param X First MPI value + * \param Y Second MPI value + */ +void mpi_swap( mpi *X, mpi *Y ); + +/** + * \brief Set value from integer + * + * \param X MPI to set + * \param z Value to use + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_lset( mpi *X, t_sint z ); + +/** + * \brief Get a specific bit from X + * + * \param X MPI to use + * \param pos Zero-based index of the bit in X + * + * \return Either a 0 or a 1 + */ +int mpi_get_bit( const mpi *X, size_t pos ); + +/** + * \brief Set a bit of X to a specific value of 0 or 1 + * + * \note Will grow X if necessary to set a bit to 1 in a not yet + * existing limb. Will not grow if bit should be set to 0 + * + * \param X MPI to use + * \param pos Zero-based index of the bit in X + * \param val The value to set the bit to (0 or 1) + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if val is not 0 or 1 + */ +int mpi_set_bit( mpi *X, size_t pos, unsigned char val ); + +/** + * \brief Return the number of zero-bits before the least significant + * '1' bit + * + * Note: Thus also the zero-based index of the least significant '1' bit + * + * \param X MPI to use + */ +size_t mpi_lsb( const mpi *X ); + +/** + * \brief Return the number of bits up to and including the most + * significant '1' bit' + * + * Note: Thus also the one-based index of the most significant '1' bit + * + * \param X MPI to use + */ +size_t mpi_msb( const mpi *X ); + +/** + * \brief Return the total size in bytes + * + * \param X MPI to use + */ +size_t mpi_size( const mpi *X ); + +/** + * \brief Import from an ASCII string + * + * \param X Destination MPI + * \param radix Input numeric base + * \param s Null-terminated string buffer + * + * \return 0 if successful, or a POLARSSL_ERR_MPI_XXX error code + */ +int mpi_read_string( mpi *X, int radix, const char *s ); + +/** + * \brief Export into an ASCII string + * + * \param X Source MPI + * \param radix Output numeric base + * \param s String buffer + * \param slen String buffer size + * + * \return 0 if successful, or a POLARSSL_ERR_MPI_XXX error code. + * *slen is always updated to reflect the amount + * of data that has (or would have) been written. + * + * \note Call this function with *slen = 0 to obtain the + * minimum required buffer size in *slen. + */ +int mpi_write_string( const mpi *X, int radix, char *s, size_t *slen ); + +#if defined(POLARSSL_FS_IO) +/** + * \brief Read X from an opened file + * + * \param X Destination MPI + * \param radix Input numeric base + * \param fin Input file handle + * + * \return 0 if successful, POLARSSL_ERR_MPI_BUFFER_TOO_SMALL if + * the file read buffer is too small or a + * POLARSSL_ERR_MPI_XXX error code + */ +int mpi_read_file( mpi *X, int radix, FILE *fin ); + +/** + * \brief Write X into an opened file, or stdout if fout is NULL + * + * \param p Prefix, can be NULL + * \param X Source MPI + * \param radix Output numeric base + * \param fout Output file handle (can be NULL) + * + * \return 0 if successful, or a POLARSSL_ERR_MPI_XXX error code + * + * \note Set fout == NULL to print X on the console. + */ +int mpi_write_file( const char *p, const mpi *X, int radix, FILE *fout ); +#endif /* POLARSSL_FS_IO */ + +/** + * \brief Import X from unsigned binary data, big endian + * + * \param X Destination MPI + * \param buf Input buffer + * \param buflen Input buffer size + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_read_binary( mpi *X, const unsigned char *buf, size_t buflen ); + +/** + * \brief Export X into unsigned binary data, big endian + * + * \param X Source MPI + * \param buf Output buffer + * \param buflen Output buffer size + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_BUFFER_TOO_SMALL if buf isn't large enough + */ +int mpi_write_binary( const mpi *X, unsigned char *buf, size_t buflen ); + +/** + * \brief Left-shift: X <<= count + * + * \param X MPI to shift + * \param count Amount to shift + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_shift_l( mpi *X, size_t count ); + +/** + * \brief Right-shift: X >>= count + * + * \param X MPI to shift + * \param count Amount to shift + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_shift_r( mpi *X, size_t count ); + +/** + * \brief Compare unsigned values + * + * \param X Left-hand MPI + * \param Y Right-hand MPI + * + * \return 1 if |X| is greater than |Y|, + * -1 if |X| is lesser than |Y| or + * 0 if |X| is equal to |Y| + */ +int mpi_cmp_abs( const mpi *X, const mpi *Y ); + +/** + * \brief Compare signed values + * + * \param X Left-hand MPI + * \param Y Right-hand MPI + * + * \return 1 if X is greater than Y, + * -1 if X is lesser than Y or + * 0 if X is equal to Y + */ +int mpi_cmp_mpi( const mpi *X, const mpi *Y ); + +/** + * \brief Compare signed values + * + * \param X Left-hand MPI + * \param z The integer value to compare to + * + * \return 1 if X is greater than z, + * -1 if X is lesser than z or + * 0 if X is equal to z + */ +int mpi_cmp_int( const mpi *X, t_sint z ); + +/** + * \brief Unsigned addition: X = |A| + |B| + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_add_abs( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Unsigned substraction: X = |A| - |B| + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if B is greater than A + */ +int mpi_sub_abs( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Signed addition: X = A + B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_add_mpi( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Signed substraction: X = A - B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_sub_mpi( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Signed addition: X = A + b + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to add + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_add_int( mpi *X, const mpi *A, t_sint b ); + +/** + * \brief Signed substraction: X = A - b + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to subtract + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_sub_int( mpi *X, const mpi *A, t_sint b ); + +/** + * \brief Baseline multiplication: X = A * B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_mul_mpi( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Baseline multiplication: X = A * b + * Note: b is an unsigned integer type, thus + * Negative values of b are ignored. + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to multiply with + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_mul_int( mpi *X, const mpi *A, t_sint b ); + +/** + * \brief Division by mpi: A = Q * B + R + * + * \param Q Destination MPI for the quotient + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if B == 0 + * + * \note Either Q or R can be NULL. + */ +int mpi_div_mpi( mpi *Q, mpi *R, const mpi *A, const mpi *B ); + +/** + * \brief Division by int: A = Q * b + R + * + * \param Q Destination MPI for the quotient + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param b Integer to divide by + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0 + * + * \note Either Q or R can be NULL. + */ +int mpi_div_int( mpi *Q, mpi *R, const mpi *A, t_sint b ); + +/** + * \brief Modulo: R = A mod B + * + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if B == 0, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if B < 0 + */ +int mpi_mod_mpi( mpi *R, const mpi *A, const mpi *B ); + +/** + * \brief Modulo: r = A mod b + * + * \param r Destination t_uint + * \param A Left-hand MPI + * \param b Integer to divide by + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if b < 0 + */ +int mpi_mod_int( t_uint *r, const mpi *A, t_sint b ); + +/** + * \brief Sliding-window exponentiation: X = A^E mod N + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param E Exponent MPI + * \param N Modular MPI + * \param _RR Speed-up MPI used for recalculations + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if N is negative or even or if + * E is negative + * + * \note _RR is used to avoid re-computing R*R mod N across + * multiple calls, which speeds up things a bit. It can + * be set to NULL if the extra performance is unneeded. + */ +int mpi_exp_mod( mpi *X, const mpi *A, const mpi *E, const mpi *N, mpi *_RR ); + +/** + * \brief Fill an MPI X with size bytes of random + * + * \param X Destination MPI + * \param size Size in bytes + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_fill_random( mpi *X, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Greatest common divisor: G = gcd(A, B) + * + * \param G Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_gcd( mpi *G, const mpi *A, const mpi *B ); + +/** + * \brief Modular inverse: X = A^-1 mod N + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param N Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if N is negative or nil + POLARSSL_ERR_MPI_NOT_ACCEPTABLE if A has no inverse mod N + */ +int mpi_inv_mod( mpi *X, const mpi *A, const mpi *N ); + +#if 0 +/** + * \brief Miller-Rabin primality test + * + * \param X MPI to check + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful (probably prime), + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_NOT_ACCEPTABLE if X is not prime + */ +int mpi_is_prime( mpi *X, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); +#endif + +/** + * \brief Prime number generation + * + * \param X Destination MPI + * \param nbits Required size of X in bits ( 3 <= nbits <= POLARSSL_MPI_MAX_BITS ) + * \param dh_flag If 1, then (X-1)/2 will be prime too + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful (probably prime), + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if nbits is < 3 + */ +int mpi_gen_prime( mpi *X, size_t nbits, int dh_flag, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int mpi_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* bignum.h */ diff --git a/gnuk/polarssl/include/polarssl/bn_mul.h b/gnuk/polarssl/include/polarssl/bn_mul.h new file mode 100644 index 0000000000..2ae2ed3340 --- /dev/null +++ b/gnuk/polarssl/include/polarssl/bn_mul.h @@ -0,0 +1,901 @@ +/** + * \file bn_mul.h + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * Multiply source vector [s] with b, add result + * to destination vector [d] and set carry c. + * + * Currently supports: + * + * . IA-32 (386+) . AMD64 / EM64T + * . IA-32 (SSE2) . Motorola 68000 + * . PowerPC, 32-bit . MicroBlaze + * . PowerPC, 64-bit . TriCore + * . SPARC v8 . ARM v3+ + * . Alpha . MIPS32 + * . C, longlong . C, generic + */ +#ifndef POLARSSL_BN_MUL_H +#define POLARSSL_BN_MUL_H + +#include "polarssl/config.h" + +#if defined(POLARSSL_HAVE_ASM) + +#if defined(__GNUC__) +#if defined(__i386__) + +#define MULADDC_INIT \ + asm( " \ + movl %%ebx, %0; \ + movl %5, %%esi; \ + movl %6, %%edi; \ + movl %7, %%ecx; \ + movl %8, %%ebx; \ + " + +#define MULADDC_CORE \ + " \ + lodsl; \ + mull %%ebx; \ + addl %%ecx, %%eax; \ + adcl $0, %%edx; \ + addl (%%edi), %%eax; \ + adcl $0, %%edx; \ + movl %%edx, %%ecx; \ + stosl; \ + " + +#if defined(POLARSSL_HAVE_SSE2) + +#define MULADDC_HUIT \ + " \ + movd %%ecx, %%mm1; \ + movd %%ebx, %%mm0; \ + movd (%%edi), %%mm3; \ + paddq %%mm3, %%mm1; \ + movd (%%esi), %%mm2; \ + pmuludq %%mm0, %%mm2; \ + movd 4(%%esi), %%mm4; \ + pmuludq %%mm0, %%mm4; \ + movd 8(%%esi), %%mm6; \ + pmuludq %%mm0, %%mm6; \ + movd 12(%%esi), %%mm7; \ + pmuludq %%mm0, %%mm7; \ + paddq %%mm2, %%mm1; \ + movd 4(%%edi), %%mm3; \ + paddq %%mm4, %%mm3; \ + movd 8(%%edi), %%mm5; \ + paddq %%mm6, %%mm5; \ + movd 12(%%edi), %%mm4; \ + paddq %%mm4, %%mm7; \ + movd %%mm1, (%%edi); \ + movd 16(%%esi), %%mm2; \ + pmuludq %%mm0, %%mm2; \ + psrlq $32, %%mm1; \ + movd 20(%%esi), %%mm4; \ + pmuludq %%mm0, %%mm4; \ + paddq %%mm3, %%mm1; \ + movd 24(%%esi), %%mm6; \ + pmuludq %%mm0, %%mm6; \ + movd %%mm1, 4(%%edi); \ + psrlq $32, %%mm1; \ + movd 28(%%esi), %%mm3; \ + pmuludq %%mm0, %%mm3; \ + paddq %%mm5, %%mm1; \ + movd 16(%%edi), %%mm5; \ + paddq %%mm5, %%mm2; \ + movd %%mm1, 8(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm7, %%mm1; \ + movd 20(%%edi), %%mm5; \ + paddq %%mm5, %%mm4; \ + movd %%mm1, 12(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm2, %%mm1; \ + movd 24(%%edi), %%mm5; \ + paddq %%mm5, %%mm6; \ + movd %%mm1, 16(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm4, %%mm1; \ + movd 28(%%edi), %%mm5; \ + paddq %%mm5, %%mm3; \ + movd %%mm1, 20(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm6, %%mm1; \ + movd %%mm1, 24(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm3, %%mm1; \ + movd %%mm1, 28(%%edi); \ + addl $32, %%edi; \ + addl $32, %%esi; \ + psrlq $32, %%mm1; \ + movd %%mm1, %%ecx; \ + " + +#define MULADDC_STOP \ + " \ + emms; \ + movl %4, %%ebx; \ + movl %%ecx, %1; \ + movl %%edi, %2; \ + movl %%esi, %3; \ + " \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ecx", "edx", "esi", "edi" \ + ); + +#else + +#define MULADDC_STOP \ + " \ + movl %4, %%ebx; \ + movl %%ecx, %1; \ + movl %%edi, %2; \ + movl %%esi, %3; \ + " \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ecx", "edx", "esi", "edi" \ + ); +#endif /* SSE2 */ +#endif /* i386 */ + +#if defined(__amd64__) || defined (__x86_64__) + +#define MULADDC_INIT \ + asm( "movq %0, %%rsi " :: "m" (s)); \ + asm( "movq %0, %%rdi " :: "m" (d)); \ + asm( "movq %0, %%rcx " :: "m" (c)); \ + asm( "movq %0, %%rbx " :: "m" (b)); \ + asm( "xorq %r8, %r8 " ); + +#define MULADDC_CORE \ + asm( "movq (%rsi),%rax " ); \ + asm( "mulq %rbx " ); \ + asm( "addq $8, %rsi " ); \ + asm( "addq %rcx, %rax " ); \ + asm( "movq %r8, %rcx " ); \ + asm( "adcq $0, %rdx " ); \ + asm( "nop " ); \ + asm( "addq %rax, (%rdi) " ); \ + asm( "adcq %rdx, %rcx " ); \ + asm( "addq $8, %rdi " ); + +#define MULADDC_STOP \ + asm( "movq %%rcx, %0 " : "=m" (c)); \ + asm( "movq %%rdi, %0 " : "=m" (d)); \ + asm( "movq %%rsi, %0 " : "=m" (s) :: \ + "rax", "rcx", "rdx", "rbx", "rsi", "rdi", "r8" ); + +#endif /* AMD64 */ + +#if defined(__mc68020__) || defined(__mcpu32__) + +#define MULADDC_INIT \ + asm( "movl %0, %%a2 " :: "m" (s)); \ + asm( "movl %0, %%a3 " :: "m" (d)); \ + asm( "movl %0, %%d3 " :: "m" (c)); \ + asm( "movl %0, %%d2 " :: "m" (b)); \ + asm( "moveq #0, %d0 " ); + +#define MULADDC_CORE \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "moveq #0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "addxl %d4, %d3 " ); + +#define MULADDC_STOP \ + asm( "movl %%d3, %0 " : "=m" (c)); \ + asm( "movl %%a3, %0 " : "=m" (d)); \ + asm( "movl %%a2, %0 " : "=m" (s) :: \ + "d0", "d1", "d2", "d3", "d4", "a2", "a3" ); + +#define MULADDC_HUIT \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "addxl %d0, %d3 " ); + +#endif /* MC68000 */ + +#if defined(__powerpc__) || defined(__ppc__) +#if defined(__powerpc64__) || defined(__ppc64__) + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( "ld r3, %0 " :: "m" (s)); \ + asm( "ld r4, %0 " :: "m" (d)); \ + asm( "ld r5, %0 " :: "m" (c)); \ + asm( "ld r6, %0 " :: "m" (b)); \ + asm( "addi r3, r3, -8 " ); \ + asm( "addi r4, r4, -8 " ); \ + asm( "addic r5, r5, 0 " ); + +#define MULADDC_CORE \ + asm( "ldu r7, 8(r3) " ); \ + asm( "mulld r8, r7, r6 " ); \ + asm( "mulhdu r9, r7, r6 " ); \ + asm( "adde r8, r8, r5 " ); \ + asm( "ld r7, 8(r4) " ); \ + asm( "addze r5, r9 " ); \ + asm( "addc r8, r8, r7 " ); \ + asm( "stdu r8, 8(r4) " ); + +#define MULADDC_STOP \ + asm( "addze r5, r5 " ); \ + asm( "addi r4, r4, 8 " ); \ + asm( "addi r3, r3, 8 " ); \ + asm( "std r5, %0 " : "=m" (c)); \ + asm( "std r4, %0 " : "=m" (d)); \ + asm( "std r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#else + +#define MULADDC_INIT \ + asm( "ld %%r3, %0 " :: "m" (s)); \ + asm( "ld %%r4, %0 " :: "m" (d)); \ + asm( "ld %%r5, %0 " :: "m" (c)); \ + asm( "ld %%r6, %0 " :: "m" (b)); \ + asm( "addi %r3, %r3, -8 " ); \ + asm( "addi %r4, %r4, -8 " ); \ + asm( "addic %r5, %r5, 0 " ); + +#define MULADDC_CORE \ + asm( "ldu %r7, 8(%r3) " ); \ + asm( "mulld %r8, %r7, %r6 " ); \ + asm( "mulhdu %r9, %r7, %r6 " ); \ + asm( "adde %r8, %r8, %r5 " ); \ + asm( "ld %r7, 8(%r4) " ); \ + asm( "addze %r5, %r9 " ); \ + asm( "addc %r8, %r8, %r7 " ); \ + asm( "stdu %r8, 8(%r4) " ); + +#define MULADDC_STOP \ + asm( "addze %r5, %r5 " ); \ + asm( "addi %r4, %r4, 8 " ); \ + asm( "addi %r3, %r3, 8 " ); \ + asm( "std %%r5, %0 " : "=m" (c)); \ + asm( "std %%r4, %0 " : "=m" (d)); \ + asm( "std %%r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#endif + +#else /* PPC32 */ + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( "lwz r3, %0 " :: "m" (s)); \ + asm( "lwz r4, %0 " :: "m" (d)); \ + asm( "lwz r5, %0 " :: "m" (c)); \ + asm( "lwz r6, %0 " :: "m" (b)); \ + asm( "addi r3, r3, -4 " ); \ + asm( "addi r4, r4, -4 " ); \ + asm( "addic r5, r5, 0 " ); + +#define MULADDC_CORE \ + asm( "lwzu r7, 4(r3) " ); \ + asm( "mullw r8, r7, r6 " ); \ + asm( "mulhwu r9, r7, r6 " ); \ + asm( "adde r8, r8, r5 " ); \ + asm( "lwz r7, 4(r4) " ); \ + asm( "addze r5, r9 " ); \ + asm( "addc r8, r8, r7 " ); \ + asm( "stwu r8, 4(r4) " ); + +#define MULADDC_STOP \ + asm( "addze r5, r5 " ); \ + asm( "addi r4, r4, 4 " ); \ + asm( "addi r3, r3, 4 " ); \ + asm( "stw r5, %0 " : "=m" (c)); \ + asm( "stw r4, %0 " : "=m" (d)); \ + asm( "stw r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#else + +#define MULADDC_INIT \ + asm( "lwz %%r3, %0 " :: "m" (s)); \ + asm( "lwz %%r4, %0 " :: "m" (d)); \ + asm( "lwz %%r5, %0 " :: "m" (c)); \ + asm( "lwz %%r6, %0 " :: "m" (b)); \ + asm( "addi %r3, %r3, -4 " ); \ + asm( "addi %r4, %r4, -4 " ); \ + asm( "addic %r5, %r5, 0 " ); + +#define MULADDC_CORE \ + asm( "lwzu %r7, 4(%r3) " ); \ + asm( "mullw %r8, %r7, %r6 " ); \ + asm( "mulhwu %r9, %r7, %r6 " ); \ + asm( "adde %r8, %r8, %r5 " ); \ + asm( "lwz %r7, 4(%r4) " ); \ + asm( "addze %r5, %r9 " ); \ + asm( "addc %r8, %r8, %r7 " ); \ + asm( "stwu %r8, 4(%r4) " ); + +#define MULADDC_STOP \ + asm( "addze %r5, %r5 " ); \ + asm( "addi %r4, %r4, 4 " ); \ + asm( "addi %r3, %r3, 4 " ); \ + asm( "stw %%r5, %0 " : "=m" (c)); \ + asm( "stw %%r4, %0 " : "=m" (d)); \ + asm( "stw %%r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#endif + +#endif /* PPC32 */ +#endif /* PPC64 */ + +#if defined(__sparc__) + +#define MULADDC_INIT \ + asm( "ld %0, %%o0 " :: "m" (s)); \ + asm( "ld %0, %%o1 " :: "m" (d)); \ + asm( "ld %0, %%o2 " :: "m" (c)); \ + asm( "ld %0, %%o3 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "ld [%o0], %o4 " ); \ + asm( "inc 4, %o0 " ); \ + asm( "ld [%o1], %o5 " ); \ + asm( "umul %o3, %o4, %o4 " ); \ + asm( "addcc %o4, %o2, %o4 " ); \ + asm( "rd %y, %g1 " ); \ + asm( "addx %g1, 0, %g1 " ); \ + asm( "addcc %o4, %o5, %o4 " ); \ + asm( "st %o4, [%o1] " ); \ + asm( "addx %g1, 0, %o2 " ); \ + asm( "inc 4, %o1 " ); + +#define MULADDC_STOP \ + asm( "st %%o2, %0 " : "=m" (c)); \ + asm( "st %%o1, %0 " : "=m" (d)); \ + asm( "st %%o0, %0 " : "=m" (s) :: \ + "g1", "o0", "o1", "o2", "o3", "o4", "o5" ); + +#endif /* SPARCv8 */ + +#if defined(__microblaze__) || defined(microblaze) + +#define MULADDC_INIT \ + asm( "lwi r3, %0 " :: "m" (s)); \ + asm( "lwi r4, %0 " :: "m" (d)); \ + asm( "lwi r5, %0 " :: "m" (c)); \ + asm( "lwi r6, %0 " :: "m" (b)); \ + asm( "andi r7, r6, 0xffff" ); \ + asm( "bsrli r6, r6, 16 " ); + +#define MULADDC_CORE \ + asm( "lhui r8, r3, 0 " ); \ + asm( "addi r3, r3, 2 " ); \ + asm( "lhui r9, r3, 0 " ); \ + asm( "addi r3, r3, 2 " ); \ + asm( "mul r10, r9, r6 " ); \ + asm( "mul r11, r8, r7 " ); \ + asm( "mul r12, r9, r7 " ); \ + asm( "mul r13, r8, r6 " ); \ + asm( "bsrli r8, r10, 16 " ); \ + asm( "bsrli r9, r11, 16 " ); \ + asm( "add r13, r13, r8 " ); \ + asm( "add r13, r13, r9 " ); \ + asm( "bslli r10, r10, 16 " ); \ + asm( "bslli r11, r11, 16 " ); \ + asm( "add r12, r12, r10 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "add r12, r12, r11 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "lwi r10, r4, 0 " ); \ + asm( "add r12, r12, r10 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "add r12, r12, r5 " ); \ + asm( "addc r5, r13, r0 " ); \ + asm( "swi r12, r4, 0 " ); \ + asm( "addi r4, r4, 4 " ); + +#define MULADDC_STOP \ + asm( "swi r5, %0 " : "=m" (c)); \ + asm( "swi r4, %0 " : "=m" (d)); \ + asm( "swi r3, %0 " : "=m" (s) :: \ + "r3", "r4" , "r5" , "r6" , "r7" , "r8" , \ + "r9", "r10", "r11", "r12", "r13" ); + +#endif /* MicroBlaze */ + +#if defined(__tricore__) + +#define MULADDC_INIT \ + asm( "ld.a %%a2, %0 " :: "m" (s)); \ + asm( "ld.a %%a3, %0 " :: "m" (d)); \ + asm( "ld.w %%d4, %0 " :: "m" (c)); \ + asm( "ld.w %%d1, %0 " :: "m" (b)); \ + asm( "xor %d5, %d5 " ); + +#define MULADDC_CORE \ + asm( "ld.w %d0, [%a2+] " ); \ + asm( "madd.u %e2, %e4, %d0, %d1 " ); \ + asm( "ld.w %d0, [%a3] " ); \ + asm( "addx %d2, %d2, %d0 " ); \ + asm( "addc %d3, %d3, 0 " ); \ + asm( "mov %d4, %d3 " ); \ + asm( "st.w [%a3+], %d2 " ); + +#define MULADDC_STOP \ + asm( "st.w %0, %%d4 " : "=m" (c)); \ + asm( "st.a %0, %%a3 " : "=m" (d)); \ + asm( "st.a %0, %%a2 " : "=m" (s) :: \ + "d0", "d1", "e2", "d4", "a2", "a3" ); + +#endif /* TriCore */ + +#if defined(__arm__) +#if defined(__ARM_FEATURE_DSP) +/* The ARM DSP instructions are available on Cortex M4, M7 and + Cortex A CPUs */ + +#define MULADDC_1024_CORE \ + "ldmia %[s]!, { r7, r8, r9, r10 } \n\t" \ + "ldmia %[d], { r3, r4, r5, r6 } \n\t" \ + "umaal r3, %2, %[b], r7 \n\t" \ + "umaal r4, %2, %[b], r8 \n\t" \ + "umaal r5, %2, %[b], r9 \n\t" \ + "umaal r6, %2, %[b], r10 \n\t" \ + "stmia %[d]!, {r3, r4, r5, r6} \n\t" + +#define MULADDC_1024_LOOP \ + asm( "tst %[i], #0xfe0 \n\t" \ + "beq 0f \n" \ +"1: sub %[i], %[i], #32 \n\t" \ + MULADDC_1024_CORE MULADDC_1024_CORE \ + MULADDC_1024_CORE MULADDC_1024_CORE \ + MULADDC_1024_CORE MULADDC_1024_CORE \ + MULADDC_1024_CORE MULADDC_1024_CORE \ + "tst %[i], #0xfe0 \n\t" \ + "bne 1b \n" \ +"0:" \ + : [s] "=r" (s), [d] "=r" (d), [c] "=r" (c), [i] "=r" (i) \ + : [b] "r" (b), "[s]" (s), "[d]" (d), "[c]" (c), "[i]" (i) \ + : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "memory", "cc" ); + +#define MULADDC_INIT \ + asm( + +#define MULADDC_CORE \ + "ldr r0, [%0], #4 \n\t" \ + "ldr r1, [%1] \n\t" \ + "umaal r1, %2, %3, r0 \n\t" \ + "str r1, [%1], #4 \n\t" + +#define MULADDC_HUIT \ + "ldmia %0!, {r0, r1, r2, r3} \n\t" \ + "ldmia %1, {r4, r5, r6, r7} \n\t" \ + "umaal r4, %2, %3, r0 \n\t" \ + "umaal r5, %2, %3, r1 \n\t" \ + "umaal r6, %2, %3, r2 \n\t" \ + "umaal r7, %2, %3, r3 \n\t" \ + "stmia %1!, {r4, r5, r6, r7} \n\t" \ + "ldmia %0!, {r0, r1, r2, r3} \n\t" \ + "ldmia %1, {r4, r5, r6, r7} \n\t" \ + "umaal r4, %2, %3, r0 \n\t" \ + "umaal r5, %2, %3, r1 \n\t" \ + "umaal r6, %2, %3, r2 \n\t" \ + "umaal r7, %2, %3, r3 \n\t" \ + "stmia %1!, {r4, r5, r6, r7} \n\t" + +#define MULADDC_STOP \ + : "=r" (s), "=r" (d), "=r" (c) \ + : "r" (b), "0" (s), "1" (d), "2" (c) \ + : "r0", "r1", "r2", "r3", "r4", "r5", \ + "r6", "r7", "memory"); + +#else /* __ARM_FEATURE_DSP */ + +#define MULADDC_1024_CORE \ + "ldmia %[s]!, { r8, r9, r10 } \n\t" \ + "ldmia %[d], { r5, r6, r7 } \n\t" \ + "adcs r5, r5, %[c] \n\t" \ + "umull r4, r8, r8, %[b] \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r5, r5, r4 \n\t" \ + "adcs r6, r6, %[c] \n\t" \ + "umull r4, r8, r9, %[b] \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r6, r6, r4 \n\t" \ + "adcs r7, r7, %[c] \n\t" \ + "umull r4, r8, r10, %[b] \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r7, r7, r4 \n\t" \ + "stmia %[d]!, { r5, r6, r7 } \n\t" + +#define MULADDC_1024_LOOP \ + asm( "tst %[i], #0xfe0 \n\t" \ + "beq 0f \n" \ +"1: ldmia %[s]!, { r8, r9, r10 } \n\t" \ + "ldmia %[d], { r5, r6, r7 } \n\t" \ + "sub %[i], %[i], #32 \n\t" \ + "adds r5, r5, %[c] \n\t" \ + "umull r4, r8, %[b], r8 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r5, r5, r4 \n\t" \ + "adcs r6, r6, %[c] \n\t" \ + "umull r4, r8, %[b], r9 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r6, r6, r4 \n\t" \ + "adcs r7, r7, %[c] \n\t" \ + "umull r4, r8, %[b], r10 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r7, r7, r4 \n\t" \ + "stmia %[d]!, { r5, r6, r7 } \n\t" \ + MULADDC_1024_CORE MULADDC_1024_CORE \ + MULADDC_1024_CORE MULADDC_1024_CORE \ + MULADDC_1024_CORE MULADDC_1024_CORE \ + MULADDC_1024_CORE MULADDC_1024_CORE \ + MULADDC_1024_CORE \ + "ldmia %[s]!, { r8, r9 } \n\t" \ + "ldmia %[d], { r5, r6 } \n\t" \ + "adcs r5, r5, %[c] \n\t" \ + "umull r4, r8, %[b], r8 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r5, r5, r4 \n\t" \ + "adcs r6, r6, %[c] \n\t" \ + "umull r4, r8, %[b], r9 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r6, r6, r4 \n\t" \ + "adc %[c], %[c], #0 \n\t" \ + "stmia %[d]!, { r5, r6 } \n\t" \ + "tst %[i], #0xfe0 \n\t" \ + "bne 1b \n" \ +"0:" \ + : [s] "=r" (s), [d] "=r" (d), [c] "=r" (c), [i] "=r" (i) \ + : [b] "r" (b), "[s]" (s), "[d]" (d), "[c]" (c), "[i]" (i) \ + : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "memory", "cc" ); + +/* Just for reference (dead code) */ +#define MULADDC_HUIT_DEAD \ + "ldmia %0!, { r4, r5 } \n\t" \ + "ldmia %1, { r8, r9 } \n\t" \ + "umull r6, r7, %3, r4 \n\t" \ + "adcs r6, r6, %2 \n\t" \ + "adc %2, r7, #0 \n\t" \ + "adds r8, r8, r6 \n\t" \ + "umull r6, r7, %3, r5 \n\t" \ + "adcs r6, r6, %2 \n\t" \ + "adc %2, r7, #0 \n\t" \ + "adds r9, r9, r6 \n\t" \ + "stmia %1!, { r8, r9 } \n\t" \ + "ldmia %0!, { r4, r5 } \n\t" \ + "ldmia %1, { r8, r9 } \n\t" \ + "umull r6, r7, %3, r4 \n\t" \ + "adcs r6, r6, %2 \n\t" \ + "adc %2, r7, #0 \n\t" \ + "adds r8, r8, r6 \n\t" \ + "umull r6, r7, %3, r5 \n\t" \ + "adcs r6, r6, %2 \n\t" \ + "adc %2, r7, #0 \n\t" \ + "adds r9, r9, r6 \n\t" \ + "stmia %1!, { r8, r9 } \n\t" \ + "ldmia %0!, { r4, r5 } \n\t" \ + "ldmia %1, { r8, r9 } \n\t" \ + "umull r6, r7, %3, r4 \n\t" \ + "adcs r6, r6, %2 \n\t" \ + "adc %2, r7, #0 \n\t" \ + "adds r8, r8, r6 \n\t" \ + "umull r6, r7, %3, r5 \n\t" \ + "adcs r6, r6, %2 \n\t" \ + "adc %2, r7, #0 \n\t" \ + "adds r9, r9, r6 \n\t" \ + "stmia %1!, { r8, r9 } \n\t" \ + "ldmia %0!, { r4, r5 } \n\t" \ + "ldmia %1, { r8, r9 } \n\t" \ + "umull r6, r7, %3, r4 \n\t" \ + "adcs r6, r6, %2 \n\t" \ + "adc %2, r7, #0 \n\t" \ + "adds r8, r8, r6 \n\t" \ + "umull r6, r7, %3, r5 \n\t" \ + "adcs r6, r6, %2 \n\t" \ + "adc %2, r7, #0 \n\t" \ + "adds r9, r9, r6 \n\t" \ + "stmia %1!, { r8, r9 } \n\t" + +#define MULADDC_INIT \ + asm( "adds %0, #0 \n\t" + +#define MULADDC_CORE \ + "ldr r5, [%1] \n\t" \ + "ldr r4, [%0], #4 \n\t" \ + "adcs r5, r5, %2 \n\t" \ + "umull r6, r7, %3, r4 \n\t" \ + "adc %2, r7, #0 \n\t" \ + "adds r5, r5, r6 \n\t" \ + "str r5, [%1], #4 \n\t" + +#define MULADDC_STOP \ + "adc %2, %2, #0 " \ + : "=r" (s), "=r" (d), "=r" (c) \ + : "r" (b), "0" (s), "1" (d), "2" (c) \ + : "r4", "r5", "r6", "r7", "memory", "cc" ); + +#endif /* __ARM_FEATURE_DSP */ +#endif /* ARMv3 */ + +#if defined(__alpha__) + +#define MULADDC_INIT \ + asm( "ldq $1, %0 " :: "m" (s)); \ + asm( "ldq $2, %0 " :: "m" (d)); \ + asm( "ldq $3, %0 " :: "m" (c)); \ + asm( "ldq $4, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "ldq $6, 0($1) " ); \ + asm( "addq $1, 8, $1 " ); \ + asm( "mulq $6, $4, $7 " ); \ + asm( "umulh $6, $4, $6 " ); \ + asm( "addq $7, $3, $7 " ); \ + asm( "cmpult $7, $3, $3 " ); \ + asm( "ldq $5, 0($2) " ); \ + asm( "addq $7, $5, $7 " ); \ + asm( "cmpult $7, $5, $5 " ); \ + asm( "stq $7, 0($2) " ); \ + asm( "addq $2, 8, $2 " ); \ + asm( "addq $6, $3, $3 " ); \ + asm( "addq $5, $3, $3 " ); + +#define MULADDC_STOP \ + asm( "stq $3, %0 " : "=m" (c)); \ + asm( "stq $2, %0 " : "=m" (d)); \ + asm( "stq $1, %0 " : "=m" (s) :: \ + "$1", "$2", "$3", "$4", "$5", "$6", "$7" ); + +#endif /* Alpha */ + +#if defined(__mips__) + +#define MULADDC_INIT \ + asm( "lw $10, %0 " :: "m" (s)); \ + asm( "lw $11, %0 " :: "m" (d)); \ + asm( "lw $12, %0 " :: "m" (c)); \ + asm( "lw $13, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "lw $14, 0($10) " ); \ + asm( "multu $13, $14 " ); \ + asm( "addi $10, $10, 4 " ); \ + asm( "mflo $14 " ); \ + asm( "mfhi $9 " ); \ + asm( "addu $14, $12, $14 " ); \ + asm( "lw $15, 0($11) " ); \ + asm( "sltu $12, $14, $12 " ); \ + asm( "addu $15, $14, $15 " ); \ + asm( "sltu $14, $15, $14 " ); \ + asm( "addu $12, $12, $9 " ); \ + asm( "sw $15, 0($11) " ); \ + asm( "addu $12, $12, $14 " ); \ + asm( "addi $11, $11, 4 " ); + +#define MULADDC_STOP \ + asm( "sw $12, %0 " : "=m" (c)); \ + asm( "sw $11, %0 " : "=m" (d)); \ + asm( "sw $10, %0 " : "=m" (s) :: \ + "$9", "$10", "$11", "$12", "$13", "$14", "$15" ); + +#endif /* MIPS */ +#endif /* GNUC */ + +#if (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) + +#define MULADDC_INIT \ + __asm mov esi, s \ + __asm mov edi, d \ + __asm mov ecx, c \ + __asm mov ebx, b + +#define MULADDC_CORE \ + __asm lodsd \ + __asm mul ebx \ + __asm add eax, ecx \ + __asm adc edx, 0 \ + __asm add eax, [edi] \ + __asm adc edx, 0 \ + __asm mov ecx, edx \ + __asm stosd + +#if defined(POLARSSL_HAVE_SSE2) + +#define EMIT __asm _emit + +#define MULADDC_HUIT \ + EMIT 0x0F EMIT 0x6E EMIT 0xC9 \ + EMIT 0x0F EMIT 0x6E EMIT 0xC3 \ + EMIT 0x0F EMIT 0x6E EMIT 0x1F \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x16 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x04 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x08 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x7E EMIT 0x0C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x5F EMIT 0x04 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x08 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xEE \ + EMIT 0x0F EMIT 0x6E EMIT 0x67 EMIT 0x0C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xFC \ + EMIT 0x0F EMIT 0x7E EMIT 0x0F \ + EMIT 0x0F EMIT 0x6E EMIT 0x56 EMIT 0x10 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x14 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x18 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x04 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x5E EMIT 0x1C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCD \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x10 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xD5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x08 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCF \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x14 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xE5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x0C \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x18 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xF5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x10 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x1C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDD \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x14 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCE \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x18 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x1C \ + EMIT 0x83 EMIT 0xC7 EMIT 0x20 \ + EMIT 0x83 EMIT 0xC6 EMIT 0x20 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x7E EMIT 0xC9 + +#define MULADDC_STOP \ + EMIT 0x0F EMIT 0x77 \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#else + +#define MULADDC_STOP \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#endif /* SSE2 */ +#endif /* MSVC */ + +#endif /* POLARSSL_HAVE_ASM */ + +#if !defined(MULADDC_CORE) +#if defined(POLARSSL_HAVE_LONGLONG) + +#define MULADDC_INIT \ +{ \ + t_dbl r; \ + t_int r0, r1; + +#define MULADDC_CORE \ + r = *(s++) * (t_dbl) b; \ + r0 = r; \ + r1 = r >> biL; \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#else +#define MULADDC_INIT \ +{ \ + t_uint s0, s1, b0, b1; \ + t_uint r0, r1, rx, ry; \ + b0 = ( b << biH ) >> biH; \ + b1 = ( b >> biH ); + +#define MULADDC_CORE \ + s0 = ( *s << biH ) >> biH; \ + s1 = ( *s >> biH ); s++; \ + rx = s0 * b1; r0 = s0 * b0; \ + ry = s1 * b0; r1 = s1 * b1; \ + r1 += ( rx >> biH ); \ + r1 += ( ry >> biH ); \ + rx <<= biH; ry <<= biH; \ + r0 += rx; r1 += (r0 < rx); \ + r0 += ry; r1 += (r0 < ry); \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#endif /* C (generic) */ +#endif /* C (longlong) */ + +#endif /* bn_mul.h */ diff --git a/gnuk/polarssl/include/polarssl/config.h b/gnuk/polarssl/include/polarssl/config.h new file mode 100644 index 0000000000..cf3cd2a6e4 --- /dev/null +++ b/gnuk/polarssl/include/polarssl/config.h @@ -0,0 +1,1018 @@ +/** + * \file config.h + * + * \brief Configuration options (set of defines) + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * This set of compile-time options may be used to enable + * or disable features selectively, and reduce the global + * memory footprint. + */ +#ifndef POLARSSL_CONFIG_H +#define POLARSSL_CONFIG_H + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif + +/** + * \name SECTION: System support + * + * This section sets system specific settings. + * \{ + */ + +/** + * \def POLARSSL_HAVE_INT8 + * + * The system uses 8-bit wide native integers. + * + * Uncomment if native integers are 8-bit wide. +#define POLARSSL_HAVE_INT8 + */ + +/** + * \def POLARSSL_HAVE_INT16 + * + * The system uses 16-bit wide native integers. + * + * Uncomment if native integers are 16-bit wide. +#define POLARSSL_HAVE_INT16 + */ + +/** + * \def POLARSSL_HAVE_LONGLONG + * + * The compiler supports the 'long long' type. + * (Only used on 32-bit platforms) + * +#define POLARSSL_HAVE_LONGLONG + */ + +/** + * \def POLARSSL_HAVE_ASM + * + * The compiler has support for asm() + * + * Uncomment to enable the use of assembly code. + * + * Requires support for asm() in compiler. + * + * Used in: + * library/timing.c + * library/padlock.c + * include/polarssl/bn_mul.h + * + */ +#define POLARSSL_HAVE_ASM + +/** + * \def POLARSSL_HAVE_SSE2 + * + * CPU supports SSE2 instruction set. + * + * Uncomment if the CPU supports SSE2 (IA-32 specific). + * +#define POLARSSL_HAVE_SSE2 + */ +/* \} name */ + +/** + * \name SECTION: PolarSSL feature support + * + * This section sets support for features that are or are not needed + * within the modules that are enabled. + * \{ + */ + +/** + * \def POLARSSL_XXX_ALT + * + * Uncomment a macro to let PolarSSL use your alternate core implementation of + * a symmetric or hash algorithm (e.g. platform specific assembly optimized + * implementations). Keep in mind that the function prototypes should remain + * the same. + * + * Example: In case you uncomment POLARSSL_AES_ALT, PolarSSL will no longer + * provide the "struct aes_context" definition and omit the base function + * declarations and implementations. "aes_alt.h" will be included from + * "aes.h" to include the new function definitions. + * + * Uncomment a macro to enable alternate implementation for core algorithm + * functions +#define POLARSSL_AES_ALT +#define POLARSSL_ARC4_ALT +#define POLARSSL_BLOWFISH_ALT +#define POLARSSL_CAMELLIA_ALT +#define POLARSSL_DES_ALT +#define POLARSSL_XTEA_ALT +#define POLARSSL_MD2_ALT +#define POLARSSL_MD4_ALT +#define POLARSSL_MD5_ALT +#define POLARSSL_SHA1_ALT +#define POLARSSL_SHA2_ALT +#define POLARSSL_SHA4_ALT + */ + +/** + * \def POLARSSL_AES_ROM_TABLES + * + * Store the AES tables in ROM. + * + * Uncomment this macro to store the AES tables in ROM. + * + */ +#define POLARSSL_AES_ROM_TABLES + +/** + * \def POLARSSL_CIPHER_MODE_CFB + * + * Enable Cipher Feedback mode (CFB) for symmetric ciphers. + */ +#define POLARSSL_CIPHER_MODE_CFB + +/** + * \def POLARSSL_CIPHER_MODE_CTR + * + * Enable Counter Block Cipher mode (CTR) for symmetric ciphers. + * +#define POLARSSL_CIPHER_MODE_CTR + */ + +/** + * \def POLARSSL_CIPHER_NULL_CIPHER + * + * Enable NULL cipher. + * Warning: Only do so when you know what you are doing. This allows for + * encryption or channels without any security! + * + * Requires POLARSSL_ENABLE_WEAK_CIPHERSUITES as well to enable + * the following ciphersuites: + * TLS_RSA_WITH_NULL_MD5 + * TLS_RSA_WITH_NULL_SHA + * TLS_RSA_WITH_NULL_SHA256 + * + * Uncomment this macro to enable the NULL cipher and ciphersuites +#define POLARSSL_CIPHER_NULL_CIPHER + */ + +/** + * \def POLARSSL_ENABLE_WEAK_CIPHERSUITES + * + * Enable weak ciphersuites in SSL / TLS + * Warning: Only do so when you know what you are doing. This allows for + * channels with virtually no security at all! + * + * This enables the following ciphersuites: + * TLS_RSA_WITH_DES_CBC_SHA + * TLS_DHE_RSA_WITH_DES_CBC_SHA + * + * Uncomment this macro to enable weak ciphersuites +#define POLARSSL_ENABLE_WEAK_CIPHERSUITES + */ + +/** + * \def POLARSSL_ERROR_STRERROR_DUMMY + * + * Enable a dummy error function to make use of error_strerror() in + * third party libraries easier. + * + * Disable if you run into name conflicts and want to really remove the + * error_strerror() + */ +#define POLARSSL_ERROR_STRERROR_DUMMY + +/** + * \def POLARSSL_GENPRIME + * + * Requires: POLARSSL_BIGNUM_C, POLARSSL_RSA_C + * + * Enable the RSA prime-number generation code. + */ +#define POLARSSL_GENPRIME + +/** + * \def POLARSSL_FS_IO + * + * Enable functions that use the filesystem. + * +#define POLARSSL_FS_IO + */ + +/** + * \def POLARSSL_NO_DEFAULT_ENTROPY_SOURCES + * + * Do not add default entropy sources. These are the platform specific, + * hardclock and HAVEGE based poll functions. + * + * This is useful to have more control over the added entropy sources in an + * application. + * + * Uncomment this macro to prevent loading of default entropy functions. +#define POLARSSL_NO_DEFAULT_ENTROPY_SOURCES + */ + +/** + * \def POLARSSL_NO_PLATFORM_ENTROPY + * + * Do not use built-in platform entropy functions. + * This is useful if your platform does not support + * standards like the /dev/urandom or Windows CryptoAPI. + * + * Uncomment this macro to disable the built-in platform entropy functions. +#define POLARSSL_NO_PLATFORM_ENTROPY + */ + +/** + * \def POLARSSL_PKCS1_V21 + * + * Requires: POLARSSL_MD_C, POLARSSL_RSA_C + * + * Enable support for PKCS#1 v2.1 encoding. + * This enables support for RSAES-OAEP and RSASSA-PSS operations. + * +#define POLARSSL_PKCS1_V21 + */ + +/** + * \def POLARSSL_RSA_NO_CRT + * + * Do not use the Chinese Remainder Theorem for the RSA private operation. + * + * Uncomment this macro to disable the use of CRT in RSA. + * +#define POLARSSL_RSA_NO_CRT + */ + +/** + * \def POLARSSL_SELF_TEST + * + * Enable the checkup functions (*_self_test). + * +#define POLARSSL_SELF_TEST + */ + +/** + * \def POLARSSL_SSL_ALL_ALERT_MESSAGES + * + * Enable sending of alert messages in case of encountered errors as per RFC. + * If you choose not to send the alert messages, PolarSSL can still communicate + * with other servers, only debugging of failures is harder. + * + * The advantage of not sending alert messages, is that no information is given + * about reasons for failures thus preventing adversaries of gaining intel. + * + * Enable sending of all alert messages + */ +#define POLARSSL_SSL_ALERT_MESSAGES + +/** + * \def POLARSSL_SSL_DEBUG_ALL + * + * Enable the debug messages in SSL module for all issues. + * Debug messages have been disabled in some places to prevent timing + * attacks due to (unbalanced) debugging function calls. + * + * If you need all error reporting you should enable this during debugging, + * but remove this for production servers that should log as well. + * + * Uncomment this macro to report all debug messages on errors introducing + * a timing side-channel. + * +#define POLARSSL_SSL_DEBUG_ALL + */ + +/** + * \def POLARSSL_SSL_HW_RECORD_ACCEL + * + * Enable hooking functions in SSL module for hardware acceleration of + * individual records. + * + * Uncomment this macro to enable hooking functions. +#define POLARSSL_SSL_HW_RECORD_ACCEL + */ + +/** + * \def POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO + * + * Enable support for receiving and parsing SSLv2 Client Hello messages for the + * SSL Server module (POLARSSL_SSL_SRV_C) + * + * Comment this macro to disable support for SSLv2 Client Hello messages. + */ +#define POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO + +/** + * \def POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION + * + * If set, the X509 parser will not break-off when parsing an X509 certificate + * and encountering an unknown critical extension. + * + * Uncomment to prevent an error. + * +#define POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION + */ + +/** + * \def POLARSSL_ZLIB_SUPPORT + * + * If set, the SSL/TLS module uses ZLIB to support compression and + * decompression of packet data. + * + * Used in: library/ssl_tls.c + * library/ssl_cli.c + * library/ssl_srv.c + * + * This feature requires zlib library and headers to be present. + * + * Uncomment to enable use of ZLIB +#define POLARSSL_ZLIB_SUPPORT + */ +/* \} name */ + +/** + * \name SECTION: PolarSSL modules + * + * This section enables or disables entire modules in PolarSSL + * \{ + */ + +/** + * \def POLARSSL_AES_C + * + * Enable the AES block cipher. + * + * Module: library/aes.c + * Caller: library/ssl_tls.c + * library/pem.c + * library/ctr_drbg.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_RSA_WITH_AES_128_CBC_SHA + * TLS_RSA_WITH_AES_256_CBC_SHA + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA + * TLS_RSA_WITH_AES_128_CBC_SHA256 + * TLS_RSA_WITH_AES_256_CBC_SHA256 + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 + * TLS_RSA_WITH_AES_128_GCM_SHA256 + * TLS_RSA_WITH_AES_256_GCM_SHA384 + * + * PEM uses AES for decrypting encrypted keys. + */ +#define POLARSSL_AES_C + +/** + * \def POLARSSL_ARC4_C + * + * Enable the ARCFOUR stream cipher. + * + * Module: library/arc4.c + * Caller: library/ssl_tls.c + * + * This module enables the following ciphersuites: + * TLS_RSA_WITH_RC4_128_MD5 + * TLS_RSA_WITH_RC4_128_SHA + */ +#define POLARSSL_ARC4_C + +/** + * \def POLARSSL_ASN1_PARSE_C + * + * Enable the generic ASN1 parser. + * + * Module: library/asn1.c + * Caller: library/x509parse.c + */ +#define POLARSSL_ASN1_PARSE_C + +/** + * \def POLARSSL_ASN1_WRITE_C + * + * Enable the generic ASN1 writer. + * + * Module: library/asn1write.c + */ +#define POLARSSL_ASN1_WRITE_C + +/** + * \def POLARSSL_BASE64_C + * + * Enable the Base64 module. + * + * Module: library/base64.c + * Caller: library/pem.c + * + * This module is required for PEM support (required by X.509). + */ +#define POLARSSL_BASE64_C + +/** + * \def POLARSSL_BIGNUM_C + * + * Enable the multi-precision integer library. + * + * Module: library/bignum.c + * Caller: library/dhm.c + * library/rsa.c + * library/ssl_tls.c + * library/x509parse.c + * + * This module is required for RSA and DHM support. + */ +#define POLARSSL_BIGNUM_C + +/** + * \def POLARSSL_BLOWFISH_C + * + * Enable the Blowfish block cipher. + * + * Module: library/blowfish.c + */ +#define POLARSSL_BLOWFISH_C + +/** + * \def POLARSSL_CAMELLIA_C + * + * Enable the Camellia block cipher. + * + * Module: library/camellia.c + * Caller: library/ssl_tls.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_RSA_WITH_CAMELLIA_128_CBC_SHA + * TLS_RSA_WITH_CAMELLIA_256_CBC_SHA + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + * TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 + */ +#define POLARSSL_CAMELLIA_C + +/** + * \def POLARSSL_CERTS_C + * + * Enable the test certificates. + * + * Module: library/certs.c + * Caller: + * + * This module is used for testing (ssl_client/server). + */ +#define POLARSSL_CERTS_C + +/** + * \def POLARSSL_CIPHER_C + * + * Enable the generic cipher layer. + * + * Module: library/cipher.c + * Caller: + * + * Uncomment to enable generic cipher wrappers. + */ +#define POLARSSL_CIPHER_C + +/** + * \def POLARSSL_CTR_DRBG_C + * + * Enable the CTR_DRBG AES-256-based random generator + * + * Module: library/ctr_drbg.c + * Caller: + * + * Requires: POLARSSL_AES_C + * + * This module provides the CTR_DRBG AES-256 random number generator. + */ +#define POLARSSL_CTR_DRBG_C + +/** + * \def POLARSSL_DEBUG_C + * + * Enable the debug functions. + * + * Module: library/debug.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * This module provides debugging functions. + */ +#define POLARSSL_DEBUG_C + +/** + * \def POLARSSL_DES_C + * + * Enable the DES block cipher. + * + * Module: library/des.c + * Caller: library/pem.c + * library/ssl_tls.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA + * + * PEM uses DES/3DES for decrypting encrypted keys. + */ +#define POLARSSL_DES_C + +/** + * \def POLARSSL_DHM_C + * + * Enable the Diffie-Hellman-Merkle key exchange. + * + * Module: library/dhm.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_DHE_RSA_WITH_DES_CBC_SHA + * TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 + * TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 + */ +#define POLARSSL_DHM_C + +/** + * \def POLARSSL_ENTROPY_C + * + * Enable the platform-specific entropy code. + * + * Module: library/entropy.c + * Caller: + * + * Requires: POLARSSL_SHA4_C + * + * This module provides a generic entropy pool + */ +#define POLARSSL_ENTROPY_C + +/** + * \def POLARSSL_ERROR_C + * + * Enable error code to error string conversion. + * + * Module: library/error.c + * Caller: + * + * This module enables err_strerror(). + */ +#define POLARSSL_ERROR_C + +/** + * \def POLARSSL_GCM_C + * + * Enable the Galois/Counter Mode (GCM) for AES + * + * Module: library/gcm.c + * + * Requires: POLARSSL_AES_C + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_RSA_WITH_AES_128_GCM_SHA256 + * TLS_RSA_WITH_AES_256_GCM_SHA384 + */ +#define POLARSSL_GCM_C + +/** + * \def POLARSSL_HAVEGE_C + * + * Enable the HAVEGE random generator. + * + * Warning: the HAVEGE random generator is not suitable for virtualized + * environments + * + * Warning: the HAVEGE random generator is dependent on timing and specific + * processor traits. It is therefore not advised to use HAVEGE as + * your applications primary random generator or primary entropy pool + * input. As a secondary input to your entropy pool, it IS able add + * the (limited) extra entropy it provides. + * + * Module: library/havege.c + * Caller: + * + * Requires: POLARSSL_TIMING_C + * + * Uncomment to enable the HAVEGE random generator. +#define POLARSSL_HAVEGE_C + */ + +/** + * \def POLARSSL_MD_C + * + * Enable the generic message digest layer. + * + * Module: library/md.c + * Caller: + * + * Uncomment to enable generic message digest wrappers. + */ +#define POLARSSL_MD_C + +/** + * \def POLARSSL_MD2_C + * + * Enable the MD2 hash algorithm + * + * Module: library/md2.c + * Caller: library/x509parse.c + * + * Uncomment to enable support for (rare) MD2-signed X.509 certs. + * +#define POLARSSL_MD2_C + */ + +/** + * \def POLARSSL_MD4_C + * + * Enable the MD4 hash algorithm + * + * Module: library/md4.c + * Caller: library/x509parse.c + * + * Uncomment to enable support for (rare) MD4-signed X.509 certs. + * +#define POLARSSL_MD4_C + */ + +/** + * \def POLARSSL_MD5_C + * + * Enable the MD5 hash algorithm + * + * Module: library/md5.c + * Caller: library/pem.c + * library/ssl_tls.c + * library/x509parse.c + * + * This module is required for SSL/TLS and X.509. + * PEM uses MD5 for decrypting encrypted keys. + */ +#define POLARSSL_MD5_C + +/** + * \def POLARSSL_NET_C + * + * Enable the TCP/IP networking routines. + * + * Module: library/net.c + * Caller: + * + * This module provides TCP/IP networking routines. + */ +#define POLARSSL_NET_C + +/** + * \def POLARSSL_PADLOCK_C + * + * Enable VIA Padlock support on x86. + * + * Module: library/padlock.c + * Caller: library/aes.c + * + * This modules adds support for the VIA PadLock on x86. + * +#define POLARSSL_PADLOCK_C + */ + +/** + * \def POLARSSL_PBKDF2_C + * + * Enable PKCS#5 PBKDF2 key derivation function + * DEPRECATED: Use POLARSSL_PKCS5_C instead + * + * Module: library/pbkdf2.c + * + * Requires: POLARSSL_PKCS5_C + * + * This module adds support for the PKCS#5 PBKDF2 key derivation function. +#define POLARSSL_PBKDF2_C + */ + +/** + * \def POLARSSL_PEM_C + * + * Enable PEM decoding + * + * Module: library/pem.c + * Caller: library/x509parse.c + * + * Requires: POLARSSL_BASE64_C + * + * This modules adds support for decoding PEM files. + */ +#define POLARSSL_PEM_C + +/** + * \def POLARSSL_PKCS5_C + * + * Enable PKCS#5 functions + * + * Module: library/pkcs5.c + * + * Requires: POLARSSL_MD_C + * + * This module adds support for the PKCS#5 functions. + */ +#define POLARSSL_PKCS5_C + +/** + * \def POLARSSL_PKCS11_C + * + * Enable wrapper for PKCS#11 smartcard support. + * + * Module: library/ssl_srv.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * Requires: POLARSSL_SSL_TLS_C + * + * This module enables SSL/TLS PKCS #11 smartcard support. + * Requires the presence of the PKCS#11 helper library (libpkcs11-helper) +#define POLARSSL_PKCS11_C + */ + +/** + * \def POLARSSL_PKCS12_C + * + * Enable PKCS#12 PBE functions + * Adds algorithms for parsing PKCS#8 encrypted private keys + * + * Module: library/pkcs12.c + * Caller: library/x509parse.c + * + * Requires: POLARSSL_ASN1_PARSE_C, POLARSSL_CIPHER_C, POLARSSL_MD_C + * Can use: POLARSSL_ARC4_C + * + * This module enables PKCS#12 functions. + */ +#define POLARSSL_PKCS12_C + +/** + * \def POLARSSL_RSA_C + * + * Enable the RSA public-key cryptosystem. + * + * Module: library/rsa.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * library/x509.c + * + * Requires: POLARSSL_BIGNUM_C + * + * This module is required for SSL/TLS and MD5-signed certificates. + */ +#define POLARSSL_RSA_C + +/** + * \def POLARSSL_SHA1_C + * + * Enable the SHA1 cryptographic hash algorithm. + * + * Module: library/sha1.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * library/x509parse.c + * + * This module is required for SSL/TLS and SHA1-signed certificates. + */ +#define POLARSSL_SHA1_C + +/** + * \def POLARSSL_SHA2_C + * + * Enable the SHA-224 and SHA-256 cryptographic hash algorithms. + * + * Module: library/sha2.c + * Caller: library/md_wrap.c + * library/x509parse.c + * + * This module adds support for SHA-224 and SHA-256. + * This module is required for the SSL/TLS 1.2 PRF function. + */ +#define POLARSSL_SHA2_C + +/** + * \def POLARSSL_SHA4_C + * + * Enable the SHA-384 and SHA-512 cryptographic hash algorithms. + * + * Module: library/sha4.c + * Caller: library/md_wrap.c + * library/x509parse.c + * + * This module adds support for SHA-384 and SHA-512. + */ +#define POLARSSL_SHA4_C + +/** + * \def POLARSSL_SSL_CACHE_C + * + * Enable simple SSL cache implementation. + * + * Module: library/ssl_cache.c + * Caller: + * + * Requires: POLARSSL_SSL_CACHE_C + */ +#define POLARSSL_SSL_CACHE_C + +/** + * \def POLARSSL_SSL_CLI_C + * + * Enable the SSL/TLS client code. + * + * Module: library/ssl_cli.c + * Caller: + * + * Requires: POLARSSL_SSL_TLS_C + * + * This module is required for SSL/TLS client support. + */ +#define POLARSSL_SSL_CLI_C + +/** + * \def POLARSSL_SSL_SRV_C + * + * Enable the SSL/TLS server code. + * + * Module: library/ssl_srv.c + * Caller: + * + * Requires: POLARSSL_SSL_TLS_C + * + * This module is required for SSL/TLS server support. + */ +#define POLARSSL_SSL_SRV_C + +/** + * \def POLARSSL_SSL_TLS_C + * + * Enable the generic SSL/TLS code. + * + * Module: library/ssl_tls.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * Requires: POLARSSL_MD5_C, POLARSSL_SHA1_C, POLARSSL_X509_PARSE_C + * + * This module is required for SSL/TLS. + */ +#define POLARSSL_SSL_TLS_C + +/** + * \def POLARSSL_TIMING_C + * + * Enable the portable timing interface. + * + * Module: library/timing.c + * Caller: library/havege.c + * + * This module is used by the HAVEGE random number generator. + */ +#define POLARSSL_TIMING_C + +/** + * \def POLARSSL_VERSION_C + * + * Enable run-time version information. + * + * Module: library/version.c + * + * This module provides run-time version information. + */ +#define POLARSSL_VERSION_C + +/** + * \def POLARSSL_X509_PARSE_C + * + * Enable X.509 certificate parsing. + * + * Module: library/x509parse.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * Requires: POLARSSL_ASN1_PARSE_C, POLARSSL_BIGNUM_C, POLARSSL_RSA_C + * + * This module is required for X.509 certificate parsing. + */ +#define POLARSSL_X509_PARSE_C + +/** + * \def POLARSSL_X509_WRITE_C + * + * Enable X.509 buffer writing. + * + * Module: library/x509write.c + * + * Requires: POLARSSL_BIGNUM_C, POLARSSL_RSA_C + * + * This module is required for X.509 certificate request writing. + */ +#define POLARSSL_X509_WRITE_C + +/** + * \def POLARSSL_XTEA_C + * + * Enable the XTEA block cipher. + * + * Module: library/xtea.c + * Caller: + */ +#define POLARSSL_XTEA_C +/* \} name */ + +/** + * \name SECTION: Module configuration options + * + * This section allows for the setting of module specific sizes and + * configuration options. The default values are already present in the + * relevant header files and should suffice for the regular use cases. + * Our advice is to enable POLARSSL_CONFIG_OPTIONS and change values here + * only if you have a good reason and know the consequences. + * + * If POLARSSL_CONFIG_OPTIONS is undefined here the options in the module + * header file take precedence. + * + * Please check the respective header file for documentation on these + * parameters (to prevent duplicate documentation). + * + * Uncomment POLARSSL_CONFIG_OPTIONS to enable using the values defined here. + * \{ + */ +//#define POLARSSL_CONFIG_OPTIONS /**< Enable config.h module value configuration */ + +#if defined(POLARSSL_CONFIG_OPTIONS) + +// MPI / BIGNUM options +// +#define POLARSSL_MPI_WINDOW_SIZE 6 /**< Maximum windows size used. */ +#define POLARSSL_MPI_MAX_SIZE 512 /**< Maximum number of bytes for usable MPIs. */ + +// CTR_DRBG options +// +#define CTR_DRBG_ENTROPY_LEN 48 /**< Amount of entropy used per seed by default */ +#define CTR_DRBG_RESEED_INTERVAL 10000 /**< Interval before reseed is performed by default */ +#define CTR_DRBG_MAX_INPUT 256 /**< Maximum number of additional input bytes */ +#define CTR_DRBG_MAX_REQUEST 1024 /**< Maximum number of requested bytes per call */ +#define CTR_DRBG_MAX_SEED_INPUT 384 /**< Maximum size of (re)seed buffer */ + +// Entropy options +// +#define ENTROPY_MAX_SOURCES 20 /**< Maximum number of sources supported */ +#define ENTROPY_MAX_GATHER 128 /**< Maximum amount requested from entropy sources */ + +// SSL Cache options +// +#define SSL_CACHE_DEFAULT_TIMEOUT 86400 /**< 1 day */ +#define SSL_CACHE_DEFAULT_MAX_ENTRIES 50 /**< Maximum entries in cache */ + +// SSL options +// +#define SSL_MAX_CONTENT_LEN 16384 /**< Size of the input / output buffer */ + +#endif /* POLARSSL_CONFIG_OPTIONS */ + +/* \} name */ +#endif /* config.h */ diff --git a/gnuk/polarssl/include/polarssl/rsa.h b/gnuk/polarssl/include/polarssl/rsa.h new file mode 100644 index 0000000000..0377e98461 --- /dev/null +++ b/gnuk/polarssl/include/polarssl/rsa.h @@ -0,0 +1,633 @@ +/** + * \file rsa.h + * + * \brief The RSA public-key cryptosystem + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_RSA_H +#define POLARSSL_RSA_H + +#include "bignum.h" + +/* + * RSA Error codes + */ +#define POLARSSL_ERR_RSA_BAD_INPUT_DATA -0x4080 /**< Bad input parameters to function. */ +#define POLARSSL_ERR_RSA_INVALID_PADDING -0x4100 /**< Input data contains invalid padding and is rejected. */ +#define POLARSSL_ERR_RSA_KEY_GEN_FAILED -0x4180 /**< Something failed during generation of a key. */ +#define POLARSSL_ERR_RSA_KEY_CHECK_FAILED -0x4200 /**< Key failed to pass the libraries validity check. */ +#define POLARSSL_ERR_RSA_PUBLIC_FAILED -0x4280 /**< The public key operation failed. */ +#define POLARSSL_ERR_RSA_PRIVATE_FAILED -0x4300 /**< The private key operation failed. */ +#define POLARSSL_ERR_RSA_VERIFY_FAILED -0x4380 /**< The PKCS#1 verification failed. */ +#define POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE -0x4400 /**< The output buffer for decryption is not large enough. */ +#define POLARSSL_ERR_RSA_RNG_FAILED -0x4480 /**< The random generator failed to generate non-zeros. */ + +/* + * PKCS#1 constants + */ +#define SIG_RSA_RAW 0 +#define SIG_RSA_MD2 2 +#define SIG_RSA_MD4 3 +#define SIG_RSA_MD5 4 +#define SIG_RSA_SHA1 5 +#define SIG_RSA_SHA224 14 +#define SIG_RSA_SHA256 11 +#define SIG_RSA_SHA384 12 +#define SIG_RSA_SHA512 13 + +#define RSA_PUBLIC 0 +#define RSA_PRIVATE 1 + +#define RSA_PKCS_V15 0 +#define RSA_PKCS_V21 1 + +#define RSA_SIGN 1 +#define RSA_CRYPT 2 + +#define ASN1_STR_CONSTRUCTED_SEQUENCE "\x30" +#define ASN1_STR_NULL "\x05" +#define ASN1_STR_OID "\x06" +#define ASN1_STR_OCTET_STRING "\x04" + +#define OID_DIGEST_ALG_MDX "\x2A\x86\x48\x86\xF7\x0D\x02\x00" +#define OID_HASH_ALG_SHA1 "\x2b\x0e\x03\x02\x1a" +#define OID_HASH_ALG_SHA2X "\x60\x86\x48\x01\x65\x03\x04\x02\x00" + +#define OID_ISO_MEMBER_BODIES "\x2a" +#define OID_ISO_IDENTIFIED_ORG "\x2b" + +/* + * ISO Member bodies OID parts + */ +#define OID_COUNTRY_US "\x86\x48" +#define OID_RSA_DATA_SECURITY "\x86\xf7\x0d" + +/* + * ISO Identified organization OID parts + */ +#define OID_OIW_SECSIG_SHA1 "\x0e\x03\x02\x1a" + +/* + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithmIdentifier, + * digest Digest } + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * Digest ::= OCTET STRING + */ +#define ASN1_HASH_MDX \ +( \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x20" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x0C" \ + ASN1_STR_OID "\x08" \ + OID_DIGEST_ALG_MDX \ + ASN1_STR_NULL "\x00" \ + ASN1_STR_OCTET_STRING "\x10" \ +) + +#define ASN1_HASH_SHA1 \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x21" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x09" \ + ASN1_STR_OID "\x05" \ + OID_HASH_ALG_SHA1 \ + ASN1_STR_NULL "\x00" \ + ASN1_STR_OCTET_STRING "\x14" + +#define ASN1_HASH_SHA1_ALT \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x1F" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x07" \ + ASN1_STR_OID "\x05" \ + OID_HASH_ALG_SHA1 \ + ASN1_STR_OCTET_STRING "\x14" + +#define ASN1_HASH_SHA2X \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x11" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x0d" \ + ASN1_STR_OID "\x09" \ + OID_HASH_ALG_SHA2X \ + ASN1_STR_NULL "\x00" \ + ASN1_STR_OCTET_STRING "\x00" + +/** + * \brief RSA context structure + */ +typedef struct +{ + int ver; /*!< always 0 */ + size_t len; /*!< size(N) in chars */ + + mpi N; /*!< public modulus */ + mpi E; /*!< public exponent */ + + mpi D; /*!< private exponent */ + mpi P; /*!< 1st prime factor */ + mpi Q; /*!< 2nd prime factor */ + mpi DP; /*!< D % (P - 1) */ + mpi DQ; /*!< D % (Q - 1) */ + mpi QP; /*!< 1 / (Q % P) */ + + mpi RN; /*!< cached R^2 mod N */ + mpi RP; /*!< cached R^2 mod P */ + mpi RQ; /*!< cached R^2 mod Q */ + + int padding; /*!< RSA_PKCS_V15 for 1.5 padding and + RSA_PKCS_v21 for OAEP/PSS */ + int hash_id; /*!< Hash identifier of md_type_t as + specified in the md.h header file + for the EME-OAEP and EMSA-PSS + encoding */ +} +rsa_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initialize an RSA context + * + * Note: Set padding to RSA_PKCS_V21 for the RSAES-OAEP + * encryption scheme and the RSASSA-PSS signature scheme. + * + * \param ctx RSA context to be initialized + * \param padding RSA_PKCS_V15 or RSA_PKCS_V21 + * \param hash_id RSA_PKCS_V21 hash identifier + * + * \note The hash_id parameter is actually ignored + * when using RSA_PKCS_V15 padding. + */ +void rsa_init( rsa_context *ctx, + int padding, + int hash_id); + +/** + * \brief Generate an RSA keypair + * + * \param ctx RSA context that will hold the key + * \param f_rng RNG function + * \param p_rng RNG parameter + * \param nbits size of the public key in bits + * \param exponent public exponent (e.g., 65537) + * + * \note rsa_init() must be called beforehand to setup + * the RSA context. + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_gen_key( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + unsigned int nbits, int exponent ); + +/** + * \brief Check a public RSA key + * + * \param ctx RSA context to be checked + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_check_pubkey( const rsa_context *ctx ); + +/** + * \brief Check a private RSA key + * + * \param ctx RSA context to be checked + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_check_privkey( const rsa_context *ctx ); + +/** + * \brief Do an RSA public key operation + * + * \param ctx RSA context + * \param input input buffer + * \param output output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note This function does NOT take care of message + * padding. Also, be sure to set input[0] = 0 or assure that + * input is smaller than N. + * + * \note The input and output buffers must be large + * enough (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_public( rsa_context *ctx, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Do an RSA private key operation + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for blinding) + * \param p_rng RNG parameter + * \param input input buffer + * \param output output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The input and output buffers must be large + * enough (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_private( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Generic wrapper to perform a PKCS#1 encryption using the + * mode from the context. Add the message padding, then do an + * RSA operation. + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for padding and PKCS#1 v2.1 encoding + * and RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param ilen contains the plaintext length + * \param input buffer holding the data to be encrypted + * \param output buffer that will hold the ciphertext + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_pkcs1_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Perform a PKCS#1 v1.5 encryption (RSAES-PKCS1-v1_5-ENCRYPT) + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for padding and RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param ilen contains the plaintext length + * \param input buffer holding the data to be encrypted + * \param output buffer that will hold the ciphertext + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsaes_pkcs1_v15_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Perform a PKCS#1 v2.1 OAEP encryption (RSAES-OAEP-ENCRYPT) + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for padding and PKCS#1 v2.1 encoding + * and RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param label buffer holding the custom label to use + * \param label_len contains the label length + * \param ilen contains the plaintext length + * \param input buffer holding the data to be encrypted + * \param output buffer that will hold the ciphertext + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsaes_oaep_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + const unsigned char *label, size_t label_len, + size_t ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Generic wrapper to perform a PKCS#1 decryption using the + * mode from the context. Do an RSA operation, then remove + * the message padding + * + * \param ctx RSA context + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param olen will contain the plaintext length + * \param input buffer holding the encrypted data + * \param output buffer that will hold the plaintext + * \param output_max_len maximum length of the output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise + * an error is thrown. + */ +int rsa_pkcs1_decrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief Perform a PKCS#1 v1.5 decryption (RSAES-PKCS1-v1_5-DECRYPT) + * + * \param ctx RSA context + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param olen will contain the plaintext length + * \param input buffer holding the encrypted data + * \param output buffer that will hold the plaintext + * \param output_max_len maximum length of the output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise + * an error is thrown. + */ +int rsa_rsaes_pkcs1_v15_decrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief Perform a PKCS#1 v2.1 OAEP decryption (RSAES-OAEP-DECRYPT) + * + * \param ctx RSA context + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param label buffer holding the custom label to use + * \param label_len contains the label length + * \param olen will contain the plaintext length + * \param input buffer holding the encrypted data + * \param output buffer that will hold the plaintext + * \param output_max_len maximum length of the output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise + * an error is thrown. + */ +int rsa_rsaes_oaep_decrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + const unsigned char *label, size_t label_len, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief Generic wrapper to perform a PKCS#1 signature using the + * mode from the context. Do a private RSA operation to sign + * a message digest + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for PKCS#1 v2.1 encoding and for + * RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer that will hold the ciphertext + * + * \return 0 if the signing operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note In case of PKCS#1 v2.1 encoding keep in mind that + * the hash_id in the RSA context is the one used for the + * encoding. hash_id in the function call is the type of hash + * that is encoded. According to RFC 3447 it is advised to + * keep both hashes the same. + */ +int rsa_pkcs1_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v1.5 signature (RSASSA-PKCS1-v1_5-SIGN) + * + * \param ctx RSA context + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer that will hold the ciphertext + * + * \return 0 if the signing operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsassa_pkcs1_v15_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v2.1 PSS signature (RSASSA-PSS-SIGN) + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for PKCS#1 v2.1 encoding and for + * RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer that will hold the ciphertext + * + * \return 0 if the signing operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note In case of PKCS#1 v2.1 encoding keep in mind that + * the hash_id in the RSA context is the one used for the + * encoding. hash_id in the function call is the type of hash + * that is encoded. According to RFC 3447 it is advised to + * keep both hashes the same. + */ +int rsa_rsassa_pss_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Generic wrapper to perform a PKCS#1 verification using the + * mode from the context. Do a public RSA operation and check + * the message digest + * + * \param ctx points to an RSA public key + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer holding the ciphertext + * + * \return 0 if the verify operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note In case of PKCS#1 v2.1 encoding keep in mind that + * the hash_id in the RSA context is the one used for the + * verification. hash_id in the function call is the type of hash + * that is verified. According to RFC 3447 it is advised to + * keep both hashes the same. + */ +int rsa_pkcs1_verify( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v1.5 verification (RSASSA-PKCS1-v1_5-VERIFY) + * + * \param ctx points to an RSA public key + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer holding the ciphertext + * + * \return 0 if the verify operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsassa_pkcs1_v15_verify( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v2.1 PSS verification (RSASSA-PSS-VERIFY) + * \brief Do a public RSA and check the message digest + * + * \param ctx points to an RSA public key + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer holding the ciphertext + * + * \return 0 if the verify operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note In case of PKCS#1 v2.1 encoding keep in mind that + * the hash_id in the RSA context is the one used for the + * verification. hash_id in the function call is the type of hash + * that is verified. According to RFC 3447 it is advised to + * keep both hashes the same. + */ +int rsa_rsassa_pss_verify( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Free the components of an RSA key + * + * \param ctx RSA Context to free + */ +void rsa_free( rsa_context *ctx ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int rsa_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* rsa.h */ diff --git a/gnuk/polarssl/library/aes.c b/gnuk/polarssl/library/aes.c new file mode 100644 index 0000000000..ed834342bb --- /dev/null +++ b/gnuk/polarssl/library/aes.c @@ -0,0 +1,1352 @@ +/* + * FIPS-197 compliant AES implementation + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The AES block cipher was designed by Vincent Rijmen and Joan Daemen. + * + * http://csrc.nist.gov/encryption/aes/rijndael/Rijndael.pdf + * http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + */ + +#include "polarssl/config.h" + +#if defined(POLARSSL_AES_C) + +#include "polarssl/aes.h" +#if defined(POLARSSL_PADLOCK_C) +#include "polarssl/padlock.h" +#endif + +#if !defined(POLARSSL_AES_ALT) + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_UINT32_LE +#define GET_UINT32_LE(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] ) \ + | ( (uint32_t) (b)[(i) + 1] << 8 ) \ + | ( (uint32_t) (b)[(i) + 2] << 16 ) \ + | ( (uint32_t) (b)[(i) + 3] << 24 ); \ +} +#endif + +#ifndef PUT_UINT32_LE +#define PUT_UINT32_LE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \ +} +#endif + +#if defined(POLARSSL_PADLOCK_C) && \ + ( defined(POLARSSL_HAVE_X86) || defined(PADLOCK_ALIGN16) ) +static int aes_padlock_ace = -1; +#endif + +#if defined(POLARSSL_AES_ROM_TABLES) +/* + * Forward S-box + */ +static const unsigned char FSb[256] = +{ + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, + 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, + 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, + 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, + 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, + 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, + 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, + 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, + 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, + 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, + 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, + 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, + 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, + 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, + 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, + 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, + 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 +}; + +/* + * Forward tables + */ +#define FT \ +\ + V(A5,63,63,C6), V(84,7C,7C,F8), V(99,77,77,EE), V(8D,7B,7B,F6), \ + V(0D,F2,F2,FF), V(BD,6B,6B,D6), V(B1,6F,6F,DE), V(54,C5,C5,91), \ + V(50,30,30,60), V(03,01,01,02), V(A9,67,67,CE), V(7D,2B,2B,56), \ + V(19,FE,FE,E7), V(62,D7,D7,B5), V(E6,AB,AB,4D), V(9A,76,76,EC), \ + V(45,CA,CA,8F), V(9D,82,82,1F), V(40,C9,C9,89), V(87,7D,7D,FA), \ + V(15,FA,FA,EF), V(EB,59,59,B2), V(C9,47,47,8E), V(0B,F0,F0,FB), \ + V(EC,AD,AD,41), V(67,D4,D4,B3), V(FD,A2,A2,5F), V(EA,AF,AF,45), \ + V(BF,9C,9C,23), V(F7,A4,A4,53), V(96,72,72,E4), V(5B,C0,C0,9B), \ + V(C2,B7,B7,75), V(1C,FD,FD,E1), V(AE,93,93,3D), V(6A,26,26,4C), \ + V(5A,36,36,6C), V(41,3F,3F,7E), V(02,F7,F7,F5), V(4F,CC,CC,83), \ + V(5C,34,34,68), V(F4,A5,A5,51), V(34,E5,E5,D1), V(08,F1,F1,F9), \ + V(93,71,71,E2), V(73,D8,D8,AB), V(53,31,31,62), V(3F,15,15,2A), \ + V(0C,04,04,08), V(52,C7,C7,95), V(65,23,23,46), V(5E,C3,C3,9D), \ + V(28,18,18,30), V(A1,96,96,37), V(0F,05,05,0A), V(B5,9A,9A,2F), \ + V(09,07,07,0E), V(36,12,12,24), V(9B,80,80,1B), V(3D,E2,E2,DF), \ + V(26,EB,EB,CD), V(69,27,27,4E), V(CD,B2,B2,7F), V(9F,75,75,EA), \ + V(1B,09,09,12), V(9E,83,83,1D), V(74,2C,2C,58), V(2E,1A,1A,34), \ + V(2D,1B,1B,36), V(B2,6E,6E,DC), V(EE,5A,5A,B4), V(FB,A0,A0,5B), \ + V(F6,52,52,A4), V(4D,3B,3B,76), V(61,D6,D6,B7), V(CE,B3,B3,7D), \ + V(7B,29,29,52), V(3E,E3,E3,DD), V(71,2F,2F,5E), V(97,84,84,13), \ + V(F5,53,53,A6), V(68,D1,D1,B9), V(00,00,00,00), V(2C,ED,ED,C1), \ + V(60,20,20,40), V(1F,FC,FC,E3), V(C8,B1,B1,79), V(ED,5B,5B,B6), \ + V(BE,6A,6A,D4), V(46,CB,CB,8D), V(D9,BE,BE,67), V(4B,39,39,72), \ + V(DE,4A,4A,94), V(D4,4C,4C,98), V(E8,58,58,B0), V(4A,CF,CF,85), \ + V(6B,D0,D0,BB), V(2A,EF,EF,C5), V(E5,AA,AA,4F), V(16,FB,FB,ED), \ + V(C5,43,43,86), V(D7,4D,4D,9A), V(55,33,33,66), V(94,85,85,11), \ + V(CF,45,45,8A), V(10,F9,F9,E9), V(06,02,02,04), V(81,7F,7F,FE), \ + V(F0,50,50,A0), V(44,3C,3C,78), V(BA,9F,9F,25), V(E3,A8,A8,4B), \ + V(F3,51,51,A2), V(FE,A3,A3,5D), V(C0,40,40,80), V(8A,8F,8F,05), \ + V(AD,92,92,3F), V(BC,9D,9D,21), V(48,38,38,70), V(04,F5,F5,F1), \ + V(DF,BC,BC,63), V(C1,B6,B6,77), V(75,DA,DA,AF), V(63,21,21,42), \ + V(30,10,10,20), V(1A,FF,FF,E5), V(0E,F3,F3,FD), V(6D,D2,D2,BF), \ + V(4C,CD,CD,81), V(14,0C,0C,18), V(35,13,13,26), V(2F,EC,EC,C3), \ + V(E1,5F,5F,BE), V(A2,97,97,35), V(CC,44,44,88), V(39,17,17,2E), \ + V(57,C4,C4,93), V(F2,A7,A7,55), V(82,7E,7E,FC), V(47,3D,3D,7A), \ + V(AC,64,64,C8), V(E7,5D,5D,BA), V(2B,19,19,32), V(95,73,73,E6), \ + V(A0,60,60,C0), V(98,81,81,19), V(D1,4F,4F,9E), V(7F,DC,DC,A3), \ + V(66,22,22,44), V(7E,2A,2A,54), V(AB,90,90,3B), V(83,88,88,0B), \ + V(CA,46,46,8C), V(29,EE,EE,C7), V(D3,B8,B8,6B), V(3C,14,14,28), \ + V(79,DE,DE,A7), V(E2,5E,5E,BC), V(1D,0B,0B,16), V(76,DB,DB,AD), \ + V(3B,E0,E0,DB), V(56,32,32,64), V(4E,3A,3A,74), V(1E,0A,0A,14), \ + V(DB,49,49,92), V(0A,06,06,0C), V(6C,24,24,48), V(E4,5C,5C,B8), \ + V(5D,C2,C2,9F), V(6E,D3,D3,BD), V(EF,AC,AC,43), V(A6,62,62,C4), \ + V(A8,91,91,39), V(A4,95,95,31), V(37,E4,E4,D3), V(8B,79,79,F2), \ + V(32,E7,E7,D5), V(43,C8,C8,8B), V(59,37,37,6E), V(B7,6D,6D,DA), \ + V(8C,8D,8D,01), V(64,D5,D5,B1), V(D2,4E,4E,9C), V(E0,A9,A9,49), \ + V(B4,6C,6C,D8), V(FA,56,56,AC), V(07,F4,F4,F3), V(25,EA,EA,CF), \ + V(AF,65,65,CA), V(8E,7A,7A,F4), V(E9,AE,AE,47), V(18,08,08,10), \ + V(D5,BA,BA,6F), V(88,78,78,F0), V(6F,25,25,4A), V(72,2E,2E,5C), \ + V(24,1C,1C,38), V(F1,A6,A6,57), V(C7,B4,B4,73), V(51,C6,C6,97), \ + V(23,E8,E8,CB), V(7C,DD,DD,A1), V(9C,74,74,E8), V(21,1F,1F,3E), \ + V(DD,4B,4B,96), V(DC,BD,BD,61), V(86,8B,8B,0D), V(85,8A,8A,0F), \ + V(90,70,70,E0), V(42,3E,3E,7C), V(C4,B5,B5,71), V(AA,66,66,CC), \ + V(D8,48,48,90), V(05,03,03,06), V(01,F6,F6,F7), V(12,0E,0E,1C), \ + V(A3,61,61,C2), V(5F,35,35,6A), V(F9,57,57,AE), V(D0,B9,B9,69), \ + V(91,86,86,17), V(58,C1,C1,99), V(27,1D,1D,3A), V(B9,9E,9E,27), \ + V(38,E1,E1,D9), V(13,F8,F8,EB), V(B3,98,98,2B), V(33,11,11,22), \ + V(BB,69,69,D2), V(70,D9,D9,A9), V(89,8E,8E,07), V(A7,94,94,33), \ + V(B6,9B,9B,2D), V(22,1E,1E,3C), V(92,87,87,15), V(20,E9,E9,C9), \ + V(49,CE,CE,87), V(FF,55,55,AA), V(78,28,28,50), V(7A,DF,DF,A5), \ + V(8F,8C,8C,03), V(F8,A1,A1,59), V(80,89,89,09), V(17,0D,0D,1A), \ + V(DA,BF,BF,65), V(31,E6,E6,D7), V(C6,42,42,84), V(B8,68,68,D0), \ + V(C3,41,41,82), V(B0,99,99,29), V(77,2D,2D,5A), V(11,0F,0F,1E), \ + V(CB,B0,B0,7B), V(FC,54,54,A8), V(D6,BB,BB,6D), V(3A,16,16,2C) + +#define V(a,b,c,d) 0x##a##b##c##d +const uint32_t FT0[256] __attribute__((weak,section(".sys.0"))) = { FT }; +#undef V + +#define V(a,b,c,d) 0x##b##c##d##a +const uint32_t FT1[256] __attribute__((weak,section(".sys.1"))) = { FT }; +#undef V + +#define V(a,b,c,d) 0x##c##d##a##b +const uint32_t FT2[256] __attribute__((weak,section(".sys.2"))) = { FT }; +#undef V + +#define V(a,b,c,d) 0x##d##a##b##c +static const uint32_t FT3[256] = { FT }; +#undef V + +#undef FT + +/* + * Reverse S-box + */ +static const unsigned char RSb[256] = +{ + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, + 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, + 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, + 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, + 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, + 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, + 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, + 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, + 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, + 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, + 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, + 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, + 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, + 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, + 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, + 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, + 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, + 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, + 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, + 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, + 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, + 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, + 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, + 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, + 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, + 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D +}; + +/* + * Reverse tables + */ +#define RT \ +\ + V(50,A7,F4,51), V(53,65,41,7E), V(C3,A4,17,1A), V(96,5E,27,3A), \ + V(CB,6B,AB,3B), V(F1,45,9D,1F), V(AB,58,FA,AC), V(93,03,E3,4B), \ + V(55,FA,30,20), V(F6,6D,76,AD), V(91,76,CC,88), V(25,4C,02,F5), \ + V(FC,D7,E5,4F), V(D7,CB,2A,C5), V(80,44,35,26), V(8F,A3,62,B5), \ + V(49,5A,B1,DE), V(67,1B,BA,25), V(98,0E,EA,45), V(E1,C0,FE,5D), \ + V(02,75,2F,C3), V(12,F0,4C,81), V(A3,97,46,8D), V(C6,F9,D3,6B), \ + V(E7,5F,8F,03), V(95,9C,92,15), V(EB,7A,6D,BF), V(DA,59,52,95), \ + V(2D,83,BE,D4), V(D3,21,74,58), V(29,69,E0,49), V(44,C8,C9,8E), \ + V(6A,89,C2,75), V(78,79,8E,F4), V(6B,3E,58,99), V(DD,71,B9,27), \ + V(B6,4F,E1,BE), V(17,AD,88,F0), V(66,AC,20,C9), V(B4,3A,CE,7D), \ + V(18,4A,DF,63), V(82,31,1A,E5), V(60,33,51,97), V(45,7F,53,62), \ + V(E0,77,64,B1), V(84,AE,6B,BB), V(1C,A0,81,FE), V(94,2B,08,F9), \ + V(58,68,48,70), V(19,FD,45,8F), V(87,6C,DE,94), V(B7,F8,7B,52), \ + V(23,D3,73,AB), V(E2,02,4B,72), V(57,8F,1F,E3), V(2A,AB,55,66), \ + V(07,28,EB,B2), V(03,C2,B5,2F), V(9A,7B,C5,86), V(A5,08,37,D3), \ + V(F2,87,28,30), V(B2,A5,BF,23), V(BA,6A,03,02), V(5C,82,16,ED), \ + V(2B,1C,CF,8A), V(92,B4,79,A7), V(F0,F2,07,F3), V(A1,E2,69,4E), \ + V(CD,F4,DA,65), V(D5,BE,05,06), V(1F,62,34,D1), V(8A,FE,A6,C4), \ + V(9D,53,2E,34), V(A0,55,F3,A2), V(32,E1,8A,05), V(75,EB,F6,A4), \ + V(39,EC,83,0B), V(AA,EF,60,40), V(06,9F,71,5E), V(51,10,6E,BD), \ + V(F9,8A,21,3E), V(3D,06,DD,96), V(AE,05,3E,DD), V(46,BD,E6,4D), \ + V(B5,8D,54,91), V(05,5D,C4,71), V(6F,D4,06,04), V(FF,15,50,60), \ + V(24,FB,98,19), V(97,E9,BD,D6), V(CC,43,40,89), V(77,9E,D9,67), \ + V(BD,42,E8,B0), V(88,8B,89,07), V(38,5B,19,E7), V(DB,EE,C8,79), \ + V(47,0A,7C,A1), V(E9,0F,42,7C), V(C9,1E,84,F8), V(00,00,00,00), \ + V(83,86,80,09), V(48,ED,2B,32), V(AC,70,11,1E), V(4E,72,5A,6C), \ + V(FB,FF,0E,FD), V(56,38,85,0F), V(1E,D5,AE,3D), V(27,39,2D,36), \ + V(64,D9,0F,0A), V(21,A6,5C,68), V(D1,54,5B,9B), V(3A,2E,36,24), \ + V(B1,67,0A,0C), V(0F,E7,57,93), V(D2,96,EE,B4), V(9E,91,9B,1B), \ + V(4F,C5,C0,80), V(A2,20,DC,61), V(69,4B,77,5A), V(16,1A,12,1C), \ + V(0A,BA,93,E2), V(E5,2A,A0,C0), V(43,E0,22,3C), V(1D,17,1B,12), \ + V(0B,0D,09,0E), V(AD,C7,8B,F2), V(B9,A8,B6,2D), V(C8,A9,1E,14), \ + V(85,19,F1,57), V(4C,07,75,AF), V(BB,DD,99,EE), V(FD,60,7F,A3), \ + V(9F,26,01,F7), V(BC,F5,72,5C), V(C5,3B,66,44), V(34,7E,FB,5B), \ + V(76,29,43,8B), V(DC,C6,23,CB), V(68,FC,ED,B6), V(63,F1,E4,B8), \ + V(CA,DC,31,D7), V(10,85,63,42), V(40,22,97,13), V(20,11,C6,84), \ + V(7D,24,4A,85), V(F8,3D,BB,D2), V(11,32,F9,AE), V(6D,A1,29,C7), \ + V(4B,2F,9E,1D), V(F3,30,B2,DC), V(EC,52,86,0D), V(D0,E3,C1,77), \ + V(6C,16,B3,2B), V(99,B9,70,A9), V(FA,48,94,11), V(22,64,E9,47), \ + V(C4,8C,FC,A8), V(1A,3F,F0,A0), V(D8,2C,7D,56), V(EF,90,33,22), \ + V(C7,4E,49,87), V(C1,D1,38,D9), V(FE,A2,CA,8C), V(36,0B,D4,98), \ + V(CF,81,F5,A6), V(28,DE,7A,A5), V(26,8E,B7,DA), V(A4,BF,AD,3F), \ + V(E4,9D,3A,2C), V(0D,92,78,50), V(9B,CC,5F,6A), V(62,46,7E,54), \ + V(C2,13,8D,F6), V(E8,B8,D8,90), V(5E,F7,39,2E), V(F5,AF,C3,82), \ + V(BE,80,5D,9F), V(7C,93,D0,69), V(A9,2D,D5,6F), V(B3,12,25,CF), \ + V(3B,99,AC,C8), V(A7,7D,18,10), V(6E,63,9C,E8), V(7B,BB,3B,DB), \ + V(09,78,26,CD), V(F4,18,59,6E), V(01,B7,9A,EC), V(A8,9A,4F,83), \ + V(65,6E,95,E6), V(7E,E6,FF,AA), V(08,CF,BC,21), V(E6,E8,15,EF), \ + V(D9,9B,E7,BA), V(CE,36,6F,4A), V(D4,09,9F,EA), V(D6,7C,B0,29), \ + V(AF,B2,A4,31), V(31,23,3F,2A), V(30,94,A5,C6), V(C0,66,A2,35), \ + V(37,BC,4E,74), V(A6,CA,82,FC), V(B0,D0,90,E0), V(15,D8,A7,33), \ + V(4A,98,04,F1), V(F7,DA,EC,41), V(0E,50,CD,7F), V(2F,F6,91,17), \ + V(8D,D6,4D,76), V(4D,B0,EF,43), V(54,4D,AA,CC), V(DF,04,96,E4), \ + V(E3,B5,D1,9E), V(1B,88,6A,4C), V(B8,1F,2C,C1), V(7F,51,65,46), \ + V(04,EA,5E,9D), V(5D,35,8C,01), V(73,74,87,FA), V(2E,41,0B,FB), \ + V(5A,1D,67,B3), V(52,D2,DB,92), V(33,56,10,E9), V(13,47,D6,6D), \ + V(8C,61,D7,9A), V(7A,0C,A1,37), V(8E,14,F8,59), V(89,3C,13,EB), \ + V(EE,27,A9,CE), V(35,C9,61,B7), V(ED,E5,1C,E1), V(3C,B1,47,7A), \ + V(59,DF,D2,9C), V(3F,73,F2,55), V(79,CE,14,18), V(BF,37,C7,73), \ + V(EA,CD,F7,53), V(5B,AA,FD,5F), V(14,6F,3D,DF), V(86,DB,44,78), \ + V(81,F3,AF,CA), V(3E,C4,68,B9), V(2C,34,24,38), V(5F,40,A3,C2), \ + V(72,C3,1D,16), V(0C,25,E2,BC), V(8B,49,3C,28), V(41,95,0D,FF), \ + V(71,01,A8,39), V(DE,B3,0C,08), V(9C,E4,B4,D8), V(90,C1,56,64), \ + V(61,84,CB,7B), V(70,B6,32,D5), V(74,5C,6C,48), V(42,57,B8,D0) + +#define V(a,b,c,d) 0x##a##b##c##d +static const uint32_t RT0[256] = { RT }; +#undef V + +#define V(a,b,c,d) 0x##b##c##d##a +static const uint32_t RT1[256] = { RT }; +#undef V + +#define V(a,b,c,d) 0x##c##d##a##b +static const uint32_t RT2[256] = { RT }; +#undef V + +#define V(a,b,c,d) 0x##d##a##b##c +static const uint32_t RT3[256] = { RT }; +#undef V + +#undef RT + +/* + * Round constants + */ +static const uint32_t RCON[10] = +{ + 0x00000001, 0x00000002, 0x00000004, 0x00000008, + 0x00000010, 0x00000020, 0x00000040, 0x00000080, + 0x0000001B, 0x00000036 +}; + +#else + +/* + * Forward S-box & tables + */ +static unsigned char FSb[256]; +static uint32_t FT0[256]; +static uint32_t FT1[256]; +static uint32_t FT2[256]; +static uint32_t FT3[256]; + +/* + * Reverse S-box & tables + */ +static unsigned char RSb[256]; +static uint32_t RT0[256]; +static uint32_t RT1[256]; +static uint32_t RT2[256]; +static uint32_t RT3[256]; + +/* + * Round constants + */ +static uint32_t RCON[10]; + +/* + * Tables generation code + */ +#define ROTL8(x) ( ( x << 8 ) & 0xFFFFFFFF ) | ( x >> 24 ) +#define XTIME(x) ( ( x << 1 ) ^ ( ( x & 0x80 ) ? 0x1B : 0x00 ) ) +#define MUL(x,y) ( ( x && y ) ? pow[(log[x]+log[y]) % 255] : 0 ) + +static int aes_init_done = 0; + +static void aes_gen_tables( void ) +{ + int i, x, y, z; + int pow[256]; + int log[256]; + + /* + * compute pow and log tables over GF(2^8) + */ + for( i = 0, x = 1; i < 256; i++ ) + { + pow[i] = x; + log[x] = i; + x = ( x ^ XTIME( x ) ) & 0xFF; + } + + /* + * calculate the round constants + */ + for( i = 0, x = 1; i < 10; i++ ) + { + RCON[i] = (uint32_t) x; + x = XTIME( x ) & 0xFF; + } + + /* + * generate the forward and reverse S-boxes + */ + FSb[0x00] = 0x63; + RSb[0x63] = 0x00; + + for( i = 1; i < 256; i++ ) + { + x = pow[255 - log[i]]; + + y = x; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y ^ 0x63; + + FSb[i] = (unsigned char) x; + RSb[x] = (unsigned char) i; + } + + /* + * generate the forward and reverse tables + */ + for( i = 0; i < 256; i++ ) + { + x = FSb[i]; + y = XTIME( x ) & 0xFF; + z = ( y ^ x ) & 0xFF; + + FT0[i] = ( (uint32_t) y ) ^ + ( (uint32_t) x << 8 ) ^ + ( (uint32_t) x << 16 ) ^ + ( (uint32_t) z << 24 ); + + FT1[i] = ROTL8( FT0[i] ); + FT2[i] = ROTL8( FT1[i] ); + FT3[i] = ROTL8( FT2[i] ); + + x = RSb[i]; + + RT0[i] = ( (uint32_t) MUL( 0x0E, x ) ) ^ + ( (uint32_t) MUL( 0x09, x ) << 8 ) ^ + ( (uint32_t) MUL( 0x0D, x ) << 16 ) ^ + ( (uint32_t) MUL( 0x0B, x ) << 24 ); + + RT1[i] = ROTL8( RT0[i] ); + RT2[i] = ROTL8( RT1[i] ); + RT3[i] = ROTL8( RT2[i] ); + } +} + +#endif + +/* + * AES key schedule (encryption) + */ +int aes_setkey_enc( aes_context *ctx, const unsigned char *key, unsigned int keysize ) +{ + unsigned int i; + uint32_t *RK; + +#if !defined(POLARSSL_AES_ROM_TABLES) + if( aes_init_done == 0 ) + { + aes_gen_tables(); + aes_init_done = 1; + + } +#endif + + switch( keysize ) + { + case 128: ctx->nr = 10; break; + case 192: ctx->nr = 12; break; + case 256: ctx->nr = 14; break; + default : return( POLARSSL_ERR_AES_INVALID_KEY_LENGTH ); + } + +#if defined(POLARSSL_PADLOCK_C) && defined(PADLOCK_ALIGN16) + if( aes_padlock_ace == -1 ) + aes_padlock_ace = padlock_supports( PADLOCK_ACE ); + + if( aes_padlock_ace ) + ctx->rk = RK = PADLOCK_ALIGN16( ctx->buf ); + else +#endif + ctx->rk = RK = ctx->buf; + + for( i = 0; i < (keysize >> 5); i++ ) + { + GET_UINT32_LE( RK[i], key, i << 2 ); + } + + switch( ctx->nr ) + { + case 10: + + for( i = 0; i < 10; i++, RK += 4 ) + { + RK[4] = RK[0] ^ RCON[i] ^ + ( (uint32_t) FSb[ ( RK[3] >> 8 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( RK[3] >> 16 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( RK[3] >> 24 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( RK[3] ) & 0xFF ] << 24 ); + + RK[5] = RK[1] ^ RK[4]; + RK[6] = RK[2] ^ RK[5]; + RK[7] = RK[3] ^ RK[6]; + } + break; + + case 12: + + for( i = 0; i < 8; i++, RK += 6 ) + { + RK[6] = RK[0] ^ RCON[i] ^ + ( (uint32_t) FSb[ ( RK[5] >> 8 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( RK[5] >> 16 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( RK[5] >> 24 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( RK[5] ) & 0xFF ] << 24 ); + + RK[7] = RK[1] ^ RK[6]; + RK[8] = RK[2] ^ RK[7]; + RK[9] = RK[3] ^ RK[8]; + RK[10] = RK[4] ^ RK[9]; + RK[11] = RK[5] ^ RK[10]; + } + break; + + case 14: + + for( i = 0; i < 7; i++, RK += 8 ) + { + RK[8] = RK[0] ^ RCON[i] ^ + ( (uint32_t) FSb[ ( RK[7] >> 8 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( RK[7] >> 16 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( RK[7] >> 24 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( RK[7] ) & 0xFF ] << 24 ); + + RK[9] = RK[1] ^ RK[8]; + RK[10] = RK[2] ^ RK[9]; + RK[11] = RK[3] ^ RK[10]; + + RK[12] = RK[4] ^ + ( (uint32_t) FSb[ ( RK[11] ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( RK[11] >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( RK[11] >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( RK[11] >> 24 ) & 0xFF ] << 24 ); + + RK[13] = RK[5] ^ RK[12]; + RK[14] = RK[6] ^ RK[13]; + RK[15] = RK[7] ^ RK[14]; + } + break; + + default: + + break; + } + + return( 0 ); +} + +/* + * AES key schedule (decryption) + */ +int aes_setkey_dec( aes_context *ctx, const unsigned char *key, unsigned int keysize ) +{ + int i, j; + aes_context cty; + uint32_t *RK; + uint32_t *SK; + int ret; + + switch( keysize ) + { + case 128: ctx->nr = 10; break; + case 192: ctx->nr = 12; break; + case 256: ctx->nr = 14; break; + default : return( POLARSSL_ERR_AES_INVALID_KEY_LENGTH ); + } + +#if defined(POLARSSL_PADLOCK_C) && defined(PADLOCK_ALIGN16) + if( aes_padlock_ace == -1 ) + aes_padlock_ace = padlock_supports( PADLOCK_ACE ); + + if( aes_padlock_ace ) + ctx->rk = RK = PADLOCK_ALIGN16( ctx->buf ); + else +#endif + ctx->rk = RK = ctx->buf; + + ret = aes_setkey_enc( &cty, key, keysize ); + if( ret != 0 ) + return( ret ); + + SK = cty.rk + cty.nr * 4; + + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + + for( i = ctx->nr - 1, SK -= 8; i > 0; i--, SK -= 8 ) + { + for( j = 0; j < 4; j++, SK++ ) + { + *RK++ = RT0[ FSb[ ( *SK ) & 0xFF ] ] ^ + RT1[ FSb[ ( *SK >> 8 ) & 0xFF ] ] ^ + RT2[ FSb[ ( *SK >> 16 ) & 0xFF ] ] ^ + RT3[ FSb[ ( *SK >> 24 ) & 0xFF ] ]; + } + } + + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + + memset( &cty, 0, sizeof( aes_context ) ); + + return( 0 ); +} + +#define AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ +{ \ + X0 = *RK++ ^ FT0[ ( Y0 ) & 0xFF ] ^ \ + FT1[ ( Y1 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y2 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y3 >> 24 ) & 0xFF ]; \ + \ + X1 = *RK++ ^ FT0[ ( Y1 ) & 0xFF ] ^ \ + FT1[ ( Y2 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y3 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y0 >> 24 ) & 0xFF ]; \ + \ + X2 = *RK++ ^ FT0[ ( Y2 ) & 0xFF ] ^ \ + FT1[ ( Y3 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y0 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y1 >> 24 ) & 0xFF ]; \ + \ + X3 = *RK++ ^ FT0[ ( Y3 ) & 0xFF ] ^ \ + FT1[ ( Y0 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y1 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y2 >> 24 ) & 0xFF ]; \ +} + +#define AES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ +{ \ + X0 = *RK++ ^ RT0[ ( Y0 ) & 0xFF ] ^ \ + RT1[ ( Y3 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y2 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y1 >> 24 ) & 0xFF ]; \ + \ + X1 = *RK++ ^ RT0[ ( Y1 ) & 0xFF ] ^ \ + RT1[ ( Y0 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y3 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y2 >> 24 ) & 0xFF ]; \ + \ + X2 = *RK++ ^ RT0[ ( Y2 ) & 0xFF ] ^ \ + RT1[ ( Y1 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y0 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y3 >> 24 ) & 0xFF ]; \ + \ + X3 = *RK++ ^ RT0[ ( Y3 ) & 0xFF ] ^ \ + RT1[ ( Y2 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y1 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y0 >> 24 ) & 0xFF ]; \ +} + +/* + * AES-ECB block encryption/decryption + */ +int aes_crypt_ecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ) +{ + int i; + uint32_t *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3; + +#if defined(POLARSSL_PADLOCK_C) && defined(POLARSSL_HAVE_X86) + if( aes_padlock_ace ) + { + if( padlock_xcryptecb( ctx, mode, input, output ) == 0 ) + return( 0 ); + + // If padlock data misaligned, we just fall back to + // unaccelerated mode + // + } +#endif + + RK = ctx->rk; + + GET_UINT32_LE( X0, input, 0 ); X0 ^= *RK++; + GET_UINT32_LE( X1, input, 4 ); X1 ^= *RK++; + GET_UINT32_LE( X2, input, 8 ); X2 ^= *RK++; + GET_UINT32_LE( X3, input, 12 ); X3 ^= *RK++; + + if( mode == AES_DECRYPT ) + { + for( i = (ctx->nr >> 1) - 1; i > 0; i-- ) + { + AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); + } + + AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + + X0 = *RK++ ^ \ + ( (uint32_t) RSb[ ( Y0 ) & 0xFF ] ) ^ + ( (uint32_t) RSb[ ( Y3 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) RSb[ ( Y2 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) RSb[ ( Y1 >> 24 ) & 0xFF ] << 24 ); + + X1 = *RK++ ^ \ + ( (uint32_t) RSb[ ( Y1 ) & 0xFF ] ) ^ + ( (uint32_t) RSb[ ( Y0 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) RSb[ ( Y3 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) RSb[ ( Y2 >> 24 ) & 0xFF ] << 24 ); + + X2 = *RK++ ^ \ + ( (uint32_t) RSb[ ( Y2 ) & 0xFF ] ) ^ + ( (uint32_t) RSb[ ( Y1 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) RSb[ ( Y0 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) RSb[ ( Y3 >> 24 ) & 0xFF ] << 24 ); + + X3 = *RK++ ^ \ + ( (uint32_t) RSb[ ( Y3 ) & 0xFF ] ) ^ + ( (uint32_t) RSb[ ( Y2 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) RSb[ ( Y1 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) RSb[ ( Y0 >> 24 ) & 0xFF ] << 24 ); + } + else /* AES_ENCRYPT */ + { + for( i = (ctx->nr >> 1) - 1; i > 0; i-- ) + { + AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); + } + + AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + + X0 = *RK++ ^ \ + ( (uint32_t) FSb[ ( Y0 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( Y1 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( Y2 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( Y3 >> 24 ) & 0xFF ] << 24 ); + + X1 = *RK++ ^ \ + ( (uint32_t) FSb[ ( Y1 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( Y2 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( Y3 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( Y0 >> 24 ) & 0xFF ] << 24 ); + + X2 = *RK++ ^ \ + ( (uint32_t) FSb[ ( Y2 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( Y3 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( Y0 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( Y1 >> 24 ) & 0xFF ] << 24 ); + + X3 = *RK++ ^ \ + ( (uint32_t) FSb[ ( Y3 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( Y0 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( Y1 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( Y2 >> 24 ) & 0xFF ] << 24 ); + } + + PUT_UINT32_LE( X0, output, 0 ); + PUT_UINT32_LE( X1, output, 4 ); + PUT_UINT32_LE( X2, output, 8 ); + PUT_UINT32_LE( X3, output, 12 ); + + return( 0 ); +} + +/* + * AES-CBC buffer encryption/decryption + */ +int aes_crypt_cbc( aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + int i; + unsigned char temp[16]; + + if( length % 16 ) + return( POLARSSL_ERR_AES_INVALID_INPUT_LENGTH ); + +#if defined(POLARSSL_PADLOCK_C) && defined(POLARSSL_HAVE_X86) + if( aes_padlock_ace ) + { + if( padlock_xcryptcbc( ctx, mode, length, iv, input, output ) == 0 ) + return( 0 ); + + // If padlock data misaligned, we just fall back to + // unaccelerated mode + // + } +#endif + + if( mode == AES_DECRYPT ) + { + while( length > 0 ) + { + memcpy( temp, input, 16 ); + aes_crypt_ecb( ctx, mode, input, output ); + + for( i = 0; i < 16; i++ ) + output[i] = (unsigned char)( output[i] ^ iv[i] ); + + memcpy( iv, temp, 16 ); + + input += 16; + output += 16; + length -= 16; + } + } + else + { + while( length > 0 ) + { + for( i = 0; i < 16; i++ ) + output[i] = (unsigned char)( input[i] ^ iv[i] ); + + aes_crypt_ecb( ctx, mode, output, output ); + memcpy( iv, output, 16 ); + + input += 16; + output += 16; + length -= 16; + } + } + + return( 0 ); +} + +#if defined(POLARSSL_CIPHER_MODE_CFB) +/* + * AES-CFB128 buffer encryption/decryption + */ +int aes_crypt_cfb128( aes_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + int c; + size_t n = *iv_off; + + if( mode == AES_DECRYPT ) + { + while( length-- ) + { + if( n == 0 ) + aes_crypt_ecb( ctx, AES_ENCRYPT, iv, iv ); + + c = *input++; + *output++ = (unsigned char)( c ^ iv[n] ); + iv[n] = (unsigned char) c; + + n = (n + 1) & 0x0F; + } + } + else + { + while( length-- ) + { + if( n == 0 ) + aes_crypt_ecb( ctx, AES_ENCRYPT, iv, iv ); + + iv[n] = *output++ = (unsigned char)( iv[n] ^ *input++ ); + + n = (n + 1) & 0x0F; + } + } + + *iv_off = n; + + return( 0 ); +} +#endif /*POLARSSL_CIPHER_MODE_CFB */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) +/* + * AES-CTR buffer encryption/decryption + */ +int aes_crypt_ctr( aes_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output ) +{ + int c, i; + size_t n = *nc_off; + + while( length-- ) + { + if( n == 0 ) { + aes_crypt_ecb( ctx, AES_ENCRYPT, nonce_counter, stream_block ); + + for( i = 16; i > 0; i-- ) + if( ++nonce_counter[i - 1] != 0 ) + break; + } + c = *input++; + *output++ = (unsigned char)( c ^ stream_block[n] ); + + n = (n + 1) & 0x0F; + } + + *nc_off = n; + + return( 0 ); +} +#endif /* POLARSSL_CIPHER_MODE_CTR */ +#endif /* !POLARSSL_AES_ALT */ + +#if defined(POLARSSL_SELF_TEST) + +#include + +/* + * AES test vectors from: + * + * http://csrc.nist.gov/archive/aes/rijndael/rijndael-vals.zip + */ +static const unsigned char aes_test_ecb_dec[3][16] = +{ + { 0x44, 0x41, 0x6A, 0xC2, 0xD1, 0xF5, 0x3C, 0x58, + 0x33, 0x03, 0x91, 0x7E, 0x6B, 0xE9, 0xEB, 0xE0 }, + { 0x48, 0xE3, 0x1E, 0x9E, 0x25, 0x67, 0x18, 0xF2, + 0x92, 0x29, 0x31, 0x9C, 0x19, 0xF1, 0x5B, 0xA4 }, + { 0x05, 0x8C, 0xCF, 0xFD, 0xBB, 0xCB, 0x38, 0x2D, + 0x1F, 0x6F, 0x56, 0x58, 0x5D, 0x8A, 0x4A, 0xDE } +}; + +static const unsigned char aes_test_ecb_enc[3][16] = +{ + { 0xC3, 0x4C, 0x05, 0x2C, 0xC0, 0xDA, 0x8D, 0x73, + 0x45, 0x1A, 0xFE, 0x5F, 0x03, 0xBE, 0x29, 0x7F }, + { 0xF3, 0xF6, 0x75, 0x2A, 0xE8, 0xD7, 0x83, 0x11, + 0x38, 0xF0, 0x41, 0x56, 0x06, 0x31, 0xB1, 0x14 }, + { 0x8B, 0x79, 0xEE, 0xCC, 0x93, 0xA0, 0xEE, 0x5D, + 0xFF, 0x30, 0xB4, 0xEA, 0x21, 0x63, 0x6D, 0xA4 } +}; + +static const unsigned char aes_test_cbc_dec[3][16] = +{ + { 0xFA, 0xCA, 0x37, 0xE0, 0xB0, 0xC8, 0x53, 0x73, + 0xDF, 0x70, 0x6E, 0x73, 0xF7, 0xC9, 0xAF, 0x86 }, + { 0x5D, 0xF6, 0x78, 0xDD, 0x17, 0xBA, 0x4E, 0x75, + 0xB6, 0x17, 0x68, 0xC6, 0xAD, 0xEF, 0x7C, 0x7B }, + { 0x48, 0x04, 0xE1, 0x81, 0x8F, 0xE6, 0x29, 0x75, + 0x19, 0xA3, 0xE8, 0x8C, 0x57, 0x31, 0x04, 0x13 } +}; + +static const unsigned char aes_test_cbc_enc[3][16] = +{ + { 0x8A, 0x05, 0xFC, 0x5E, 0x09, 0x5A, 0xF4, 0x84, + 0x8A, 0x08, 0xD3, 0x28, 0xD3, 0x68, 0x8E, 0x3D }, + { 0x7B, 0xD9, 0x66, 0xD5, 0x3A, 0xD8, 0xC1, 0xBB, + 0x85, 0xD2, 0xAD, 0xFA, 0xE8, 0x7B, 0xB1, 0x04 }, + { 0xFE, 0x3C, 0x53, 0x65, 0x3E, 0x2F, 0x45, 0xB5, + 0x6F, 0xCD, 0x88, 0xB2, 0xCC, 0x89, 0x8F, 0xF0 } +}; + +#if defined(POLARSSL_CIPHER_MODE_CFB) +/* + * AES-CFB128 test vectors from: + * + * http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + */ +static const unsigned char aes_test_cfb128_key[3][32] = +{ + { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }, + { 0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52, + 0xC8, 0x10, 0xF3, 0x2B, 0x80, 0x90, 0x79, 0xE5, + 0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C, 0x6B, 0x7B }, + { 0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, + 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81, + 0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61, 0x08, 0xD7, + 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4 } +}; + +static const unsigned char aes_test_cfb128_iv[16] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F +}; + +static const unsigned char aes_test_cfb128_pt[64] = +{ + 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, + 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A, + 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, + 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51, + 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, + 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF, + 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, + 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 +}; + +static const unsigned char aes_test_cfb128_ct[3][64] = +{ + { 0x3B, 0x3F, 0xD9, 0x2E, 0xB7, 0x2D, 0xAD, 0x20, + 0x33, 0x34, 0x49, 0xF8, 0xE8, 0x3C, 0xFB, 0x4A, + 0xC8, 0xA6, 0x45, 0x37, 0xA0, 0xB3, 0xA9, 0x3F, + 0xCD, 0xE3, 0xCD, 0xAD, 0x9F, 0x1C, 0xE5, 0x8B, + 0x26, 0x75, 0x1F, 0x67, 0xA3, 0xCB, 0xB1, 0x40, + 0xB1, 0x80, 0x8C, 0xF1, 0x87, 0xA4, 0xF4, 0xDF, + 0xC0, 0x4B, 0x05, 0x35, 0x7C, 0x5D, 0x1C, 0x0E, + 0xEA, 0xC4, 0xC6, 0x6F, 0x9F, 0xF7, 0xF2, 0xE6 }, + { 0xCD, 0xC8, 0x0D, 0x6F, 0xDD, 0xF1, 0x8C, 0xAB, + 0x34, 0xC2, 0x59, 0x09, 0xC9, 0x9A, 0x41, 0x74, + 0x67, 0xCE, 0x7F, 0x7F, 0x81, 0x17, 0x36, 0x21, + 0x96, 0x1A, 0x2B, 0x70, 0x17, 0x1D, 0x3D, 0x7A, + 0x2E, 0x1E, 0x8A, 0x1D, 0xD5, 0x9B, 0x88, 0xB1, + 0xC8, 0xE6, 0x0F, 0xED, 0x1E, 0xFA, 0xC4, 0xC9, + 0xC0, 0x5F, 0x9F, 0x9C, 0xA9, 0x83, 0x4F, 0xA0, + 0x42, 0xAE, 0x8F, 0xBA, 0x58, 0x4B, 0x09, 0xFF }, + { 0xDC, 0x7E, 0x84, 0xBF, 0xDA, 0x79, 0x16, 0x4B, + 0x7E, 0xCD, 0x84, 0x86, 0x98, 0x5D, 0x38, 0x60, + 0x39, 0xFF, 0xED, 0x14, 0x3B, 0x28, 0xB1, 0xC8, + 0x32, 0x11, 0x3C, 0x63, 0x31, 0xE5, 0x40, 0x7B, + 0xDF, 0x10, 0x13, 0x24, 0x15, 0xE5, 0x4B, 0x92, + 0xA1, 0x3E, 0xD0, 0xA8, 0x26, 0x7A, 0xE2, 0xF9, + 0x75, 0xA3, 0x85, 0x74, 0x1A, 0xB9, 0xCE, 0xF8, + 0x20, 0x31, 0x62, 0x3D, 0x55, 0xB1, 0xE4, 0x71 } +}; +#endif /* POLARSSL_CIPHER_MODE_CFB */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) +/* + * AES-CTR test vectors from: + * + * http://www.faqs.org/rfcs/rfc3686.html + */ + +static const unsigned char aes_test_ctr_key[3][16] = +{ + { 0xAE, 0x68, 0x52, 0xF8, 0x12, 0x10, 0x67, 0xCC, + 0x4B, 0xF7, 0xA5, 0x76, 0x55, 0x77, 0xF3, 0x9E }, + { 0x7E, 0x24, 0x06, 0x78, 0x17, 0xFA, 0xE0, 0xD7, + 0x43, 0xD6, 0xCE, 0x1F, 0x32, 0x53, 0x91, 0x63 }, + { 0x76, 0x91, 0xBE, 0x03, 0x5E, 0x50, 0x20, 0xA8, + 0xAC, 0x6E, 0x61, 0x85, 0x29, 0xF9, 0xA0, 0xDC } +}; + +static const unsigned char aes_test_ctr_nonce_counter[3][16] = +{ + { 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }, + { 0x00, 0x6C, 0xB6, 0xDB, 0xC0, 0x54, 0x3B, 0x59, + 0xDA, 0x48, 0xD9, 0x0B, 0x00, 0x00, 0x00, 0x01 }, + { 0x00, 0xE0, 0x01, 0x7B, 0x27, 0x77, 0x7F, 0x3F, + 0x4A, 0x17, 0x86, 0xF0, 0x00, 0x00, 0x00, 0x01 } +}; + +static const unsigned char aes_test_ctr_pt[3][48] = +{ + { 0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x62, + 0x6C, 0x6F, 0x63, 0x6B, 0x20, 0x6D, 0x73, 0x67 }, + + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F }, + + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23 } +}; + +static const unsigned char aes_test_ctr_ct[3][48] = +{ + { 0xE4, 0x09, 0x5D, 0x4F, 0xB7, 0xA7, 0xB3, 0x79, + 0x2D, 0x61, 0x75, 0xA3, 0x26, 0x13, 0x11, 0xB8 }, + { 0x51, 0x04, 0xA1, 0x06, 0x16, 0x8A, 0x72, 0xD9, + 0x79, 0x0D, 0x41, 0xEE, 0x8E, 0xDA, 0xD3, 0x88, + 0xEB, 0x2E, 0x1E, 0xFC, 0x46, 0xDA, 0x57, 0xC8, + 0xFC, 0xE6, 0x30, 0xDF, 0x91, 0x41, 0xBE, 0x28 }, + { 0xC1, 0xCF, 0x48, 0xA8, 0x9F, 0x2F, 0xFD, 0xD9, + 0xCF, 0x46, 0x52, 0xE9, 0xEF, 0xDB, 0x72, 0xD7, + 0x45, 0x40, 0xA4, 0x2B, 0xDE, 0x6D, 0x78, 0x36, + 0xD5, 0x9A, 0x5C, 0xEA, 0xAE, 0xF3, 0x10, 0x53, + 0x25, 0xB2, 0x07, 0x2F } +}; + +static const int aes_test_ctr_len[3] = + { 16, 32, 36 }; +#endif /* POLARSSL_CIPHER_MODE_CTR */ + +/* + * Checkup routine + */ +int aes_self_test( int verbose ) +{ + int i, j, u, v; + unsigned char key[32]; + unsigned char buf[64]; + unsigned char prv[16]; + unsigned char iv[16]; +#if defined(POLARSSL_CIPHER_MODE_CTR) || defined(POLARSSL_CIPHER_MODE_CFB) + size_t offset; +#endif +#if defined(POLARSSL_CIPHER_MODE_CTR) + int len; + unsigned char nonce_counter[16]; + unsigned char stream_block[16]; +#endif + aes_context ctx; + + memset( key, 0, 32 ); + + /* + * ECB mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if( verbose != 0 ) + printf( " AES-ECB-%3d (%s): ", 128 + u * 64, + ( v == AES_DECRYPT ) ? "dec" : "enc" ); + + memset( buf, 0, 16 ); + + if( v == AES_DECRYPT ) + { + aes_setkey_dec( &ctx, key, 128 + u * 64 ); + + for( j = 0; j < 10000; j++ ) + aes_crypt_ecb( &ctx, v, buf, buf ); + + if( memcmp( buf, aes_test_ecb_dec[u], 16 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + else + { + aes_setkey_enc( &ctx, key, 128 + u * 64 ); + + for( j = 0; j < 10000; j++ ) + aes_crypt_ecb( &ctx, v, buf, buf ); + + if( memcmp( buf, aes_test_ecb_enc[u], 16 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + + if( verbose != 0 ) + printf( "passed\n" ); + } + + if( verbose != 0 ) + printf( "\n" ); + + /* + * CBC mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if( verbose != 0 ) + printf( " AES-CBC-%3d (%s): ", 128 + u * 64, + ( v == AES_DECRYPT ) ? "dec" : "enc" ); + + memset( iv , 0, 16 ); + memset( prv, 0, 16 ); + memset( buf, 0, 16 ); + + if( v == AES_DECRYPT ) + { + aes_setkey_dec( &ctx, key, 128 + u * 64 ); + + for( j = 0; j < 10000; j++ ) + aes_crypt_cbc( &ctx, v, 16, iv, buf, buf ); + + if( memcmp( buf, aes_test_cbc_dec[u], 16 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + else + { + aes_setkey_enc( &ctx, key, 128 + u * 64 ); + + for( j = 0; j < 10000; j++ ) + { + unsigned char tmp[16]; + + aes_crypt_cbc( &ctx, v, 16, iv, buf, buf ); + + memcpy( tmp, prv, 16 ); + memcpy( prv, buf, 16 ); + memcpy( buf, tmp, 16 ); + } + + if( memcmp( prv, aes_test_cbc_enc[u], 16 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + + if( verbose != 0 ) + printf( "passed\n" ); + } + + if( verbose != 0 ) + printf( "\n" ); + +#if defined(POLARSSL_CIPHER_MODE_CFB) + /* + * CFB128 mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if( verbose != 0 ) + printf( " AES-CFB128-%3d (%s): ", 128 + u * 64, + ( v == AES_DECRYPT ) ? "dec" : "enc" ); + + memcpy( iv, aes_test_cfb128_iv, 16 ); + memcpy( key, aes_test_cfb128_key[u], 16 + u * 8 ); + + offset = 0; + aes_setkey_enc( &ctx, key, 128 + u * 64 ); + + if( v == AES_DECRYPT ) + { + memcpy( buf, aes_test_cfb128_ct[u], 64 ); + aes_crypt_cfb128( &ctx, v, 64, &offset, iv, buf, buf ); + + if( memcmp( buf, aes_test_cfb128_pt, 64 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + else + { + memcpy( buf, aes_test_cfb128_pt, 64 ); + aes_crypt_cfb128( &ctx, v, 64, &offset, iv, buf, buf ); + + if( memcmp( buf, aes_test_cfb128_ct[u], 64 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + + if( verbose != 0 ) + printf( "passed\n" ); + } + + if( verbose != 0 ) + printf( "\n" ); +#endif /* POLARSSL_CIPHER_MODE_CFB */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) + /* + * CTR mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if( verbose != 0 ) + printf( " AES-CTR-128 (%s): ", + ( v == AES_DECRYPT ) ? "dec" : "enc" ); + + memcpy( nonce_counter, aes_test_ctr_nonce_counter[u], 16 ); + memcpy( key, aes_test_ctr_key[u], 16 ); + + offset = 0; + aes_setkey_enc( &ctx, key, 128 ); + + if( v == AES_DECRYPT ) + { + len = aes_test_ctr_len[u]; + memcpy( buf, aes_test_ctr_ct[u], len ); + + aes_crypt_ctr( &ctx, len, &offset, nonce_counter, stream_block, buf, buf ); + + if( memcmp( buf, aes_test_ctr_pt[u], len ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + else + { + len = aes_test_ctr_len[u]; + memcpy( buf, aes_test_ctr_pt[u], len ); + + aes_crypt_ctr( &ctx, len, &offset, nonce_counter, stream_block, buf, buf ); + + if( memcmp( buf, aes_test_ctr_ct[u], len ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + + if( verbose != 0 ) + printf( "passed\n" ); + } + + if( verbose != 0 ) + printf( "\n" ); +#endif /* POLARSSL_CIPHER_MODE_CTR */ + + return( 0 ); +} + +#endif + +#endif diff --git a/gnuk/polarssl/library/bignum.c b/gnuk/polarssl/library/bignum.c new file mode 100644 index 0000000000..0d8fbc9970 --- /dev/null +++ b/gnuk/polarssl/library/bignum.c @@ -0,0 +1,2560 @@ +/* + * Multi-precision integer library + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * This MPI implementation is based on: + * + * http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf + * http://www.stillhq.com/extracted/gnupg-api/mpi/ + * http://math.libtomcrypt.com/files/tommath.pdf + */ + +#include "polarssl/config.h" + +#if defined(POLARSSL_BIGNUM_C) + +#include "polarssl/bignum.h" +#include "polarssl/bn_mul.h" + +#include + +#define ciL (sizeof(t_uint)) /* chars in limb */ +#define biL (ciL << 3) /* bits in limb */ +#define biH (ciL << 2) /* half limb size */ + +/* + * Convert between bits/chars and number of limbs + */ +#define BITS_TO_LIMBS(i) (((i) + biL - 1) / biL) +#define CHARS_TO_LIMBS(i) (((i) + ciL - 1) / ciL) + +/* + * Initialize one MPI + */ +void mpi_init( mpi *X ) +{ + if( X == NULL ) + return; + + X->s = 1; + X->n = 0; + X->p = NULL; +} + +/* + * Unallocate one MPI + */ +void mpi_free( mpi *X ) +{ + if( X == NULL ) + return; + + if( X->p != NULL ) + { + memset( X->p, 0, X->n * ciL ); + free( X->p ); + } + + X->s = 1; + X->n = 0; + X->p = NULL; +} + +/* + * Enlarge to the specified number of limbs + */ +int mpi_grow( mpi *X, size_t nblimbs ) +{ + t_uint *p; + + if( nblimbs > POLARSSL_MPI_MAX_LIMBS ) + return( POLARSSL_ERR_MPI_MALLOC_FAILED ); + + if( X->n < nblimbs ) + { + if( ( p = (t_uint *) malloc( nblimbs * ciL ) ) == NULL ) + return( POLARSSL_ERR_MPI_MALLOC_FAILED ); + + memset( p, 0, nblimbs * ciL ); + + if( X->p != NULL ) + { + memcpy( p, X->p, X->n * ciL ); + memset( X->p, 0, X->n * ciL ); + free( X->p ); + } + + X->n = nblimbs; + X->p = p; + } + + return( 0 ); +} + +/* + * Copy the contents of Y into X + */ +int mpi_copy( mpi *X, const mpi *Y ) +{ + int ret; + size_t i; + + if( X == Y ) + return( 0 ); + + for( i = Y->n - 1; i > 0; i-- ) + if( Y->p[i] != 0 ) + break; + i++; + + X->s = Y->s; + + MPI_CHK( mpi_grow( X, i ) ); + + memset( X->p, 0, X->n * ciL ); + memcpy( X->p, Y->p, i * ciL ); + +cleanup: + + return( ret ); +} + +/* + * Swap the contents of X and Y + */ +void mpi_swap( mpi *X, mpi *Y ) +{ + mpi T; + + memcpy( &T, X, sizeof( mpi ) ); + memcpy( X, Y, sizeof( mpi ) ); + memcpy( Y, &T, sizeof( mpi ) ); +} + +/* + * Set value from integer + */ +int mpi_lset( mpi *X, t_sint z ) +{ + int ret; + + MPI_CHK( mpi_grow( X, 1 ) ); + memset( X->p, 0, X->n * ciL ); + + X->p[0] = ( z < 0 ) ? -z : z; + X->s = ( z < 0 ) ? -1 : 1; + +cleanup: + + return( ret ); +} + +/* + * Get a specific bit + */ +int mpi_get_bit( const mpi *X, size_t pos ) +{ + if( X->n * biL <= pos ) + return( 0 ); + + return ( X->p[pos / biL] >> ( pos % biL ) ) & 0x01; +} + +/* + * Set a bit to a specific value of 0 or 1 + */ +int mpi_set_bit( mpi *X, size_t pos, unsigned char val ) +{ + int ret = 0; + size_t off = pos / biL; + size_t idx = pos % biL; + + if( val != 0 && val != 1 ) + return POLARSSL_ERR_MPI_BAD_INPUT_DATA; + + if( X->n * biL <= pos ) + { + if( val == 0 ) + return ( 0 ); + + MPI_CHK( mpi_grow( X, off + 1 ) ); + } + + X->p[off] = ( X->p[off] & ~( 0x01 << idx ) ) | ( val << idx ); + +cleanup: + + return( ret ); +} + +/* + * Return the number of least significant bits + */ +size_t mpi_lsb( const mpi *X ) +{ + size_t i, j, count = 0; + + for( i = 0; i < X->n; i++ ) + for( j = 0; j < biL; j++, count++ ) + if( ( ( X->p[i] >> j ) & 1 ) != 0 ) + return( count ); + + return( 0 ); +} + +#if !defined(POLARSSL_HAVE_UDBL) +/* + * Count leading zero bits in a given integer + */ +static size_t int_clz( const t_uint x ) +{ + size_t j; + t_uint mask = (t_uint) 1 << (biL - 1); + + for( j = 0; j < biL; j++ ) + { + if( x & mask ) break; + + mask >>= 1; + } + + return j; +} +#endif + +/* + * Return the number of most significant bits + */ +size_t mpi_msb( const mpi *X ) +{ + size_t i, j; + + for( i = X->n - 1; i > 0; i-- ) + if( X->p[i] != 0 ) + break; + + for( j = biL; j > 0; j-- ) + if( ( ( X->p[i] >> ( j - 1 ) ) & 1 ) != 0 ) + break; + + return( ( i * biL ) + j ); +} + +/* + * Return the total size in bytes + */ +size_t mpi_size( const mpi *X ) +{ + return( ( mpi_msb( X ) + 7 ) >> 3 ); +} + +/* + * Convert an ASCII character to digit value + */ +static int mpi_get_digit( t_uint *d, int radix, char c ) +{ + *d = 255; + + if( c >= 0x30 && c <= 0x39 ) *d = c - 0x30; + if( c >= 0x41 && c <= 0x46 ) *d = c - 0x37; + if( c >= 0x61 && c <= 0x66 ) *d = c - 0x57; + + if( *d >= (t_uint) radix ) + return( POLARSSL_ERR_MPI_INVALID_CHARACTER ); + + return( 0 ); +} + +/* + * Import from an ASCII string + */ +int mpi_read_string( mpi *X, int radix, const char *s ) +{ + int ret; + size_t i, j, slen, n; + t_uint d; + mpi T; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &T ); + + slen = strlen( s ); + + if( radix == 16 ) + { + n = BITS_TO_LIMBS( slen << 2 ); + + MPI_CHK( mpi_grow( X, n ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = slen, j = 0; i > 0; i--, j++ ) + { + if( i == 1 && s[i - 1] == '-' ) + { + X->s = -1; + break; + } + + MPI_CHK( mpi_get_digit( &d, radix, s[i - 1] ) ); + X->p[j / (2 * ciL)] |= d << ( (j % (2 * ciL)) << 2 ); + } + } + else + { + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = 0; i < slen; i++ ) + { + if( i == 0 && s[i] == '-' ) + { + X->s = -1; + continue; + } + + MPI_CHK( mpi_get_digit( &d, radix, s[i] ) ); + MPI_CHK( mpi_mul_int( &T, X, radix ) ); + + if( X->s == 1 ) + { + MPI_CHK( mpi_add_int( X, &T, d ) ); + } + else + { + MPI_CHK( mpi_sub_int( X, &T, d ) ); + } + } + } + +cleanup: + + mpi_free( &T ); + + return( ret ); +} + +/* + * Helper to write the digits high-order first + */ +static int mpi_write_hlp( mpi *X, int radix, char **p ) +{ + int ret; + t_uint r; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + MPI_CHK( mpi_mod_int( &r, X, radix ) ); + MPI_CHK( mpi_div_int( X, NULL, X, radix ) ); + + if( mpi_cmp_int( X, 0 ) != 0 ) + MPI_CHK( mpi_write_hlp( X, radix, p ) ); + + if( r < 10 ) + *(*p)++ = (char)( r + 0x30 ); + else + *(*p)++ = (char)( r + 0x37 ); + +cleanup: + + return( ret ); +} + +/* + * Export into an ASCII string + */ +int mpi_write_string( const mpi *X, int radix, char *s, size_t *slen ) +{ + int ret = 0; + size_t n; + char *p; + mpi T; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + n = mpi_msb( X ); + if( radix >= 4 ) n >>= 1; + if( radix >= 16 ) n >>= 1; + n += 3; + + if( *slen < n ) + { + *slen = n; + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + } + + p = s; + mpi_init( &T ); + + if( X->s == -1 ) + *p++ = '-'; + + if( radix == 16 ) + { + int c; + size_t i, j, k; + + for( i = X->n, k = 0; i > 0; i-- ) + { + for( j = ciL; j > 0; j-- ) + { + c = ( X->p[i - 1] >> ( ( j - 1 ) << 3) ) & 0xFF; + + if( c == 0 && k == 0 && ( i + j + 3 ) != 0 ) + continue; + + *(p++) = "0123456789ABCDEF" [c / 16]; + *(p++) = "0123456789ABCDEF" [c % 16]; + k = 1; + } + } + } + else + { + MPI_CHK( mpi_copy( &T, X ) ); + + if( T.s == -1 ) + T.s = 1; + + MPI_CHK( mpi_write_hlp( &T, radix, &p ) ); + } + + *p++ = '\0'; + *slen = p - s; + +cleanup: + + mpi_free( &T ); + + return( ret ); +} + +#if defined(POLARSSL_FS_IO) +/* + * Read X from an opened file + */ +int mpi_read_file( mpi *X, int radix, FILE *fin ) +{ + t_uint d; + size_t slen; + char *p; + /* + * Buffer should have space for (short) label and decimal formatted MPI, + * newline characters and '\0' + */ + char s[ POLARSSL_MPI_RW_BUFFER_SIZE ]; + + memset( s, 0, sizeof( s ) ); + if( fgets( s, sizeof( s ) - 1, fin ) == NULL ) + return( POLARSSL_ERR_MPI_FILE_IO_ERROR ); + + slen = strlen( s ); + if( slen == sizeof( s ) - 2 ) + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + + if( s[slen - 1] == '\n' ) { slen--; s[slen] = '\0'; } + if( s[slen - 1] == '\r' ) { slen--; s[slen] = '\0'; } + + p = s + slen; + while( --p >= s ) + if( mpi_get_digit( &d, radix, *p ) != 0 ) + break; + + return( mpi_read_string( X, radix, p + 1 ) ); +} + +/* + * Write X into an opened file (or stdout if fout == NULL) + */ +int mpi_write_file( const char *p, const mpi *X, int radix, FILE *fout ) +{ + int ret; + size_t n, slen, plen; + /* + * Buffer should have space for (short) label and decimal formatted MPI, + * newline characters and '\0' + */ + char s[ POLARSSL_MPI_RW_BUFFER_SIZE ]; + + n = sizeof( s ); + memset( s, 0, n ); + n -= 2; + + MPI_CHK( mpi_write_string( X, radix, s, (size_t *) &n ) ); + + if( p == NULL ) p = ""; + + plen = strlen( p ); + slen = strlen( s ); + s[slen++] = '\r'; + s[slen++] = '\n'; + + if( fout != NULL ) + { + if( fwrite( p, 1, plen, fout ) != plen || + fwrite( s, 1, slen, fout ) != slen ) + return( POLARSSL_ERR_MPI_FILE_IO_ERROR ); + } + else + printf( "%s%s", p, s ); + +cleanup: + + return( ret ); +} +#endif /* POLARSSL_FS_IO */ + +/* + * Import X from unsigned binary data, big endian + */ +int mpi_read_binary( mpi *X, const unsigned char *buf, size_t buflen ) +{ + int ret; + size_t i, j, n; + + for( n = 0; n < buflen; n++ ) + if( buf[n] != 0 ) + break; + + MPI_CHK( mpi_grow( X, CHARS_TO_LIMBS( buflen - n ) ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = buflen, j = 0; i > n; i--, j++ ) + X->p[j / ciL] |= ((t_uint) buf[i - 1]) << ((j % ciL) << 3); + +cleanup: + + return( ret ); +} + +/* + * Export X into unsigned binary data, big endian + */ +int mpi_write_binary( const mpi *X, unsigned char *buf, size_t buflen ) +{ + size_t i, j, n; + + n = mpi_size( X ); + + if( buflen < n ) + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + + memset( buf, 0, buflen ); + + for( i = buflen - 1, j = 0; n > 0; i--, j++, n-- ) + buf[i] = (unsigned char)( X->p[j / ciL] >> ((j % ciL) << 3) ); + + return( 0 ); +} + +/* + * Left-shift: X <<= count + */ +int mpi_shift_l( mpi *X, size_t count ) +{ + int ret; + size_t i, v0, t1; + t_uint r0 = 0, r1; + + v0 = count / (biL ); + t1 = count & (biL - 1); + + i = mpi_msb( X ) + count; + + if( X->n * biL < i ) + MPI_CHK( mpi_grow( X, BITS_TO_LIMBS( i ) ) ); + + ret = 0; + + /* + * shift by count / limb_size + */ + if( v0 > 0 ) + { + for( i = X->n; i > v0; i-- ) + X->p[i - 1] = X->p[i - v0 - 1]; + + for( ; i > 0; i-- ) + X->p[i - 1] = 0; + } + + /* + * shift by count % limb_size + */ + if( t1 > 0 ) + { + for( i = v0; i < X->n; i++ ) + { + r1 = X->p[i] >> (biL - t1); + X->p[i] <<= t1; + X->p[i] |= r0; + r0 = r1; + } + } + +cleanup: + + return( ret ); +} + +/* + * Right-shift: X >>= count + */ +int mpi_shift_r( mpi *X, size_t count ) +{ + size_t i, v0, v1; + t_uint r0 = 0, r1; + + v0 = count / biL; + v1 = count & (biL - 1); + + if( v0 > X->n || ( v0 == X->n && v1 > 0 ) ) + return mpi_lset( X, 0 ); + + /* + * shift by count / limb_size + */ + if( v0 > 0 ) + { + for( i = 0; i < X->n - v0; i++ ) + X->p[i] = X->p[i + v0]; + + for( ; i < X->n; i++ ) + X->p[i] = 0; + } + + /* + * shift by count % limb_size + */ + if( v1 > 0 ) + { + for( i = X->n; i > 0; i-- ) + { + r1 = X->p[i - 1] << (biL - v1); + X->p[i - 1] >>= v1; + X->p[i - 1] |= r0; + r0 = r1; + } + } + + return( 0 ); +} + +/* + * Compare unsigned values + */ +static int mpi_cmp_abs_limbs (size_t n, const t_uint *p0, const t_uint *p1) +{ + size_t i, j; + + p0 += n; + for( i = n; i > 0; i-- ) + if( *--p0 != 0 ) + break; + + p1 += n; + for( j = n; j > 0; j-- ) + if( *--p1 != 0 ) + break; + + if( i == 0 && j == 0 ) + return( 0 ); + + if( i > j ) return( 1 ); + if( j > i ) return( -1 ); + + for( ; i > 0; i-- ) + { + if( *p0 > *p1 ) return( 1 ); + if( *p0 < *p1 ) return( -1 ); + p0--; p1--; + } + + return( 0 ); +} + +/* + * Compare unsigned values + */ +int mpi_cmp_abs( const mpi *X, const mpi *Y ) +{ + size_t i, j; + + for( i = X->n; i > 0; i-- ) + if( X->p[i - 1] != 0 ) + break; + + for( j = Y->n; j > 0; j-- ) + if( Y->p[j - 1] != 0 ) + break; + + if( i == 0 && j == 0 ) + return( 0 ); + + if( i > j ) return( 1 ); + if( j > i ) return( -1 ); + + for( ; i > 0; i-- ) + { + if( X->p[i - 1] > Y->p[i - 1] ) return( 1 ); + if( X->p[i - 1] < Y->p[i - 1] ) return( -1 ); + } + + return( 0 ); +} + +/* + * Compare signed values + */ +int mpi_cmp_mpi( const mpi *X, const mpi *Y ) +{ + size_t i, j; + + for( i = X->n; i > 0; i-- ) + if( X->p[i - 1] != 0 ) + break; + + for( j = Y->n; j > 0; j-- ) + if( Y->p[j - 1] != 0 ) + break; + + if( i == 0 && j == 0 ) + return( 0 ); + + if( i > j ) return( X->s ); + if( j > i ) return( -Y->s ); + + if( X->s > 0 && Y->s < 0 ) return( 1 ); + if( Y->s > 0 && X->s < 0 ) return( -1 ); + + for( ; i > 0; i-- ) + { + if( X->p[i - 1] > Y->p[i - 1] ) return( X->s ); + if( X->p[i - 1] < Y->p[i - 1] ) return( -X->s ); + } + + return( 0 ); +} + +/* + * Compare signed values + */ +int mpi_cmp_int( const mpi *X, t_sint z ) +{ + mpi Y; + t_uint p[1]; + + *p = ( z < 0 ) ? -z : z; + Y.s = ( z < 0 ) ? -1 : 1; + Y.n = 1; + Y.p = p; + + return( mpi_cmp_mpi( X, &Y ) ); +} + +/* + * Unsigned addition: X = |A| + |B| (HAC 14.7) + */ +int mpi_add_abs( mpi *X, const mpi *A, const mpi *B ) +{ + int ret; + size_t i, j; + t_uint *o, *p, c; + + if( X == B ) + { + const mpi *T = A; A = X; B = T; + } + + if( X != A ) + MPI_CHK( mpi_copy( X, A ) ); + + /* + * X should always be positive as a result of unsigned additions. + */ + X->s = 1; + + for( j = B->n; j > 0; j-- ) + if( B->p[j - 1] != 0 ) + break; + + MPI_CHK( mpi_grow( X, j ) ); + + o = B->p; p = X->p; c = 0; + + for( i = 0; i < j; i++, o++, p++ ) + { + *p += c; c = ( *p < c ); + *p += *o; c += ( *p < *o ); + } + + while( c != 0 ) + { + if( i >= X->n ) + { + MPI_CHK( mpi_grow( X, i + 1 ) ); + p = X->p + i; + } + + *p += c; c = ( *p < c ); i++; p++; + } + +cleanup: + + return( ret ); +} + +/* + * Helper for mpi substraction + */ +static t_uint mpi_sub_hlp( size_t n, const t_uint *s, t_uint *d ) +{ + size_t i; + t_uint c, z; + + for( i = c = 0; i < n; i++, s++, d++ ) + { + z = ( *d < c ); *d -= c; + c = ( *d < *s ) + z; *d -= *s; + } + + return c; +} + +/* + * Unsigned substraction: X = |A| - |B| (HAC 14.9) + */ +int mpi_sub_abs( mpi *X, const mpi *A, const mpi *B ) +{ + mpi TB; + int ret; + size_t n; + t_uint *d; + t_uint c, z; + + if( mpi_cmp_abs( A, B ) < 0 ) + return( POLARSSL_ERR_MPI_NEGATIVE_VALUE ); + + mpi_init( &TB ); + + if( X == B ) + { + MPI_CHK( mpi_copy( &TB, B ) ); + B = &TB; + } + + if( X != A ) + MPI_CHK( mpi_copy( X, A ) ); + + /* + * X should always be positive as a result of unsigned substractions. + */ + X->s = 1; + + ret = 0; + + for( n = B->n; n > 0; n-- ) + if( B->p[n - 1] != 0 ) + break; + + c = mpi_sub_hlp( n, B->p, X->p ); + d = X->p + n; + + while( c != 0 ) + { + z = ( *d < c ); *d -= c; + c = z; d++; + } + +cleanup: + + mpi_free( &TB ); + + return( ret ); +} + +/* + * Signed addition: X = A + B + */ +int mpi_add_mpi( mpi *X, const mpi *A, const mpi *B ) +{ + int ret, s = A->s; + + if( A->s * B->s < 0 ) + { + if( mpi_cmp_abs( A, B ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( X, A, B ) ); + X->s = s; + } + else + { + MPI_CHK( mpi_sub_abs( X, B, A ) ); + X->s = -s; + } + } + else + { + MPI_CHK( mpi_add_abs( X, A, B ) ); + X->s = s; + } + +cleanup: + + return( ret ); +} + +/* + * Signed substraction: X = A - B + */ +int mpi_sub_mpi( mpi *X, const mpi *A, const mpi *B ) +{ + int ret, s = A->s; + + if( A->s * B->s > 0 ) + { + if( mpi_cmp_abs( A, B ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( X, A, B ) ); + X->s = s; + } + else + { + MPI_CHK( mpi_sub_abs( X, B, A ) ); + X->s = -s; + } + } + else + { + MPI_CHK( mpi_add_abs( X, A, B ) ); + X->s = s; + } + +cleanup: + + return( ret ); +} + +/* + * Signed addition: X = A + b + */ +int mpi_add_int( mpi *X, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_add_mpi( X, A, &_B ) ); +} + +/* + * Signed substraction: X = A - b + */ +int mpi_sub_int( mpi *X, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_sub_mpi( X, A, &_B ) ); +} + +/* + * Helper for mpi multiplication + */ +static +#if defined(__APPLE__) && defined(__arm__) +/* + * Apple LLVM version 4.2 (clang-425.0.24) (based on LLVM 3.2svn) + * appears to need this to prevent bad ARM code generation at -O3. + */ +__attribute__ ((noinline)) +#endif +t_uint mpi_mul_hlp( size_t i, const t_uint *s, t_uint *d, t_uint b ) +{ + t_uint c = 0, t = 0; + +#if defined(MULADDC_1024_LOOP) + MULADDC_1024_LOOP + + for( ; i > 0; i-- ) + { + MULADDC_INIT + MULADDC_CORE + MULADDC_STOP + } +#elif defined(MULADDC_HUIT) + for( ; i >= 8; i -= 8 ) + { + MULADDC_INIT + MULADDC_HUIT + MULADDC_STOP + } + + for( ; i > 0; i-- ) + { + MULADDC_INIT + MULADDC_CORE + MULADDC_STOP + } +#else + for( ; i >= 16; i -= 16 ) + { + MULADDC_INIT + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_STOP + } + + for( ; i >= 8; i -= 8 ) + { + MULADDC_INIT + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_STOP + } + + for( ; i > 0; i-- ) + { + MULADDC_INIT + MULADDC_CORE + MULADDC_STOP + } +#endif + + t++; + + *d += c; c = ( *d < c ); + return c; +} + +/* + * Baseline multiplication: X = A * B (HAC 14.12) + */ +int mpi_mul_mpi( mpi *X, const mpi *A, const mpi *B ) +{ + int ret; + size_t i, j, k; + mpi TA, TB; + + mpi_init( &TA ); mpi_init( &TB ); + + if( X == A ) { MPI_CHK( mpi_copy( &TA, A ) ); A = &TA; } + if( X == B ) { MPI_CHK( mpi_copy( &TB, B ) ); B = &TB; } + + for( i = A->n; i > 0; i-- ) + if( A->p[i - 1] != 0 ) + break; + + for( j = B->n; j > 0; j-- ) + if( B->p[j - 1] != 0 ) + break; + + MPI_CHK( mpi_grow( X, i + j ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for(k = 0; k < j; k++ ) + mpi_mul_hlp( i, A->p, X->p + k, B->p[k]); + + X->s = A->s * B->s; + +cleanup: + + mpi_free( &TB ); mpi_free( &TA ); + + return( ret ); +} + +/* + * Baseline multiplication: X = A * b + */ +int mpi_mul_int( mpi *X, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + _B.s = 1; + _B.n = 1; + _B.p = p; + p[0] = b; + + return( mpi_mul_mpi( X, A, &_B ) ); +} + +/* + * Unsigned integer divide - 64bit dividend and 32bit divisor + */ +static t_uint int_div_int(t_uint u1, t_uint u0, t_uint d, t_uint *r) +{ +#if defined(POLARSSL_HAVE_UDBL) + t_udbl dividend, quotient; +#else + const t_uint radix = (t_uint) 1 << biH; + const t_uint uint_halfword_mask = ( (t_uint) 1 << biH ) - 1; + t_uint d0, d1, q0, q1, rAX, r0, quotient; + t_uint u0_msw, u0_lsw; + size_t s; +#endif + + /* + * Check for overflow + */ + if(( 0 == d ) || ( u1 >= d )) + { + if (r != NULL) *r = (~0UL); + + return (~0UL); + } + +#if defined(POLARSSL_HAVE_UDBL) + dividend = (t_udbl) u1 << biL; + dividend |= (t_udbl) u0; + quotient = dividend / d; + if( quotient > ( (t_udbl) 1 << biL ) - 1 ) + quotient = ( (t_udbl) 1 << biL ) - 1; + + if( r != NULL ) + *r = (t_uint)( dividend - (quotient * d ) ); + + return (t_uint) quotient; +#else + + /* + * Algorithm D, Section 4.3.1 - The Art of Computer Programming + * Vol. 2 - Seminumerical Algorithms, Knuth + */ + + /* + * Normalize the divisor, d, and dividend, u0, u1 + */ + s = int_clz( d ); + d = d << s; + + u1 = u1 << s; + u1 |= ( u0 >> ( biL - s ) ) & ( -(t_sint)s >> ( biL - 1 ) ); + u0 = u0 << s; + + d1 = d >> biH; + d0 = d & uint_halfword_mask; + + u0_msw = u0 >> biH; + u0_lsw = u0 & uint_halfword_mask; + + /* + * Find the first quotient and remainder + */ + q1 = u1 / d1; + r0 = u1 - d1 * q1; + + while( q1 >= radix || ( q1 * d0 > radix * r0 + u0_msw ) ) + { + q1 -= 1; + r0 += d1; + + if ( r0 >= radix ) break; + } + + rAX = (u1 * radix) + (u0_msw - q1 * d); + q0 = rAX / d1; + r0 = rAX - q0 * d1; + + while( q0 >= radix || ( q0 * d0 > radix * r0 + u0_lsw ) ) + { + q0 -= 1; + r0 += d1; + + if ( r0 >= radix ) break; + } + + if (r != NULL) + *r = (rAX * radix + u0_lsw - q0 * d) >> s; + + quotient = q1 * radix + q0; + + return quotient; +#endif +} + +/* + * Division by mpi: A = Q * B + R (HAC 14.20) + */ +int mpi_div_mpi( mpi *Q, mpi *R, const mpi *A, const mpi *B ) +{ + int ret; + size_t i, n, t, k; + mpi X, Y, Z, T1, T2; + + if( mpi_cmp_int( B, 0 ) == 0 ) + return( POLARSSL_ERR_MPI_DIVISION_BY_ZERO ); + + mpi_init( &X ); mpi_init( &Y ); mpi_init( &Z ); + mpi_init( &T1 ); mpi_init( &T2 ); + + if( mpi_cmp_abs( A, B ) < 0 ) + { + if( Q != NULL ) MPI_CHK( mpi_lset( Q, 0 ) ); + if( R != NULL ) MPI_CHK( mpi_copy( R, A ) ); + return( 0 ); + } + + MPI_CHK( mpi_copy( &X, A ) ); + MPI_CHK( mpi_copy( &Y, B ) ); + X.s = Y.s = 1; + + MPI_CHK( mpi_grow( &Z, A->n + 2 ) ); + MPI_CHK( mpi_lset( &Z, 0 ) ); + MPI_CHK( mpi_grow( &T1, 2 ) ); + MPI_CHK( mpi_grow( &T2, 3 ) ); + + k = mpi_msb( &Y ) % biL; + if( k < biL - 1 ) + { + k = biL - 1 - k; + MPI_CHK( mpi_shift_l( &X, k ) ); + MPI_CHK( mpi_shift_l( &Y, k ) ); + } + else k = 0; + + n = X.n - 1; + t = Y.n - 1; + MPI_CHK( mpi_shift_l( &Y, biL * (n - t) ) ); + + while( mpi_cmp_mpi( &X, &Y ) >= 0 ) + { + Z.p[n - t]++; + mpi_sub_mpi( &X, &X, &Y ); + } + mpi_shift_r( &Y, biL * (n - t) ); + + for( i = n; i > t ; i-- ) + { + if( X.p[i] >= Y.p[t] ) + Z.p[i - t - 1] = ~0UL; + else + { + Z.p[i - t - 1] = int_div_int( X.p[i], X.p[i-1], Y.p[t], NULL); + } + + Z.p[i - t - 1]++; + do + { + Z.p[i - t - 1]--; + + MPI_CHK( mpi_lset( &T1, 0 ) ); + T1.p[0] = (t < 1) ? 0 : Y.p[t - 1]; + T1.p[1] = Y.p[t]; + MPI_CHK( mpi_mul_int( &T1, &T1, Z.p[i - t - 1] ) ); + + MPI_CHK( mpi_lset( &T2, 0 ) ); + T2.p[0] = (i < 2) ? 0 : X.p[i - 2]; + T2.p[1] = (i < 1) ? 0 : X.p[i - 1]; + T2.p[2] = X.p[i]; + } + while( mpi_cmp_mpi( &T1, &T2 ) > 0 ); + + MPI_CHK( mpi_mul_int( &T1, &Y, Z.p[i - t - 1] ) ); + MPI_CHK( mpi_shift_l( &T1, biL * (i - t - 1) ) ); + MPI_CHK( mpi_sub_mpi( &X, &X, &T1 ) ); + + while( mpi_cmp_int( &X, 0 ) < 0 ) + { + MPI_CHK( mpi_copy( &T1, &Y ) ); + MPI_CHK( mpi_shift_l( &T1, biL * (i - t - 1) ) ); + MPI_CHK( mpi_add_mpi( &X, &X, &T1 ) ); + Z.p[i - t - 1]--; + } + } + + if( Q != NULL ) + { + mpi_copy( Q, &Z ); + Q->s = A->s * B->s; + } + + if( R != NULL ) + { + mpi_shift_r( &X, k ); + X.s = A->s; + mpi_copy( R, &X ); + + if( mpi_cmp_int( R, 0 ) == 0 ) + R->s = 1; + } + +cleanup: + + mpi_free( &X ); mpi_free( &Y ); mpi_free( &Z ); + mpi_free( &T1 ); mpi_free( &T2 ); + + return( ret ); +} + +/* + * Division by int: A = Q * b + R + */ +int mpi_div_int( mpi *Q, mpi *R, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_div_mpi( Q, R, A, &_B ) ); +} + +/* + * Modulo: R = A mod B + */ +int mpi_mod_mpi( mpi *R, const mpi *A, const mpi *B ) +{ + int ret; + + if( mpi_cmp_int( B, 0 ) < 0 ) + return POLARSSL_ERR_MPI_NEGATIVE_VALUE; + + MPI_CHK( mpi_div_mpi( NULL, R, A, B ) ); + + while( mpi_cmp_int( R, 0 ) < 0 ) + MPI_CHK( mpi_add_mpi( R, R, B ) ); + + while( mpi_cmp_mpi( R, B ) >= 0 ) + MPI_CHK( mpi_sub_mpi( R, R, B ) ); + +cleanup: + + return( ret ); +} + +/* + * Modulo: r = A mod b + */ +int mpi_mod_int( t_uint *r, const mpi *A, t_sint b ) +{ + size_t i; + t_uint x, y, z; + + if( b == 0 ) + return( POLARSSL_ERR_MPI_DIVISION_BY_ZERO ); + + if( b < 0 ) + return POLARSSL_ERR_MPI_NEGATIVE_VALUE; + + /* + * handle trivial cases + */ + if( b == 1 ) + { + *r = 0; + return( 0 ); + } + + if( b == 2 ) + { + *r = A->p[0] & 1; + return( 0 ); + } + + /* + * general case + */ + for( i = A->n, y = 0; i > 0; i-- ) + { + x = A->p[i - 1]; + y = ( y << biH ) | ( x >> biH ); + z = y / b; + y -= z * b; + + x <<= biH; + y = ( y << biH ) | ( x >> biH ); + z = y / b; + y -= z * b; + } + + /* + * If A is negative, then the current y represents a negative value. + * Flipping it to the positive side. + */ + if( A->s < 0 && y != 0 ) + y = b - y; + + *r = y; + + return( 0 ); +} + +/* + * Fast Montgomery initialization (thanks to Tom St Denis) + */ +static void mpi_montg_init( t_uint *mm, const mpi *N ) +{ + t_uint x, m0 = N->p[0]; + + x = m0; + x += ( ( m0 + 2 ) & 4 ) << 1; + x *= ( 2 - ( m0 * x ) ); + + if( biL >= 16 ) x *= ( 2 - ( m0 * x ) ); + if( biL >= 32 ) x *= ( 2 - ( m0 * x ) ); + if( biL >= 64 ) x *= ( 2 - ( m0 * x ) ); + + *mm = ~x + 1; +} + +/* + * Montgomery multiplication: A = A * B * R^-1 mod N (HAC 14.36) + * A is placed at the upper half of D. + */ +static void mpi_montmul( size_t n, const t_uint *np, t_uint mm, t_uint *d, + const t_uint *bp ) +{ + size_t i; + t_uint u0, u1, c = 0; + + for( i = 0; i < n; i++ ) + { + /* + * T = (T + u0*B + u1*N) / 2^biL + */ + u0 = d[n]; + d[n] = c; + u1 = ( d[0] + u0 * bp[0] ) * mm; + + mpi_mul_hlp( n, bp, d, u0 ); + c = mpi_mul_hlp( n, np, d, u1 ); + d++; + } + + /* prevent timing attacks */ + if( ((mpi_cmp_abs_limbs ( n, d, np ) >= 0) | c) ) + mpi_sub_hlp( n, np, d ); + else + mpi_sub_hlp( n, d - n, d - n); +} + +/* + * Montgomery reduction: A = A * R^-1 mod N + * A is placed at the upper half of D. + */ +static void mpi_montred( size_t n, const t_uint *np, t_uint mm, t_uint *d ) +{ + size_t i, j; + t_uint u0, u1, c = 0; + + for( i = 0; i < n; i++ ) + { + /* + * T = (T + u0 + u1*N) / 2^biL + */ + u0 = d[n]; + d[n] = c; + u1 = (d[0] + u0) * mm; + + d[0] += u0; + c = (d[0] < u0); + for (j = 1; j < n; j++) + { + d[j] += c; c = ( d[j] < c ); + } + + c = mpi_mul_hlp( n, np, d, u1 ); + d++; + } + + /* prevent timing attacks */ + if( ((mpi_cmp_abs_limbs ( n, d, np ) >= 0) | c) ) + mpi_sub_hlp( n, np, d ); + else + mpi_sub_hlp( n, d - n, d - n); +} + +/* + * Montgomery square: A = A * A * R^-1 mod N + * A is placed at the upper half of D. + * + * n : number of limbs of N + * np: pointer to limbs of bignum N + * mm: m' = -N^(-1) mod b where b = 2^number-of-bit-in-limb + * d (destination): the result [<-- temp -->][<--- A ---->] + * lower part upper part + * n-limb n-limb + */ +static void mpi_montsqr( size_t n, const t_uint *np, t_uint mm, t_uint *d ) +{ +#if defined(POLARSSL_HAVE_ASM) && defined(__arm__) + size_t i; + register t_uint c = 0; + + for (i = 0; i < n; i++) + { + t_uint *wij = &d[i*2]; + t_uint *xj = &d[i+n]; + t_uint x_i; + + x_i = *xj; + *xj++ = c; + +#if defined(__ARM_FEATURE_DSP) + asm (/* (C,R4,R5) := w_i_i + x_i*x_i; w_i_i := R5; */ + "mov %[c], #0\n\t" + "ldr r5, [%[wij]]\n\t" /* R5 := w_i_i; */ + "mov r4, %[c]\n\t" + "umlal r5, r4, %[x_i], %[x_i]\n\t" + "str r5, [%[wij]], #4\n\t" + "cmp %[xj], %[x_max1]\n\t" + "bhi 0f\n\t" + "mov r9, %[c]\n\t" /* R9 := 0, the constant ZERO from here. */ + "beq 1f\n" + "2:\n\t" + "ldmia %[xj]!, { r7, r8 }\n\t" + "ldmia %[wij], { r5, r6 }\n\t" + /* (C,R4,R5) := (C,R4) + w_i_j + 2*x_i*x_j; */ + "umaal r5, r4, %[x_i], r7\n\t" + "umlal r5, %[c], %[x_i], r7\n\t" + "umaal r4, %[c], r9, r9\n\t" + /* (C,R4,R6) := (C,R4) + w_i_j + 2*x_i*x_j; */ + "umaal r6, r4, %[x_i], r8\n\t" + "umlal r6, %[c], %[x_i], r8\n\t" + "umaal r4, %[c], r9, r9\n\t" + /**/ + "stmia %[wij]!, { r5, r6 }\n\t" + "cmp %[xj], %[x_max1]\n\t" + "bcc 2b\n\t" + "bne 0f\n" + "1:\n\t" + /* (C,R4,R5) := (C,R4) + w_i_j + 2*x_i*x_j; */ + "ldr r5, [%[wij]]\n\t" + "ldr r6, [%[xj]], #4\n\t" + "umaal r5, r4, %[x_i], r6\n\t" + "umlal r5, %[c], %[x_i], r6\n\t" + "umaal r4, %[c], r9, r9\n\t" + "str r5, [%[wij]], #4\n" + "0:\n\t" + "ldr r5, [%[wij]]\n\t" + "adds r4, r4, r5\n\t" + "adc %[c], %[c], #0\n\t" + "str r4, [%[wij]]" + : [c] "=&r" (c), [wij] "=r" (wij), [xj] "=r" (xj) + : [x_i] "r" (x_i), [x_max1] "r" (&d[n*2-1]), + "[wij]" (wij), "[xj]" (xj) + : "r4", "r5", "r6", "r7", "r8", "r9", "memory", "cc"); +#else + asm (/* (C,R4,R5) := w_i_i + x_i*x_i; w_i_i := R5; */ + "mov %[c], #0\n\t" + "ldr r5, [%[wij]]\n\t" /* R5 := w_i_i; */ + "mov r4, %[c]\n\t" + "umlal r5, r4, %[x_i], %[x_i]\n\t" + "str r5, [%[wij]], #4\n\t" + "cmp %[xj], %[x_max1]\n\t" + "bhi 0f\n\t" + "mov r9, %[c]\n\t" /* R9 := 0, the constant ZERO from here. */ + "beq 1f\n" + "2:\n\t" + "ldmia %[xj]!, { r7, r8 }\n\t" + "ldmia %[wij], { r5, r6 }\n\t" + /* (C,R4,R5) := (C,R4) + w_i_j + 2*x_i*x_j; */ + "umull r7, r12, %[x_i], r7\n\t" + "adds r5, r5, r4\n\t" + "adc r4, %[c], r9\n\t" + "adds r5, r5, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], r9, r9\n\t" + "adds r5, r5, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], %[c], r9\n\t" + /* (C,R4,R6) := (C,R4) + w_i_j + 2*x_i*x_j; */ + "adds r6, r6, r4\n\t" + "adc r4, %[c], r9\n\t" + "umull r7, r12, %[x_i], r8\n\t" + "adds r6, r6, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], r9, r9\n\t" + "adds r6, r6, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], %[c], r9\n\t" + /**/ + "stmia %[wij]!, { r5, r6 }\n\t" + "cmp %[xj], %[x_max1]\n\t" + "bcc 2b\n\t" + "bne 0f\n" + "1:\n\t" + /* (C,R4,R5) := (C,R4) + w_i_j + 2*x_i*x_j; */ + "ldr r5, [%[wij]]\n\t" + "ldr r6, [%[xj]], #4\n\t" + "adds r5, r5, r4\n\t" + "adc r4, %[c], r9\n\t" + "umull r7, r12, %[x_i], r6\n\t" + "adds r5, r5, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], r9, r9\n\t" + "adds r5, r5, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], %[c], r9\n\t" + "str r5, [%[wij]], #4\n" + "0:\n\t" + "ldr r5, [%[wij]]\n\t" + "adds r4, r4, r5\n\t" + "adc %[c], %[c], #0\n\t" + "str r4, [%[wij]]" + : [c] "=&r" (c), [wij] "=r" (wij), [xj] "=r" (xj) + : [x_i] "r" (x_i), [x_max1] "r" (&d[n*2-1]), + "[wij]" (wij), "[xj]" (xj) + : "r4", "r5", "r6", "r7", "r8", "r9", "r12", "memory", "cc"); +#endif + + c += mpi_mul_hlp( n, np, &d[i], d[i] * mm ); + } + + d += n; + + /* prevent timing attacks */ + if( ((mpi_cmp_abs_limbs ( n, d, np ) >= 0) | c) ) + mpi_sub_hlp( n, np, d ); + else + mpi_sub_hlp( n, d - n, d - n); +#else + t_uint a_input[n]; + + memcpy (a_input, &d[n], sizeof (a_input)); + mpi_montmul (n, np, mm, d, a_input); +#endif +} + +/* + * Sliding-window exponentiation: X = A^E mod N (HAC 14.85) + */ +#if MEMORY_SIZE >= 32 +#define MAX_WSIZE 6 +#elif MEMORY_SIZE >= 24 +#define MAX_WSIZE 5 +#else +#define MAX_WSIZE 4 +#endif +int mpi_exp_mod( mpi *X, const mpi *A, const mpi *E, const mpi *N, mpi *_RR ) +{ + int ret; + size_t i = mpi_msb( E ); + size_t wsize = ( i > 1024 ) ? MAX_WSIZE : + ( i > 671 ) ? 6 : ( i > 239 ) ? 5 : + ( i > 79 ) ? 4 : ( i > 23 ) ? 3 : 1; + size_t wbits, one = 1; + size_t nblimbs; + size_t bufsize, nbits; + t_uint ei, mm, state; + mpi RR; + t_uint d[N->n*2]; + t_uint w1[N->n]; + t_uint wn[(one << (wsize - 1))][N->n]; + + if( mpi_cmp_int( N, 0 ) < 0 || ( N->p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + if( mpi_cmp_int( E, 0 ) < 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + if( A->s == -1 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + /* + * Init temps and window size + */ + mpi_montg_init( &mm, N ); + + /* + * If 1st call, pre-compute R^2 mod N + */ + if( _RR == NULL || _RR->p == NULL ) + { + mpi T; + + mpi_init( &RR ); + T.s = 1; T.n = N->n * 2; T.p = d; + memset (d, 0, 2 * N->n * ciL); /* Set D zero. */ + mpi_sub_hlp( N->n, N->p, d + N->n); + MPI_CHK( mpi_mod_mpi( &RR, &T, N ) ); + MPI_CHK( mpi_grow( &RR, N->n ) ); + + if( _RR != NULL ) + memcpy( _RR, &RR, sizeof( mpi ) ); + + /* The condition of "the lower half of D is all zero" is kept. */ + } + else { + memcpy( &RR, _RR, sizeof( mpi ) ); + memset (d, 0, N->n * ciL); /* Set lower half of D zero. */ + } + + MPI_CHK( mpi_grow( X, N->n ) ); + + /* + * W[1] = A * R^2 * R^-1 mod N = A * R mod N + */ + if( mpi_cmp_mpi( A, N ) >= 0 ) { + mpi W1; + W1.s = 1; W1.n = N->n; W1.p = d + N->n; + mpi_mod_mpi( &W1, A, N ); + } else { + memset (d + N->n, 0, N->n * ciL); + memcpy (d + N->n, A->p, A->n * ciL); + } + + mpi_montmul( N->n, N->p, mm, d, RR.p ); + memcpy (w1, d + N->n, N->n * ciL); + + { + /* + * W[1 << (wsize - 1)] = W[1] ^ ( 2 ^ (wsize - 1) ) + */ + for( i = 0; i < wsize - 1; i++ ) + mpi_montsqr( N->n, N->p, mm, d ); + memcpy (wn[0], d + N->n, N->n * ciL); + + /* + * W[i] = W[i - 1] * W[1] + */ + for( i = 1; i < (one << (wsize - 1)); i++ ) + { + mpi_montmul( N->n, N->p, mm, d, w1 ); + memcpy (wn[i], d + N->n, N->n * ciL); + } + } + + /* + * X = R^2 * R^-1 mod N = R mod N + */ + memcpy (d + N->n, RR.p, N->n * ciL); + mpi_montred( N->n, N->p, mm, d ); + + nblimbs = E->n; + bufsize = 0; + nbits = 0; + wbits = 0; + state = 0; + + while( 1 ) + { + if( bufsize == 0 ) + { + if( nblimbs-- == 0 ) + break; + + bufsize = sizeof( t_uint ) << 3; + } + + bufsize--; + + ei = (E->p[nblimbs] >> bufsize) & 1; + + /* + * skip leading 0s + */ + if( ei == 0 && state == 0 ) + continue; + + if( ei == 0 && state == 1 ) + { + /* + * out of window, square X + */ + mpi_montsqr( N->n, N->p, mm, d ); + continue; + } + + /* + * add ei to current window + */ + state = 2; + + nbits++; + wbits |= (ei << (wsize - nbits)); + + if( nbits == wsize ) + { + /* + * X = X^wsize R^-1 mod N + */ + for( i = 0; i < wsize; i++ ) + mpi_montsqr( N->n, N->p, mm, d ); + + /* + * X = X * W[wbits] R^-1 mod N + */ + mpi_montmul( N->n, N->p, mm, d, wn[wbits - (one << (wsize - 1))]); + + state--; + nbits = 0; + wbits = 0; + } + } + + /* + * process the remaining bits + */ + for( i = 0; i < nbits; i++ ) + { + mpi_montsqr( N->n, N->p, mm, d ); + + wbits <<= 1; + + if( (wbits & (one << wsize)) != 0 ) + mpi_montmul( N->n, N->p, mm, d, w1); + } + + /* + * X = A^E * R * R^-1 mod N = A^E mod N + */ + mpi_montred( N->n, N->p, mm, d ); + memcpy (X->p, d + N->n, N->n * ciL); + +cleanup: + + if( _RR == NULL ) + mpi_free( &RR ); + + return( ret ); +} + +/* + * Greatest common divisor: G = gcd(A, B) (HAC 14.54) + */ +int mpi_gcd( mpi *G, const mpi *A, const mpi *B ) +{ + int ret; + size_t lz, lzt; + mpi TG, TA, TB; + + mpi_init( &TG ); mpi_init( &TA ); mpi_init( &TB ); + + MPI_CHK( mpi_copy( &TA, A ) ); + MPI_CHK( mpi_copy( &TB, B ) ); + + lz = mpi_lsb( &TA ); + lzt = mpi_lsb( &TB ); + + if ( lzt < lz ) + lz = lzt; + + MPI_CHK( mpi_shift_r( &TA, lz ) ); + MPI_CHK( mpi_shift_r( &TB, lz ) ); + + TA.s = TB.s = 1; + + while( mpi_cmp_int( &TA, 0 ) != 0 ) + { + MPI_CHK( mpi_shift_r( &TA, mpi_lsb( &TA ) ) ); + MPI_CHK( mpi_shift_r( &TB, mpi_lsb( &TB ) ) ); + + if( mpi_cmp_mpi( &TA, &TB ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( &TA, &TA, &TB ) ); + MPI_CHK( mpi_shift_r( &TA, 1 ) ); + } + else + { + MPI_CHK( mpi_sub_abs( &TB, &TB, &TA ) ); + MPI_CHK( mpi_shift_r( &TB, 1 ) ); + } + } + + MPI_CHK( mpi_shift_l( &TB, lz ) ); + MPI_CHK( mpi_copy( G, &TB ) ); + +cleanup: + + mpi_free( &TG ); mpi_free( &TA ); mpi_free( &TB ); + + return( ret ); +} + +int mpi_fill_random( mpi *X, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + + MPI_CHK( mpi_grow( X, CHARS_TO_LIMBS( size ) ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + MPI_CHK( f_rng( p_rng, (unsigned char *) X->p, size ) ); + +cleanup: + return( ret ); +} + +/* + * Modular inverse: X = A^-1 mod N (HAC 14.61 / 14.64) + */ +int mpi_inv_mod( mpi *X, const mpi *A, const mpi *N ) +{ + int ret; + mpi G, TA, TU, U1, U2, TB, TV, V1, V2; + + if( mpi_cmp_int( N, 0 ) <= 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &TA ); mpi_init( &TU ); mpi_init( &U1 ); mpi_init( &U2 ); + mpi_init( &G ); mpi_init( &TB ); mpi_init( &TV ); + mpi_init( &V1 ); mpi_init( &V2 ); + + MPI_CHK( mpi_gcd( &G, A, N ) ); + + if( mpi_cmp_int( &G, 1 ) != 0 ) + { + ret = POLARSSL_ERR_MPI_NOT_ACCEPTABLE; + goto cleanup; + } + + MPI_CHK( mpi_mod_mpi( &TA, A, N ) ); + MPI_CHK( mpi_copy( &TU, &TA ) ); + MPI_CHK( mpi_copy( &TB, N ) ); + MPI_CHK( mpi_copy( &TV, N ) ); + + MPI_CHK( mpi_lset( &U1, 1 ) ); + MPI_CHK( mpi_lset( &U2, 0 ) ); + MPI_CHK( mpi_lset( &V1, 0 ) ); + MPI_CHK( mpi_lset( &V2, 1 ) ); + + do + { + while( ( TU.p[0] & 1 ) == 0 ) + { + MPI_CHK( mpi_shift_r( &TU, 1 ) ); + + if( ( U1.p[0] & 1 ) != 0 || ( U2.p[0] & 1 ) != 0 ) + { + MPI_CHK( mpi_add_mpi( &U1, &U1, &TB ) ); + MPI_CHK( mpi_sub_mpi( &U2, &U2, &TA ) ); + } + + MPI_CHK( mpi_shift_r( &U1, 1 ) ); + MPI_CHK( mpi_shift_r( &U2, 1 ) ); + } + + while( ( TV.p[0] & 1 ) == 0 ) + { + MPI_CHK( mpi_shift_r( &TV, 1 ) ); + + if( ( V1.p[0] & 1 ) != 0 || ( V2.p[0] & 1 ) != 0 ) + { + MPI_CHK( mpi_add_mpi( &V1, &V1, &TB ) ); + MPI_CHK( mpi_sub_mpi( &V2, &V2, &TA ) ); + } + + MPI_CHK( mpi_shift_r( &V1, 1 ) ); + MPI_CHK( mpi_shift_r( &V2, 1 ) ); + } + + if( mpi_cmp_mpi( &TU, &TV ) >= 0 ) + { + MPI_CHK( mpi_sub_mpi( &TU, &TU, &TV ) ); + MPI_CHK( mpi_sub_mpi( &U1, &U1, &V1 ) ); + MPI_CHK( mpi_sub_mpi( &U2, &U2, &V2 ) ); + } + else + { + MPI_CHK( mpi_sub_mpi( &TV, &TV, &TU ) ); + MPI_CHK( mpi_sub_mpi( &V1, &V1, &U1 ) ); + MPI_CHK( mpi_sub_mpi( &V2, &V2, &U2 ) ); + } + } + while( mpi_cmp_int( &TU, 0 ) != 0 ); + + while( mpi_cmp_int( &V1, 0 ) < 0 ) + MPI_CHK( mpi_add_mpi( &V1, &V1, N ) ); + + while( mpi_cmp_mpi( &V1, N ) >= 0 ) + MPI_CHK( mpi_sub_mpi( &V1, &V1, N ) ); + + MPI_CHK( mpi_copy( X, &V1 ) ); + +cleanup: + + mpi_free( &TA ); mpi_free( &TU ); mpi_free( &U1 ); mpi_free( &U2 ); + mpi_free( &G ); mpi_free( &TB ); mpi_free( &TV ); + mpi_free( &V1 ); mpi_free( &V2 ); + + return( ret ); +} + +#if defined(POLARSSL_GENPRIME) + +static const int small_prime[] = +{ +#if 0 + 3, 5, 7, 11, 13, 17, 19, 23, + 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, + 139, 149, 151, 157, 163, 167, 173, 179, + 181, 191, 193, 197, 199, 211, 223, 227, + 229, 233, 239, 241, 251, 257, 263, 269, + 271, 277, 281, 283, 293, 307, 311, 313, + 317, 331, 337, 347, 349, 353, 359, 367, + 373, 379, 383, 389, 397, 401, 409, 419, + 421, 431, 433, 439, 443, 449, 457, 461, + 463, 467, 479, 487, 491, 499, 503, 509, + 521, 523, 541, 547, 557, 563, 569, 571, + 577, 587, 593, 599, 601, 607, 613, 617, + 619, 631, 641, 643, 647, 653, 659, 661, + 673, 677, 683, 691, 701, +#else + 97, +#endif + 709, 719, 727, + 733, 739, 743, 751, 757, 761, 769, 773, + 787, +#if 0 + 797, +#endif + 809, 811, 821, 823, 827, 829, + 839, 853, 857, 859, 863, 877, 881, 883, + 887, 907, 911, 919, 929, 937, 941, 947, + 953, 967, 971, 977, 983, 991, 997, +#if 1 + 1009, + 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, + 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, + 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, + 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, + 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, + 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, + 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, + 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, + 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, + 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, + 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, + 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, + 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, + 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, + 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, + 1889, +#endif + + -103 +}; + +/* + * From Public domain code of JKISS RNG. + * + * Reference: David Jones, UCL Bioinformatics Group + * Good Practice in (Pseudo) Random Number Generation for + * Bioinformatics Applications + * + */ +struct jkiss_state { uint32_t x, y, z, c; }; +static struct jkiss_state jkiss_state_v; + +int prng_seed (int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret; + + struct jkiss_state *s = &jkiss_state_v; + + MPI_CHK ( f_rng (p_rng, (unsigned char *)s, sizeof (struct jkiss_state)) ); + while (s->y == 0) + MPI_CHK ( f_rng (p_rng, (unsigned char *)&s->y, sizeof (uint32_t)) ); + s->z |= 1; /* avoiding z=c=0 */ + +cleanup: + return ret; +} + +static uint32_t +jkiss (struct jkiss_state *s) +{ + uint64_t t; + + s->x = 314527869 * s->x + 1234567; + s->y ^= s->y << 5; + s->y ^= s->y >> 7; + s->y ^= s->y << 22; + t = 4294584393ULL * s->z + s->c; + s->c = (uint32_t)(t >> 32); + s->z = (uint32_t)t; + + return s->x + s->y + s->z; +} + +static int mpi_fill_pseudo_random ( mpi *X, size_t size) +{ + int ret; + uint32_t *p, *p_end; + + MPI_CHK( mpi_grow( X, CHARS_TO_LIMBS( size ) ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + /* Assume little endian. */ + p = (uint32_t *)X->p; + p_end = (uint32_t *)(X->p + (size/sizeof (uint32_t))); + while (p < p_end) + *p++ = jkiss (&jkiss_state_v); + + if ((size%sizeof (uint32_t))) + *p = jkiss (&jkiss_state_v) & ((1 << (8*(size % sizeof (uint32_t)))) - 1); + +cleanup: + return ret; +} + +/* + * Miller-Rabin primality test (HAC 4.24) + */ +static +int mpi_is_prime( mpi *X) +{ + int ret, xs; + size_t i, j, n, s; + mpi W, R, T, A, RR; + + if( mpi_cmp_int( X, 0 ) == 0 || + mpi_cmp_int( X, 1 ) == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + + if( mpi_cmp_int( X, 2 ) == 0 ) + return( 0 ); + + mpi_init( &W ); mpi_init( &R ); mpi_init( &T ); mpi_init( &A ); + mpi_init( &RR ); + + xs = X->s; X->s = 1; + ret = 0; + +#if 0 + /* + * test trivial factors first + */ + if( ( X->p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); +#endif + + for( i = 0; small_prime[i] > 0; i++ ) + { + t_uint r; + + if( mpi_cmp_int( X, small_prime[i] ) <= 0 ) + return( 0 ); + + MPI_CHK( mpi_mod_int( &r, X, small_prime[i] ) ); + + if( r == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + } + + /* + * W = |X| - 1 + * R = W >> lsb( W ) + */ + MPI_CHK( mpi_sub_int( &W, X, 1 ) ); + s = mpi_lsb( &W ); + MPI_CHK( mpi_copy( &R, &W ) ); + MPI_CHK( mpi_shift_r( &R, s ) ); + i = mpi_msb( X ); + + /* Fermat primality test with 2. */ + mpi_lset (&T, 2); + MPI_CHK( mpi_exp_mod( &T, &T, &W, X, &RR ) ); + if ( mpi_cmp_int (&T, 1) != 0) + { + ret = POLARSSL_ERR_MPI_NOT_ACCEPTABLE; + goto cleanup; + } + + + /* + * HAC, table 4.4 + */ + n = ( ( i >= 1300 ) ? 2 : ( i >= 850 ) ? 3 : + ( i >= 650 ) ? 4 : ( i >= 350 ) ? 8 : + ( i >= 250 ) ? 12 : ( i >= 150 ) ? 18 : 27 ); + + for( i = 0; i < n; i++ ) + { + /* + * pick a random A, 1 < A < |X| - 1 + */ + MPI_CHK( mpi_fill_pseudo_random( &A, X->n * ciL ) ); + + if( mpi_cmp_mpi( &A, &W ) >= 0 ) + { + j = mpi_msb( &A ) - mpi_msb( &W ); + MPI_CHK( mpi_shift_r( &A, j + 1 ) ); + } + A.p[0] |= 3; + + /* + * A = A^R mod |X| + */ + MPI_CHK( mpi_exp_mod( &A, &A, &R, X, &RR ) ); + + if( mpi_cmp_mpi( &A, &W ) == 0 || + mpi_cmp_int( &A, 1 ) == 0 ) + continue; + + j = 1; + while( j < s && mpi_cmp_mpi( &A, &W ) != 0 ) + { + /* + * A = A * A mod |X| + */ + MPI_CHK( mpi_mul_mpi( &T, &A, &A ) ); + MPI_CHK( mpi_mod_mpi( &A, &T, X ) ); + + if( mpi_cmp_int( &A, 1 ) == 0 ) + break; + + j++; + } + + /* + * not prime if A != |X| - 1 or A == 1 + */ + if( mpi_cmp_mpi( &A, &W ) != 0 || + mpi_cmp_int( &A, 1 ) == 0 ) + { + ret = POLARSSL_ERR_MPI_NOT_ACCEPTABLE; + break; + } + } + +cleanup: + + X->s = xs; + + mpi_free( &W ); mpi_free( &R ); mpi_free( &T ); mpi_free( &A ); + mpi_free( &RR ); + + return( ret ); +} + + +/* + * Value M: multiply all primes up to 701 (except 97) and 797 + * (so that MAX_A will be convenient value) + */ +#ifdef __LP64__ +#define M_LIMBS 16 +#else +#define M_LIMBS 31 +#endif +#define M_SIZE 122 + +static const t_uint limbs_M[] = { /* Little endian */ +#ifdef __LP64__ + 0x9344A6AB84EEB59EUL, 0xEC855CDAFF21529FUL, + 0x477E991E009BAB38UL, 0x2EEA23579F5B86F3UL, + 0xAC17D30441D6502FUL, 0x38FF52B90A468A6DUL, + 0x63630419FD42E5EFUL, 0x48CE17D091DB2572UL, + 0x708AB00AE3B57D0EUL, 0xF8A9DE08CD723598UL, + 0x731411374432C93BUL, 0x554DF2612779FAB3UL, + 0xDEEBDA58953D2BA5UL, 0xD1D66F2F5F57D007UL, + 0xB85C9607E84E9F2BUL, 0x000000000000401DUL +#else + 0x84EEB59E, 0x9344A6AB, 0xFF21529F, 0xEC855CDA, + 0x009BAB38, 0x477E991E, 0x9F5B86F3, 0x2EEA2357, + 0x41D6502F, 0xAC17D304, 0x0A468A6D, 0x38FF52B9, + 0xFD42E5EF, 0x63630419, 0x91DB2572, 0x48CE17D0, + 0xE3B57D0E, 0x708AB00A, 0xCD723598, 0xF8A9DE08, + 0x4432C93B, 0x73141137, 0x2779FAB3, 0x554DF261, + 0x953D2BA5, 0xDEEBDA58, 0x5F57D007, 0xD1D66F2F, + 0xE84E9F2B, 0xB85C9607, 0x0000401D +#endif +}; + +static const mpi M[1] = {{ 1, M_LIMBS, (t_uint *)limbs_M }}; + +/* + * MAX_A : 2^1024 / M - 1 + */ +#ifdef __LP64__ +#define MAX_A_LIMBS 1 +#else +#define MAX_A_LIMBS 2 +#endif +#define MAX_A_FILL_SIZE 6 +static const t_uint limbs_MAX_A[] = { /* Little endian */ +#ifdef __LP64__ + 0x0003FE2556A2B35FUL +#else + 0x56A2B35F, 0x0003FE25 +#endif +}; + +static const mpi MAX_A[1] = {{ 1, MAX_A_LIMBS, (t_uint *)limbs_MAX_A }}; + +/* + * Prime number generation + * + * Special version for 1024-bit only. Ignores DH_FLAG. + */ +int mpi_gen_prime( mpi *X, size_t nbits, int dh_flag, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + mpi B[1], G[1]; + + (void)dh_flag; + if (nbits != 1024) + return POLARSSL_ERR_MPI_BAD_INPUT_DATA; + + mpi_init ( B ); mpi_init ( G ); + + /* + * Get random value 1 to M-1 avoiding bias, and proceed when it is + * coprime to all small primes. + */ + do + { + MPI_CHK ( mpi_fill_random ( B, M_SIZE, f_rng, p_rng ) ); + B->p[0] |= 0x1; + B->p[M_LIMBS - 1] &= 0x00007FFF; + if (mpi_cmp_abs (B, M) >= 0) + continue; + + MPI_CHK ( mpi_gcd ( G, B, M ) ); + } + while (mpi_cmp_int ( G, 1 ) != 0); + + /* + * Get random value avoiding bias, comput P with the value, + * check if it's big enough, lastly, check if it's prime. + */ + while (1) + { + MPI_CHK( mpi_fill_random( X, MAX_A_FILL_SIZE, f_rng, p_rng ) ); + MPI_CHK ( mpi_sub_abs (X, MAX_A, X) ); + + MPI_CHK ( mpi_mul_mpi ( X, X, M ) ); + MPI_CHK ( mpi_add_abs ( X, X, B ) ); + if (X->n <= M_LIMBS || (X->p[M_LIMBS-1] & 0xc0000000) == 0) + continue; + ret = mpi_is_prime ( X ); + if (ret == 0 || ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE) + break; + } + +cleanup: + + mpi_free ( B ); mpi_free ( G ); + + return ret; +} + +#endif + +#if defined(POLARSSL_SELF_TEST) + +#define GCD_PAIR_COUNT 3 + +static const int gcd_pairs[GCD_PAIR_COUNT][3] = +{ + { 693, 609, 21 }, + { 1764, 868, 28 }, + { 768454923, 542167814, 1 } +}; + +/* + * Checkup routine + */ +int mpi_self_test( int verbose ) +{ + int ret, i; + mpi A, E, N, X, Y, U, V; + + mpi_init( &A ); mpi_init( &E ); mpi_init( &N ); mpi_init( &X ); + mpi_init( &Y ); mpi_init( &U ); mpi_init( &V ); + + MPI_CHK( mpi_read_string( &A, 16, + "EFE021C2645FD1DC586E69184AF4A31E" \ + "D5F53E93B5F123FA41680867BA110131" \ + "944FE7952E2517337780CB0DB80E61AA" \ + "E7C8DDC6C5C6AADEB34EB38A2F40D5E6" ) ); + + MPI_CHK( mpi_read_string( &E, 16, + "B2E7EFD37075B9F03FF989C7C5051C20" \ + "34D2A323810251127E7BF8625A4F49A5" \ + "F3E27F4DA8BD59C47D6DAABA4C8127BD" \ + "5B5C25763222FEFCCFC38B832366C29E" ) ); + + MPI_CHK( mpi_read_string( &N, 16, + "0066A198186C18C10B2F5ED9B522752A" \ + "9830B69916E535C8F047518A889A43A5" \ + "94B6BED27A168D31D4A52F88925AA8F5" ) ); + + MPI_CHK( mpi_mul_mpi( &X, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "602AB7ECA597A3D6B56FF9829A5E8B85" \ + "9E857EA95A03512E2BAE7391688D264A" \ + "A5663B0341DB9CCFD2C4C5F421FEC814" \ + "8001B72E848A38CAE1C65F78E56ABDEF" \ + "E12D3C039B8A02D6BE593F0BBBDA56F1" \ + "ECF677152EF804370C1A305CAF3B5BF1" \ + "30879B56C61DE584A0F53A2447A51E" ) ); + + if( verbose != 0 ) + printf( " MPI test #1 (mul_mpi): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + + MPI_CHK( mpi_div_mpi( &X, &Y, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "256567336059E52CAE22925474705F39A94" ) ); + + MPI_CHK( mpi_read_string( &V, 16, + "6613F26162223DF488E9CD48CC132C7A" \ + "0AC93C701B001B092E4E5B9F73BCD27B" \ + "9EE50D0657C77F374E903CDFA4C642" ) ); + + if( verbose != 0 ) + printf( " MPI test #2 (div_mpi): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 || + mpi_cmp_mpi( &Y, &V ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + + MPI_CHK( mpi_exp_mod( &X, &A, &E, &N, NULL ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "36E139AEA55215609D2816998ED020BB" \ + "BD96C37890F65171D948E9BC7CBAA4D9" \ + "325D24D6A3C12710F10A09FA08AB87" ) ); + + if( verbose != 0 ) + printf( " MPI test #3 (exp_mod): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + +#if defined(POLARSSL_GENPRIME) + MPI_CHK( mpi_inv_mod( &X, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "003A0AAEDD7E784FC07D8F9EC6E3BFD5" \ + "C3DBA76456363A10869622EAC2DD84EC" \ + "C5B8A74DAC4D09E03B5E0BE779F2DF61" ) ); + + if( verbose != 0 ) + printf( " MPI test #4 (inv_mod): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); +#endif + + if( verbose != 0 ) + printf( " MPI test #5 (simple gcd): " ); + + for ( i = 0; i < GCD_PAIR_COUNT; i++) + { + MPI_CHK( mpi_lset( &X, gcd_pairs[i][0] ) ); + MPI_CHK( mpi_lset( &Y, gcd_pairs[i][1] ) ); + + MPI_CHK( mpi_gcd( &A, &X, &Y ) ); + + if( mpi_cmp_int( &A, gcd_pairs[i][2] ) != 0 ) + { + if( verbose != 0 ) + printf( "failed at %d\n", i ); + + return( 1 ); + } + } + + if( verbose != 0 ) + printf( "passed\n" ); + +cleanup: + + if( ret != 0 && verbose != 0 ) + printf( "Unexpected error, return code = %08X\n", ret ); + + mpi_free( &A ); mpi_free( &E ); mpi_free( &N ); mpi_free( &X ); + mpi_free( &Y ); mpi_free( &U ); mpi_free( &V ); + + if( verbose != 0 ) + printf( "\n" ); + + return( ret ); +} + +#endif + +#endif diff --git a/gnuk/polarssl/library/rsa.c b/gnuk/polarssl/library/rsa.c new file mode 100644 index 0000000000..3e9b82973e --- /dev/null +++ b/gnuk/polarssl/library/rsa.c @@ -0,0 +1,1521 @@ +/* + * The RSA public-key cryptosystem + * + * Copyright (C) 2006-2011, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * RSA was designed by Ron Rivest, Adi Shamir and Len Adleman. + * + * http://theory.lcs.mit.edu/~rivest/rsapaper.pdf + * http://www.cacr.math.uwaterloo.ca/hac/about/chap8.pdf + */ + +#include "polarssl/config.h" + +#if defined(POLARSSL_RSA_C) + +#include "polarssl/rsa.h" + +#if defined(POLARSSL_PKCS1_V21) +#include "polarssl/md.h" +#endif + +#include + +/* + * Initialize an RSA context + */ +void rsa_init( rsa_context *ctx, + int padding, + int hash_id ) +{ + memset( ctx, 0, sizeof( rsa_context ) ); + + ctx->padding = padding; + ctx->hash_id = hash_id; +} + +#if defined(POLARSSL_GENPRIME) + +/* + * Generate an RSA keypair + */ +int rsa_gen_key( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + unsigned int nbits, int exponent ) +{ + int ret; + mpi P1, Q1, H, G; + + if( f_rng == NULL || nbits < 128 || exponent < 3 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + mpi_init( &P1 ); mpi_init( &Q1 ); mpi_init( &H ); mpi_init( &G ); + + /* + * find primes P and Q with Q < P so that: + * GCD( E, (P-1)*(Q-1) ) == 1 + */ + MPI_CHK( mpi_lset( &ctx->E, exponent ) ); + + do + { + MPI_CHK( mpi_gen_prime( &ctx->P, ( nbits + 1 ) >> 1, 0, + f_rng, p_rng ) ); + + MPI_CHK( mpi_gen_prime( &ctx->Q, ( nbits + 1 ) >> 1, 0, + f_rng, p_rng ) ); + + if( mpi_cmp_mpi( &ctx->P, &ctx->Q ) < 0 ) + mpi_swap( &ctx->P, &ctx->Q ); + + if( mpi_cmp_mpi( &ctx->P, &ctx->Q ) == 0 ) + continue; + + MPI_CHK( mpi_mul_mpi( &ctx->N, &ctx->P, &ctx->Q ) ); + if( mpi_msb( &ctx->N ) != nbits ) + continue; + + MPI_CHK( mpi_sub_int( &P1, &ctx->P, 1 ) ); + MPI_CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) ); + MPI_CHK( mpi_mul_mpi( &H, &P1, &Q1 ) ); + MPI_CHK( mpi_gcd( &G, &ctx->E, &H ) ); + } + while( mpi_cmp_int( &G, 1 ) != 0 ); + + /* + * D = E^-1 mod ((P-1)*(Q-1)) + * DP = D mod (P - 1) + * DQ = D mod (Q - 1) + * QP = Q^-1 mod P + */ + MPI_CHK( mpi_inv_mod( &ctx->D , &ctx->E, &H ) ); + MPI_CHK( mpi_mod_mpi( &ctx->DP, &ctx->D, &P1 ) ); + MPI_CHK( mpi_mod_mpi( &ctx->DQ, &ctx->D, &Q1 ) ); + MPI_CHK( mpi_inv_mod( &ctx->QP, &ctx->Q, &ctx->P ) ); + + ctx->len = ( mpi_msb( &ctx->N ) + 7 ) >> 3; + +cleanup: + + mpi_free( &P1 ); mpi_free( &Q1 ); mpi_free( &H ); mpi_free( &G ); + + if( ret != 0 ) + { + rsa_free( ctx ); + return( POLARSSL_ERR_RSA_KEY_GEN_FAILED + ret ); + } + + return( 0 ); +} + +#endif + +/* + * Check a public RSA key + */ +int rsa_check_pubkey( const rsa_context *ctx ) +{ + if( !ctx->N.p || !ctx->E.p ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( ( ctx->N.p[0] & 1 ) == 0 || + ( ctx->E.p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( mpi_msb( &ctx->N ) < 128 || + mpi_msb( &ctx->N ) > POLARSSL_MPI_MAX_BITS ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( mpi_msb( &ctx->E ) < 2 || + mpi_msb( &ctx->E ) > 64 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + return( 0 ); +} + +/* + * Check a private RSA key + */ +int rsa_check_privkey( const rsa_context *ctx ) +{ + int ret; + mpi PQ, DE, P1, Q1, H, I, G, G2, L1, L2, DP, DQ, QP; + + if( ( ret = rsa_check_pubkey( ctx ) ) != 0 ) + return( ret ); + + if( !ctx->P.p || !ctx->Q.p || !ctx->D.p ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + mpi_init( &PQ ); mpi_init( &DE ); mpi_init( &P1 ); mpi_init( &Q1 ); + mpi_init( &H ); mpi_init( &I ); mpi_init( &G ); mpi_init( &G2 ); + mpi_init( &L1 ); mpi_init( &L2 ); mpi_init( &DP ); mpi_init( &DQ ); + mpi_init( &QP ); + + MPI_CHK( mpi_mul_mpi( &PQ, &ctx->P, &ctx->Q ) ); + MPI_CHK( mpi_mul_mpi( &DE, &ctx->D, &ctx->E ) ); + MPI_CHK( mpi_sub_int( &P1, &ctx->P, 1 ) ); + MPI_CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) ); + MPI_CHK( mpi_mul_mpi( &H, &P1, &Q1 ) ); + MPI_CHK( mpi_gcd( &G, &ctx->E, &H ) ); + + MPI_CHK( mpi_gcd( &G2, &P1, &Q1 ) ); + MPI_CHK( mpi_div_mpi( &L1, &L2, &H, &G2 ) ); + MPI_CHK( mpi_mod_mpi( &I, &DE, &L1 ) ); + + MPI_CHK( mpi_mod_mpi( &DP, &ctx->D, &P1 ) ); + MPI_CHK( mpi_mod_mpi( &DQ, &ctx->D, &Q1 ) ); + MPI_CHK( mpi_inv_mod( &QP, &ctx->Q, &ctx->P ) ); + /* + * Check for a valid PKCS1v2 private key + */ + if( mpi_cmp_mpi( &PQ, &ctx->N ) != 0 || + mpi_cmp_mpi( &DP, &ctx->DP ) != 0 || + mpi_cmp_mpi( &DQ, &ctx->DQ ) != 0 || + mpi_cmp_mpi( &QP, &ctx->QP ) != 0 || + mpi_cmp_int( &L2, 0 ) != 0 || + mpi_cmp_int( &I, 1 ) != 0 || + mpi_cmp_int( &G, 1 ) != 0 ) + { + ret = POLARSSL_ERR_RSA_KEY_CHECK_FAILED; + } + +cleanup: + mpi_free( &PQ ); mpi_free( &DE ); mpi_free( &P1 ); mpi_free( &Q1 ); + mpi_free( &H ); mpi_free( &I ); mpi_free( &G ); mpi_free( &G2 ); + mpi_free( &L1 ); mpi_free( &L2 ); mpi_free( &DP ); mpi_free( &DQ ); + mpi_free( &QP ); + + if( ret == POLARSSL_ERR_RSA_KEY_CHECK_FAILED ) + return( ret ); + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED + ret ); + + return( 0 ); +} + +/* + * Do an RSA public key operation + */ +int rsa_public( rsa_context *ctx, + const unsigned char *input, + unsigned char *output ) +{ + int ret; + size_t olen; + mpi T; + + mpi_init( &T ); + + MPI_CHK( mpi_read_binary( &T, input, ctx->len ) ); + + if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 ) + { + mpi_free( &T ); + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + olen = ctx->len; + MPI_CHK( mpi_exp_mod( &T, &T, &ctx->E, &ctx->N, &ctx->RN ) ); + MPI_CHK( mpi_write_binary( &T, output, olen ) ); + +cleanup: + + mpi_free( &T ); + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_PUBLIC_FAILED + ret ); + + return( 0 ); +} + +/* + * Do an RSA private key operation + */ +int rsa_private( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + const unsigned char *input, + unsigned char *output ) +{ + int ret; + size_t olen; + mpi T, T1, T2, Vi, Vf; + + mpi_init( &T ); mpi_init( &T1 ); mpi_init( &T2 ); + mpi_init( &Vi ); mpi_init( &Vf ); + + MPI_CHK( mpi_read_binary( &T, input, ctx->len ) ); + +#if 0 + if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 ) + { + mpi_free( &T ); + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } +#endif + +#if defined(POLARSSL_RSA_NO_CRT) + ((void) f_rng); + ((void) p_rng); + MPI_CHK( mpi_exp_mod( &T, &T, &ctx->D, &ctx->N, &ctx->RN ) ); +#else + if( f_rng != NULL ) + { + /* + * Blinding + * T = T * Vi mod N + */ + /* Unblinding value: Vf = random number */ + MPI_CHK( mpi_fill_random( &Vf, ctx->len - 1, f_rng, p_rng ) ); + + /* Mathematically speaking, the algorithm should check Vf + * against 0, P and Q (Vf should be relatively prime to N, and 0 < Vf < N), + * so that Vf^-1 exists. + */ + + /* Blinding value: Vi = Vf^(-e) mod N */ + MPI_CHK( mpi_inv_mod( &Vi, &Vf, &ctx->N ) ); + MPI_CHK( mpi_exp_mod( &Vi, &Vi, &ctx->E, &ctx->N, &ctx->RN ) ); + + MPI_CHK( mpi_mul_mpi( &T, &T, &Vi ) ); + MPI_CHK( mpi_mod_mpi( &T, &T, &ctx->N ) ); + } + + /* + * faster decryption using the CRT + * + * T1 = input ^ dP mod P + * T2 = input ^ dQ mod Q + */ + MPI_CHK( mpi_exp_mod( &T1, &T, &ctx->DP, &ctx->P, &ctx->RP ) ); + MPI_CHK( mpi_exp_mod( &T2, &T, &ctx->DQ, &ctx->Q, &ctx->RQ ) ); + + /* + * T = (T1 - T2) * (Q^-1 mod P) mod P + */ + MPI_CHK( mpi_sub_mpi( &T, &T1, &T2 ) ); + MPI_CHK( mpi_mul_mpi( &T1, &T, &ctx->QP ) ); + MPI_CHK( mpi_mod_mpi( &T, &T1, &ctx->P ) ); + + /* + * output = T2 + T * Q + */ + MPI_CHK( mpi_mul_mpi( &T1, &T, &ctx->Q ) ); + MPI_CHK( mpi_add_mpi( &T, &T2, &T1 ) ); + + if( f_rng != NULL ) + { + /* + * Unblind + * T = T * Vf mod N + */ + MPI_CHK( mpi_mul_mpi( &T, &T, &Vf ) ); + MPI_CHK( mpi_mod_mpi( &T, &T, &ctx->N ) ); + } +#endif + + olen = ctx->len; + MPI_CHK( mpi_write_binary( &T, output, olen ) ); + +cleanup: + + mpi_free( &T ); mpi_free( &T1 ); mpi_free( &T2 ); + mpi_free( &Vi ); mpi_free( &Vf ); + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_PRIVATE_FAILED + ret ); + + return( 0 ); +} + +#if defined(POLARSSL_PKCS1_V21) +/** + * Generate and apply the MGF1 operation (from PKCS#1 v2.1) to a buffer. + * + * \param dst buffer to mask + * \param dlen length of destination buffer + * \param src source of the mask generation + * \param slen length of the source buffer + * \param md_ctx message digest context to use + */ +static void mgf_mask( unsigned char *dst, size_t dlen, unsigned char *src, size_t slen, + md_context_t *md_ctx ) +{ + unsigned char mask[POLARSSL_MD_MAX_SIZE]; + unsigned char counter[4]; + unsigned char *p; + unsigned int hlen; + size_t i, use_len; + + memset( mask, 0, POLARSSL_MD_MAX_SIZE ); + memset( counter, 0, 4 ); + + hlen = md_ctx->md_info->size; + + // Generate and apply dbMask + // + p = dst; + + while( dlen > 0 ) + { + use_len = hlen; + if( dlen < hlen ) + use_len = dlen; + + md_starts( md_ctx ); + md_update( md_ctx, src, slen ); + md_update( md_ctx, counter, 4 ); + md_finish( md_ctx, mask ); + + for( i = 0; i < use_len; ++i ) + *p++ ^= mask[i]; + + counter[3]++; + + dlen -= use_len; + } +} +#endif + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSAES-OAEP-ENCRYPT function + */ +int rsa_rsaes_oaep_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + const unsigned char *label, size_t label_len, + size_t ilen, + const unsigned char *input, + unsigned char *output ) +{ + size_t olen; + int ret; + unsigned char *p = output; + unsigned int hlen; + const md_info_t *md_info; + md_context_t md_ctx; + + if( ctx->padding != RSA_PKCS_V21 || f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + md_info = md_info_from_type( ctx->hash_id ); + + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + hlen = md_get_size( md_info ); + + if( olen < ilen + 2 * hlen + 2 || f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + memset( output, 0, olen ); + + *p++ = 0; + + // Generate a random octet string seed + // + if( ( ret = f_rng( p_rng, p, hlen ) ) != 0 ) + return( POLARSSL_ERR_RSA_RNG_FAILED + ret ); + + p += hlen; + + // Construct DB + // + md( md_info, label, label_len, p ); + p += hlen; + p += olen - 2 * hlen - 2 - ilen; + *p++ = 1; + memcpy( p, input, ilen ); + + md_init_ctx( &md_ctx, md_info ); + + // maskedDB: Apply dbMask to DB + // + mgf_mask( output + hlen + 1, olen - hlen - 1, output + 1, hlen, + &md_ctx ); + + // maskedSeed: Apply seedMask to seed + // + mgf_mask( output + 1, hlen, output + hlen + 1, olen - hlen - 1, + &md_ctx ); + + md_free_ctx( &md_ctx ); + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, output, output ) + : rsa_private( ctx, f_rng, p_rng, output, output ) ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +/* + * Implementation of the PKCS#1 v2.1 RSAES-PKCS1-V1_5-ENCRYPT function + */ +int rsa_rsaes_pkcs1_v15_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ) +{ + size_t nb_pad, olen; + int ret; + unsigned char *p = output; + + if( ctx->padding != RSA_PKCS_V15 || f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + + if( olen < ilen + 11 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + nb_pad = olen - 3 - ilen; + + *p++ = 0; + if( mode == RSA_PUBLIC ) + { + *p++ = RSA_CRYPT; + + while( nb_pad-- > 0 ) + { + int rng_dl = 100; + + do { + ret = f_rng( p_rng, p, 1 ); + } while( *p == 0 && --rng_dl && ret == 0 ); + + // Check if RNG failed to generate data + // + if( rng_dl == 0 || ret != 0) + return POLARSSL_ERR_RSA_RNG_FAILED + ret; + + p++; + } + } + else + { + *p++ = RSA_SIGN; + + while( nb_pad-- > 0 ) + *p++ = 0xFF; + } + + *p++ = 0; + memcpy( p, input, ilen ); + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, output, output ) + : rsa_private( ctx, f_rng, p_rng, output, output ) ); +} + +/* + * Add the message padding, then do an RSA operation + */ +int rsa_pkcs1_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ) +{ + switch( ctx->padding ) + { + case RSA_PKCS_V15: + return rsa_rsaes_pkcs1_v15_encrypt( ctx, f_rng, p_rng, mode, ilen, + input, output ); + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsaes_oaep_encrypt( ctx, f_rng, p_rng, mode, NULL, 0, + ilen, input, output ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSAES-OAEP-DECRYPT function + */ +int rsa_rsaes_oaep_decrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + const unsigned char *label, size_t label_len, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ) +{ + int ret; + size_t ilen; + unsigned char *p; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + unsigned char lhash[POLARSSL_MD_MAX_SIZE]; + unsigned int hlen; + const md_info_t *md_info; + md_context_t md_ctx; + + if( ctx->padding != RSA_PKCS_V21 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ilen = ctx->len; + + if( ilen < 16 || ilen > sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, input, buf ) + : rsa_private( ctx, f_rng, p_rng, input, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + if( *p++ != 0 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + md_info = md_info_from_type( ctx->hash_id ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + hlen = md_get_size( md_info ); + + md_init_ctx( &md_ctx, md_info ); + + // Generate lHash + // + md( md_info, label, label_len, lhash ); + + // seed: Apply seedMask to maskedSeed + // + mgf_mask( buf + 1, hlen, buf + hlen + 1, ilen - hlen - 1, + &md_ctx ); + + // DB: Apply dbMask to maskedDB + // + mgf_mask( buf + hlen + 1, ilen - hlen - 1, buf + 1, hlen, + &md_ctx ); + + p += hlen; + md_free_ctx( &md_ctx ); + + // Check validity + // + if( memcmp( lhash, p, hlen ) != 0 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + p += hlen; + + while( *p == 0 && p < buf + ilen ) + p++; + + if( p == buf + ilen ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + if( *p++ != 0x01 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + if (ilen - (p - buf) > output_max_len) + return( POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE ); + + *olen = ilen - (p - buf); + memcpy( output, p, *olen ); + + return( 0 ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +/* + * Implementation of the PKCS#1 v2.1 RSAES-PKCS1-V1_5-DECRYPT function + */ +int rsa_rsaes_pkcs1_v15_decrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len) +{ + int ret, correct = 1; + size_t ilen, pad_count = 0; + unsigned char *p, *q; + unsigned char bt; + unsigned char buf[ctx->len]; + + if( ctx->padding != RSA_PKCS_V15 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ilen = ctx->len; + + if( ilen < 16) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, input, buf ) + : rsa_private( ctx, f_rng, p_rng, input, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + if( *p++ != 0 ) + correct = 0; + + bt = *p++; + if( ( bt != RSA_CRYPT && mode == RSA_PRIVATE ) || + ( bt != RSA_SIGN && mode == RSA_PUBLIC ) ) + { + correct = 0; + } + + if( bt == RSA_CRYPT ) + { + while( *p != 0 && p < buf + ilen - 1 ) + pad_count += ( *p++ != 0 ); + + correct &= ( *p == 0 && p < buf + ilen - 1 ); + + q = p; + + // Also pass over all other bytes to reduce timing differences + // + while ( q < buf + ilen - 1 ) + pad_count += ( *q++ != 0 ); + + // Prevent compiler optimization of pad_count + // + correct |= pad_count & 0x100000; /* Always 0 unless 1M bit keys */ + p++; + } + else + { + while( *p == 0xFF && p < buf + ilen - 1 ) + pad_count += ( *p++ == 0xFF ); + + correct &= ( *p == 0 && p < buf + ilen - 1 ); + + q = p; + + // Also pass over all other bytes to reduce timing differences + // + while ( q < buf + ilen - 1 ) + pad_count += ( *q++ != 0 ); + + // Prevent compiler optimization of pad_count + // + correct |= pad_count & 0x100000; /* Always 0 unless 1M bit keys */ + p++; + } + + if( correct == 0 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + if (ilen - (p - buf) > output_max_len) + return( POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE ); + + *olen = ilen - (p - buf); + memcpy( output, p, *olen ); + + return( 0 ); +} + +/* + * Do an RSA operation, then remove the message padding + */ +int rsa_pkcs1_decrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len) +{ + switch( ctx->padding ) + { + case RSA_PKCS_V15: + return rsa_rsaes_pkcs1_v15_decrypt( ctx, f_rng, p_rng, mode, olen, + input, output, output_max_len ); + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsaes_oaep_decrypt( ctx, f_rng, p_rng, mode, NULL, 0, + olen, input, output, output_max_len ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PSS-SIGN function + */ +int rsa_rsassa_pss_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + size_t olen; + unsigned char *p = sig; + unsigned char salt[POLARSSL_MD_MAX_SIZE]; + unsigned int slen, hlen, offset = 0; + int ret; + size_t msb; + const md_info_t *md_info; + md_context_t md_ctx; + + if( ctx->padding != RSA_PKCS_V21 || f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + + switch( hash_id ) + { + case SIG_RSA_MD2: + case SIG_RSA_MD4: + case SIG_RSA_MD5: + hashlen = 16; + break; + + case SIG_RSA_SHA1: + hashlen = 20; + break; + + case SIG_RSA_SHA224: + hashlen = 28; + break; + + case SIG_RSA_SHA256: + hashlen = 32; + break; + + case SIG_RSA_SHA384: + hashlen = 48; + break; + + case SIG_RSA_SHA512: + hashlen = 64; + break; + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + md_info = md_info_from_type( ctx->hash_id ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + hlen = md_get_size( md_info ); + slen = hlen; + + if( olen < hlen + slen + 2 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + memset( sig, 0, olen ); + + msb = mpi_msb( &ctx->N ) - 1; + + // Generate salt of length slen + // + if( ( ret = f_rng( p_rng, salt, slen ) ) != 0 ) + return( POLARSSL_ERR_RSA_RNG_FAILED + ret ); + + // Note: EMSA-PSS encoding is over the length of N - 1 bits + // + msb = mpi_msb( &ctx->N ) - 1; + p += olen - hlen * 2 - 2; + *p++ = 0x01; + memcpy( p, salt, slen ); + p += slen; + + md_init_ctx( &md_ctx, md_info ); + + // Generate H = Hash( M' ) + // + md_starts( &md_ctx ); + md_update( &md_ctx, p, 8 ); + md_update( &md_ctx, hash, hashlen ); + md_update( &md_ctx, salt, slen ); + md_finish( &md_ctx, p ); + + // Compensate for boundary condition when applying mask + // + if( msb % 8 == 0 ) + offset = 1; + + // maskedDB: Apply dbMask to DB + // + mgf_mask( sig + offset, olen - hlen - 1 - offset, p, hlen, &md_ctx ); + + md_free_ctx( &md_ctx ); + + msb = mpi_msb( &ctx->N ) - 1; + sig[0] &= 0xFF >> ( olen * 8 - msb ); + + p += hlen; + *p++ = 0xBC; + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, sig ) + : rsa_private( ctx, f_rng, p_rng, sig, sig ) ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PKCS1-V1_5-SIGN function + */ +/* + * Do an RSA operation to sign the message digest + */ +int rsa_rsassa_pkcs1_v15_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + size_t nb_pad, olen; + unsigned char *p = sig; + + if( ctx->padding != RSA_PKCS_V15 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + + switch( hash_id ) + { + case SIG_RSA_RAW: + nb_pad = olen - 3 - hashlen; + break; + + case SIG_RSA_MD2: + case SIG_RSA_MD4: + case SIG_RSA_MD5: + nb_pad = olen - 3 - 34; + break; + + case SIG_RSA_SHA1: + nb_pad = olen - 3 - 35; + break; + + case SIG_RSA_SHA224: + nb_pad = olen - 3 - 47; + break; + + case SIG_RSA_SHA256: + nb_pad = olen - 3 - 51; + break; + + case SIG_RSA_SHA384: + nb_pad = olen - 3 - 67; + break; + + case SIG_RSA_SHA512: + nb_pad = olen - 3 - 83; + break; + + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + if( ( nb_pad < 8 ) || ( nb_pad > olen ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + *p++ = 0; + *p++ = RSA_SIGN; + memset( p, 0xFF, nb_pad ); + p += nb_pad; + *p++ = 0; + + switch( hash_id ) + { + case SIG_RSA_RAW: + memcpy( p, hash, hashlen ); + break; + + case SIG_RSA_MD2: + memcpy( p, ASN1_HASH_MDX, 18 ); + memcpy( p + 18, hash, 16 ); + p[13] = 2; break; + + case SIG_RSA_MD4: + memcpy( p, ASN1_HASH_MDX, 18 ); + memcpy( p + 18, hash, 16 ); + p[13] = 4; break; + + case SIG_RSA_MD5: + memcpy( p, ASN1_HASH_MDX, 18 ); + memcpy( p + 18, hash, 16 ); + p[13] = 5; break; + + case SIG_RSA_SHA1: + memcpy( p, ASN1_HASH_SHA1, 15 ); + memcpy( p + 15, hash, 20 ); + break; + + case SIG_RSA_SHA224: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 28 ); + p[1] += 28; p[14] = 4; p[18] += 28; break; + + case SIG_RSA_SHA256: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 32 ); + p[1] += 32; p[14] = 1; p[18] += 32; break; + + case SIG_RSA_SHA384: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 48 ); + p[1] += 48; p[14] = 2; p[18] += 48; break; + + case SIG_RSA_SHA512: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 64 ); + p[1] += 64; p[14] = 3; p[18] += 64; break; + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, sig ) + : rsa_private( ctx, f_rng, p_rng, sig, sig ) ); +} + +/* + * Do an RSA operation to sign the message digest + */ +int rsa_pkcs1_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + (void)f_rng; + (void)p_rng; + switch( ctx->padding ) + { + case RSA_PKCS_V15: + return rsa_rsassa_pkcs1_v15_sign( ctx, f_rng, p_rng, mode, hash_id, + hashlen, hash, sig ); + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsassa_pss_sign( ctx, f_rng, p_rng, mode, hash_id, + hashlen, hash, sig ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PSS-VERIFY function + */ +int rsa_rsassa_pss_verify( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + int ret; + size_t siglen; + unsigned char *p; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + unsigned char result[POLARSSL_MD_MAX_SIZE]; + unsigned char zeros[8]; + unsigned int hlen; + size_t slen, msb; + const md_info_t *md_info; + md_context_t md_ctx; + + if( ctx->padding != RSA_PKCS_V21 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + siglen = ctx->len; + + if( siglen < 16 || siglen > sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, buf ) + : rsa_private( ctx, f_rng, p_rng, sig, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + if( buf[siglen - 1] != 0xBC ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + switch( hash_id ) + { + case SIG_RSA_MD2: + case SIG_RSA_MD4: + case SIG_RSA_MD5: + hashlen = 16; + break; + + case SIG_RSA_SHA1: + hashlen = 20; + break; + + case SIG_RSA_SHA224: + hashlen = 28; + break; + + case SIG_RSA_SHA256: + hashlen = 32; + break; + + case SIG_RSA_SHA384: + hashlen = 48; + break; + + case SIG_RSA_SHA512: + hashlen = 64; + break; + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + md_info = md_info_from_type( ctx->hash_id ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + hlen = md_get_size( md_info ); + slen = siglen - hlen - 1; + + memset( zeros, 0, 8 ); + + // Note: EMSA-PSS verification is over the length of N - 1 bits + // + msb = mpi_msb( &ctx->N ) - 1; + + // Compensate for boundary condition when applying mask + // + if( msb % 8 == 0 ) + { + p++; + siglen -= 1; + } + if( buf[0] >> ( 8 - siglen * 8 + msb ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + md_init_ctx( &md_ctx, md_info ); + + mgf_mask( p, siglen - hlen - 1, p + siglen - hlen - 1, hlen, &md_ctx ); + + buf[0] &= 0xFF >> ( siglen * 8 - msb ); + + while( *p == 0 && p < buf + siglen ) + p++; + + if( p == buf + siglen || + *p++ != 0x01 ) + { + md_free_ctx( &md_ctx ); + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } + + slen -= p - buf; + + // Generate H = Hash( M' ) + // + md_starts( &md_ctx ); + md_update( &md_ctx, zeros, 8 ); + md_update( &md_ctx, hash, hashlen ); + md_update( &md_ctx, p, slen ); + md_finish( &md_ctx, result ); + + md_free_ctx( &md_ctx ); + + if( memcmp( p + slen, result, hlen ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PKCS1-v1_5-VERIFY function + */ +int rsa_rsassa_pkcs1_v15_verify( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig ) +{ + int ret; + size_t len, siglen; + unsigned char *p, c; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + + if( ctx->padding != RSA_PKCS_V15 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + siglen = ctx->len; + + if( siglen < 16 || siglen > sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, buf ) + : rsa_private( ctx, f_rng, p_rng, sig, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + if( *p++ != 0 || *p++ != RSA_SIGN ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + while( *p != 0 ) + { + if( p >= buf + siglen - 1 || *p != 0xFF ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + p++; + } + p++; + + len = siglen - ( p - buf ); + + if( len == 33 && hash_id == SIG_RSA_SHA1 ) + { + if( memcmp( p, ASN1_HASH_SHA1_ALT, 13 ) == 0 && + memcmp( p + 13, hash, 20 ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + if( len == 34 ) + { + c = p[13]; + p[13] = 0; + + if( memcmp( p, ASN1_HASH_MDX, 18 ) != 0 ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + if( ( c == 2 && hash_id == SIG_RSA_MD2 ) || + ( c == 4 && hash_id == SIG_RSA_MD4 ) || + ( c == 5 && hash_id == SIG_RSA_MD5 ) ) + { + if( memcmp( p + 18, hash, 16 ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + } + + if( len == 35 && hash_id == SIG_RSA_SHA1 ) + { + if( memcmp( p, ASN1_HASH_SHA1, 15 ) == 0 && + memcmp( p + 15, hash, 20 ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + if( ( len == 19 + 28 && p[14] == 4 && hash_id == SIG_RSA_SHA224 ) || + ( len == 19 + 32 && p[14] == 1 && hash_id == SIG_RSA_SHA256 ) || + ( len == 19 + 48 && p[14] == 2 && hash_id == SIG_RSA_SHA384 ) || + ( len == 19 + 64 && p[14] == 3 && hash_id == SIG_RSA_SHA512 ) ) + { + c = p[1] - 17; + p[1] = 17; + p[14] = 0; + + if( p[18] == c && + memcmp( p, ASN1_HASH_SHA2X, 18 ) == 0 && + memcmp( p + 19, hash, c ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + + if( len == hashlen && hash_id == SIG_RSA_RAW ) + { + if( memcmp( p, hash, hashlen ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + + return( POLARSSL_ERR_RSA_INVALID_PADDING ); +} + +/* + * Do an RSA operation and check the message digest + */ +int rsa_pkcs1_verify( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig ) +{ + switch( ctx->padding ) + { + case RSA_PKCS_V15: + return rsa_rsassa_pkcs1_v15_verify( ctx, f_rng, p_rng, mode, + hash_id, hashlen, hash, sig ); + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsassa_pss_verify( ctx, f_rng, p_rng, mode, hash_id, + hashlen, hash, sig ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +/* + * Free the components of an RSA key + */ +void rsa_free( rsa_context *ctx ) +{ + mpi_free( &ctx->RQ ); mpi_free( &ctx->RP ); mpi_free( &ctx->RN ); + mpi_free( &ctx->QP ); mpi_free( &ctx->DQ ); mpi_free( &ctx->DP ); + mpi_free( &ctx->Q ); mpi_free( &ctx->P ); mpi_free( &ctx->D ); + mpi_free( &ctx->E ); mpi_free( &ctx->N ); +} + +#if defined(POLARSSL_SELF_TEST) + +#include "polarssl/sha1.h" + +/* + * Example RSA-1024 keypair, for test purposes + */ +#define KEY_LEN 128 + +#define RSA_N "9292758453063D803DD603D5E777D788" \ + "8ED1D5BF35786190FA2F23EBC0848AEA" \ + "DDA92CA6C3D80B32C4D109BE0F36D6AE" \ + "7130B9CED7ACDF54CFC7555AC14EEBAB" \ + "93A89813FBF3C4F8066D2D800F7C38A8" \ + "1AE31942917403FF4946B0A83D3D3E05" \ + "EE57C6F5F5606FB5D4BC6CD34EE0801A" \ + "5E94BB77B07507233A0BC7BAC8F90F79" + +#define RSA_E "10001" + +#define RSA_D "24BF6185468786FDD303083D25E64EFC" \ + "66CA472BC44D253102F8B4A9D3BFA750" \ + "91386C0077937FE33FA3252D28855837" \ + "AE1B484A8A9A45F7EE8C0C634F99E8CD" \ + "DF79C5CE07EE72C7F123142198164234" \ + "CABB724CF78B8173B9F880FC86322407" \ + "AF1FEDFDDE2BEB674CA15F3E81A1521E" \ + "071513A1E85B5DFA031F21ECAE91A34D" + +#define RSA_P "C36D0EB7FCD285223CFB5AABA5BDA3D8" \ + "2C01CAD19EA484A87EA4377637E75500" \ + "FCB2005C5C7DD6EC4AC023CDA285D796" \ + "C3D9E75E1EFC42488BB4F1D13AC30A57" + +#define RSA_Q "C000DF51A7C77AE8D7C7370C1FF55B69" \ + "E211C2B9E5DB1ED0BF61D0D9899620F4" \ + "910E4168387E3C30AA1E00C339A79508" \ + "8452DD96A9A5EA5D9DCA68DA636032AF" + +#define RSA_DP "C1ACF567564274FB07A0BBAD5D26E298" \ + "3C94D22288ACD763FD8E5600ED4A702D" \ + "F84198A5F06C2E72236AE490C93F07F8" \ + "3CC559CD27BC2D1CA488811730BB5725" + +#define RSA_DQ "4959CBF6F8FEF750AEE6977C155579C7" \ + "D8AAEA56749EA28623272E4F7D0592AF" \ + "7C1F1313CAC9471B5C523BFE592F517B" \ + "407A1BD76C164B93DA2D32A383E58357" + +#define RSA_QP "9AE7FBC99546432DF71896FC239EADAE" \ + "F38D18D2B2F0E2DD275AA977E2BF4411" \ + "F5A3B2A5D33605AEBBCCBA7FEB9F2D2F" \ + "A74206CEC169D74BF5A8C50D6F48EA08" + +#define PT_LEN 24 +#define RSA_PT "\xAA\xBB\xCC\x03\x02\x01\x00\xFF\xFF\xFF\xFF\xFF" \ + "\x11\x22\x33\x0A\x0B\x0C\xCC\xDD\xDD\xDD\xDD\xDD" + +static int myrand( void *rng_state, unsigned char *output, size_t len ) +{ + size_t i; + + if( rng_state != NULL ) + rng_state = NULL; + + for( i = 0; i < len; ++i ) + output[i] = rand(); + + return( 0 ); +} + +/* + * Checkup routine + */ +int rsa_self_test( int verbose ) +{ + size_t len; + rsa_context rsa; + unsigned char rsa_plaintext[PT_LEN]; + unsigned char rsa_decrypted[PT_LEN]; + unsigned char rsa_ciphertext[KEY_LEN]; +#if defined(POLARSSL_SHA1_C) + unsigned char sha1sum[20]; +#endif + + rsa_init( &rsa, RSA_PKCS_V15, 0 ); + + rsa.len = KEY_LEN; + mpi_read_string( &rsa.N , 16, RSA_N ); + mpi_read_string( &rsa.E , 16, RSA_E ); + mpi_read_string( &rsa.D , 16, RSA_D ); + mpi_read_string( &rsa.P , 16, RSA_P ); + mpi_read_string( &rsa.Q , 16, RSA_Q ); + mpi_read_string( &rsa.DP, 16, RSA_DP ); + mpi_read_string( &rsa.DQ, 16, RSA_DQ ); + mpi_read_string( &rsa.QP, 16, RSA_QP ); + + if( verbose != 0 ) + printf( " RSA key validation: " ); + + if( rsa_check_pubkey( &rsa ) != 0 || + rsa_check_privkey( &rsa ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n PKCS#1 encryption : " ); + + memcpy( rsa_plaintext, RSA_PT, PT_LEN ); + + if( rsa_pkcs1_encrypt( &rsa, &myrand, NULL, RSA_PUBLIC, PT_LEN, + rsa_plaintext, rsa_ciphertext ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n PKCS#1 decryption : " ); + + if( rsa_pkcs1_decrypt( &rsa, &myrand, NULL, RSA_PRIVATE, &len, + rsa_ciphertext, rsa_decrypted, + sizeof(rsa_decrypted) ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( memcmp( rsa_decrypted, rsa_plaintext, len ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + +#if defined(POLARSSL_SHA1_C) + if( verbose != 0 ) + printf( "passed\n PKCS#1 data sign : " ); + + sha1( rsa_plaintext, PT_LEN, sha1sum ); + + if( rsa_pkcs1_sign( &rsa, &myrand, NULL, RSA_PRIVATE, SIG_RSA_SHA1, 20, + sha1sum, rsa_ciphertext ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n PKCS#1 sig. verify: " ); + + if( rsa_pkcs1_verify( &rsa, &myrand, NULL, RSA_PUBLIC, SIG_RSA_SHA1, 20, + sha1sum, rsa_ciphertext ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n\n" ); +#endif /* POLARSSL_SHA1_C */ + + rsa_free( &rsa ); + + return( 0 ); +} + +#endif + +#endif diff --git a/gnuk/regnual/Makefile b/gnuk/regnual/Makefile new file mode 100644 index 0000000000..f150597b10 --- /dev/null +++ b/gnuk/regnual/Makefile @@ -0,0 +1,53 @@ +# Makefile for reGNUal + +PROJECT = regnual + +OBJS = regnual.o usb-stm32f103.o reset.o + +include ../src/config.mk + +LDSCRIPT= regnual.ld + +################################### +MCU = cortex-m3 + +TRGT = arm-none-eabi- +CC = $(TRGT)gcc +LD = $(TRGT)ld +OBJCOPY = $(TRGT)objcopy +OBJDUMP = $(TRGT)objdump + +# THUMB-specific options here +TOPT = -mthumb -DTHUMB -mno-thumb-interwork +# Define C warning options here +CWARN = -Wall -Wextra -Wstrict-prototypes +MCFLAGS= -mcpu=$(MCU) +DEFS += -DFREE_STANDING + +CFLAGS = -O2 -g +CFLAGS += -Wa,-alms=$(notdir $(<:.c=.lst)) -fpie +CFLAGS += $(CWARN) -I . -I ../chopstx -fno-common $(MCFLAGS) $(TOPT) $(DEFS) + +LDFLAGS = -T$(LDSCRIPT) -nostartfiles $(MCFLAGS) $(TOPT) + + +#################### + +all: regnual.hex + +regnual.o: regnual.c ../chopstx/sys.h ../chopstx/usb_lld.h + +regnual.hex: regnual.elf + $(OBJCOPY) -Obinary regnual.elf regnual.bin + $(OBJCOPY) -Oihex regnual.elf regnual.hex + +usb-stm32f103.o: ../chopstx/mcu/usb-stm32f103.c + $(CC) $(CFLAGS) -c -o usb-stm32f103.o ../chopstx/mcu/usb-stm32f103.c + +regnual.elf: $(OBJS) $(LDSCRIPT) + $(CC) $(LDFLAGS) -o regnual.elf $(OBJS) + +clean: + -rm -f $(OBJS) regnual.elf regnual.hex regnual.bin *.lst + +distclean: clean diff --git a/gnuk/regnual/regnual.c b/gnuk/regnual/regnual.c new file mode 100644 index 0000000000..1ee5c56414 --- /dev/null +++ b/gnuk/regnual/regnual.c @@ -0,0 +1,455 @@ +/* + * regnual.c -- Firmware installation for STM32F103 Flash ROM + * + * Copyright (C) 2012, 2013, 2015, 2016, 2017 + * 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 . + * + */ + +/* + * ReGNUal + */ + +#include "types.h" +#include "usb_lld.h" +#include "sys.h" + +extern void *memset (void *s, int c, size_t n); + +extern void set_led (int); +extern int flash_write (uint32_t dst_addr, const uint8_t *src, size_t len); +extern int flash_protect (void); +extern void nvic_system_reset (void); + + +#define FLASH_START_ADDR 0x08000000 /* Fixed for all STM32F1. */ +#define FLASH_OFFSET 0x1000 /* First pages are not-writable. */ +#define FLASH_START (FLASH_START_ADDR+FLASH_OFFSET) +#define FLASH_SIZE_REG ((uint16_t *)0x1ffff7e0) +static uint32_t flash_end; + + +#define ENDP0_RXADDR (0x40) +#define ENDP0_TXADDR (0x80) + +/* USB Standard Device Descriptor */ +static const uint8_t regnual_device_desc[] = { + 18, /* bLength */ + DEVICE_DESCRIPTOR, /* bDescriptorType */ + 0x10, 0x01, /* bcdUSB = 1.1 */ + 0xFF, /* bDeviceClass: VENDOR */ + 0x00, /* bDeviceSubClass */ + 0x00, /* bDeviceProtocol */ + 0x40, /* bMaxPacketSize0 */ +#include "../src/usb-vid-pid-ver.c.inc" + 1, /* Index of string descriptor describing manufacturer */ + 2, /* Index of string descriptor describing product */ + 3, /* Index of string descriptor describing the device's serial number */ + 0x01 /* bNumConfigurations */ +}; + +#if defined(USB_SELF_POWERED) +#define REGNUAL_FEATURE_INIT 0xC0 /* self powered */ +#else +#define REGNUAL_FEATURE_INIT 0x80 /* bus powered */ +#endif + +static const uint8_t regnual_config_desc[] = { + 9, + CONFIG_DESCRIPTOR, /* bDescriptorType: Configuration */ + 18, 0, /* wTotalLength: no of returned bytes */ + 1, /* bNumInterfaces: single vendor interface */ + 0x01, /* bConfigurationValue: Configuration value */ + 0x00, /* iConfiguration: None */ + REGNUAL_FEATURE_INIT, /* bmAttributes: bus powered */ + 50, /* MaxPower 100 mA */ + + /* Interface Descriptor */ + 9, + INTERFACE_DESCRIPTOR, /* bDescriptorType: Interface */ + 0, /* bInterfaceNumber: Index of this interface */ + 0, /* Alternate setting for this interface */ + 0, /* bNumEndpoints: None */ + 0xFF, + 0, + 0, + 0, /* string index for interface */ +}; + +static const uint8_t regnual_string_lang_id[] = { + 4, /* bLength */ + STRING_DESCRIPTOR, + 0x09, 0x04 /* LangID = 0x0409: US-English */ +}; + +#include "../src/usb-strings.c.inc" + +static const uint8_t regnual_string_serial[] = { + 8*2+2, + STRING_DESCRIPTOR, + /* FSIJ-0.0 */ + 'F', 0, 'S', 0, 'I', 0, 'J', 0, '-', 0, + '0', 0, '.', 0, '0', 0, +}; + + +static void +usb_device_reset (struct usb_dev *dev) +{ + usb_lld_reset (dev, REGNUAL_FEATURE_INIT); + + /* Initialize Endpoint 0 */ + usb_lld_setup_endpoint (ENDP0, EP_CONTROL, 0, ENDP0_RXADDR, ENDP0_TXADDR, + 64); +} + +#define USB_REGNUAL_MEMINFO 0 +#define USB_REGNUAL_SEND 1 +#define USB_REGNUAL_RESULT 2 +#define USB_REGNUAL_FLASH 3 +#define USB_REGNUAL_PROTECT 4 +#define USB_REGNUAL_FINISH 5 + +static uint32_t mem[256/4]; +static uint32_t result; + + +static uint32_t rbit (uint32_t v) +{ + uint32_t r; + + asm ("rbit %0, %1" : "=r" (r) : "r" (v)); + return r; +} + +static uint32_t fetch (int i) +{ + uint32_t v; + + v = mem[i]; + return rbit (v); +} + +struct CRC { + __IO uint32_t DR; + __IO uint8_t IDR; + uint8_t RESERVED0; + uint16_t RESERVED1; + __IO uint32_t CR; +}; + +#define CRC_CR_RESET 0x01 +static uint32_t calc_crc32 (void) +{ + struct CRC *CRC = (struct CRC *)0x40023000; + int i; + + CRC->CR = CRC_CR_RESET; + + for (i = 0; i < 256/4; i++) + CRC->DR = fetch (i); + + return rbit (CRC->DR); +} + + +static void +usb_ctrl_write_finish (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT); + + if (type_rcp == (VENDOR_REQUEST | DEVICE_RECIPIENT) + && USB_SETUP_SET (arg->type)) + { + if (arg->request == USB_REGNUAL_SEND && arg->value == 0) + result = calc_crc32 (); + else if (arg->request == USB_REGNUAL_FLASH) + { + uint32_t dst_addr = (0x08000000 + arg->value * 0x100); + + result = flash_write (dst_addr, (const uint8_t *)mem, 256); + } + else if (arg->request == USB_REGNUAL_PROTECT && arg->value == 0) + result = flash_protect (); + else if (arg->request == USB_REGNUAL_FINISH && arg->value == 0) + nvic_system_reset (); + } +} + +static int +usb_setup (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT); + + if (type_rcp == (VENDOR_REQUEST | DEVICE_RECIPIENT)) + { + if (USB_SETUP_GET (arg->type)) + { + if (arg->request == USB_REGNUAL_MEMINFO) + { + const uint8_t *mem_info[2]; + + mem_info[0] = (const uint8_t *)FLASH_START; + mem_info[1] = (const uint8_t *)flash_end; + return usb_lld_ctrl_send (dev, mem_info, sizeof (mem_info)); + } + else if (arg->request == USB_REGNUAL_RESULT) + return usb_lld_ctrl_send (dev, &result, sizeof (uint32_t)); + } + else /* SETUP_SET */ + { + if (arg->request == USB_REGNUAL_SEND) + { + if (arg->value != 0 || arg->index + arg->len > 256) + return -1; + + if (arg->index + arg->len < 256) + memset ((uint8_t *)mem + arg->index + arg->len, 0xff, + 256 - (arg->index + arg->len)); + + return usb_lld_ctrl_recv (dev, mem + arg->index, arg->len); + } + else if (arg->request == USB_REGNUAL_FLASH && arg->len == 0 + && arg->index == 0) + { + uint32_t dst_addr = (0x08000000 + arg->value * 0x100); + + if (dst_addr + 256 <= flash_end) + return usb_lld_ctrl_ack (dev); + } + else if (arg->request == USB_REGNUAL_PROTECT && arg->len == 0 + && arg->value == 0 && arg->index == 0) + return usb_lld_ctrl_ack (dev); + else if (arg->request == USB_REGNUAL_FINISH && arg->len == 0 + && arg->value == 0 && arg->index == 0) + return usb_lld_ctrl_ack (dev); + } + } + + return -1; +} + +static int +usb_get_descriptor (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + uint8_t desc_type = (arg->value >> 8); + uint8_t desc_index = (arg->value & 0xff); + + if (rcp != DEVICE_RECIPIENT) + return -1; + + if (desc_type == DEVICE_DESCRIPTOR) + return usb_lld_ctrl_send (dev, regnual_device_desc, + sizeof (regnual_device_desc)); + else if (desc_type == CONFIG_DESCRIPTOR) + return usb_lld_ctrl_send (dev, regnual_config_desc, + sizeof (regnual_config_desc)); + else if (desc_type == STRING_DESCRIPTOR) + { + const uint8_t *str; + int size; + + switch (desc_index) + { + case 0: + str = regnual_string_lang_id; + size = sizeof (regnual_string_lang_id); + break; + case 1: + str = gnuk_string_vendor; + size = sizeof (gnuk_string_vendor); + break; + case 2: + str = gnuk_string_product; + size = sizeof (gnuk_string_product); + break; + case 3: + str = regnual_string_serial; + size = sizeof (regnual_string_serial); + break; + default: + return -1; + } + + return usb_lld_ctrl_send (dev, str, size); + } + + return -1; +} + +static int +usb_set_configuration (struct usb_dev *dev) +{ + uint8_t current_conf; + + current_conf = usb_lld_current_configuration (dev); + if (current_conf == 0) + { + if (dev->dev_req.value != 1) + return -1; + + usb_lld_set_configuration (dev, 1); + } + else if (current_conf != dev->dev_req.value) + { + if (dev->dev_req.value != 0) + return -1; + + usb_lld_set_configuration (dev, 0); + } + + /* Do nothing when current_conf == value */ + return usb_lld_ctrl_ack (dev); +} + + +static void wait (int count) +{ + int i; + + for (i = 0; i < count; i++) + asm volatile ("" : : "r" (i) : "memory"); +} + +#define WAIT 2400000 + +/* NVIC: Nested Vectored Interrupt Controller. */ +struct NVIC { + volatile uint32_t ISER[8]; + uint32_t unused1[24]; + volatile uint32_t ICER[8]; + uint32_t unused2[24]; + volatile uint32_t ISPR[8]; + uint32_t unused3[24]; + volatile uint32_t ICPR[8]; + uint32_t unused4[24]; + volatile uint32_t IABR[8]; + uint32_t unused5[56]; + volatile uint32_t IPR[60]; +}; +static struct NVIC *const NVIC = (struct NVIC *const)0xE000E100; +#define NVIC_ISER(n) (NVIC->ISER[n >> 5]) + +static void nvic_enable_intr (uint8_t irq_num) +{ + NVIC_ISER (irq_num) = 1 << (irq_num & 0x1f); +} + +#define USB_LP_CAN1_RX0_IRQn 20 +static struct usb_dev dev; + +int +main (int argc, char *argv[]) +{ + (void)argc; (void)argv; + + set_led (0); + +#if defined(STM32F103_OVERRIDE_FLASH_SIZE_KB) + flash_end = FLASH_START_ADDR + STM32F103_OVERRIDE_FLASH_SIZE_KB*1024; +#else + flash_end = FLASH_START_ADDR + (*FLASH_SIZE_REG)*1024; +#endif + + /* + * NVIC interrupt priority was set by Gnuk. + * USB interrupt is disabled by NVIC setting. + * We enable the interrupt again by nvic_enable_intr. + */ + usb_lld_init (&dev, REGNUAL_FEATURE_INIT); + nvic_enable_intr (USB_LP_CAN1_RX0_IRQn); + + while (1) + { + set_led (1); + wait (WAIT); + set_led (0); + wait (WAIT); + } +} + +void +usb_interrupt_handler (void) +{ + uint8_t ep_num; + int e; + + e = usb_lld_event_handler (&dev); + ep_num = USB_EVENT_ENDP (e); + + if (ep_num == 0) + switch (USB_EVENT_ID (e)) + { + case USB_EVENT_DEVICE_RESET: + usb_device_reset (&dev); + break; + + case USB_EVENT_DEVICE_ADDRESSED: + break; + + case USB_EVENT_GET_DESCRIPTOR: + if (usb_get_descriptor (&dev) < 0) + usb_lld_ctrl_error (&dev); + break; + + case USB_EVENT_SET_CONFIGURATION: + if (usb_set_configuration (&dev) < 0) + usb_lld_ctrl_error (&dev); + break; + + case USB_EVENT_SET_INTERFACE: + usb_lld_ctrl_error (&dev); + break; + + case USB_EVENT_CTRL_REQUEST: + /* Device specific device request. */ + if (usb_setup (&dev) < 0) + usb_lld_ctrl_error (&dev); + break; + + case USB_EVENT_GET_STATUS_INTERFACE: + usb_lld_ctrl_error (&dev); + break; + + case USB_EVENT_GET_INTERFACE: + usb_lld_ctrl_error (&dev); + break; + + case USB_EVENT_SET_FEATURE_DEVICE: + case USB_EVENT_SET_FEATURE_ENDPOINT: + case USB_EVENT_CLEAR_FEATURE_DEVICE: + case USB_EVENT_CLEAR_FEATURE_ENDPOINT: + usb_lld_ctrl_ack (&dev); + break; + + case USB_EVENT_CTRL_WRITE_FINISH: + /* Control WRITE transfer finished. */ + usb_ctrl_write_finish (&dev); + break; + + case USB_EVENT_OK: + case USB_EVENT_DEVICE_SUSPEND: + default: + break; + } +} diff --git a/gnuk/regnual/regnual.ld b/gnuk/regnual/regnual.ld new file mode 100644 index 0000000000..0b1ba98596 --- /dev/null +++ b/gnuk/regnual/regnual.ld @@ -0,0 +1,103 @@ +/* + * ST32F103 memory setup. + */ +__main_stack_size__ = 0x0400; +__process_stack_size__ = 0x0200; +__stacks_total_size__ = __main_stack_size__ + __process_stack_size__; + +MEMORY +{ + ram0 : org = 0x20000000, len = 0x1400 + ram1 : org = 0x20001400, len = 20k - 0x1400 +} + +vector = 0x08000000; + +__ram_start__ = ORIGIN(ram0); +__ram_size__ = 20k; +__ram_end__ = __ram_start__ + __ram_size__; + +SECTIONS +{ + . = 0; + + .text : ALIGN(16) SUBALIGN(16) + { + _text = .; + KEEP(*(.vectors)) + *(.text.entry) + *(.text) + *(.text.*) + *(.rodata) + *(.rodata.*) + *(.glue_7t) + *(.glue_7) + *(.gcc*) + } > ram1 + + .got : + { + *(.got) + *(.got.*) + } > ram1 + + .ctors : + { + PROVIDE(_ctors_start_ = .); + KEEP(*(SORT(.ctors.*))) + KEEP(*(.ctors)) + PROVIDE(_ctors_end_ = .); + } > ram1 + + .dtors : + { + PROVIDE(_dtors_start_ = .); + KEEP(*(SORT(.dtors.*))) + KEEP(*(.dtors)) + PROVIDE(_dtors_end_ = .); + } > ram1 + + .ARM.extab : {*(.ARM.extab* .gnu.linkonce.armextab.*)} + + __exidx_start = .; + .ARM.exidx : {*(.ARM.exidx* .gnu.linkonce.armexidx.*)} > ram1 + __exidx_end = .; + + .eh_frame_hdr : {*(.eh_frame_hdr)} + + .eh_frame : ONLY_IF_RO {*(.eh_frame)} + + . = ALIGN(4); + _etext = .; + _textdata = _etext; + + .data : + { + _data = .; + *(.data) + . = ALIGN(4); + *(.data.*) + . = ALIGN(4); + *(.ramtext) + . = ALIGN(4); + _edata = .; + } > ram1 + + .bss : + { + _bss_start = .; + *(.bss) + . = ALIGN(4); + *(.bss.*) + . = ALIGN(4); + *(COMMON) + . = ALIGN(4); + _bss_end = .; + } > ram1 + + PROVIDE(end = .); + _end = .; +} + +__heap_base__ = _end; +__heap_end__ = __ram_end__ - __stacks_total_size__; diff --git a/gnuk/regnual/reset.c b/gnuk/regnual/reset.c new file mode 100644 index 0000000000..f88f054ab7 --- /dev/null +++ b/gnuk/regnual/reset.c @@ -0,0 +1,92 @@ +#include "types.h" + +static void fatal (void) +{ + for (;;); +} + +static void none (void) +{ +} + +/* + * Note: the address of this routine 'entry' will be in the vectors as + * RESET, but this will be called from application. It's not RESET + * state, then. + * + * This routine doesn't change PSP and MSP. Application should + * prepare those stack pointers. + */ +static __attribute__ ((naked,section(".text.entry"))) +void entry (void) +{ + asm volatile ("mov r0, pc\n\t" + "bic r0, r0, #255\n\t" /* R0 := vector_table address */ + "mov r1, #0x90\n\t" /* R1 := numbers of entries * 4 */ + "ldr r3, .L01\n" /* R3 := -0x20001400 fixed addr */ + "0:\n\t" + "ldr r2, [r0, r1]\n\t" + "add r2, r0\n\t" /* Relocate: R0 - 0x20001400 */ + "add r2, r3\n\t" + "str r2, [r0, r1]\n\t" + "subs r1, r1, #4\n\t" + "bne 0b\n\t" + /* Relocation done. */ + "add r0, r3\n\t" + "ldr r3, .L00\n" + ".LPIC00:\n\t" + "add r3, pc\n\t" /* R3 := @_GLOBAL_OFFSET_TABLE_ */ + /* Compute the address of BSS. */ + "ldr r4, .L00+4\n\t" + "ldr r1, [r3, r4]\n\t" + "add r1, r0\n\t" /* relocate bss_start */ + "ldr r4, .L00+8\n\t" + "ldr r2, [r3, r4]\n" + "add r2, r0\n\t" /* relocate bss_end */ + /* Clear BSS. */ + "mov r0, #0\n\t" + "0:\n\t" + "str r0, [r1], #4\n\t" + "cmp r2, r1\n\t" + "bhi 0b\n\t" + "cpsie i\n\t" /* Enable interrupts */ + "mov r0, #0\n\t" + "mov r1, r0\n\t" + "bl main\n" + "1:\n\t" + "b 1b\n\t" + ".align 2\n" + ".L01:\n\t" + ".word -0x20001400\n" + ".L00:\n\t" + ".word _GLOBAL_OFFSET_TABLE_-(.LPIC00+4)\n\t" + ".word _bss_start(GOT)\n\t" + ".word _bss_end(GOT)" + : /* no output */ : /* no input */ : "memory"); +} + +typedef void (*handler)(void); +extern uint8_t __ram_end__; +extern void usb_interrupt_handler (void); + +handler vector_table[] __attribute__ ((section(".vectors"))) = { + (handler)&__ram_end__, + entry, + fatal, /* nmi */ + fatal, /* hard fault */ + /* 10 */ + fatal, /* mem manage */ + fatal, /* bus fault */ + fatal, /* usage fault */ + none, + /* 20 */ + none, none, none, none, none, none, none, none, + /* 40 */ + none, none, none, none, none, none, none, none, + /* 60 */ + none, none, none, none, none, none, none, none, + /* 80 */ + none, none, none, none, + /* 90 */ + usb_interrupt_handler, +}; diff --git a/gnuk/regnual/types.h b/gnuk/regnual/types.h new file mode 100644 index 0000000000..cef27e881a --- /dev/null +++ b/gnuk/regnual/types.h @@ -0,0 +1,13 @@ +typedef unsigned long size_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned int uintptr_t; + +#define TRUE 1 +#define FALSE 0 + +#define NULL 0 + +#define __IO volatile diff --git a/gnuk/src/.gdbinit b/gnuk/src/.gdbinit new file mode 100644 index 0000000000..3e083920cd --- /dev/null +++ b/gnuk/src/.gdbinit @@ -0,0 +1,3 @@ +set arm force-mode thumb +set arm fallback-mode thumb +target remote localhost:3333 diff --git a/gnuk/src/COPYING b/gnuk/src/COPYING new file mode 100644 index 0000000000..94a9ed024d --- /dev/null +++ b/gnuk/src/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/gnuk/src/Makefile b/gnuk/src/Makefile new file mode 100644 index 0000000000..5c728e650d --- /dev/null +++ b/gnuk/src/Makefile @@ -0,0 +1,90 @@ +# Makefile for Gnuk + +# Define project name here +PROJECT = gnuk + +CHOPSTX = ../chopstx + +CSRC = main.c call-rsa.c \ + usb_desc.c usb_ctrl.c \ + usb-ccid.c openpgp.c ac.c openpgp-do.c flash.c \ + bn.c mod.c \ + modp256r1.c jpc_p256r1.c ec_p256r1.c call-ec_p256r1.c \ + modp256k1.c jpc_p256k1.c ec_p256k1.c call-ec_p256k1.c \ + mod25638.c ecc-edwards.c ecc-mont.c sha512.c \ + random.c neug.c sha256.c + +INCDIR = + +CRYPTDIR = ../polarssl +CRYPTSRCDIR = $(CRYPTDIR)/library +CRYPTINCDIR = $(CRYPTDIR)/include +CRYPTSRC = $(CRYPTSRCDIR)/bignum.c $(CRYPTSRCDIR)/rsa.c $(CRYPTSRCDIR)/aes.c + +CSRC += $(CRYPTSRC) +INCDIR += $(CRYPTINCDIR) + +include config.mk + +USE_SYS = yes +USE_USB = yes +USE_ADC = yes +USE_EVENTFLAG = yes + +ifeq ($(EMULATION),) +DEFS += -DFLASH_UPGRADE_SUPPORT +else +DEFS += -DBN256_C_IMPLEMENTATION +endif + +ifneq ($(ENABLE_DEBUG),) +CSRC += debug.c +endif + +ifneq ($(ENABLE_PINPAD),) +CSRC += pin-$(ENABLE_PINPAD).c +endif + +ifeq ($(ENABLE_PINPAD),dnd) +CSRC += usb-msc.c +endif + +ifeq ($(CHIP),stm32f103) +CSRC += mcu-stm32f103.c +endif + +################################### +CC = $(CROSS)gcc +LD = $(CROSS)gcc +OBJCOPY = $(CROSS)objcopy + +CWARN = -Wall -Wextra -Wstrict-prototypes +OPT = -O3 -Os -g + +####################### +include $(CHOPSTX)/rules.mk + +board.h: + @echo Please run configure to have a symbolic link \'board.h\' + @exit 1 + +sys.c: board.h + +build/bignum.o: OPT = -O3 -g + +distclean: clean + -rm -f gnuk.ld config.h board.h config.mk \ + usb-strings.c.inc usb-vid-pid-ver.c.inc + +ifneq ($(EMULATION),) +# By specifying DESTDIR on invocation of "make", you can install +# program to different ROOT. + +# The variables prefix, exec_prefix, libexecdir are defined in +# config.mk. + +install: build/gnuk + test -d "$(DESTDIR)$(libexecdir)" || mkdir -p "$(DESTDIR)$(libexecdir)" + install -c build/gnuk "$(DESTDIR)$(libexecdir)" + +endif diff --git a/gnuk/src/ac.c b/gnuk/src/ac.c new file mode 100644 index 0000000000..f5c2fb360c --- /dev/null +++ b/gnuk/src/ac.c @@ -0,0 +1,289 @@ +/* + * ac.c -- Check access condition + * + * Copyright (C) 2010, 2012, 2013 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 +#include + +#include "config.h" + +#include "gnuk.h" +#include "sha256.h" +#include "random.h" + +uint8_t volatile auth_status; /* Initialized to AC_NONE_AUTHORIZED */ + +int +ac_check_status (uint8_t ac_flag) +{ + if (ac_flag == AC_ALWAYS) + return 1; + else if (ac_flag == AC_NEVER) + return 0; + else + return (ac_flag & auth_status)? 1 : 0; +} + +void +ac_reset_pso_cds (void) +{ + gpg_do_clear_prvkey (GPG_KEY_FOR_SIGNING); + auth_status &= ~AC_PSO_CDS_AUTHORIZED; +} + +void +ac_reset_other (void) +{ + gpg_do_clear_prvkey (GPG_KEY_FOR_DECRYPTION); + gpg_do_clear_prvkey (GPG_KEY_FOR_AUTHENTICATION); + auth_status &= ~AC_OTHER_AUTHORIZED; +} + +int +verify_user_0 (uint8_t access, const uint8_t *pw, int buf_len, int pw_len_known, + const uint8_t *ks_pw1, int save_ks) +{ + int pw_len; + int r1, r2; + uint8_t keystring[KEYSTRING_MD_SIZE]; + const uint8_t *salt; + int salt_len; + + if (gpg_pw_locked (PW_ERR_PW1)) + return 0; + + if (ks_pw1 == NULL) + { + pw_len = strlen (OPENPGP_CARD_INITIAL_PW1); + salt = NULL; + salt_len = 0; + if ((pw_len_known >= 0 && pw_len_known != pw_len) + || buf_len < pw_len + || strncmp ((const char *)pw, OPENPGP_CARD_INITIAL_PW1, pw_len)) + goto failure; + } + else + { + pw_len = ks_pw1[0] & PW_LEN_MASK; + salt = KS_GET_SALT (ks_pw1); + salt_len = SALT_SIZE; + + if ((pw_len_known >= 0 && pw_len_known != pw_len) + || buf_len < pw_len) + goto failure; + } + + s2k (salt, salt_len, pw, pw_len, keystring); + if (save_ks) + memcpy (keystring_md_pw3, keystring, KEYSTRING_MD_SIZE); + + if (access == AC_PSO_CDS_AUTHORIZED) + { + r1 = gpg_do_load_prvkey (GPG_KEY_FOR_SIGNING, BY_USER, keystring); + r2 = 0; + } + else + { + r1 = gpg_do_load_prvkey (GPG_KEY_FOR_DECRYPTION, BY_USER, keystring); + r2 = gpg_do_load_prvkey (GPG_KEY_FOR_AUTHENTICATION, BY_USER, keystring); + } + + if (r1 < 0 || r2 < 0 + || (r1 == 0 && r2 == 0 && ks_pw1 != NULL + && ((ks_pw1[0] & PW_LEN_KEYSTRING_BIT) == 0 + || memcmp (KS_GET_KEYSTRING (ks_pw1), + keystring, KEYSTRING_MD_SIZE) != 0))) + { + failure: + gpg_pw_increment_err_counter (PW_ERR_PW1); + return -1; + } + + gpg_pw_reset_err_counter (PW_ERR_PW1); + return pw_len; +} + +/* + * Verify for "Perform Security Operation : Compute Digital Signature" + */ +int +verify_pso_cds (const uint8_t *pw, int pw_len) +{ + const uint8_t *ks_pw1 = gpg_do_read_simple (NR_DO_KEYSTRING_PW1); + int r; + + DEBUG_INFO ("verify_pso_cds\r\n"); + DEBUG_BYTE (pw_len); + + r = verify_user_0 (AC_PSO_CDS_AUTHORIZED, pw, pw_len, pw_len, ks_pw1, 0); + if (r > 0) + auth_status |= AC_PSO_CDS_AUTHORIZED; + return r; +} + +int +verify_other (const uint8_t *pw, int pw_len) +{ + const uint8_t *ks_pw1 = gpg_do_read_simple (NR_DO_KEYSTRING_PW1); + int r; + + DEBUG_INFO ("verify_other\r\n"); + DEBUG_BYTE (pw_len); + + r = verify_user_0 (AC_OTHER_AUTHORIZED, pw, pw_len, pw_len, ks_pw1, 0); + if (r > 0) + auth_status |= AC_OTHER_AUTHORIZED; + return r; +} + + +static int +verify_admin_00 (const uint8_t *pw, int buf_len, int pw_len_known, + const uint8_t *ks, int save_ks) +{ + int pw_len; + int r1, r2; + uint8_t keystring[KEYSTRING_MD_SIZE]; + const uint8_t *salt; + int salt_len; + + pw_len = ks[0] & PW_LEN_MASK; + salt = KS_GET_SALT (ks); + salt_len = SALT_SIZE; + + if ((pw_len_known >= 0 && pw_len_known != pw_len) || buf_len < pw_len) + return -1; + + s2k (salt, salt_len, pw, pw_len, keystring); + if (save_ks) + memcpy (keystring_md_pw3, keystring, KEYSTRING_MD_SIZE); + + r1 = gpg_do_load_prvkey (GPG_KEY_FOR_SIGNING, BY_ADMIN, keystring); + r2 = 0; + + if (r1 < 0 || r2 < 0) + return -1; + else if (r1 == 0 && r2 == 0) + if ((ks[0] & PW_LEN_KEYSTRING_BIT) == 0 + || memcmp (KS_GET_KEYSTRING (ks), keystring, KEYSTRING_MD_SIZE) != 0) + return -1; + + return pw_len; +} + +uint8_t keystring_md_pw3[KEYSTRING_MD_SIZE]; +uint8_t admin_authorized; + +int +verify_admin_0 (const uint8_t *pw, int buf_len, int pw_len_known, + const uint8_t *pw3_keystring, int save_ks) +{ + int pw_len; + + if (pw3_keystring != NULL) + { + if (gpg_pw_locked (PW_ERR_PW3)) + return 0; + + pw_len = verify_admin_00 (pw, buf_len, pw_len_known, pw3_keystring, + save_ks); + if (pw_len < 0) + { + failure: + gpg_pw_increment_err_counter (PW_ERR_PW3); + return -1; + } + + admin_authorized = BY_ADMIN; + success: /* OK, the admin is now authenticated. */ + gpg_pw_reset_err_counter (PW_ERR_PW3); + return pw_len; + } + else + { + const uint8_t *ks_pw1 = gpg_do_read_simple (NR_DO_KEYSTRING_PW1); + + if (ks_pw1 != NULL) + { /* empty PW3, but PW1 exists */ + int r = verify_user_0 (AC_PSO_CDS_AUTHORIZED, + pw, buf_len, pw_len_known, ks_pw1, save_ks); + + if (r > 0) + admin_authorized = BY_USER; + + return r; + } + + if (gpg_pw_locked (PW_ERR_PW3)) + return 0; + + /* + * For the case of empty PW3 (with empty PW1), pass phrase + * should be OPENPGP_CARD_INITIAL_PW3 + */ + pw_len = strlen (OPENPGP_CARD_INITIAL_PW3); + if ((pw_len_known >=0 && pw_len_known != pw_len) + || buf_len < pw_len + || strncmp ((const char *)pw, OPENPGP_CARD_INITIAL_PW3, pw_len)) + goto failure; + + admin_authorized = BY_ADMIN; + if (save_ks) + s2k (NULL, 0, pw, pw_len, keystring_md_pw3); + goto success; + } +} + + +int +verify_admin (const uint8_t *pw, int pw_len) +{ + int r; + const uint8_t *pw3_keystring; + + pw3_keystring = gpg_do_read_simple (NR_DO_KEYSTRING_PW3); + r = verify_admin_0 (pw, pw_len, pw_len, pw3_keystring, 1); + if (r <= 0) + return r; + + auth_status |= AC_ADMIN_AUTHORIZED; + return 1; +} + +void +ac_reset_admin (void) +{ + memset (keystring_md_pw3, 0, KEYSTRING_MD_SIZE); + auth_status &= ~AC_ADMIN_AUTHORIZED; + admin_authorized = 0; +} + +void +ac_fini (void) +{ + memset (keystring_md_pw3, 0, KEYSTRING_MD_SIZE); + gpg_do_clear_prvkey (GPG_KEY_FOR_SIGNING); + gpg_do_clear_prvkey (GPG_KEY_FOR_DECRYPTION); + gpg_do_clear_prvkey (GPG_KEY_FOR_AUTHENTICATION); + auth_status = AC_NONE_AUTHORIZED; + admin_authorized = 0; +} diff --git a/gnuk/src/affine.h b/gnuk/src/affine.h new file mode 100644 index 0000000000..2f6e875f13 --- /dev/null +++ b/gnuk/src/affine.h @@ -0,0 +1,8 @@ +/** + * @brief Affine coordinates + */ +typedef struct +{ + bn256 x[1]; + bn256 y[1]; +} ac; diff --git a/gnuk/src/bn.c b/gnuk/src/bn.c new file mode 100644 index 0000000000..afc7662a87 --- /dev/null +++ b/gnuk/src/bn.c @@ -0,0 +1,428 @@ +/* + * bn.c -- 256-bit (and 512-bit) bignum calculation + * + * Copyright (C) 2011, 2013, 2014 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 +#include +#ifndef BN256_NO_RANDOM +#include "random.h" +#endif +#include "bn.h" + +uint32_t +bn256_add (bn256 *X, const bn256 *A, const bn256 *B) +{ + int i; + uint32_t v; + uint32_t carry = 0; + uint32_t *px; + const uint32_t *pa, *pb; + + px = X->word; + pa = A->word; + pb = B->word; + + for (i = 0; i < BN256_WORDS; i++) + { + v = *pb; + *px = *pa + carry; + carry = (*px < carry); + *px += v; + carry += (*px < v); + px++; + pa++; + pb++; + } + + return carry; +} + +uint32_t +bn256_sub (bn256 *X, const bn256 *A, const bn256 *B) +{ + int i; + uint32_t v; + uint32_t borrow = 0; + uint32_t *px; + const uint32_t *pa, *pb; + + px = X->word; + pa = A->word; + pb = B->word; + + for (i = 0; i < BN256_WORDS; i++) + { + uint32_t borrow0 = (*pa < borrow); + + v = *pb; + *px = *pa - borrow; + borrow = (*px < v) + borrow0; + *px -= v; + px++; + pa++; + pb++; + } + + return borrow; +} + +uint32_t +bn256_add_uint (bn256 *X, const bn256 *A, uint32_t w) +{ + int i; + uint32_t carry = w; + uint32_t *px; + const uint32_t *pa; + + px = X->word; + pa = A->word; + + for (i = 0; i < BN256_WORDS; i++) + { + *px = *pa + carry; + carry = (*px < carry); + px++; + pa++; + } + + return carry; +} + +uint32_t +bn256_sub_uint (bn256 *X, const bn256 *A, uint32_t w) +{ + int i; + uint32_t borrow = w; + uint32_t *px; + const uint32_t *pa; + + px = X->word; + pa = A->word; + + for (i = 0; i < BN256_WORDS; i++) + { + uint32_t borrow0 = (*pa < borrow); + + *px = *pa - borrow; + borrow = borrow0; + px++; + pa++; + } + + return borrow; +} + +#ifndef BN256_C_IMPLEMENTATION +#define ASM_IMPLEMENTATION 1 +#endif +void +bn256_mul (bn512 *X, const bn256 *A, const bn256 *B) +{ +#if ASM_IMPLEMENTATION +#include "muladd_256.h" + const uint32_t *s; + uint32_t *d; + uint32_t w; + uint32_t c; + + memset (X->word, 0, sizeof (uint32_t)*BN256_WORDS*2); + + s = A->word; d = &X->word[0]; w = B->word[0]; MULADD_256 (s, d, w, c); + s = A->word; d = &X->word[1]; w = B->word[1]; MULADD_256 (s, d, w, c); + s = A->word; d = &X->word[2]; w = B->word[2]; MULADD_256 (s, d, w, c); + s = A->word; d = &X->word[3]; w = B->word[3]; MULADD_256 (s, d, w, c); + s = A->word; d = &X->word[4]; w = B->word[4]; MULADD_256 (s, d, w, c); + s = A->word; d = &X->word[5]; w = B->word[5]; MULADD_256 (s, d, w, c); + s = A->word; d = &X->word[6]; w = B->word[6]; MULADD_256 (s, d, w, c); + s = A->word; d = &X->word[7]; w = B->word[7]; MULADD_256 (s, d, w, c); +#else + int i, j, k; + int i_beg, i_end; + uint32_t r0, r1, r2; + + r0 = r1 = r2 = 0; + for (k = 0; k <= (BN256_WORDS - 1)*2; k++) + { + if (k < BN256_WORDS) + { + i_beg = 0; + i_end = k; + } + else + { + i_beg = k - BN256_WORDS + 1; + i_end = BN256_WORDS - 1; + } + + for (i = i_beg; i <= i_end; i++) + { + uint64_t uv; + uint32_t u, v; + uint32_t carry; + + j = k - i; + + uv = ((uint64_t )A->word[i])*((uint64_t )B->word[j]); + v = uv; + u = (uv >> 32); + r0 += v; + carry = (r0 < v); + r1 += carry; + carry = (r1 < carry); + r1 += u; + carry += (r1 < u); + r2 += carry; + } + + X->word[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + + X->word[k] = r0; +#endif +} + +void +bn256_sqr (bn512 *X, const bn256 *A) +{ +#if ASM_IMPLEMENTATION + int i; + + memset (X->word, 0, sizeof (bn512)); + for (i = 0; i < BN256_WORDS; i++) + { + uint32_t *wij = &X->word[i*2]; + const uint32_t *xj = &A->word[i]; + uint32_t x_i = *xj++; + uint32_t c; + + asm (/* (C,R4,R5) := w_i_i + x_i*x_i; w_i_i := R5; */ + "mov %[c], #0\n\t" + "ldr r5, [%[wij]]\n\t" /* R5 := w_i_i; */ + "mov r4, %[c]\n\t" + "umlal r5, r4, %[x_i], %[x_i]\n\t" + "str r5, [%[wij]], #4\n\t" + "cmp %[xj], %[x_max1]\n\t" + "bhi 0f\n\t" + "mov r9, %[c]\n\t" /* R9 := 0, the constant ZERO from here. */ + "beq 1f\n" + "2:\n\t" + "ldmia %[xj]!, { r7, r8 }\n\t" + "ldmia %[wij], { r5, r6 }\n\t" + /* (C,R4,R5) := (C,R4) + w_i_j + 2*x_i*x_j; */ + "umull r7, r12, %[x_i], r7\n\t" + "adds r5, r5, r4\n\t" + "adc r4, %[c], r9\n\t" + "adds r5, r5, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], r9, r9\n\t" + "adds r5, r5, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], %[c], r9\n\t" + /* (C,R4,R6) := (C,R4) + w_i_j + 2*x_i*x_j; */ + "adds r6, r6, r4\n\t" + "adc r4, %[c], r9\n\t" + "umull r7, r12, %[x_i], r8\n\t" + "adds r6, r6, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], r9, r9\n\t" + "adds r6, r6, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], %[c], r9\n\t" + /**/ + "stmia %[wij]!, { r5, r6 }\n\t" + "cmp %[xj], %[x_max1]\n\t" + "bcc 2b\n\t" + "bne 0f\n" + "1:\n\t" + /* (C,R4,R5) := (C,R4) + w_i_j + 2*x_i*x_j; */ + "ldr r5, [%[wij]]\n\t" + "ldr r6, [%[xj]], #4\n\t" + "adds r5, r5, r4\n\t" + "adc r4, %[c], r9\n\t" + "umull r7, r12, %[x_i], r6\n\t" + "adds r5, r5, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], r9, r9\n\t" + "adds r5, r5, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], %[c], r9\n\t" + "str r5, [%[wij]], #4\n" + "0:\n\t" + "ldr r5, [%[wij]]\n\t" + "adds r4, r4, r5\n\t" + "adc %[c], %[c], #0\n\t" + "str r4, [%[wij]], #4" + : [c] "=&r" (c), [wij] "=r" (wij), [xj] "=r" (xj) + : [x_i] "r" (x_i), [x_max1] "r" (&A->word[BN256_WORDS-1]), + "[wij]" (wij), "[xj]" (xj) + : "r4", "r5", "r6", "r7", "r8", "r9", "r12", "memory", "cc"); + + if (i < BN256_WORDS - 1) + *wij = c; + } +#else + int i, j, k; + int i_beg, i_end; + uint32_t r0, r1, r2; + + r0 = r1 = r2 = 0; + for (k = 0; k <= (BN256_WORDS - 1)*2; k++) + { + if (k < BN256_WORDS) + { + i_beg = 0; + i_end = k/2; + } + else + { + i_beg = k - BN256_WORDS + 1; + i_end = k/2; + } + + for (i = i_beg; i <= i_end; i++) + { + uint64_t uv; + uint32_t u, v; + uint32_t carry; + + j = k - i; + + uv = ((uint64_t )A->word[i])*((uint64_t )A->word[j]); + if (i < j) + { + r2 += ((uv >> 63) != 0); + uv <<= 1; + } + v = uv; + u = (uv >> 32); + r0 += v; + carry = (r0 < v); + r1 += carry; + carry = (r1 < carry); + r1 += u; + carry += (r1 < u); + r2 += carry; + } + + X->word[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + + X->word[k] = r0; +#endif +} + +uint32_t +bn256_shift (bn256 *X, const bn256 *A, int shift) +{ + int i; + uint32_t carry = 0, next_carry; + + if (shift > 0) + { + for (i = 0; i < BN256_WORDS; i++) + { + next_carry = A->word[i] >> (32 - shift); + X->word[i] = (A->word[i] << shift) | carry; + carry = next_carry; + } + } + else + { + shift = -shift; + + for (i = BN256_WORDS - 1; i >= 0; i--) + { + next_carry = A->word[i] & ((1 << shift) - 1); + X->word[i] = (A->word[i] >> shift) | (carry << (32 - shift)); + carry = next_carry; + } + } + + return carry; +} + +int +bn256_is_zero (const bn256 *X) +{ + int i; + int r = 1; + + for (i = 0; i < BN256_WORDS; i++) + r &= (X->word[i] == 0); + + return r; +} + +int +bn256_is_even (const bn256 *X) +{ + return !(X->word[0] & 1); +} + +int +bn256_is_ge (const bn256 *A, const bn256 *B) +{ + uint32_t borrow; + bn256 tmp[1]; + + borrow = bn256_sub (tmp, A, B); + return borrow == 0; +} + + +int +bn256_cmp (const bn256 *A, const bn256 *B) +{ + uint32_t borrow; + int is_zero; + bn256 tmp[1]; + + borrow = bn256_sub (tmp, A, B); + is_zero = bn256_is_zero (tmp); + return is_zero ? 0 : (borrow ? -1 : 1); +} + + +#ifndef BN256_NO_RANDOM +void +bn256_random (bn256 *X) +{ + const uint8_t *rand = random_bytes_get (); + + X->word[7] = ((uint32_t *)rand)[7]; + X->word[6] = ((uint32_t *)rand)[6]; + X->word[5] = ((uint32_t *)rand)[5]; + X->word[4] = ((uint32_t *)rand)[4]; + X->word[3] = ((uint32_t *)rand)[3]; + X->word[2] = ((uint32_t *)rand)[2]; + X->word[1] = ((uint32_t *)rand)[1]; + X->word[0] = ((uint32_t *)rand)[0]; + + random_bytes_free (rand); +} +#endif diff --git a/gnuk/src/bn.h b/gnuk/src/bn.h new file mode 100644 index 0000000000..d22eea0844 --- /dev/null +++ b/gnuk/src/bn.h @@ -0,0 +1,23 @@ +#define BN256_WORDS 8 +typedef struct bn256 { + uint32_t word[ BN256_WORDS ]; /* Little endian */ +} bn256; + +#define BN512_WORDS 16 +typedef struct bn512 { + uint32_t word[ BN512_WORDS ]; /* Little endian */ +} bn512; + +uint32_t bn256_add (bn256 *X, const bn256 *A, const bn256 *B); +uint32_t bn256_sub (bn256 *X, const bn256 *A, const bn256 *B); +uint32_t bn256_add_uint (bn256 *X, const bn256 *A, uint32_t w); +uint32_t bn256_sub_uint (bn256 *X, const bn256 *A, uint32_t w); + +void bn256_mul (bn512 *X, const bn256 *A, const bn256 *B); +void bn256_sqr (bn512 *X, const bn256 *A); +uint32_t bn256_shift (bn256 *X, const bn256 *A, int shift); +int bn256_is_zero (const bn256 *X); +int bn256_is_even (const bn256 *X); +int bn256_is_ge (const bn256 *A, const bn256 *B); +int bn256_cmp (const bn256 *A, const bn256 *B); +void bn256_random (bn256 *X); diff --git a/gnuk/src/board.h b/gnuk/src/board.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gnuk/src/call-ec.c b/gnuk/src/call-ec.c new file mode 100644 index 0000000000..c739173f46 --- /dev/null +++ b/gnuk/src/call-ec.c @@ -0,0 +1,136 @@ +/* + * call-ec.c - interface between Gnuk and Elliptic curve over GF(prime) + * + * Copyright (C) 2013, 2014, 2017 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 "field-group-select.h" + +/* We are little-endian in the computation, but the protocol is big-endian. */ + +#define ECDSA_BYTE_SIZE 32 +#define ECDH_BYTE_SIZE 32 + +int +FUNC(ecdsa_sign) (const uint8_t *hash, uint8_t *output, + const uint8_t *key_data) +{ + int i; + bn256 r[1], s[1], z[1], d[1]; + uint8_t *p; + + p = (uint8_t *)d; + for (i = 0; i < ECDSA_BYTE_SIZE; i++) + p[ECDSA_BYTE_SIZE - i - 1] = key_data[i]; + + p = (uint8_t *)z; + for (i = 0; i < ECDSA_BYTE_SIZE; i++) + p[ECDSA_BYTE_SIZE - i - 1] = hash[i]; + + FUNC(ecdsa) (r, s, z, d); + p = (uint8_t *)r; + for (i = 0; i < ECDSA_BYTE_SIZE; i++) + *output++ = p[ECDSA_BYTE_SIZE - i - 1]; + p = (uint8_t *)s; + for (i = 0; i < ECDSA_BYTE_SIZE; i++) + *output++ = p[ECDSA_BYTE_SIZE - i - 1]; + return 0; +} + +int +FUNC(ecc_compute_public) (const uint8_t *key_data, uint8_t *pubkey) +{ + uint8_t *p, *p1; + ac q[1]; + bn256 k[1]; + int i; + + p = (uint8_t *)k; + for (i = 0; i < ECDSA_BYTE_SIZE; i++) + p[ECDSA_BYTE_SIZE - i - 1] = key_data[i]; + if (FUNC(compute_kG) (q, k) < 0) + return -1; + + p = pubkey; + p1 = (uint8_t *)q->x; + for (i = 0; i < ECDSA_BYTE_SIZE; i++) + *p++ = p1[ECDSA_BYTE_SIZE - i - 1]; + p1 = (uint8_t *)q->y; + for (i = 0; i < ECDSA_BYTE_SIZE; i++) + *p++ = p1[ECDSA_BYTE_SIZE - i - 1]; + + return 0; +} + +int +FUNC(ecdh_decrypt) (const uint8_t *input, uint8_t *output, + const uint8_t *key_data) +{ + bn256 k[1]; + ac X[1], P[1]; + int i; + uint8_t *p0; + const uint8_t *p1; + int r; + + p0 = (uint8_t *)k; + for (i = 0; i < ECDH_BYTE_SIZE; i++) + p0[ECDH_BYTE_SIZE - i - 1] = key_data[i]; + + p1 = input+1; /* skip '04' */ + p0 = (uint8_t *)P->x; + for (i = 0; i < ECDH_BYTE_SIZE; i++) + p0[ECDH_BYTE_SIZE - i - 1] = *p1++; + p0 = (uint8_t *)P->y; + for (i = 0; i < ECDH_BYTE_SIZE; i++) + p0[ECDH_BYTE_SIZE - i - 1] = *p1++; + + r = FUNC(compute_kP) (X, k, P); + if (r == 0) + { + p0 = output; + p1 = (const uint8_t *)X->x; + *p0++ = 4; + for (i = 0; i < ECDH_BYTE_SIZE; i++) + *p0++ = p1[ECDH_BYTE_SIZE - i - 1]; + p1 = (const uint8_t *)X->y; + for (i = 0; i < ECDH_BYTE_SIZE; i++) + *p0++ = p1[ECDH_BYTE_SIZE - i - 1]; + } + + return r; +} + + +/** + * @brief Check if a secret d0 is valid or not + * + * @param D0 scalar D0: secret + * @param D1 scalar D1: secret candidate N-D0 + * + * Return 0 on error. + * Return -1 when D1 should be used as the secret + * Return 1 when D0 should be used as the secret + */ +int +FUNC(ecc_check_secret) (const uint8_t *d0, uint8_t *d1) +{ + return FUNC(check_secret) ((const bn256 *)d0, (bn256 *)d1); +} diff --git a/gnuk/src/call-ec_p256k1.c b/gnuk/src/call-ec_p256k1.c new file mode 100644 index 0000000000..efc7cadb8d --- /dev/null +++ b/gnuk/src/call-ec_p256k1.c @@ -0,0 +1,34 @@ +/* + * call-ec_p256k1.c - interface between Gnuk and Elliptic curve over + * GF(p256k1) + * + * Copyright (C) 2014, 2017 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 +#include +#include "bn.h" +#include "affine.h" +#include "jpc-ac_p256k1.h" +#include "ec_p256k1.h" + +#define FIELD p256k1 + +#include "call-ec.c" diff --git a/gnuk/src/call-ec_p256r1.c b/gnuk/src/call-ec_p256r1.c new file mode 100644 index 0000000000..e851cca267 --- /dev/null +++ b/gnuk/src/call-ec_p256r1.c @@ -0,0 +1,34 @@ +/* + * call-ec_p256r1.c - interface between Gnuk and Elliptic curve over + * GF(p256r1) + * + * Copyright (C) 2014, 2017 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 +#include +#include "bn.h" +#include "affine.h" +#include "jpc-ac_p256r1.h" +#include "ec_p256r1.h" + +#define FIELD p256r1 + +#include "call-ec.c" diff --git a/gnuk/src/call-rsa.c b/gnuk/src/call-rsa.c new file mode 100644 index 0000000000..57b7c9e15e --- /dev/null +++ b/gnuk/src/call-rsa.c @@ -0,0 +1,274 @@ +/* + * call-rsa.c -- Glue code between RSA computation and OpenPGP card protocol + * + * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2017 + * 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 +#include +#include + +#include "config.h" + +#include "gnuk.h" +#include "status-code.h" +#include "random.h" +#include "polarssl/config.h" +#include "polarssl/rsa.h" + +static rsa_context rsa_ctx; +static struct chx_cleanup clp; + +static void +rsa_cleanup (void *arg) +{ + (void)arg; + rsa_free (&rsa_ctx); +} + + +int +rsa_sign (const uint8_t *raw_message, uint8_t *output, int msg_len, + struct key_data *kd, int pubkey_len) +{ + mpi P1, Q1, H; + int ret = 0; + unsigned char temp[pubkey_len]; + + rsa_init (&rsa_ctx, RSA_PKCS_V15, 0); + + mpi_init (&P1); mpi_init (&Q1); mpi_init (&H); + + rsa_ctx.len = pubkey_len; + MPI_CHK( mpi_lset (&rsa_ctx.E, 0x10001) ); + MPI_CHK( mpi_read_binary (&rsa_ctx.P, &kd->data[0], pubkey_len / 2) ); + MPI_CHK( mpi_read_binary (&rsa_ctx.Q, &kd->data[pubkey_len / 2], + pubkey_len / 2) ); +#if 0 + MPI_CHK( mpi_mul_mpi (&rsa_ctx.N, &rsa_ctx.P, &rsa_ctx.Q) ); +#endif + MPI_CHK( mpi_sub_int (&P1, &rsa_ctx.P, 1) ); + MPI_CHK( mpi_sub_int (&Q1, &rsa_ctx.Q, 1) ); + MPI_CHK( mpi_mul_mpi (&H, &P1, &Q1) ); + MPI_CHK( mpi_inv_mod (&rsa_ctx.D , &rsa_ctx.E, &H) ); + MPI_CHK( mpi_mod_mpi (&rsa_ctx.DP, &rsa_ctx.D, &P1) ); + MPI_CHK( mpi_mod_mpi (&rsa_ctx.DQ, &rsa_ctx.D, &Q1) ); + MPI_CHK( mpi_inv_mod (&rsa_ctx.QP, &rsa_ctx.Q, &rsa_ctx.P) ); + cleanup: + mpi_free (&P1); mpi_free (&Q1); mpi_free (&H); + if (ret == 0) + { + int cs; + + DEBUG_INFO ("RSA sign..."); + clp.next = NULL; + clp.routine = rsa_cleanup; + clp.arg = NULL; + chopstx_cleanup_push (&clp); + cs = chopstx_setcancelstate (0); /* Allow cancellation. */ + ret = rsa_rsassa_pkcs1_v15_sign (&rsa_ctx, NULL, NULL, + RSA_PRIVATE, SIG_RSA_RAW, + msg_len, raw_message, temp); + memcpy (output, temp, pubkey_len); + chopstx_setcancelstate (cs); + chopstx_cleanup_pop (0); + } + + rsa_free (&rsa_ctx); + if (ret != 0) + { + DEBUG_INFO ("fail:"); + DEBUG_SHORT (ret); + return -1; + } + else + { + DEBUG_INFO ("done.\r\n"); + GPG_SUCCESS (); + return 0; + } +} + +/* + * LEN: length in byte + */ +int +modulus_calc (const uint8_t *p, int len, uint8_t *pubkey) +{ + mpi P, Q, N; + int ret; + + mpi_init (&P); mpi_init (&Q); mpi_init (&N); + MPI_CHK( mpi_read_binary (&P, p, len / 2) ); + MPI_CHK( mpi_read_binary (&Q, p + len / 2, len / 2) ); + MPI_CHK( mpi_mul_mpi (&N, &P, &Q) ); + MPI_CHK( mpi_write_binary (&N, pubkey, len) ); + cleanup: + mpi_free (&P); mpi_free (&Q); mpi_free (&N); + if (ret != 0) + return -1; + + return 0; +} + + +int +rsa_decrypt (const uint8_t *input, uint8_t *output, int msg_len, + struct key_data *kd, unsigned int *output_len_p) +{ + mpi P1, Q1, H; + int ret; +#ifdef GNU_LINUX_EMULATION + size_t output_len; +#endif + + DEBUG_INFO ("RSA decrypt:"); + DEBUG_WORD ((uint32_t)&ret); + + rsa_init (&rsa_ctx, RSA_PKCS_V15, 0); + mpi_init (&P1); mpi_init (&Q1); mpi_init (&H); + + rsa_ctx.len = msg_len; + DEBUG_WORD (msg_len); + + MPI_CHK( mpi_lset (&rsa_ctx.E, 0x10001) ); + MPI_CHK( mpi_read_binary (&rsa_ctx.P, &kd->data[0], msg_len / 2) ); + MPI_CHK( mpi_read_binary (&rsa_ctx.Q, &kd->data[msg_len / 2], msg_len / 2) ); +#if 0 + MPI_CHK( mpi_mul_mpi (&rsa_ctx.N, &rsa_ctx.P, &rsa_ctx.Q) ); +#endif + MPI_CHK( mpi_sub_int (&P1, &rsa_ctx.P, 1) ); + MPI_CHK( mpi_sub_int (&Q1, &rsa_ctx.Q, 1) ); + MPI_CHK( mpi_mul_mpi (&H, &P1, &Q1) ); + MPI_CHK( mpi_inv_mod (&rsa_ctx.D , &rsa_ctx.E, &H) ); + MPI_CHK( mpi_mod_mpi (&rsa_ctx.DP, &rsa_ctx.D, &P1) ); + MPI_CHK( mpi_mod_mpi (&rsa_ctx.DQ, &rsa_ctx.D, &Q1) ); + MPI_CHK( mpi_inv_mod (&rsa_ctx.QP, &rsa_ctx.Q, &rsa_ctx.P) ); + cleanup: + mpi_free (&P1); mpi_free (&Q1); mpi_free (&H); + if (ret == 0) + { + int cs; + + DEBUG_INFO ("RSA decrypt ..."); + clp.next = NULL; + clp.routine = rsa_cleanup; + clp.arg = NULL; + chopstx_cleanup_push (&clp); + cs = chopstx_setcancelstate (0); /* Allow cancellation. */ +#ifdef GNU_LINUX_EMULATION + ret = rsa_rsaes_pkcs1_v15_decrypt (&rsa_ctx, NULL, NULL, + RSA_PRIVATE, &output_len, input, + output, MAX_RES_APDU_DATA_SIZE); + *output_len_p = (unsigned int)output_len; +#else + ret = rsa_rsaes_pkcs1_v15_decrypt (&rsa_ctx, NULL, NULL, + RSA_PRIVATE, output_len_p, input, + output, MAX_RES_APDU_DATA_SIZE); +#endif + chopstx_setcancelstate (cs); + chopstx_cleanup_pop (0); + } + + rsa_free (&rsa_ctx); + if (ret != 0) + { + DEBUG_INFO ("fail:"); + DEBUG_SHORT (ret); + return -1; + } + else + { + DEBUG_INFO ("done.\r\n"); + GPG_SUCCESS (); + return 0; + } +} + +int +rsa_verify (const uint8_t *pubkey, int pubkey_len, + const uint8_t *hash, const uint8_t *sig) +{ + int ret; + + rsa_init (&rsa_ctx, RSA_PKCS_V15, 0); + rsa_ctx.len = pubkey_len; + MPI_CHK( mpi_lset (&rsa_ctx.E, 0x10001) ); + MPI_CHK( mpi_read_binary (&rsa_ctx.N, pubkey, pubkey_len) ); + + DEBUG_INFO ("RSA verify..."); + + MPI_CHK( rsa_rsassa_pkcs1_v15_verify (&rsa_ctx, NULL, NULL, + RSA_PUBLIC, SIG_RSA_SHA256, 32, + hash, sig) ); + cleanup: + rsa_free (&rsa_ctx); + if (ret != 0) + { + DEBUG_INFO ("fail:"); + DEBUG_SHORT (ret); + return -1; + } + else + { + DEBUG_INFO ("verified.\r\n"); + return 0; + } +} + +#define RSA_EXPONENT 0x10001 + +int +rsa_genkey (int pubkey_len, uint8_t *pubkey, uint8_t *p_q) +{ + int ret; + uint8_t index = 0; + uint8_t *p = p_q; + uint8_t *q = p_q + pubkey_len / 2; + int cs; + + extern int prng_seed (int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + extern void neug_flush (void); + + neug_flush (); + prng_seed (random_gen, &index); + rsa_init (&rsa_ctx, RSA_PKCS_V15, 0); + + clp.next = NULL; + clp.routine = rsa_cleanup; + clp.arg = NULL; + chopstx_cleanup_push (&clp); + cs = chopstx_setcancelstate (0); /* Allow cancellation. */ + MPI_CHK( rsa_gen_key (&rsa_ctx, random_gen, &index, pubkey_len * 8, + RSA_EXPONENT) ); + MPI_CHK( mpi_write_binary (&rsa_ctx.P, p, pubkey_len / 2) ); + MPI_CHK( mpi_write_binary (&rsa_ctx.Q, q, pubkey_len / 2) ); + MPI_CHK( mpi_write_binary (&rsa_ctx.N, pubkey, pubkey_len) ); + + cleanup: + chopstx_setcancelstate (cs); + chopstx_cleanup_pop (1); + if (ret != 0) + return -1; + else + return 0; +} diff --git a/gnuk/src/config.h b/gnuk/src/config.h new file mode 100644 index 0000000000..3c7dd45806 --- /dev/null +++ b/gnuk/src/config.h @@ -0,0 +1,11 @@ +#undef DEBUG +#ifdef DEBUG +#define ENABLE_VIRTUAL_COM_PORT 1 +#endif +#undef DFU_SUPPORT +#undef PINPAD_SUPPORT + +#undef CERTDO_SUPPORT +#undef HID_CARD_CHANGE_SUPPORT +#define SERIALNO_STR_LEN 11 +#undef LIFE_CYCLE_MANAGEMENT_SUPPORT diff --git a/gnuk/src/config.h.in b/gnuk/src/config.h.in new file mode 100644 index 0000000000..572316700c --- /dev/null +++ b/gnuk/src/config.h.in @@ -0,0 +1,11 @@ +@DEBUG_DEFINE@ +#ifdef DEBUG +#define ENABLE_VIRTUAL_COM_PORT 1 +#endif +@DFU_DEFINE@ +@PINPAD_DEFINE@ +@PINPAD_MORE_DEFINE@ +@CERTDO_DEFINE@ +@HID_CARD_CHANGE_DEFINE@ +@SERIALNO_STR_LEN_DEFINE@ +@LIFE_CYCLE_MANAGEMENT_DEFINE@ diff --git a/gnuk/src/config.mk b/gnuk/src/config.mk new file mode 100644 index 0000000000..7535359406 --- /dev/null +++ b/gnuk/src/config.mk @@ -0,0 +1,11 @@ +CHIP=stm32f103 +EMULATION= +CROSS=arm-none-eabi- +MCU=cortex-m3 +DEFS= -DMEMORY_SIZE=20 +LDSCRIPT=gnuk.ld +LIBS= +# ENABLE_DEBUG=1 +# ENABLE_PINPAD= +ENABLE_FRAUCHEKY= +ENABLE_OUTPUT_HEX=yes diff --git a/gnuk/src/configure b/gnuk/src/configure new file mode 100644 index 0000000000..09925e2e5d --- /dev/null +++ b/gnuk/src/configure @@ -0,0 +1,451 @@ +#! /bin/bash + +# This is bash which supports ANSI-C Quoting +nl=$'\n' + +# +# This file is *NOT* generated by GNU Autoconf, but written by NIIBE Yutaka +# +# Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 +# Free Software Initiative of Japan +# +# 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 . + +# +# Submodule check +# +if ! test -f ../chopstx/rules.mk; then + echo "Submodule 'chopstx' not found" >&2 + echo "You might need: git submodule update --init" >&2 + exit 1 +fi + +# Default settings +help=no +vidpid=none +target=FST_01 +with_dfu=default +debug=no +sys1_compat=yes +pinpad=no +certdo=no +hid_card_change=no +factory_reset=no +flash_override="" +# For emulation +prefix=/usr/local +exec_prefix='${prefix}' +libexecdir='${exec_prefix}/libexec' + +# Revision number +if test -e ../.git; then + if type git >/dev/null 2>&1; then + REVISION=$(git describe --dirty="-modified") + else + # echo 'No git available, please install git' + GIT_REVISION=$(sed -e 's/^\(.......\).*$/g\1/' "../.git/$(sed -e 's/^ref: //' ../.git/HEAD)") + REVISION=$(cat ../VERSION)-$GIT_REVISION + fi +else + REVISION=$(cat ../VERSION) +fi + +# Process each option +for option; do + case $option in + *=*) optarg=$(expr "X$option" : '[^=]*=\(.*\)') ;; + *) optarg=yes ;; + esac + + case $option in + -h | --help) + help=yes ;; + --vidpid=*) + vidpid=$optarg ;; + --target=*) + target=$optarg ;; + --enable-debug) + debug=yes ;; + --disable-debug) + debug=no ;; + --enable-pinpad=*) + pinpad=$optarg ;; + --disable-pinpad) + pinpad=no ;; + --enable-certdo) + certdo=yes ;; + --disable-certdo) + certdo=no ;; + --enable-hid-card-change) + hid_card_change=yes ;; + --disable-hid-card-change) + hid_card_change=no ;; + --enable-sys1-compat) + sys1_compat=yes ;; + --disable-sys1-compat) + sys1_compat=no ;; + --enable-factory-reset) + factory_reset=yes ;; + --disable-factory-reset) + factory_reset=no ;; + --with-dfu) + with_dfu=yes ;; + --without-dfu) + with_dfu=no ;; + # + # For emulation + # + --prefix=*) + prefix=optarg ;; + --exec-prefix=*) + exec_prefix=optarg ;; + --libexecdir=*) + libexecdir=optarg ;; + *) + echo "Unrecognized option \`$option'" >&2 + echo "Try \`$0 --help' for more information." >&2 + exit 1 + ;; + esac +done + +if test "$help" = "yes"; then + cat <] + --target=TARGET specify target [FST_01] + supported targets are: + FST_01 + FST_01G + OLIMEX_STM32_H103 + MAPLE_MINI + ST_DONGLE + ST_NUCLEO_F103 + NITROKEY_START + BLUE_PILL + STM8S_DISCOVERY + CQ_STARM + STM32_PRIMER2 + STBEE + STBEE_MINI + FST_01_00 (unreleased version with 8MHz XTAL) + --enable-factory-reset + support life cycle management [no] + --enable-debug debug with virtual COM port [no] + --enable-pinpad=cir + PIN entry support [no] + --enable-certdo support CERT.3 data object [no] + --enable-sys1-compat enable SYS 1.0 compatibility [yes] + executable is target dependent + --disable-sys1-compat disable SYS 1.0 compatibility [no] + executable is target independent + but requires SYS 2.0 or newer + --with-dfu build image for DFU [] +EOF + exit 0 +fi + +BOARD_HEADER_FILE=board-$(echo $target | tr '_[:upper:]' '-[:lower:]').h +echo "Header file is: $BOARD_HEADER_FILE" +ln -sf "../chopstx/board/$BOARD_HEADER_FILE" board.h + +# Flash page size in byte +FLASH_PAGE_SIZE=1024 +# Flash memory size in KiB +FLASH_SIZE=128 +# Memory size in KiB +MEMORY_SIZE=20 + +# Settings for TARGET +case $target in +BLUE_PILL|STM8S_DISCOVERY) + # It's 64KB version of STM32F103, but actually has 128KB + flash_override="-DSTM32F103_OVERRIDE_FLASH_SIZE_KB=128" + ;; +CQ_STARM|STBEE_MINI) + if test "$with_dfu" = "default"; then + with_dfu=yes; + fi ;; +STM32_PRIMER2) + FLASH_PAGE_SIZE=2048 + FLASH_SIZE=512 + MEMORY_SIZE=64 + ;; +STBEE) + FLASH_PAGE_SIZE=2048 + FLASH_SIZE=512 + MEMORY_SIZE=64 + if test "$with_dfu" = "default"; then + with_dfu=yes; + fi ;; +*) + ;; +esac + +if test "$target" = "GNU_LINUX"; then + ldscript="" + chip="gnu-linux" + emulation="yes" + cross="" + mcu="none" + def_emulation="-DGNU_LINUX_EMULATION" + def_memory_size="-DMEMORY_SIZE=1024" + enable_hexoutput="" + libs="-lpthread" +else + ldscript="gnuk.ld" + chip="stm32f103" + emulation="" + cross="arm-none-eabi-" + mcu="cortex-m3" + def_emulation="" + def_memory_size="-DMEMORY_SIZE=$MEMORY_SIZE" + enable_hexoutput=yes + libs="" +fi + +if test "$emulation" = "yes"; then + if test "$vidpid" = "none"; then + vidpid=0000:0000 + else + echo "Please don't specify VID:PID for emulation at compile time;" + echo "It is a user who should specify VID:PID at run time." + exit 1 + fi +else + if test "$vidpid" = "none"; then + echo "Please specify Vendor ID and Product ID by --vidpid option." >&2 + exit 1 + fi +fi + +# --enable-debug option +if test "$debug" = "yes"; then + DEBUG_MAKE_OPTION="ENABLE_DEBUG=1" + DEBUG_DEFINE="#define DEBUG 1" + echo "Debug option enabled" +else + DEBUG_MAKE_OPTION="# ENABLE_DEBUG=1" + DEBUG_DEFINE="#undef DEBUG" + echo "Debug option disabled" +fi + +# --with-dfu option +if test "$with_dfu" = "yes"; then + if test "$target" = "FST_01" -o "$target" = "FST_01G" \ + -o "$target" = "FST_01_00"; then + echo "FST-01 doesn't have DFU loader, you should not use --with-dfu." >&2 + exit 1 + fi + echo "Configured for DFU" + ORIGIN=0x08003000 + FLASH_SIZE=$((FLASH_SIZE - 12)) + DFU_DEFINE="#define DFU_SUPPORT 1" +else + with_dfu=no + echo "Configured for bare system (no-DFU)" + ORIGIN=0x08000000 + DFU_DEFINE="#undef DFU_SUPPORT" +fi + +# --enable-pinpad option +if test "$pinpad" = "no"; then + PINPAD_MAKE_OPTION="# ENABLE_PINPAD=" + PINPAD_DEFINE="#undef PINPAD_SUPPORT" + PINPAD_MORE_DEFINE="" + echo "PIN pad option disabled" +else + PINPAD_MAKE_OPTION="ENABLE_PINPAD=$pinpad" + PINPAD_DEFINE="#define PINPAD_SUPPORT 1" + PINPAD_MORE_DEFINE="#define PINPAD_${pinpad^^[a-z]}_SUPPORT 1" + echo "PIN pad option enabled ($pinpad)" +fi + +# --enable-certdo option +if test "$certdo" = "yes"; then + CERTDO_DEFINE="#define CERTDO_SUPPORT 1" + echo "CERT.3 Data Object is supported" +else + CERTDO_DEFINE="#undef CERTDO_SUPPORT" + echo "CERT.3 Data Object is NOT supported" +fi + +# --enable-hid-card-change option +if test "$hid_card_change" = "yes"; then + HID_CARD_CHANGE_DEFINE="#define HID_CARD_CHANGE_SUPPORT 1" + echo "Card insert/removal by HID device is supported" +else + HID_CARD_CHANGE_DEFINE="#undef HID_CARD_CHANGE_SUPPORT" + echo "Card insert/removal by HID device is NOT supported" +fi + +# --enable-factory-reset option +if test "$factory_reset" = "yes"; then + LIFE_CYCLE_MANAGEMENT_DEFINE="#define LIFE_CYCLE_MANAGEMENT_SUPPORT 1" + echo "Life cycle management is supported" +else + LIFE_CYCLE_MANAGEMENT_DEFINE="#undef LIFE_CYCLE_MANAGEMENT_SUPPORT" + echo "Life cycle management is NOT supported" +fi + +### !!! Replace following string of "FSIJ" to yours !!! #### +SERIALNO="FSIJ-$(sed -e 's%^[^/]*/%%' <../VERSION)-" + +SERIALNO_STR_LEN_DEFINE="#define SERIALNO_STR_LEN ${#SERIALNO}" + + +if test "$sys1_compat" = "yes"; then + CONFIG="$target:dfu=$with_dfu:debug=$debug:pinpad=$pinpad:certdo=$certdo:factory_reset=$factory_reset" +else + if test "$with_dfu" = "yes"; then + echo "Common binary can't support DFU loader, don't use --with-dfu." >&2 + exit 1 + fi + # Override settings for common binary. Safer side. + FLASH_PAGE_SIZE=2048 + FLASH_SIZE=128 + MEMORY_SIZE=20 + CONFIG="common:debug=$debug:pinpad=$pinpad:certdo=$certdo:factory_reset=$factory_reset" +fi + +output_vid_pid_version () { + echo "$VIDPID" | sed -n -e "s%^\([0-9a-f][0-9a-f]\)\([0-9a-f][0-9a-f]\):\([0-9a-f][0-9a-f]\)\([0-9a-f][0-9a-f]\)$% 0x\2, 0x\1, /* idVendor */\\${nl} 0x\4, 0x\3, /* idProduct */%p" + echo "$VERSION" | sed -n -e "s%^\([0-9a-f][0-9a-f]\)\([0-9a-f][0-9a-f]\)$% 0x\2, 0x\1, /* bcdDevice */%p" +} + +output_vendor_product_serial_strings () { + name=$1 + + echo "static const uint8_t ${name}string_vendor[] = {" + echo " ${#VENDOR}*2+2, /* bLength */" + echo " STRING_DESCRIPTOR, /* bDescriptorType */" + echo " /* Manufacturer: \"$VENDOR\" */" + echo "$VENDOR" | sed -e "s/\(........\)/\1\\${nl}/g" | sed -n -e "s/\(.\)/'\1', 0, /g" -e "s/^/ /" -e "/^ ./s/ $//p" + echo '};' + echo + echo "static const uint8_t ${name}string_product[] = {" + echo " ${#PRODUCT}*2+2, /* bLength */" + echo " STRING_DESCRIPTOR, /* bDescriptorType */" + echo " /* Product name: \"$PRODUCT\" */" + echo "$PRODUCT" | sed -e "s/\(........\)/\1\\${nl}/g" | sed -n -e "s/\(.\)/'\1', 0, /g" -e "s/^/ /" -e "/^ ./s/ $//p" + echo '};' + + if test -n "$name"; then + echo + echo "const uint8_t ${name}string_serial[] = {" + echo " ${#SERIALNO}*2+2+16, /* bLength */" + echo " STRING_DESCRIPTOR, /* bDescriptorType */" + echo " /* Serial number: \"$SERIALNO\" */" + echo "$SERIALNO" | sed -e "s/\(........\)/\1\\${nl}/g" | sed -n -e "s/\(.\)/'\1', 0, /g" -e "s/^/ /" -e "/^ ./s/ $//p" + if test "$emulation" = "yes"; then + echo " 'E', 0, 'M', 0, 'U', 0, 'L', 0," + echo " 'A', 0, 'T', 0, 'E', 0, 'D', 0," + else + echo " 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff," + echo " 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff," + fi + echo '};' + echo + echo '#ifdef USB_STRINGS_FOR_GNUK' + echo "static const uint8_t ${name}revision_detail[] = {" + echo " ${#REVISION}*2+2, /* bLength */" + echo " STRING_DESCRIPTOR, /* bDescriptorType */" + echo " /* revision detail: \"$REVISION\" */" + echo "$REVISION" | sed -e "s/\(........\)/\1\\${nl}/g" | sed -n -e "s/\(.\)/'\1', 0, /g" -e "s/^/ /" -e "/^ ./s/ $//p" + echo '};' + echo + echo "static const uint8_t ${name}config_options[] = {" + echo " ${#CONFIG}*2+2, /* bLength */" + echo " STRING_DESCRIPTOR, /* bDescriptorType */" + echo " /* configure options: \"$CONFIG\" */" + echo $CONFIG | sed -e "s/\(........\)/\1\\${nl}/g" | sed -n -e "s/\(.\)/'\1', 0, /g" -e "s/^/ /" -e "/^ ./s/ $//p" + echo '};' + echo '#endif' + fi +} + +if !(IFS=" " + while read -r VIDPID VERSION PRODUCT VENDOR; do + if test "$vidpid" = "$VIDPID"; then + output_vid_pid_version > usb-vid-pid-ver.c.inc + output_vendor_product_serial_strings gnuk_ >usb-strings.c.inc + exit 0 + fi + done; exit 1) < ../GNUK_USB_DEVICE_ID +then + echo "Please specify valid Vendor ID and Product ID." >&2 + echo "Check ../GNUK_USB_DEVICE_ID." >&2 + exit 1 +fi + +if test "$sys1_compat" = "no"; then + # Disable when you are sure that it's sys version 3.0 or later. + # Note that Gnuk 1.0 and NeuG (until 0.06) uses sys version 1.0. + # Disabling the compatibility, executable will be target independent, + # assuming the clock initialization will be done by clock_init in + # SYS. + use_sys3="-DUSE_SYS3" +else + use_sys3="" +fi + + +(echo "CHIP=$chip"; + echo "EMULATION=$emulation"; + echo "CROSS=$cross"; + echo "MCU=$mcu"; + echo "DEFS=$use_sys3 $flash_override $def_emulation $def_memory_size"; + echo "LDSCRIPT=$ldscript"; + echo "LIBS=$libs"; + echo "$DEBUG_MAKE_OPTION"; + echo "$PINPAD_MAKE_OPTION"; + echo "ENABLE_FRAUCHEKY=$enable_fraucheky"; + echo "ENABLE_OUTPUT_HEX=$enable_hexoutput" + if test "$emulation" = "yes"; then + echo "prefix=$prefix" + echo "exec_prefix=$exec_prefix" + echo "libexecdir=$libexecdir" + fi +) > config.mk + +if test "$certdo" = "yes"; then + sed -e "/^@CERTDO_SUPPORT_START@$/ d" -e "/^@CERTDO_SUPPORT_END@$/ d" \ + -e "s/@ORIGIN@/$ORIGIN/" -e "s/@FLASH_SIZE@/$FLASH_SIZE/" \ + -e "s/@MEMORY_SIZE@/$MEMORY_SIZE/" \ + -e "s/@FLASH_PAGE_SIZE@/$FLASH_PAGE_SIZE/" \ + < gnuk.ld.in > gnuk.ld +else + sed -e "/^@CERTDO_SUPPORT_START@$/,/^@CERTDO_SUPPORT_END@$/ d" \ + -e "s/@ORIGIN@/$ORIGIN/" -e "s/@FLASH_SIZE@/$FLASH_SIZE/" \ + -e "s/@MEMORY_SIZE@/$MEMORY_SIZE/" \ + -e "s/@FLASH_PAGE_SIZE@/$FLASH_PAGE_SIZE/" \ + < gnuk.ld.in > gnuk.ld +fi +sed -e "s/@DEBUG_DEFINE@/$DEBUG_DEFINE/" \ + -e "s/@DFU_DEFINE@/$DFU_DEFINE/" \ + -e "s/@PINPAD_DEFINE@/$PINPAD_DEFINE/" \ + -e "s/@PINPAD_MORE_DEFINE@/$PINPAD_MORE_DEFINE/" \ + -e "s/@CERTDO_DEFINE@/$CERTDO_DEFINE/" \ + -e "s/@HID_CARD_CHANGE_DEFINE@/$HID_CARD_CHANGE_DEFINE/" \ + -e "s/@LIFE_CYCLE_MANAGEMENT_DEFINE@/$LIFE_CYCLE_MANAGEMENT_DEFINE/" \ + -e "s/@SERIALNO_STR_LEN_DEFINE@/$SERIALNO_STR_LEN_DEFINE/" \ + < config.h.in > config.h +exit 0 diff --git a/gnuk/src/crypt.mk b/gnuk/src/crypt.mk new file mode 100644 index 0000000000..999b77463f --- /dev/null +++ b/gnuk/src/crypt.mk @@ -0,0 +1,6 @@ +CRYPTDIR = ../polarssl +CRYPTSRCDIR = $(CRYPTDIR)/library +CRYPTINCDIR = $(CRYPTDIR)/include +CRYPTSRC = $(CRYPTSRCDIR)/bignum.c $(CRYPTSRCDIR)/rsa.c \ + $(CRYPTSRCDIR)/aes.c \ + sha256.c call-rsa.c diff --git a/gnuk/src/debug.c b/gnuk/src/debug.c new file mode 100644 index 0000000000..2de40925ee --- /dev/null +++ b/gnuk/src/debug.c @@ -0,0 +1,126 @@ +/* + * debug.c -- Debuging with virtual COM port + * + * 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 +#include + +extern void _write (const char *s, int len); + +static void +put_hex (uint8_t nibble) +{ + uint8_t c; + + if (nibble < 0x0a) + c = '0' + nibble; + else + c = 'a' + nibble - 0x0a; + + _write ((const char *)&c, 1); +} + +void +put_byte (uint8_t b) +{ + put_hex (b >> 4); + put_hex (b &0x0f); + _write ("\r\n", 2); +} + +void +put_byte_with_no_nl (uint8_t b) +{ + _write (" ", 1); + put_hex (b >> 4); + put_hex (b &0x0f); +} + +void +put_short (uint16_t x) +{ + put_hex (x >> 12); + put_hex ((x >> 8)&0x0f); + put_hex ((x >> 4)&0x0f); + put_hex (x & 0x0f); + _write ("\r\n", 2); +} + +void +put_word (uint32_t x) +{ + put_hex (x >> 28); + put_hex ((x >> 24)&0x0f); + put_hex ((x >> 20)&0x0f); + put_hex ((x >> 16)&0x0f); + put_hex ((x >> 12)&0x0f); + put_hex ((x >> 8)&0x0f); + put_hex ((x >> 4)&0x0f); + put_hex (x & 0x0f); + _write ("\r\n", 2); +} + +void +put_int (uint32_t x) +{ + char s[10]; + int i; + + for (i = 0; i < 10; i++) + { + s[i] = '0' + (x % 10); + x /= 10; + if (x == 0) + break; + } + + while (i) + { + _write (s+i, 1); + i--; + } + + _write (s, 1); + _write ("\r\n", 2); +} + +void +put_binary (const char *s, int len) +{ + int i; + + for (i = 0; i < len; i++) + { + put_byte_with_no_nl (s[i]); + if ((i & 0x0f) == 0x0f) + _write ("\r\n", 2); + } + _write ("\r\n", 2); +} + +void +put_string (const char *s) +{ + _write (s, strlen (s)); +} + + diff --git a/gnuk/src/debug.h b/gnuk/src/debug.h new file mode 100644 index 0000000000..091c0c516f --- /dev/null +++ b/gnuk/src/debug.h @@ -0,0 +1,9 @@ +struct stdout { + chopstx_mutex_t m; + /**/ + chopstx_mutex_t m_dev; + chopstx_cond_t cond_dev; + uint8_t connected; +}; + +extern struct stdout stdout; diff --git a/gnuk/src/ec_p256k1.c b/gnuk/src/ec_p256k1.c new file mode 100644 index 0000000000..c63978e054 --- /dev/null +++ b/gnuk/src/ec_p256k1.c @@ -0,0 +1,233 @@ +/* -*- coding: utf-8 -*- + * ec_p256k1.c - Elliptic curve over GF(p256k1) + * + * Copyright (C) 2014 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 . + * + */ + +/* + * Note: we don't take advantage of the specific feature of this curve, + * but use same method of computation as NIST P-256 curve. That's due + * to some software patent(s). + */ + +#include +#include +#include "bn.h" +#include "modp256k1.h" +#include "affine.h" +#include "jpc-ac_p256k1.h" +#include "mod.h" +#include "ec_p256k1.h" + +#define FIELD p256k1 +#define COEFFICIENT_A_IS_ZERO 1 + +/* + * a = 0, b = 7 + */ +#if 0 +static const bn256 coefficient_a[1] = { + {{ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }} +}; +#endif + +static const bn256 coefficient_b[1] = { + {{ 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }} +}; + + +static const ac precomputed_KG[15] = { + { + {{{ 0x16f81798, 0x59f2815b, 0x2dce28d9, 0x029bfcdb, + 0xce870b07, 0x55a06295, 0xf9dcbbac, 0x79be667e }}}, + {{{ 0xfb10d4b8, 0x9c47d08f, 0xa6855419, 0xfd17b448, + 0x0e1108a8, 0x5da4fbfc, 0x26a3c465, 0x483ada77 }}} + }, { + {{{ 0x42d0e6bd, 0x13b7e0e7, 0xdb0f5e53, 0xf774d163, + 0x104d6ecb, 0x82a2147c, 0x243c4e25, 0x3322d401 }}}, + {{{ 0x6c28b2a0, 0x24f3a2e9, 0xa2873af6, 0x2805f63e, + 0x4ddaf9b7, 0xbfb019bc, 0xe9664ef5, 0x56e70797 }}} + }, { + {{{ 0x829d122a, 0xdca81127, 0x67e99549, 0x8f17f314, + 0x6a8a9e73, 0x9b889085, 0x846dd99d, 0x583fdfd9 }}}, + {{{ 0x63c4eac4, 0xf3c7719e, 0xb734b37a, 0xb44685a3, + 0x572a47a6, 0x9f92d2d6, 0x2ff57d81, 0xabc6232f }}} + }, { + {{{ 0x9ec4c0da, 0x1b7b444c, 0x723ea335, 0xe88c5678, + 0x981f162e, 0x9239c1ad, 0xf63b5f33, 0x8f68b9d2 }}}, + {{{ 0x501fff82, 0xf23cbf79, 0x95510bfd, 0xbbea2cfe, + 0xb6be215d, 0xde1d90c2, 0xba063986, 0x662a9f2d }}} + }, { + {{{ 0x114cbf09, 0x63c5e885, 0x7be77e3e, 0x2f27ce93, + 0xf54a3e33, 0xdaa6d12d, 0x3eff872c, 0x8b300e51 }}}, + {{{ 0xb3b10a39, 0x26c6ff28, 0x9aaf7169, 0x08f6a7aa, + 0x6b8238ea, 0x446f0d46, 0x7f43c0cc, 0x1cec3067 }}} + }, { + {{{ 0x075e9070, 0xba16ce6a, 0x9b5cfe37, 0xbc26893d, + 0x9c510774, 0xe1ddadfe, 0xfe3ae2f4, 0x90922d88 }}}, + {{{ 0x5c08824a, 0x653943cc, 0xfce8f4bc, 0x06d74475, + 0x533c615d, 0x8d101fa7, 0x742108a9, 0x7b1903f6 }}} + }, { + {{{ 0x6ebdc96c, 0x1bcfa45c, 0x1c7584ba, 0xe400bc04, + 0x74cf531f, 0x6395e20e, 0xc5131b30, 0x1edd0bb1 }}}, + {{{ 0xe358cf9e, 0xa117161b, 0x2724d11c, 0xe490d6f0, + 0xee6dd8c9, 0xf75062f6, 0xfba373e4, 0x31e03b2b }}} + }, { + {{{ 0x2120e2b3, 0x7f3b58fa, 0x7f47f9aa, 0x7a58fdce, + 0x4ce6e521, 0xe7be4ae3, 0x1f51bdba, 0xeaa649f2 }}}, + {{{ 0xba5ad93d, 0xd47a5305, 0xf13f7e59, 0x01a6b965, + 0x9879aa5a, 0xc69a80f8, 0x5bbbb03a, 0xbe3279ed }}} + }, { + {{{ 0x27bb4d71, 0xcf291a33, 0x33524832, 0x6caf7d6b, + 0x766584ee, 0x6e0ee131, 0xd064c589, 0x160cb0f6 }}}, + {{{ 0x17136e8d, 0x9d5de554, 0x1aab720e, 0xe3f2d468, + 0xccf75cc2, 0xd1378b49, 0xc4ff16e1, 0x6920c375 }}} + }, { + {{{ 0x1a9ee611, 0x3eef9e96, 0x9cc37faf, 0xfe4d7bf3, + 0xb321d965, 0x462aa9b3, 0x208736c5, 0x1702da3e }}}, + {{{ 0x3a545ceb, 0xfba57bbf, 0x7ea858f5, 0x6dbcd766, + 0x680d92f1, 0x088e897c, 0xbc626c80, 0x468c1fd8 }}} + }, { + {{{ 0xb188660a, 0xb40f85c7, 0x99bc3c36, 0xc5873c19, + 0x7f33b54c, 0x3c7b4541, 0x1f8c9bf8, 0x4cd3a93c }}}, + {{{ 0x33099cb0, 0xf8dce380, 0x2edd2f33, 0x7a167dd6, + 0x0ffe35b7, 0x576d8987, 0xc68ace5c, 0xd2de0386 }}} + }, { + {{{ 0x6658bb08, 0x9a9e0a72, 0xc589607b, 0xe23c5f2a, + 0xf2bfb4c8, 0xa048ca14, 0xc62c2291, 0x4d9a0f89 }}}, + {{{ 0x0f827294, 0x427b5f31, 0x9f2c35cd, 0x1ea7a8b5, + 0x85a3c00f, 0x95442e56, 0x9b57975a, 0x8cb83121 }}} + }, { + {{{ 0x51f5cf67, 0x4333f0da, 0xf4f0d3cb, 0x6d3ea47c, + 0xa05a831f, 0x442fda14, 0x016d3e81, 0x6a496013 }}}, + {{{ 0xe52e0f48, 0xf647318c, 0x4a0d5ff1, 0x5ff3a66e, + 0x61199ba8, 0x046ed81a, 0x3e79c23a, 0x578edf08 }}} + }, { + {{{ 0x3ea01ea7, 0xb8f996f8, 0x7497bb15, 0xc0045d33, + 0x6205647c, 0xc4749dc9, 0x0efd22c9, 0xd8946054 }}}, + {{{ 0x12774ad5, 0x062dcb09, 0x8be06e3a, 0xcb13f310, + 0x235de1a9, 0xca281d35, 0x69c3645c, 0xaf8a7412 }}} + }, { + {{{ 0xbeb8b1e2, 0x8808ca5f, 0xea0dda76, 0x0262b204, + 0xddeb356b, 0xb6fffffc, 0xfbb83870, 0x52de253a }}}, + {{{ 0x8f8d21ea, 0x961f40c0, 0x002f03ed, 0x89686278, + 0x38e421ea, 0x0ff834d7, 0xd36fb8db, 0x3a270d6f }}} + } +}; + +static const ac precomputed_2E_KG[15] = { + { + {{{ 0x39a48db0, 0xefd7835b, 0x9b3c03bf, 0x9f1215a2, + 0x9b7bde45, 0x2791d0a0, 0x696e7167, 0x100f44da }}}, + {{{ 0x2bc65a09, 0x0fbd5cd6, 0xff5195ac, 0xb7ff4a18, + 0x0c090666, 0x2ec8f330, 0x92a00b77, 0xcdd9e131 }}} + }, { + {{{ 0x40fb27b6, 0x32427e28, 0xbe430576, 0xc76e3db2, + 0x61686aa5, 0x10f238ad, 0xbe778b1b, 0xfea74e3d }}}, + {{{ 0xf23cb96f, 0x701d3db7, 0x973f7b77, 0x126b596b, + 0xccb6af93, 0x7cf674de, 0x9b0b1329, 0x6e0568db }}} + }, { + {{{ 0x2c8118bc, 0x6cac5154, 0x399ddd98, 0x19bd4b34, + 0x2e9c8949, 0x47248a8d, 0x2cefa3b1, 0x734cb6a8 }}}, + {{{ 0x1e410fd5, 0xf1b340ad, 0xc4873539, 0xa2982bee, + 0xd4de4530, 0x7b5a3ea4, 0x42202574, 0xae46e10e }}} + }, { + {{{ 0xac1f98cd, 0xcbfc99c8, 0x4d7f0308, 0x52348905, + 0x1cc66021, 0xfaed8a9c, 0x4a474870, 0x9c3919a8 }}}, + {{{ 0xd4fc599d, 0xbe7e5e03, 0x6c64c8e6, 0x905326f7, + 0xf260e641, 0x584f044b, 0x4a4ddd57, 0xddb84f0f }}} + }, { + {{{ 0xed7cebed, 0xc4aacaa8, 0x4fae424e, 0xb75d2dce, + 0xba20735e, 0xa01585a2, 0xba122399, 0x3d75f24b }}}, + {{{ 0xd5570dce, 0xcbe4606f, 0x2da192c2, 0x9d00bfd7, + 0xa57b7265, 0x9c3ce86b, 0xec4edf5e, 0x987a22f1 }}} + }, { + {{{ 0x73ea0665, 0x211b9715, 0xf3a1abbb, 0x86f485d4, + 0xcd076f0e, 0xabd242d8, 0x0ba5dc88, 0x862332ab }}}, + {{{ 0x7b784911, 0x09af505c, 0xcaf4fae7, 0xc89544e8, + 0xae9a32eb, 0x256625f6, 0x606d1a3f, 0xe2532b72 }}} + }, { + {{{ 0x0deaf885, 0x79e9f313, 0x46df21c9, 0x938ff76e, + 0xa953bb2c, 0x1968f5fb, 0x29155f27, 0xdff538bf }}}, + {{{ 0x31d5d020, 0xf7bae0b1, 0x1a676a8d, 0x5afdc787, + 0xfa9d53ff, 0x11b4f032, 0xc5959167, 0x86ba433e }}} + }, { + {{{ 0x9475b7ba, 0x884fdff0, 0xe4918b3d, 0xe039e730, + 0xf5018cdb, 0x3d3e57ed, 0x1943785c, 0x95939698 }}}, + {{{ 0x7524f2fd, 0xe9b8abf8, 0xc8709385, 0x9c653f64, + 0x4b9cd684, 0x8ba0386a, 0x88c331dd, 0x2e7e5528 }}} + }, { + {{{ 0xeefe79e5, 0x940bef53, 0xbe9b87f3, 0xc518d286, + 0x7833042c, 0x9e0c7c76, 0x11fbe152, 0x104e2cb5 }}}, + {{{ 0x50bbec83, 0xc0d35e0f, 0x4acd0fcc, 0xee4879be, + 0x006085ee, 0xc8d80f5d, 0x72fe1ac1, 0x3c51bc1c }}} + }, { + {{{ 0xb2de976e, 0x06187f61, 0xf5e4b4b6, 0x52869e18, + 0x38d332ca, 0x74d4facd, 0xb3a2f8d9, 0x5c1c90b4 }}}, + {{{ 0xdaa37893, 0x98644d09, 0xabe39818, 0x682435a8, + 0x469c53a0, 0x17e46617, 0x77dc2e64, 0x642f9632 }}} + }, { + {{{ 0x222f6c54, 0xad2101c5, 0xfa74785e, 0xb05c7a58, + 0x489bcdaf, 0xce55fa79, 0xffe88d54, 0xc1f920fd }}}, + {{{ 0x9065e490, 0x32553ab0, 0x35329f74, 0x7611b9af, + 0xab7b24c0, 0x57df19ef, 0x6181c447, 0xb9a78749 }}} + }, { + {{{ 0xa80b7ea8, 0x392f156f, 0x8ae4a8bf, 0x57ab7ca0, + 0x50c4b178, 0xac320747, 0x0e781feb, 0x146041b9 }}}, + {{{ 0x845279b2, 0xd343f075, 0x7387afa5, 0x2d4fe757, + 0xa72f3c39, 0x151e0948, 0x550da168, 0x41a6d54e }}} + }, { + {{{ 0x075a0010, 0xb3134ed3, 0x7ae93e23, 0x9fa76f4b, + 0x7bb4daaa, 0xc0db256f, 0x464dd8a3, 0x7668dc27 }}}, + {{{ 0x9f5da977, 0x150063f5, 0x05efce00, 0x3acac5c8, + 0x884493fe, 0xc8e12ffc, 0x88f06bd2, 0x4ab936d8 }}} + }, { + {{{ 0x5d09ea98, 0x996fde77, 0x4145da58, 0x16ddf512, + 0xdc2fb225, 0xa97a6ca8, 0xfbdcdf5a, 0xc7331f30 }}}, + {{{ 0x86a86e52, 0x838f99e0, 0x77795edd, 0x68d39b29, + 0x9f412aaa, 0xe4e4f97e, 0x30d25352, 0xe5cc2c0a }}} + }, { + {{{ 0x9c21ff71, 0xb3d68650, 0xddbe3884, 0x11e7589d, + 0x423bac67, 0x7efd4055, 0x46957425, 0x587a7293 }}}, + {{{ 0x8f5a8fc6, 0x360adc2e, 0xbd69f12e, 0x6f8bbafb, + 0x0a3f3b4d, 0xf671f423, 0x59942dc3, 0xb49acb47 }}} + } +}; + +/* + * N: order of G + * 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 + */ +static const bn256 N[1] = { + {{ 0xd0364141, 0xbfd25e8c, 0xaf48a03b, 0xbaaedce6, + 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff }} +}; + +/* + * MU = 2^512 / N + * MU = ( (1 << 256) | MU_lower ) + */ +static const bn256 MU_lower[1] = { + {{ 0x2fc9bec0, 0x402da173, 0x50b75fc4, 0x45512319, + 0x1, 0x0, 0x0, 0x0 }} +}; + + +#include "ecc.c" diff --git a/gnuk/src/ec_p256k1.h b/gnuk/src/ec_p256k1.h new file mode 100644 index 0000000000..ec0e213ca9 --- /dev/null +++ b/gnuk/src/ec_p256k1.h @@ -0,0 +1,4 @@ +int compute_kP_p256k1 (ac *X, const bn256 *K, const ac *P); +int compute_kG_p256k1 (ac *X, const bn256 *K); +void ecdsa_p256k1 (bn256 *r, bn256 *s, const bn256 *z, const bn256 *d); +int check_secret_p256k1 (const bn256 *q, bn256 *d1); diff --git a/gnuk/src/ec_p256r1.c b/gnuk/src/ec_p256r1.c new file mode 100644 index 0000000000..07bd66ca41 --- /dev/null +++ b/gnuk/src/ec_p256r1.c @@ -0,0 +1,226 @@ +/* -*- coding: utf-8 -*- + * ec_p256r1.c - Elliptic curve over GF(p256r1) + * + * Copyright (C) 2014 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 +#include +#include "bn.h" +#include "modp256r1.h" +#include "affine.h" +#include "jpc-ac_p256r1.h" +#include "mod.h" +#include "ec_p256r1.h" + +#define FIELD p256r1 +#define COEFFICIENT_A_IS_MINUS_3 1 + +/* + * a = -3 mod p256r1 + */ +static const bn256 coefficient_a[1] = { + {{ 0xfffffffc, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0x00000000, 0x00000001, 0xffffffff }} +}; + +static const bn256 coefficient_b[1] = { + {{ 0x27d2604b, 0x3bce3c3e, 0xcc53b0f6, 0x651d06b0, + 0x769886bc, 0xb3ebbd55, 0xaa3a93e7, 0x5ac635d8 }} +}; + + +static const ac precomputed_KG[15] = { + { + {{{ 0xd898c296, 0xf4a13945, 0x2deb33a0, 0x77037d81, + 0x63a440f2, 0xf8bce6e5, 0xe12c4247, 0x6b17d1f2 }}}, + {{{ 0x37bf51f5, 0xcbb64068, 0x6b315ece, 0x2bce3357, + 0x7c0f9e16, 0x8ee7eb4a, 0xfe1a7f9b, 0x4fe342e2 }}} + }, { + {{{ 0x8e14db63, 0x90e75cb4, 0xad651f7e, 0x29493baa, + 0x326e25de, 0x8492592e, 0x2811aaa5, 0x0fa822bc }}}, + {{{ 0x5f462ee7, 0xe4112454, 0x50fe82f5, 0x34b1a650, + 0xb3df188b, 0x6f4ad4bc, 0xf5dba80d, 0xbff44ae8 }}} + }, { + {{{ 0x097992af, 0x93391ce2, 0x0d35f1fa, 0xe96c98fd, + 0x95e02789, 0xb257c0de, 0x89d6726f, 0x300a4bbc }}}, + {{{ 0xc08127a0, 0xaa54a291, 0xa9d806a5, 0x5bb1eead, + 0xff1e3c6f, 0x7f1ddb25, 0xd09b4644, 0x72aac7e0 }}} + }, { + {{{ 0xd789bd85, 0x57c84fc9, 0xc297eac3, 0xfc35ff7d, + 0x88c6766e, 0xfb982fd5, 0xeedb5e67, 0x447d739b }}}, + {{{ 0x72e25b32, 0x0c7e33c9, 0xa7fae500, 0x3d349b95, + 0x3a4aaff7, 0xe12e9d95, 0x834131ee, 0x2d4825ab }}} + }, { + {{{ 0x2a1d367f, 0x13949c93, 0x1a0a11b7, 0xef7fbd2b, + 0xb91dfc60, 0xddc6068b, 0x8a9c72ff, 0xef951932 }}}, + {{{ 0x7376d8a8, 0x196035a7, 0x95ca1740, 0x23183b08, + 0x022c219c, 0xc1ee9807, 0x7dbb2c9b, 0x611e9fc3 }}} + }, { + {{{ 0x0b57f4bc, 0xcae2b192, 0xc6c9bc36, 0x2936df5e, + 0xe11238bf, 0x7dea6482, 0x7b51f5d8, 0x55066379 }}}, + {{{ 0x348a964c, 0x44ffe216, 0xdbdefbe1, 0x9fb3d576, + 0x8d9d50e5, 0x0afa4001, 0x8aecb851, 0x15716484 }}} + }, { + {{{ 0xfc5cde01, 0xe48ecaff, 0x0d715f26, 0x7ccd84e7, + 0xf43e4391, 0xa2e8f483, 0xb21141ea, 0xeb5d7745 }}}, + {{{ 0x731a3479, 0xcac917e2, 0x2844b645, 0x85f22cfe, + 0x58006cee, 0x0990e6a1, 0xdbecc17b, 0xeafd72eb }}} + }, { + {{{ 0x313728be, 0x6cf20ffb, 0xa3c6b94a, 0x96439591, + 0x44315fc5, 0x2736ff83, 0xa7849276, 0xa6d39677 }}}, + {{{ 0xc357f5f4, 0xf2bab833, 0x2284059b, 0x824a920c, + 0x2d27ecdf, 0x66b8babd, 0x9b0b8816, 0x674f8474 }}} + }, { + {{{ 0x677c8a3e, 0x2df48c04, 0x0203a56b, 0x74e02f08, + 0xb8c7fedb, 0x31855f7d, 0x72c9ddad, 0x4e769e76 }}}, + {{{ 0xb824bbb0, 0xa4c36165, 0x3b9122a5, 0xfb9ae16f, + 0x06947281, 0x1ec00572, 0xde830663, 0x42b99082 }}} + }, { + {{{ 0xdda868b9, 0x6ef95150, 0x9c0ce131, 0xd1f89e79, + 0x08a1c478, 0x7fdc1ca0, 0x1c6ce04d, 0x78878ef6 }}}, + {{{ 0x1fe0d976, 0x9c62b912, 0xbde08d4f, 0x6ace570e, + 0x12309def, 0xde53142c, 0x7b72c321, 0xb6cb3f5d }}} + }, { + {{{ 0xc31a3573, 0x7f991ed2, 0xd54fb496, 0x5b82dd5b, + 0x812ffcae, 0x595c5220, 0x716b1287, 0x0c88bc4d }}}, + {{{ 0x5f48aca8, 0x3a57bf63, 0xdf2564f3, 0x7c8181f4, + 0x9c04e6aa, 0x18d1b5b3, 0xf3901dc6, 0xdd5ddea3 }}} + }, { + {{{ 0x3e72ad0c, 0xe96a79fb, 0x42ba792f, 0x43a0a28c, + 0x083e49f3, 0xefe0a423, 0x6b317466, 0x68f344af }}}, + {{{ 0x3fb24d4a, 0xcdfe17db, 0x71f5c626, 0x668bfc22, + 0x24d67ff3, 0x604ed93c, 0xf8540a20, 0x31b9c405 }}} + }, { + {{{ 0xa2582e7f, 0xd36b4789, 0x4ec39c28, 0xd1a1014, + 0xedbad7a0, 0x663c62c3, 0x6f461db9, 0x4052bf4b }}}, + {{{ 0x188d25eb, 0x235a27c3, 0x99bfcc5b, 0xe724f339, + 0x71d70cc8, 0x862be6bd, 0x90b0fc61, 0xfecf4d51 }}} + }, { + {{{ 0xa1d4cfac, 0x74346c10, 0x8526a7a4, 0xafdf5cc0, + 0xf62bff7a, 0x123202a8, 0xc802e41a, 0x1eddbae2 }}}, + {{{ 0xd603f844, 0x8fa0af2d, 0x4c701917, 0x36e06b7e, + 0x73db33a0, 0x0c45f452, 0x560ebcfc, 0x43104d86 }}} + }, { + {{{ 0x0d1d78e5, 0x9615b511, 0x25c4744b, 0x66b0de32, + 0x6aaf363a, 0x0a4a46fb, 0x84f7a21c, 0xb48e26b4 }}}, + {{{ 0x21a01b2d, 0x06ebb0f6, 0x8b7b0f98, 0xc004e404, + 0xfed6f668, 0x64131bcd, 0x4d4d3dab, 0xfac01540 }}} + } +}; + +static const ac precomputed_2E_KG[15] = { + { + {{{ 0x185a5943, 0x3a5a9e22, 0x5c65dfb6, 0x1ab91936, + 0x262c71da, 0x21656b32, 0xaf22af89, 0x7fe36b40 }}}, + {{{ 0x699ca101, 0xd50d152c, 0x7b8af212, 0x74b3d586, + 0x07dca6f1, 0x9f09f404, 0x25b63624, 0xe697d458 }}} + }, { + {{{ 0x7512218e, 0xa84aa939, 0x74ca0141, 0xe9a521b0, + 0x18a2e902, 0x57880b3a, 0x12a677a6, 0x4a5b5066 }}}, + {{{ 0x4c4f3840, 0x0beada7a, 0x19e26d9d, 0x626db154, + 0xe1627d40, 0xc42604fb, 0xeac089f1, 0xeb13461c }}} + }, { + {{{ 0x27a43281, 0xf9faed09, 0x4103ecbc, 0x5e52c414, + 0xa815c857, 0xc342967a, 0x1c6a220a, 0x0781b829 }}}, + {{{ 0xeac55f80, 0x5a8343ce, 0xe54a05e3, 0x88f80eee, + 0x12916434, 0x97b2a14f, 0xf0151593, 0x690cde8d }}} + }, { + {{{ 0xf7f82f2a, 0xaee9c75d, 0x4afdf43a, 0x9e4c3587, + 0x37371326, 0xf5622df4, 0x6ec73617, 0x8a535f56 }}}, + {{{ 0x223094b7, 0xc5f9a0ac, 0x4c8c7669, 0xcde53386, + 0x085a92bf, 0x37e02819, 0x68b08bd7, 0x0455c084 }}} + }, { + {{{ 0x9477b5d9, 0x0c0a6e2c, 0x876dc444, 0xf9a4bf62, + 0xb6cdc279, 0x5050a949, 0xb77f8276, 0x06bada7a }}}, + {{{ 0xea48dac9, 0xc8b4aed1, 0x7ea1070f, 0xdebd8a4b, + 0x1366eb70, 0x427d4910, 0x0e6cb18a, 0x5b476dfd }}} + }, { + {{{ 0x278c340a, 0x7c5c3e44, 0x12d66f3b, 0x4d546068, + 0xae23c5d8, 0x29a751b1, 0x8a2ec908, 0x3e29864e }}}, + {{{ 0x26dbb850, 0x142d2a66, 0x765bd780, 0xad1744c4, + 0xe322d1ed, 0x1f150e68, 0x3dc31e7e, 0x239b90ea }}} + }, { + {{{ 0x7a53322a, 0x78c41652, 0x09776f8e, 0x305dde67, + 0xf8862ed4, 0xdbcab759, 0x49f72ff7, 0x820f4dd9 }}}, + {{{ 0x2b5debd4, 0x6cc544a6, 0x7b4e8cc4, 0x75be5d93, + 0x215c14d3, 0x1b481b1b, 0x783a05ec, 0x140406ec }}} + }, { + {{{ 0xe895df07, 0x6a703f10, 0x01876bd8, 0xfd75f3fa, + 0x0ce08ffe, 0xeb5b06e7, 0x2783dfee, 0x68f6b854 }}}, + {{{ 0x78712655, 0x90c76f8a, 0xf310bf7f, 0xcf5293d2, + 0xfda45028, 0xfbc8044d, 0x92e40ce6, 0xcbe1feba }}} + }, { + {{{ 0x4396e4c1, 0xe998ceea, 0x6acea274, 0xfc82ef0b, + 0x2250e927, 0x230f729f, 0x2f420109, 0xd0b2f94d }}}, + {{{ 0xb38d4966, 0x4305addd, 0x624c3b45, 0x10b838f8, + 0x58954e7a, 0x7db26366, 0x8b0719e5, 0x97145982 }}} + }, { + {{{ 0x23369fc9, 0x4bd6b726, 0x53d0b876, 0x57f2929e, + 0xf2340687, 0xc2d5cba4, 0x4a866aba, 0x96161000 }}}, + {{{ 0x2e407a5e, 0x49997bcd, 0x92ddcb24, 0x69ab197d, + 0x8fe5131c, 0x2cf1f243, 0xcee75e44, 0x7acb9fad }}} + }, { + {{{ 0x23d2d4c0, 0x254e8394, 0x7aea685b, 0xf57f0c91, + 0x6f75aaea, 0xa60d880f, 0xa333bf5b, 0x24eb9acc }}}, + {{{ 0x1cda5dea, 0xe3de4ccb, 0xc51a6b4f, 0xfeef9341, + 0x8bac4c4d, 0x743125f8, 0xacd079cc, 0x69f891c5 }}} + }, { + {{{ 0x702476b5, 0xeee44b35, 0xe45c2258, 0x7ed031a0, + 0xbd6f8514, 0xb422d1e7, 0x5972a107, 0xe51f547c }}}, + {{{ 0xc9cf343d, 0xa25bcd6f, 0x097c184e, 0x8ca922ee, + 0xa9fe9a06, 0xa62f98b3, 0x25bb1387, 0x1c309a2b }}} + }, { + {{{ 0x1967c459, 0x9295dbeb, 0x3472c98e, 0xb0014883, + 0x08011828, 0xc5049777, 0xa2c4e503, 0x20b87b8a }}}, + {{{ 0xe057c277, 0x3063175d, 0x8fe582dd, 0x1bd53933, + 0x5f69a044, 0x0d11adef, 0x919776be, 0xf5c6fa49 }}} + }, { + {{{ 0x0fd59e11, 0x8c944e76, 0x102fad5f, 0x3876cba1, + 0xd83faa56, 0xa454c3fa, 0x332010b9, 0x1ed7d1b9 }}}, + {{{ 0x0024b889, 0xa1011a27, 0xac0cd344, 0x05e4d0dc, + 0xeb6a2a24, 0x52b520f0, 0x3217257a, 0x3a2b03f0 }}} + }, { + {{{ 0xdf1d043d, 0xf20fc2af, 0xb58d5a62, 0xf330240d, + 0xa0058c3b, 0xfc7d229c, 0xc78dd9f6, 0x15fee545 }}}, + {{{ 0x5bc98cda, 0x501e8288, 0xd046ac04, 0x41ef80e5, + 0x461210fb, 0x557d9f49, 0xb8753f81, 0x4ab5b6b2 }}} + } +}; + +/* + * N: order of G + */ +static const bn256 N[1] = { + {{ 0xfc632551, 0xf3b9cac2, 0xa7179e84, 0xbce6faad, + 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff }} +}; + +/* + * MU = 2^512 / N + * MU = ( (1 << 256) | MU_lower ) + */ +static const bn256 MU_lower[1] = { + {{ 0xeedf9bfe, 0x012ffd85, 0xdf1a6c21, 0x43190552, + 0xffffffff, 0xfffffffe, 0xffffffff, 0x00000000 }} +}; + + +#include "ecc.c" diff --git a/gnuk/src/ec_p256r1.h b/gnuk/src/ec_p256r1.h new file mode 100644 index 0000000000..c8b9742113 --- /dev/null +++ b/gnuk/src/ec_p256r1.h @@ -0,0 +1,4 @@ +int compute_kP_p256r1 (ac *X, const bn256 *K, const ac *P); +int compute_kG_p256r1 (ac *X, const bn256 *K); +void ecdsa_p256r1 (bn256 *r, bn256 *s, const bn256 *z, const bn256 *d); +int check_secret_p256r1 (const bn256 *q, bn256 *d1); diff --git a/gnuk/src/ecc-edwards.c b/gnuk/src/ecc-edwards.c new file mode 100644 index 0000000000..903f48dbeb --- /dev/null +++ b/gnuk/src/ecc-edwards.c @@ -0,0 +1,951 @@ +/* -*- coding: utf-8 -*- + * ecc-edwards.c - Elliptic curve computation for + * the twisted Edwards curve: -x^2 + y^2 = 1 + d*x^2*y^2 + * + * Copyright (C) 2014, 2017 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 +#include + +#include "bn.h" +#include "mod.h" +#include "mod25638.h" +#include "sha512.h" + +/* + * References: + * + * [1] Daniel J. Bernstein, Niels Duif, Tanja Lange, Peter Schwabe, Bo-Yin Yang. + * High-speed high-security signatures. + * Journal of Cryptographic Engineering 2 (2012), 77--89. + * http://cr.yp.to/papers.html#ed25519 + * + * [2] Daniel J. Bernstein, Peter Birkner, Marc Joye, Tanja Lange, + * Christiane Peters. + * Twisted Edwards curves. + * Pages 389--405 in Progress in cryptology---AFRICACRYPT 2008. + * http://cr.yp.to/papers.html#twisted + */ + +/* + * IMPLEMENTATION NOTE + * + * (0) We assume that the processor has no cache, nor branch target + * prediction. Thus, we don't avoid indexing by secret value. + * We don't avoid conditional jump if both cases have same timing, + * either. + * + * (1) We use Radix-32 field arithmetic. It's a representation like + * 2^256-38, but it's more redundant. For example, "1" can be + * represented in three ways in 256-bit: 1, 2^255-18, and + * 2^256-37. + * + * (2) We use fixed base comb multiplication. Scalar is 252-bit. + * There are various possible choices for 252 = 2 * 2 * 3 * 3 * 7. + * Current choice of total size is 3KB. We use three tables, and + * a table has 16 points (3 * 1KB). + * + * Window size W = 4-bit, E = 21. + * <--21-bit- + * <---42-bit---------- + * [ ][########][////////][ ][########][////////] + * <-------63-bit---------------- + * <-----------84-bit---------------------- + * <--------------105-bit---------------------------- + * + * [ ][########][////////][ ][########][////////] + * <-126-bit- + * <-147-bit- + * <----168-bit-------- + * + * <-------189-bit--------------- + * <----------210-bit---------------------- + * <-------------231-bit----------------------------- + */ + +/* + * Identity element: (0,1) + * Negation: -(x,y) = (-x,y) + * + * d: -0x2DFC9311D490018C7338BF8688861767FF8FF5B2BEBE27548A14B235ECA6874A + * order: + * 0x1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED + * Gx: 0x216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A + * Gy: 0x6666666666666666666666666666666666666666666666666666666666666658 + */ + +/* d + 2^255 - 19 */ +static const bn256 coefficient_d[1] = { + {{ 0x135978a3, 0x75eb4dca, 0x4141d8ab, 0x00700a4d, + 0x7779e898, 0x8cc74079, 0x2b6ffe73, 0x52036cee }} }; + + +/** + * @brief Projective Twisted Coordinates + */ +typedef struct +{ + bn256 x[1]; + bn256 y[1]; + bn256 z[1]; +} ptc; + +#include "affine.h" + + +static int +mod25519_is_neg (const bn256 *a) +{ + return (a->word[0] & 1); +} + + +/** + * @brief X = 2 * A + * + * Compute (X3 : Y3 : Z3) = 2 * (X1 : Y1 : Z1) + */ +static void +point_double (ptc *X, const ptc *A) +{ + bn256 b[1], d[1], e[1]; + + /* Compute: B = (X1 + Y1)^2 */ + mod25638_add (b, A->x, A->y); + mod25638_sqr (b, b); + + /* Compute: C = X1^2 : E */ + mod25638_sqr (e, A->x); + + /* Compute: D = Y1^2 */ + mod25638_sqr (d, A->y); + + /* E = aC; where a = -1 */ + /* Compute: D - E = D + C : Y3_tmp */ + mod25638_add (X->y, e, d); + + /* Compute: -F = -(E + D) = C - D; where a = -1 : E */ + mod25638_sub (e, e, d); + + /* Compute: H = Z1^2 : D */ + mod25638_sqr (d, A->z); + + /* Compute: -J = 2*H - F : D */ + mod25638_add (d, d, d); + mod25638_add (d, d, e); + + /* Compute: X3 = (B-C-D)*J = -J*(C+D-B) = -J*(Y3_tmp-B) */ + mod25638_sub (X->x, X->y, b); + mod25638_mul (X->x, X->x, d); + + /* Compute: Y3 = -F*(D-E) = -F*Y3_tmp */ + mod25638_mul (X->y, X->y, e); + + /* Z3 = -F*-J */ + mod25638_mul (X->z, e, d); +} + + +/** + * @brief X = A + B + * + * @param X Destination PTC + * @param A PTC + * @param B AC + * + * Compute: (X3 : Y3 : Z3) = (X1 : Y1 : Z1) + (X2 : Y2 : 1) + */ +static void +point_add (ptc *X, const ptc *A, const ac *B) +{ + bn256 c[1], d[1], e[1], tmp[1]; + + /* Compute: C = X1 * X2 */ + mod25638_mul (c, A->x, B->x); + + /* Compute: D = Y1 * Y2 */ + mod25638_mul (d, A->y, B->y); + + /* Compute: E = d * C * D */ + mod25638_mul (e, c, d); + mod25638_mul (e, coefficient_d, e); + + /* Compute: C_1 = C + D */ + mod25638_add (c, c, d); + + /* Compute: D_1 = Z1^2 : B */ + mod25638_sqr (d, A->z); + + /* tmp = D_1 - E : F */ + mod25638_sub (tmp, d, e); + + /* D_2 = D_1 + E : G */ + mod25638_add (d, d, e); + + /* X3_final = Z1 * tmp * ((X1 + Y1) * (X2 + Y2) - C_1) */ + mod25638_add (X->x, A->x, A->y); + mod25638_add (e, B->x, B->y); + mod25638_mul (e, X->x, e); + mod25638_sub (e, e, c); + mod25638_mul (e, tmp, e); + mod25638_mul (X->x, A->z, e); + + /* Y3_final = Z1 * D_2 * C_1 */ + mod25638_mul (c, d, c); + mod25638_mul (X->y, A->z, c); + + /* Z3_final = tmp * D_2 */ + mod25638_mul (X->z, tmp, d); + + /* A = Z1 */ + /* B = A^2 */ + /* C = X1 * X2 */ + /* D = Y1 * Y2 */ + /* E = d * C * D */ + /* F = B - E */ + /* G = B + E */ + /* X3 = A * F * ((X1 + Y1) * (X2 + Y2) - C - D) */ + /* Y3 = A * G * (D - aC); where a = -1 */ + /* Z3 = F * G */ +} + + +/** + * @brief X = convert A + * + * @param X Destination AC + * @param A PTC + * + * (X1:Y1:Z1) represents the affine point (x=X1/Z1, y=Y1/Z1) + */ +static void +point_ptc_to_ac (ac *X, const ptc *A) +{ + bn256 z_inv[1]; + + /* + * A->z may be bigger than p25519, or two times bigger than p25519. + * But this is no problem for computation of mod_inv. + */ + mod_inv (z_inv, A->z, p25519); + + mod25638_mul (X->x, A->x, z_inv); + mod25519_reduce (X->x); + mod25638_mul (X->y, A->y, z_inv); + mod25519_reduce (X->y); +} + + +static const ac precomputed_KG[16] = { + { {{{ 0, 0, 0, 0, 0, 0, 0, 0 }}}, + {{{ 1, 0, 0, 0, 0, 0, 0, 0 }}} }, + { {{{ 0x8f25d51a, 0xc9562d60, 0x9525a7b2, 0x692cc760, + 0xfdd6dc5c, 0xc0a4e231, 0xcd6e53fe, 0x216936d3 }}}, + {{{ 0x66666658, 0x66666666, 0x66666666, 0x66666666, + 0x66666666, 0x66666666, 0x66666666, 0x66666666 }}} }, + { {{{ 0x3713af22, 0xac7137bd, 0xac634604, 0x25ed77a4, + 0xa815e038, 0xce0d0064, 0xbca90151, 0x041c030f }}}, + {{{ 0x0780f989, 0xe9b33fcf, 0x3d4445e7, 0xe4e97c2a, + 0x655e5c16, 0xc67dc71c, 0xee43fb7a, 0x72467625 }}} }, + { {{{ 0x3ee99893, 0x76a19171, 0x7ba9b065, 0xe647edd9, + 0x6aeae260, 0x31f39299, 0x5f4a9bb2, 0x6d9e4545 }}}, + {{{ 0x94cae280, 0xc41433da, 0x79061211, 0x8e842de8, + 0xa259dc8a, 0xaab95e0b, 0x99013cd0, 0x28bd5fc3 }}} }, + { {{{ 0x7d23ea24, 0x59e22c56, 0x0460850e, 0x1e745a88, + 0xda13ef4b, 0x4583ff4c, 0x95083f85, 0x1f13202c }}}, + {{{ 0x90275f48, 0xad42025c, 0xb55c4778, 0x0085087e, + 0xfdfd7ffa, 0xf21109e7, 0x6c381b7e, 0x66336d35 }}} }, + { {{{ 0xd00851f2, 0xaa9476ab, 0x4a61600b, 0xe7838534, + 0x1a52df87, 0x0de65625, 0xbd675870, 0x5f0dd494 }}}, + {{{ 0xe23493ba, 0xf20aec1b, 0x3414b0a8, 0x8f7f2741, + 0xa80e1eb6, 0x497e74bd, 0xe9365b15, 0x1648eaac }}} }, + { {{{ 0x04ac2b69, 0x5b78dcec, 0x32001a73, 0xecdb66ce, + 0xb34cf697, 0xb75832f4, 0x3a2bce94, 0x7aaf57c5 }}}, + {{{ 0x60fdfc6f, 0xb32ed2ce, 0x757924c6, 0x77bf20be, + 0x48742dd1, 0xaebd15dd, 0x55d38439, 0x6311bb16 }}} }, + { {{{ 0x42ff5c97, 0x139cdd73, 0xdbd82964, 0xee4c359e, + 0x70611a3f, 0x91c1cd94, 0x8075dbcb, 0x1d0c34f6 }}}, + {{{ 0x5f931219, 0x43eaa549, 0xa23d35a6, 0x3737aba7, + 0x46f167bb, 0x54b1992f, 0xb74a9944, 0x01a11f3c }}} }, + { {{{ 0xba46b161, 0x67a5310e, 0xd9d67f6c, 0x790f8527, + 0x2f6cc814, 0x359c5b5f, 0x7786383d, 0x7b6a5565 }}}, + {{{ 0x663ab0d3, 0xf1431b60, 0x09995826, 0x14a32d8f, + 0xeddb8571, 0x61d526f6, 0x0eac739a, 0x0cb7acea }}} }, + { {{{ 0x4a2d009f, 0x5eb1a697, 0xd8df987a, 0xdacb43b4, + 0x8397f958, 0x4870f214, 0x8a175fbb, 0x5aa0c67c }}}, + {{{ 0x78887db3, 0x27dbbd4c, 0x64e322ab, 0xe327b707, + 0x7cbe4e3b, 0x87e293fa, 0xbda72395, 0x17040799 }}} }, + { {{{ 0x99d1e696, 0xc833a5a2, 0x2d9d5877, 0x969bff8e, + 0x2216fa67, 0x383a533a, 0x684d3925, 0x338bbe0a }}}, + {{{ 0xd6cfb491, 0x35b5aae8, 0xaa12f3f8, 0x4a588279, + 0x2e30380e, 0xa7c2e708, 0x9e4b3d62, 0x69f13e09 }}} }, + { {{{ 0x27f1cd56, 0xec0dc2ef, 0xdb11cc97, 0x1af11548, + 0x9ebc7613, 0xb642f86a, 0xcb77c3b9, 0x5ce45e73 }}}, + {{{ 0x3eddd6de, 0x5d128786, 0x4859eab7, 0x16f9a6b4, + 0xd8782345, 0x55c53916, 0xdb7b202a, 0x6b1dfa87 }}} }, + { {{{ 0x19e30528, 0x2461a8ed, 0x665cfb1c, 0xaf756bf9, + 0x3a6e8673, 0x0fcafd1d, 0x45d10f48, 0x0d264435 }}}, + {{{ 0x5431db67, 0x543fd4c6, 0x60932432, 0xc153a5b3, + 0xd2119aa4, 0x41d5b8eb, 0x8b09b6a5, 0x36bd9ab4 }}} }, + { {{{ 0x21e06738, 0x6d39f935, 0x3765dd86, 0x4e6a7c59, + 0xa4730880, 0xefc0dd80, 0x4079fe2f, 0x40617e56 }}}, + {{{ 0x921439b9, 0xbc83cdff, 0x98833c09, 0xd5cccc06, + 0xda13cdcb, 0xe315c425, 0x67ff5370, 0x37bc6e84 }}} }, + { {{{ 0xf643b5f5, 0x65e7f028, 0x0ffbf5a8, 0x5b0d4831, + 0xf4085f62, 0x0f540498, 0x0db7bd1b, 0x6f0bb035 }}}, + {{{ 0x9733742c, 0x51f65571, 0xf513409f, 0x2fc047a0, + 0x355facf6, 0x07f45010, 0x3a989a9c, 0x5cd416a9 }}} }, + { {{{ 0x748f2a67, 0x0bdd7208, 0x415b7f7f, 0x0cf0b80b, + 0x57aa0119, 0x44afdd5f, 0x430dc946, 0x05d68802 }}}, + {{{ 0x1a60eeb2, 0x420c46e5, 0x665024f5, 0xc60a9b33, + 0x48c51347, 0x37520265, 0x00a21bfb, 0x6f4be0af }}} } +}; + +static const ac precomputed_2E_KG[16] = { + { {{{ 0, 0, 0, 0, 0, 0, 0, 0 }}}, + {{{ 1, 0, 0, 0, 0, 0, 0, 0 }}} }, + { {{{ 0x199c4f7d, 0xec314ac0, 0xb2ebaaf9, 0x66a39c16, + 0xedd4d15f, 0xab1c92b8, 0x57d9eada, 0x482a4cdf }}}, + {{{ 0x6e4eb04b, 0xbd513b11, 0x25e4fd6a, 0x3f115fa5, + 0x14519298, 0x0b3c5fc6, 0x81c2f7a8, 0x7391de43 }}} }, + { {{{ 0x1254fe02, 0xa57dca18, 0x6da34368, 0xa56a2a14, + 0x63e7328e, 0x44c6e34f, 0xca63ab3e, 0x3f748617 }}}, + {{{ 0x7dc1641e, 0x5a13dc52, 0xee4e9ca1, 0x4cbb2899, + 0x1ba9acee, 0x3938a289, 0x420fc47b, 0x0fed89e6 }}} }, + { {{{ 0x49cbad08, 0x3c193f32, 0x15e80ef5, 0xdda71ef1, + 0x9d128c33, 0xda44186c, 0xbf98c24f, 0x54183ede }}}, + {{{ 0x93d165c1, 0x2cb483f7, 0x177f44aa, 0x51762ace, + 0xb4ab035d, 0xb3fe651b, 0xa0b0d4e5, 0x426c99c3 }}} }, + { {{{ 0xef3f3fb1, 0xb3fcf4d8, 0x065060a0, 0x7052292b, + 0x24240b15, 0x18795ff8, 0x9989ffcc, 0x13aea184 }}}, + {{{ 0xc2b81f44, 0x1930c101, 0x10600555, 0x672d6ca4, + 0x1b25e570, 0xfbddbff2, 0x8ca12b70, 0x0884949c }}} }, + { {{{ 0x00564bbf, 0x9983a033, 0xde61b72d, 0x95587d25, + 0xeb17ad71, 0xb6719dfb, 0xc0bc3517, 0x46871ad0 }}}, + {{{ 0xe95a6693, 0xb034fb61, 0x76eabad9, 0x5b0d8d18, + 0x884785dc, 0xad295dd0, 0x74a1276a, 0x359debad }}} }, + { {{{ 0xe89fb5ca, 0x2e5a2686, 0x5656c6c5, 0xd3d200ba, + 0x9c969001, 0xef4c051e, 0x02cb45f4, 0x0d4ea946 }}}, + {{{ 0x76d6e506, 0xa6f8a422, 0x63209e23, 0x454c768f, + 0x2b372386, 0x5c12fd04, 0xdbfee11f, 0x1aedbd3e }}} }, + { {{{ 0x00dbf569, 0x700ab50f, 0xd335b313, 0x9553643c, + 0xa17dc97e, 0xeea9bddf, 0x3350a2bd, 0x0d12fe3d }}}, + {{{ 0xa16a3dee, 0xe5ac35fe, 0xf81950c3, 0x4ae4664a, + 0x3dbbf921, 0x75c63df4, 0x2958a5a6, 0x545b109c }}} }, + { {{{ 0x0a61b29c, 0xd7a52a98, 0x65aca9ee, 0xe21e0acb, + 0x5985dcbe, 0x57a69c0f, 0xeb87a534, 0x3c0c1e7b }}}, + {{{ 0x6384bd2f, 0xf0a0b50d, 0xc6939e4b, 0xff349a34, + 0x6e2f1973, 0x922c4554, 0xf1347631, 0x74e826b2 }}} }, + { {{{ 0xa655803c, 0xd7eaa066, 0x38292c5c, 0x09504e76, + 0x2c874953, 0xe298a02e, 0x8932b73f, 0x225093ed }}}, + {{{ 0xe69c3efd, 0xf93e2b4d, 0x8a87c799, 0xa2cbd5fc, + 0x85dba986, 0xdf41da94, 0xccee8edc, 0x36fe85e7 }}} }, + { {{{ 0x7d742813, 0x78df7dc5, 0x4a193e64, 0x333bcc6d, + 0x6a966d2d, 0x8242aa25, 0x4cd36d32, 0x03500a94 }}}, + {{{ 0x580505d7, 0xd5d110fc, 0xfa11e1e9, 0xb2f47e16, + 0x06eab6b4, 0xd0030f92, 0x62c91d46, 0x2dc80d5f }}} }, + { {{{ 0x2a75e492, 0x5788b01a, 0xbae31352, 0x992acf54, + 0x8159db27, 0x4591b980, 0xd3d84740, 0x36c6533c }}}, + {{{ 0x103883b5, 0xc44c7c00, 0x515d0820, 0x10329423, + 0x71b9dc16, 0xbd306903, 0xf88f8d32, 0x7edd5a95 }}} }, + { {{{ 0x005523d7, 0xfd63b1ac, 0xad70dd21, 0x74482e0d, + 0x02b56105, 0x67c9d9d0, 0x5971b456, 0x4d318012 }}}, + {{{ 0x841106df, 0xdc9a6f6d, 0xa326987f, 0x7c52ed9d, + 0x00607ea0, 0x4dbeaa6f, 0x6959e688, 0x115c221d }}} }, + { {{{ 0xc80f7c16, 0xf8718464, 0xe9930634, 0x05dc8f40, + 0xc2e9d5f4, 0xefa699bb, 0x021da209, 0x2469e813 }}}, + {{{ 0xc602a3c4, 0x75c02845, 0x0a200f9d, 0x49d1b2ce, + 0x2fb3ec8f, 0xd21b75e4, 0xd72a7545, 0x10dd726a }}} }, + { {{{ 0x63ef1a6c, 0xeda58527, 0x051705e0, 0xb3fc0e72, + 0x44f1161f, 0xbda6f3ee, 0xf339efe5, 0x7680aebf }}}, + {{{ 0xb1b070a7, 0xe8d3fd01, 0xdbfbaaa0, 0xc3ff7dbf, + 0xa320c916, 0xd81ef6f2, 0x62a3b54d, 0x3e22a1fb }}} }, + { {{{ 0xb1fa18c8, 0xcdbb9187, 0xcb483a17, 0x8ddb5f6b, + 0xea49af98, 0xc0a880b9, 0xf2dfddd0, 0x53bf600b }}}, + {{{ 0x9e25b164, 0x4217404c, 0xafb74aa7, 0xfabf06ee, + 0x2b9f233c, 0xb17712ae, 0xd0eb909e, 0x71f0b344 }}} } +}; + +static const ac precomputed_4E_KG[16] = { + { {{{ 0, 0, 0, 0, 0, 0, 0, 0 }}}, + {{{ 1, 0, 0, 0, 0, 0, 0, 0 }}} }, + { {{{ 0xe388a820, 0xbb6ec091, 0x5182278a, 0xa928b283, + 0xa9a6eb83, 0x2259174d, 0x45500054, 0x184b48cb }}}, + {{{ 0x26e77c33, 0xfe324dba, 0x83faf453, 0x6679a5e3, + 0x2380ef73, 0xdd60c268, 0x03dc33a9, 0x3ee0e07a }}} }, + { {{{ 0xce974493, 0x403aff28, 0x9bf6f5c4, 0x84076bf4, + 0xecd898fb, 0xec57038c, 0xb663ed49, 0x2898ffaa }}}, + {{{ 0xf335163d, 0xf4b3bc46, 0xfa4fb6c6, 0xe613a0f4, + 0xb9934557, 0xe759d6bc, 0xab6c9477, 0x094f3b96 }}} }, + { {{{ 0x6afffe9e, 0x168bb5a0, 0xee748c29, 0x950f7ad7, + 0xda17203d, 0xa4850a2b, 0x77289e0f, 0x0062f7a7 }}}, + {{{ 0x4b3829fa, 0x6265d4e9, 0xbdfcd386, 0x4f155ada, + 0x475795f6, 0x9f38bda4, 0xdece4a4c, 0x560ed4b3 }}} }, + { {{{ 0x141e648a, 0xdad4570a, 0x019b965c, 0x8bbf674c, + 0xdb08fe30, 0xd7a8d50d, 0xa2851109, 0x7efb45d3 }}}, + {{{ 0xd0c28cda, 0x52e818ac, 0xa321d436, 0x792257dd, + 0x9d71f8b7, 0x867091c6, 0x11a1bf56, 0x0fe1198b }}} }, + { {{{ 0x06137ab1, 0x4e848339, 0x3e6674cc, 0x5673e864, + 0x0140502b, 0xad882043, 0x6ea1e46a, 0x34b5c0cb }}}, + {{{ 0x1d70aa7c, 0x29786814, 0x8cdbb8aa, 0x840ae3f9, + 0xbd4801fb, 0x78b4d622, 0xcf18ae9a, 0x6cf4e146 }}} }, + { {{{ 0x36297168, 0x95c270ad, 0x942e7812, 0x2303ce80, + 0x0205cf0e, 0x71908cc2, 0x32bcd754, 0x0cc15edd }}}, + {{{ 0x2c7ded86, 0x1db94364, 0xf141b22c, 0xc694e39b, + 0x5e5a9312, 0xf22f64ef, 0x3c5e6155, 0x649b8859 }}} }, + { {{{ 0xb6417945, 0x0d5611c6, 0xac306c97, 0x9643fdbf, + 0x0df500ff, 0xe81faaa4, 0x6f50e615, 0x0792c79b }}}, + {{{ 0xd2af8c8d, 0xb45bbc49, 0x84f51bfe, 0x16c615ab, + 0xc1d02d32, 0xdc57c526, 0x3c8aaa55, 0x5fb9a9a6 }}} }, + { {{{ 0xdee40b98, 0x82faa8db, 0x6d520674, 0xff8a5208, + 0x446ac562, 0x1f8c510f, 0x2cc6b66e, 0x4676d381 }}}, + {{{ 0x2e7429f4, 0x8f1aa780, 0x8ed6bdf6, 0x2a95c1bf, + 0x457fa0eb, 0x051450a0, 0x744c57b1, 0x7d89e2b7 }}} }, + { {{{ 0x3f95ea15, 0xb6bdacd2, 0x2f1a5d69, 0xc9a9d1b1, + 0xf4d22d72, 0xd4c2f1a9, 0x4dc516b5, 0x73ecfdf1 }}}, + {{{ 0x05391e08, 0xa1ce93cd, 0x7b8aac17, 0x98f1e99e, + 0xa098cbb3, 0x9ba84f2e, 0xf9bdd37a, 0x1425aa8b }}} }, + { {{{ 0x966abfc0, 0x8a385bf4, 0xf081a640, 0x55e5e8bc, + 0xee26f5ff, 0x835dff85, 0xe509e1ea, 0x4927e622 }}}, + {{{ 0x352334b0, 0x164c8dbc, 0xa3fea31f, 0xcac1ad63, + 0x682fd457, 0x9b87a676, 0x1a53145f, 0x75f382ff }}} }, + { {{{ 0xc3efcb46, 0x16b944f5, 0x68cb184c, 0x1fb55714, + 0x9ccf2dc8, 0xf1c2b116, 0x808283d8, 0x7417e00f }}}, + {{{ 0x930199ba, 0x1ea67a22, 0x718990d8, 0x9fbaf765, + 0x8f3d5d57, 0x231fc664, 0xe5853194, 0x38141a19 }}} }, + { {{{ 0x2f81290d, 0xb9f00390, 0x04a9ca6c, 0x44877827, + 0xe1dbdd65, 0x65d7f9b9, 0xf7c6698a, 0x7133424c }}}, + {{{ 0xa7cd250f, 0x604cfb3c, 0x5acc18f3, 0x460c3c4b, + 0xb518e3eb, 0xa53e50e0, 0x98a40196, 0x2b4b9267 }}} }, + { {{{ 0xc5dbd06c, 0x591b0672, 0xaa1eeb65, 0x10d43dca, + 0xcd2517af, 0x420cdef8, 0x0b695a8a, 0x513a307e }}}, + {{{ 0x66503215, 0xee9d6a7b, 0x088fd9a4, 0xdea58720, + 0x973afe12, 0x8f3cbbea, 0x872f2538, 0x005c2350 }}} }, + { {{{ 0x35af3291, 0xe5024b70, 0x4f5e669a, 0x1d3eec2d, + 0x6e79d539, 0xc1f6d766, 0x795b5248, 0x34ec043f }}}, + {{{ 0x400960b6, 0xb2763511, 0x29e57df0, 0xff7a3d84, + 0x1666c1f1, 0xaeac7792, 0x66084bc0, 0x72426e97 }}} }, + { {{{ 0x44f826ca, 0x5b1c3199, 0x790aa408, 0x68b00b73, + 0x69e9b92b, 0xaf0984b4, 0x3ffe9093, 0x5fe6736f }}}, + {{{ 0xffd49312, 0xd67f2889, 0x5cb9ed21, 0x3520d747, + 0x3c65a606, 0x94f893b1, 0x2d65496f, 0x2fee5e8c }}} } +}; + +/** + * @brief X = k * G + * + * @param K scalar k + * + * Return -1 on error. + * Return 0 on success. + */ +static void +compute_kG_25519 (ac *X, const bn256 *K) +{ + ptc Q[1]; + int i; + + /* identity element */ + memset (Q, 0, sizeof (ptc)); + Q->y->word[0] = 1; + Q->z->word[0] = 1; + + for (i = 20; i >= 0; i--) + { + int k0, k1, k2; + + k0 = ((K->word[0] >> i) & 1) + | (i < 1 ? ((K->word[1] >> 30) & 2) + : (((K->word[2] >> (i-1)) & 1) << 1)) + | (i < 2 ? ((K->word[3] >> (i+28)) & 4) + : (((K->word[4] >> (i-2)) & 1) << 2)) + | (i < 3 ? ((K->word[5] >> (i+26)) & 8) + : (((K->word[6] >> (i-3)) & 1) << 3)); + + k1 = (i < 11 ? ((K->word[0] >> (i+21)) & 1) + : ((K->word[1] >> (i-11)) & 1)) + | (i < 12 ? ((K->word[2] >> (i+19)) & 2) + : (((K->word[3] >> (i-12)) & 1) << 1)) + | (i < 13 ? ((K->word[4] >> (i+17)) & 4) + : (((K->word[5] >> (i-13)) & 1) << 2)) + | (i < 14 ? ((K->word[6] >> (i+15)) & 8) + : (((K->word[7] >> (i-14)) & 1) << 3)); + + k2 = ((K->word[1] >> (i+10)) & 1) + | ((K->word[3] >> (i+8)) & 2) + | ((K->word[5] >> (i+6)) & 4) + | ((K->word[7] >> (i+4)) & 8); + + point_double (Q, Q); + point_add (Q, Q, &precomputed_KG[k0]); + point_add (Q, Q, &precomputed_2E_KG[k1]); + point_add (Q, Q, &precomputed_4E_KG[k2]); + } + + point_ptc_to_ac (X, Q); +} + + +#define BN416_WORDS 13 +#define BN128_WORDS 4 + +/* M: The order of the generator G. */ +static const bn256 M[1] = { + {{ 0x5CF5D3ED, 0x5812631A, 0xA2F79CD6, 0x14DEF9DE, + 0x00000000, 0x00000000, 0x00000000, 0x10000000 }} +}; + +#define C ((const uint32_t *)M) + +static void +bnX_mul_C (uint32_t *r, const uint32_t *q, int q_size) +{ + int i, j, k; + int i_beg, i_end; + uint32_t r0, r1, r2; + + r0 = r1 = r2 = 0; + for (k = 0; k <= q_size + BN128_WORDS - 2; k++) + { + if (q_size < BN128_WORDS) + if (k < q_size) + { + i_beg = 0; + i_end = k; + } + else + { + i_beg = k - q_size + 1; + i_end = k; + if (i_end > BN128_WORDS - 1) + i_end = BN128_WORDS - 1; + } + else + if (k < BN128_WORDS) + { + i_beg = 0; + i_end = k; + } + else + { + i_beg = k - BN128_WORDS + 1; + i_end = k; + if (i_end > q_size - 1) + i_end = q_size - 1; + } + + for (i = i_beg; i <= i_end; i++) + { + uint64_t uv; + uint32_t u, v; + uint32_t carry; + + j = k - i; + if (q_size < BN128_WORDS) + uv = ((uint64_t )q[j])*((uint64_t )C[i]); + else + uv = ((uint64_t )q[i])*((uint64_t )C[j]); + v = uv; + u = (uv >> 32); + r0 += v; + carry = (r0 < v); + r1 += carry; + carry = (r1 < carry); + r1 += u; + carry += (r1 < u); + r2 += carry; + } + + r[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + + r[k] = r0; +} + +/** + * @brief R = A mod M (using M=2^252+C) (Barret reduction) + * + * See HAC 14.47. + */ +static void +mod_reduce_M (bn256 *R, const bn512 *A) +{ + uint32_t q[BN256_WORDS+1]; + uint32_t tmp[BN416_WORDS]; + bn256 r[1]; + uint32_t carry, next_carry; + int i; +#define borrow carry + + q[8] = A->word[15]>>28; + carry = A->word[15] & 0x0fffffff; + for (i = BN256_WORDS - 1; i >= 0; i--) + { + next_carry = A->word[i+7] & 0x0fffffff; + q[i] = (A->word[i+7] >> 28) | (carry << 4); + carry = next_carry; + } + memcpy (R, A, sizeof (bn256)); + R->word[7] &= 0x0fffffff; + + /* Q_size: 9 */ + bnX_mul_C (tmp, q, 9); /* TMP = Q*C */ + /* Q = tmp / 2^252 */ + carry = tmp[12] & 0x0fffffff; + for (i = 4; i >= 0; i--) + { + next_carry = tmp[i+7] & 0x0fffffff; + q[i] = (tmp[i+7] >> 28) | (carry << 4); + carry = next_carry; + } + /* R' = tmp % 2^252 */ + memcpy (r, tmp, sizeof (bn256)); + r->word[7] &= 0x0fffffff; + /* R -= R' */ + borrow = bn256_sub (R, R, r); + if (borrow) + bn256_add (R, R, M); + else + bn256_add ((bn256 *)tmp, R, M); + + /* Q_size: 5 */ + bnX_mul_C (tmp, q, 5); /* TMP = Q*C */ + carry = tmp[8] & 0x0fffffff; + q[0] = (tmp[7] >> 28) | (carry << 4); + /* R' = tmp % 2^252 */ + memcpy (r, tmp, sizeof (bn256)); + r->word[7] &= 0x0fffffff; + /* R += R' */ + bn256_add (R, R, r); + borrow = bn256_sub (R, R, M); + if (borrow) + bn256_add (R, R, M); + else + bn256_add ((bn256 *)tmp, R, M); + + /* Q_size: 1 */ + bnX_mul_C (tmp, q, 1); /* TMP = Q*C */ + /* R' = tmp % 2^252 */ + memset (((uint8_t *)r)+(sizeof (uint32_t)*5), 0, sizeof (uint32_t)*3); + memcpy (r, tmp, sizeof (uint32_t)*5); + /* R -= R' */ + borrow = bn256_sub (R, R, r); + if (borrow) + bn256_add (R, R, M); + else + bn256_add ((bn256 *)tmp, R, M); +#undef borrow +} + + +int +eddsa_sign_25519 (const uint8_t *input, size_t ilen, uint32_t *out, + const bn256 *a, const uint8_t *seed, const bn256 *pk) +{ + bn256 *r, *s; + sha512_context ctx; + uint8_t hash[64]; + bn256 tmp[1]; + ac R[1]; + uint32_t carry, borrow; + + r = (bn256 *)out; + s = (bn256 *)(out+(32/4)); + + sha512_start (&ctx); + sha512_update (&ctx, seed, sizeof (bn256)); /* It's upper half of the hash */ + sha512_update (&ctx, input, ilen); + sha512_finish (&ctx, hash); + + mod_reduce_M (r, (bn512 *)hash); + compute_kG_25519 (R, r); + + /* EdDSA encoding. */ + memcpy (tmp, R->y, sizeof (bn256)); + tmp->word[7] ^= mod25519_is_neg (R->x) * 0x80000000; + + sha512_start (&ctx); + sha512_update (&ctx, (uint8_t *)tmp, sizeof (bn256)); + sha512_update (&ctx, (uint8_t *)pk, sizeof (bn256)); + sha512_update (&ctx, input, ilen); + sha512_finish (&ctx, (uint8_t *)hash); + + mod_reduce_M (s, (bn512 *)hash); + bn256_mul ((bn512 *)hash, s, a); + mod_reduce_M (s, (bn512 *)hash); + carry = bn256_add (s, s, r); + borrow = bn256_sub (s, s, M); + + memcpy (r, tmp, sizeof (bn256)); + + if ((borrow && !carry)) + bn256_add (s, s, M); + else + bn256_add (tmp, s, M); + + return 0; +} + +static void +eddsa_public_key_25519 (bn256 *pk, const bn256 *a) +{ + ac R[1]; + ptc X[1]; + bn256 a0[1]; + + bn256_shift (a0, a, -3); + compute_kG_25519 (R, a0); + memcpy (X, R, sizeof (ac)); + memset (X->z, 0, sizeof (bn256)); + X->z->word[0] = 1; + point_double (X, X); + point_double (X, X); + point_double (X, X); + point_ptc_to_ac (R, X); + /* EdDSA encoding. */ + memcpy (pk, R->y, sizeof (bn256)); + pk->word[7] ^= mod25519_is_neg (R->x) * 0x80000000; +} + + +void +eddsa_compute_public_25519 (const uint8_t *kd, uint8_t *pubkey) +{ + eddsa_public_key_25519 ((bn256 *)pubkey, (const bn256 *)kd); +} + + +#if 0 +/** + * check if P is on the curve. + * + * Return -1 on error. + * Return 0 on success. + */ +static int +point_is_on_the_curve (const ac *P) +{ + bn256 s[1], t[1]; + + /* Twisted Edwards curve: a*x^2 + y^2 = 1 + d*x^2*y^2 */ +} + +int +compute_kP_25519 (ac *X, const bn256 *K, const ac *P); +#endif + +#ifdef PRINT_OUT_TABLE +static const ptc G[1] = {{ + {{{ 0x8f25d51a, 0xc9562d60, 0x9525a7b2, 0x692cc760, + 0xfdd6dc5c, 0xc0a4e231, 0xcd6e53fe, 0x216936d3 }}}, + {{{ 0x66666658, 0x66666666, 0x66666666, 0x66666666, + 0x66666666, 0x66666666, 0x66666666, 0x66666666 }}}, + {{{ 1, 0, 0, 0, 0, 0, 0, 0 }}}, +}}; + +#include + +#ifdef TESTING_EDDSA +static void +print_bn256 (const bn256 *X) +{ + int i; + + for (i = 7; i >= 0; i--) + printf ("%08x", X->word[i]); + puts (""); +} +#endif + +#if 0 +static void +print_point (const ac *X) +{ + int i; + +#ifdef PRINT_OUT_TABLE_AS_C + fputs (" { {{{ ", stdout); + for (i = 0; i < 4; i++) + printf ("0x%08x, ", X->x->word[i]); + fputs ("\n ", stdout); + for (; i < 7; i++) + printf ("0x%08x, ", X->x->word[i]); + printf ("0x%08x }}},\n", X->x->word[i]); + fputs (" {{{ ", stdout); + for (i = 0; i < 4; i++) + printf ("0x%08x, ", X->y->word[i]); + fputs ("\n ", stdout); + for (; i < 7; i++) + printf ("0x%08x, ", X->y->word[i]); + printf ("0x%08x }}} },\n", X->y->word[i]); +#else + puts ("--"); + for (i = 7; i >= 0; i--) + printf ("%08x", X->x->word[i]); + puts (""); + for (i = 7; i >= 0; i--) + printf ("%08x", X->y->word[i]); + puts (""); + puts ("--"); +#endif +} + +static void +print_point_ptc (const ptc *X) +{ + int i; + + puts ("---"); + for (i = 7; i >= 0; i--) + printf ("%08x", X->x->word[i]); + puts (""); + for (i = 7; i >= 0; i--) + printf ("%08x", X->y->word[i]); + puts (""); + for (i = 7; i >= 0; i--) + printf ("%08x", X->z->word[i]); + puts (""); + puts ("---"); +} +#endif + + +#ifndef TESTING_EDDSA +static void power_2 (ac *A, ptc *a, int N) +{ + int i; + + for (i = 0; i < N; i++) + ed_double_25638 (a, a); + ptc_to_ac_25519 (A, a); +} + +static void print_table (ac *a0001, ac *a0010, ac *a0100, ac *a1000) +{ + int i; + ptc a[1]; + ac x[1]; + + for (i = 1; i < 16; i++) + { + /* A := Identity Element */ + memset (a, 0, sizeof (ptc)); + a->y->word[0] = 1; + a->z->word[0] = 1; + + if ((i & 1)) + ed_add_25638 (a, a, a0001); + if ((i & 2)) + ed_add_25638 (a, a, a0010); + if ((i & 4)) + ed_add_25638 (a, a, a0100); + if ((i & 8)) + ed_add_25638 (a, a, a1000); + + ptc_to_ac_25519 (x, a); + print_point (x); + } + + fputs ("\n", stdout); +} + +static void compute_and_print_table (ac *a0001, ac *a0010, ac *a0100, ac *a1000) +{ + ptc a[1]; + + memcpy (a, a0001, sizeof (ac)); + memset (a->z, 0, sizeof (bn256)); + a->z->word[0] = 1; + power_2 (a0010, a, 63); + power_2 (a0100, a, 63); + power_2 (a1000, a, 63); + print_table (a0001, a0010, a0100, a1000); +} +#endif + +int +main (int argc, char *argv[]) +{ +#ifdef TESTING_EDDSA + uint8_t hash[64]; + bn256 a[1]; + uint8_t r_s[64]; + bn256 pk[1]; + bn256 *r, *s; + + const bn256 sk[1] = { + {{ 0x9db1619d, 0x605afdef, 0xf44a84ba, 0xc42cec92, + 0x69c54944, 0x1969327b, 0x03ac3b70, 0x607fae1c }} }; + + const bn256 r_expected[1] = { + {{ 0x004356e5, 0x72ac60c3, 0xcce28690, 0x8a826e80, + 0x1e7f8784, 0x74d9e5b8, 0x65e073d8, 0x55014922 }} }; + + const bn256 s_expected[1] = { + {{ 0x1582b85f, 0xac3ba390, 0x70391ec6, 0x6bb4f91c, + 0xf0f55bd2, 0x24be5b59, 0x43415165, 0x0b107a8e }} }; + + r = (bn256 *)r_s; + s = (bn256 *)(r_s+32); + + sha512 ((uint8_t *)sk, sizeof (bn256), hash); + hash[0] &= 248; + hash[31] &= 127; + hash[31] |= 64; + memcpy (a, hash, sizeof (bn256)); + + eddsa_public_key_25519 (pk, a); + eddsa_sign_25519 ((const uint8_t *)"", 0, r_s, a, hash+32, pk); + + if (memcmp (r, r_expected, sizeof (bn256)) != 0 + || memcmp (s, s_expected, sizeof (bn256)) != 0) + { + print_bn256 (r); + print_bn256 (s); + return 1; + } +#else + ac a0001[1], a0010[1], a0100[1], a1000[1]; + ptc a[1]; + + memcpy (a, G, sizeof (ptc)); + ptc_to_ac_25519 (a0001, a); + compute_and_print_table (a0001, a0010, a0100, a1000); + + memcpy (a, a0001, sizeof (ac)); + memset (a->z, 0, sizeof (bn256)); + a->z->word[0] = 1; + power_2 (a0001, a, 21); + compute_and_print_table (a0001, a0010, a0100, a1000); + + memcpy (a, a0001, sizeof (ac)); + memset (a->z, 0, sizeof (bn256)); + a->z->word[0] = 1; + power_2 (a0001, a, 21); + compute_and_print_table (a0001, a0010, a0100, a1000); +#endif + + return 0; +} +#endif diff --git a/gnuk/src/ecc-mont.c b/gnuk/src/ecc-mont.c new file mode 100644 index 0000000000..635b09fd68 --- /dev/null +++ b/gnuk/src/ecc-mont.c @@ -0,0 +1,226 @@ +/* -*- coding: utf-8 -*- + * ecc-mont.c - Elliptic curve computation for + * the Montgomery curve: y^2 = x^3 + 486662*x^2 + x. + * + * Copyright (C) 2014, 2015, 2017 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 +#include +#include "bn.h" +#include "mod25638.h" +#include "mod.h" + +/* + * References: + * + * [1] D. J. Bernstein. Curve25519: new Diffie-Hellman speed records. + * Proceedings of PKC 2006, to appear. + * http://cr.yp.to/papers.html#curve25519. Date: 2006.02.09. + * + * [2] D. J. Bernstein. Can we avoid tests for zero in fast + * elliptic-curve arithmetic? + * http://cr.yp.to/papers.html#curvezero. Date: 2006.07.26. + * + */ + +/* + * IMPLEMENTATION NOTE + * + * (0) We assume that the processor has no cache, nor branch target + * prediction. Thus, we don't avoid indexing by secret value. + * We don't avoid conditional jump if both cases have same timing, + * either. + * + * (1) We use Radix-32 field arithmetic. It's a representation like + * 2^256-38, but it's more redundant. For example, "1" can be + * represented in three ways in 256-bit: 1, 2^255-18, and + * 2^256-37. + * + * (2) We use Montgomery double-and-add. + * + */ + +#ifndef BN256_C_IMPLEMENTATION +#define ASM_IMPLEMENTATION 1 +#endif +/* + * + * 121665 = 0x1db41 + * 1 1101 1011 0100 0001 + */ +static void +mod25638_mul_121665 (bn256 *x, const bn256 *a) +{ +#if ASM_IMPLEMENTATION +#include "muladd_256.h" + const uint32_t *s; + uint32_t *d; + uint32_t w; + uint32_t c; + + s = a->word; + d = x->word; + memset (d, 0, sizeof (bn256)); + w = 121665; + MULADD_256_ASM (s, d, w, c); +#else + uint32_t c, c1; + bn256 m[1]; + + c = c1 = bn256_shift (m, a, 6); c += bn256_add (x, a, m); + c1 <<= 2; c1 |= bn256_shift (m, m, 2); c = c + c1 + bn256_add (x, x, m); + c1 <<= 1; c1 |= bn256_shift (m, m, 1); c = c + c1 + bn256_add (x, x, m); + c1 <<= 2; c1 |= bn256_shift (m, m, 2); c = c + c1 + bn256_add (x, x, m); + c1 <<= 1; c1 |= bn256_shift (m, m, 1); c = c + c1 + bn256_add (x, x, m); + c1 <<= 2; c1 |= bn256_shift (m, m, 2); c = c + c1 + bn256_add (x, x, m); + c1 <<= 1; c1 |= bn256_shift (m, m, 1); c = c + c1 + bn256_add (x, x, m); + c1 <<= 1; c1 |= bn256_shift (m, m, 1); c = c + c1 + bn256_add (x, x, m); +#endif + c = bn256_add_uint (x, x, c*38); + x->word[0] += c * 38; +} + + +typedef struct +{ + bn256 x[1]; + bn256 z[1]; +} pt; + + +/** + * @brief Process Montgomery double-and-add + * + * With Q0, Q1, DIF (= Q0 - Q1), compute PRD = 2Q0, SUM = Q0 + Q1 + * Q0 and Q1 are clobbered. + * + */ +static void +mont_d_and_a (pt *prd, pt *sum, pt *q0, pt *q1, const bn256 *dif_x) +{ + mod25638_add (sum->x, q1->x, q1->z); + mod25638_sub (q1->z, q1->x, q1->z); + mod25638_add (prd->x, q0->x, q0->z); + mod25638_sub (q0->z, q0->x, q0->z); + mod25638_mul (q1->x, q0->z, sum->x); + mod25638_mul (q1->z, prd->x, q1->z); + mod25638_sqr (q0->x, prd->x); + mod25638_sqr (q0->z, q0->z); + mod25638_add (sum->x, q1->x, q1->z); + mod25638_sub (q1->z, q1->x, q1->z); + mod25638_mul (prd->x, q0->x, q0->z); + mod25638_sub (q0->z, q0->x, q0->z); + mod25638_sqr (sum->x, sum->x); + mod25638_sqr (sum->z, q1->z); + mod25638_mul_121665 (prd->z, q0->z); + mod25638_mul (sum->z, sum->z, dif_x); + mod25638_add (prd->z, q0->x, prd->z); + mod25638_mul (prd->z, prd->z, q0->z); +} + + +/** + * @brief RES = x-coordinate of [n]Q + * + * @param N Scalar N (three least significant bits are 000) + * @param Q_X x-coordinate of Q + * + */ +static void +compute_nQ (bn256 *res, const bn256 *n, const bn256 *q_x) +{ + int i, j; + pt p0[1], p1[1], p0_[1], p1_[1]; + + /* P0 = O = (1:0) */ + memset (p0->x, 0, sizeof (bn256)); + p0->x->word[0] = 1; + memset (p0->z, 0, sizeof (bn256)); + + /* P1 = (X:1) */ + memcpy (p1->x, q_x, sizeof (bn256)); + memset (p1->z, 0, sizeof (bn256)); + p1->z->word[0] = 1; + + for (i = 0; i < 8; i++) + { + uint32_t u = n->word[7-i]; + + for (j = 0; j < 16; j++) + { + pt *q0, *q1; + pt *sum_n, *prd_n; + + if ((u & 0x80000000)) + q0 = p1, q1 = p0, sum_n = p0_, prd_n = p1_; + else + q0 = p0, q1 = p1, sum_n = p1_, prd_n = p0_; + mont_d_and_a (prd_n, sum_n, q0, q1, q_x); + + if ((u & 0x40000000)) + q0 = p1_, q1 = p0_, sum_n = p0, prd_n = p1; + else + q0 = p0_, q1 = p1_, sum_n = p1, prd_n = p0; + mont_d_and_a (prd_n, sum_n, q0, q1, q_x); + + u <<= 2; + } + } + + /* We know the LSB of N is always 0. Thus, result is always in P0. */ + /* + * p0->z may be zero here, but our mod_inv doesn't raise error for 0, + * but returns 0 (like the implementation of z^(p-2)), thus, RES will + * be 0 in that case, which is correct value. + */ + mod_inv (res, p0->z, p25519); + mod25638_mul (res, res, p0->x); + mod25519_reduce (res); +} + + +void +ecdh_compute_public_25519 (const uint8_t *key_data, uint8_t *pubkey) +{ + bn256 gx[1]; + bn256 k[1]; + + memset (gx, 0, sizeof (bn256)); + gx[0].word[0] = 9; /* Gx = 9 */ + memcpy (k, key_data, sizeof (bn256)); + + compute_nQ ((bn256 *)pubkey, k, gx); +} + +int +ecdh_decrypt_curve25519 (const uint8_t *input, uint8_t *output, + const uint8_t *key_data) +{ + bn256 q_x[1]; + bn256 k[1]; + bn256 shared[1]; + + memcpy (q_x, input, sizeof (bn256)); + memcpy (k, key_data, sizeof (bn256)); + compute_nQ (shared, k, q_x); + memcpy (output, shared, sizeof (bn256)); + return 0; +} diff --git a/gnuk/src/ecc.c b/gnuk/src/ecc.c new file mode 100644 index 0000000000..2d637e98bd --- /dev/null +++ b/gnuk/src/ecc.c @@ -0,0 +1,398 @@ +/* -*- coding: utf-8 -*- + * ecc.c - Elliptic curve over GF(prime) + * + * Copyright (C) 2011, 2013, 2014, 2015 + * 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 . + * + */ + +/* + * References: + * + * [1] Suite B Implementer's Guide to FIPS 186-3 (ECDSA), February 3, 2010. + * + * [2] Michael Brown, Darrel Hankerson, Julio López, and Alfred Menezes, + * Software Implementation of the NIST Elliptic Curves Over Prime Fields, + * Proceedings of the 2001 Conference on Topics in Cryptology: The + * Cryptographer's Track at RSA + * Pages 250-265, Springer-Verlag London, UK, 2001 + * ISBN:3-540-41898-9 + * + * [3] Mustapha Hedabou, Pierre Pinel, Lucien Bénéteau, + * A comb method to render ECC resistant against Side Channel Attacks, + * 2004 + */ + +#include "field-group-select.h" + +/* + * Coefficients + */ +/* + * static const bn256 *coefficient_a; + * static const bn256 *coefficient_b; + */ +/* + * N: order of G + */ +/* + * static const bn256 N[1]; + */ +/* + * MU = 2^512 / N + * MU = ( (1 << 256) | MU_lower ) + */ +/* + * static const bn256 MU_lower[1]; + */ + +/* + * w = 4 + * m = 256 + * d = 64 + * e = 32 + */ + +/* + * static const ac precomputed_KG[15]; + * static const ac precomputed_2E_KG[15]; + */ + +#if TEST +/* + * Generator of Elliptic curve over GF(p256) + */ +const ac *G = &precomputed_KG[0]; +#endif + + +static int +get_vk (const bn256 *K, int i) +{ + uint32_t w0, w1, w2, w3; + + if (i < 32) + { + w3 = K->word[6]; w2 = K->word[4]; w1 = K->word[2]; w0 = K->word[0]; + } + else + { + w3 = K->word[7]; w2 = K->word[5]; w1 = K->word[3]; w0 = K->word[1]; + i -= 32; + } + + w3 >>= i; w2 >>= i; w1 >>= i; w0 >>= i; + return ((w3 & 1) << 3) | ((w2 & 1) << 2) | ((w1 & 1) << 1) | (w0 & 1); +} + + +/** + * @brief X = k * G + * + * @param K scalar k + * + * Return -1 on error. + * Return 0 on success. + */ +int +FUNC(compute_kG) (ac *X, const bn256 *K) +{ + uint8_t index[64]; /* Lower 4-bit for index absolute value, msb is + for sign (encoded as: 0 means 1, 1 means -1). */ + bn256 K_dash[1]; + jpc Q[1], tmp[1], *dst; + int i; + int vk; + uint32_t k_is_even = bn256_is_even (K); + + bn256_sub_uint (K_dash, K, k_is_even); + /* It keeps the condition: 1 <= K' <= N - 2, and K' is odd. */ + + /* Fill index. */ + vk = get_vk (K_dash, 0); + for (i = 1; i < 64; i++) + { + int vk_next, is_zero; + + vk_next = get_vk (K_dash, i); + is_zero = (vk_next == 0); + index[i-1] = (vk - 1) | (is_zero << 7); + vk = (is_zero ? vk : vk_next); + } + index[63] = vk - 1; + + memset (Q->z, 0, sizeof (bn256)); /* infinity */ + for (i = 31; i >= 0; i--) + { + FUNC(jpc_double) (Q, Q); + FUNC(jpc_add_ac_signed) (Q, Q, &precomputed_2E_KG[index[i+32]&0x0f], + index[i+32] >> 7); + FUNC(jpc_add_ac_signed) (Q, Q, &precomputed_KG[index[i]&0x0f], + index[i] >> 7); + } + + dst = k_is_even ? Q : tmp; + FUNC(jpc_add_ac) (dst, Q, &precomputed_KG[0]); + + return FUNC(jpc_to_ac) (X, Q); +} + + + +/** + * check if P is on the curve. + * + * Return -1 on error. + * Return 0 on success. + */ +static int +point_is_on_the_curve (const ac *P) +{ + bn256 s[1], t[1]; + + /* Elliptic curve: y^2 = x^3 + a*x + b */ + MFNC(sqr) (s, P->x); + MFNC(mul) (s, s, P->x); + +#ifndef COEFFICIENT_A_IS_ZERO + MFNC(mul) (t, coefficient_a, P->x); + MFNC(add) (s, s, t); +#endif + MFNC(add) (s, s, coefficient_b); + + MFNC(sqr) (t, P->y); + if (bn256_cmp (s, t) == 0) + return 0; + else + return -1; +} + + +static int +get_vk_kP (const bn256 *K, int i) +{ + uint32_t w; + uint8_t blk = i/32; + uint8_t pos = i%32; + uint8_t col = 3*(pos % 11) + (pos >= 11) + (pos >= 22); + uint8_t word_index = (blk * 3) + (pos / 11); + + w = ((K->word[word_index] >> col) & 7); + if (word_index < 7 && (pos == 10 || pos == 21)) + { + uint8_t mask; + uint8_t shift; + + word_index++; + if (pos == 10) + { + shift = 2; + mask = 4; + } + else + { + shift = 1; + mask = 6; + } + + w |= ((K->word[word_index] << shift) & mask); + } + + return w; +} + +/** + * @brief X = k * P + * + * @param K scalar k + * @param P P in affine coordiate + * + * Return -1 on error. + * Return 0 on success. + * + * For the curve (cofactor is 1 and n is prime), possible error cases are: + * + * P is not on the curve. + * P = G, k = n + * Something wrong in the code. + * + * Mathmatically, k=1 and P=O is another possible case, but O cannot be + * represented by affine coordinate. + */ +int +FUNC(compute_kP) (ac *X, const bn256 *K, const ac *P) +{ + uint8_t index[86]; /* Lower 2-bit for index absolute value, msb is + for sign (encoded as: 0 means 1, 1 means -1). */ + bn256 K_dash[1]; + uint32_t k_is_even = bn256_is_even (K); + jpc Q[1], tmp[1], *dst; + int i; + int vk; + ac P3[1], P5[1], P7[1]; + const ac *p_Pi[4]; + + if (point_is_on_the_curve (P) < 0) + return -1; + + if (bn256_sub (K_dash, K, N) == 0) /* >= N, it's too big. */ + return -1; + + bn256_sub_uint (K_dash, K, k_is_even); + /* It keeps the condition: 1 <= K' <= N - 2, and K' is odd. */ + + p_Pi[0] = P; + p_Pi[1] = P3; + p_Pi[2] = P5; + p_Pi[3] = P7; + + { + jpc Q1[1]; + + memcpy (Q->x, P->x, sizeof (bn256)); + memcpy (Q->y, P->y, sizeof (bn256)); + memset (Q->z, 0, sizeof (bn256)); + Q->z->word[0] = 1; + + FUNC(jpc_double) (Q, Q); + FUNC(jpc_add_ac) (Q1, Q, P); + if (FUNC(jpc_to_ac) (P3, Q1) < 0) /* Never occurs, except coding errors. */ + return -1; + FUNC(jpc_double) (Q, Q); + FUNC(jpc_add_ac) (Q1, Q, P); + if (FUNC(jpc_to_ac) (P5, Q1) < 0) /* Never occurs, except coding errors. */ + return -1; + + memcpy (Q->x, P3->x, sizeof (bn256)); + memcpy (Q->y, P3->y, sizeof (bn256)); + memset (Q->z, 0, sizeof (bn256)); + Q->z->word[0] = 1; + FUNC(jpc_double) (Q, Q); + FUNC(jpc_add_ac) (Q1, Q, P); + if (FUNC(jpc_to_ac) (P7, Q1) < 0) /* Never occurs, except coding errors. */ + return -1; + } + + /* Fill index. */ + vk = get_vk_kP (K_dash, 0); + for (i = 1; i < 86; i++) + { + int vk_next, is_even; + + vk_next = get_vk_kP (K_dash, i); + is_even = ((vk_next & 1) == 0); + index[i-1] = (is_even << 7) | ((is_even?7-vk:vk-1) >> 1); + vk = vk_next + is_even; + } + index[85] = ((vk - 1) >> 1); + + memset (Q->z, 0, sizeof (bn256)); /* infinity */ + for (i = 85; i >= 0; i--) + { + FUNC(jpc_double) (Q, Q); + FUNC(jpc_double) (Q, Q); + FUNC(jpc_double) (Q, Q); + FUNC(jpc_add_ac_signed) (Q, Q, p_Pi[index[i]&0x03], index[i] >> 7); + } + + dst = k_is_even ? Q : tmp; + FUNC(jpc_add_ac) (dst, Q, P); + + return FUNC(jpc_to_ac) (X, Q); +} + + +/** + * @brief Compute signature (r,s) of hash string z with secret key d + */ +void +FUNC(ecdsa) (bn256 *r, bn256 *s, const bn256 *z, const bn256 *d) +{ + bn256 k[1]; + ac KG[1]; + bn512 tmp[1]; + bn256 k_inv[1]; + uint32_t carry; +#define borrow carry +#define tmp_k k_inv + + do + { + do + { + bn256_random (k); + if (bn256_add_uint (k, k, 1)) + continue; + if (bn256_sub (tmp_k, k, N) == 0) /* >= N, it's too big. */ + continue; + /* 1 <= k <= N - 1 */ + FUNC(compute_kG) (KG, k); + borrow = bn256_sub (r, KG->x, N); + if (borrow) + memcpy (r, KG->x, sizeof (bn256)); + else + memcpy (KG->x, r, sizeof (bn256)); + } + while (bn256_is_zero (r)); + + mod_inv (k_inv, k, N); + bn256_mul (tmp, r, d); + mod_reduce (s, tmp, N, MU_lower); + carry = bn256_add (s, s, z); + if (carry) + bn256_sub (s, s, N); + else + bn256_sub ((bn256 *)tmp, s, N); + bn256_mul (tmp, s, k_inv); + mod_reduce (s, tmp, N, MU_lower); + } + while (bn256_is_zero (s)); + +#undef tmp_k +#undef borrow +} + + +/** + * @brief Check if a secret d0 is valid or not + * + * @param D0 scalar D0: secret + * @param D1 scalar D1: secret candidate N-D0 + * + * Return 0 on error. + * Return -1 when D1 should be used as the secret + * Return 1 when D0 should be used as the secret + */ +int +FUNC(check_secret) (const bn256 *d0, bn256 *d1) +{ + ac Q0[1], Q1[1]; + + if (bn256_is_zero (d0) || bn256_sub (d1, N, d0) != 0) + /* == 0 or >= N, it's not valid. */ + return 0; + + FUNC(compute_kG) (Q0, d0); + FUNC(compute_kG) (Q1, d1); + + /* + * Jivsov compliant key check + */ + return bn256_cmp (Q1[0].y, Q0[0].y); +} diff --git a/gnuk/src/field-group-select.h b/gnuk/src/field-group-select.h new file mode 100644 index 0000000000..78cb6a5162 --- /dev/null +++ b/gnuk/src/field-group-select.h @@ -0,0 +1,7 @@ +#define CONCAT0(a,b) a##b +#define CONCAT1(a,b) CONCAT0(a,b) +#define CONCAT2(a,b,c) CONCAT1(a,b##c) +#define CONCAT3(a,b,c) CONCAT2(a,b,c) + +#define FUNC(func) CONCAT1(func##_,FIELD) +#define MFNC(func) CONCAT3(mod,FIELD,_##func) diff --git a/gnuk/src/flash.c b/gnuk/src/flash.c new file mode 100644 index 0000000000..9fbad9d66a --- /dev/null +++ b/gnuk/src/flash.c @@ -0,0 +1,752 @@ +/* + * flash.c -- Data Objects (DO) and GPG Key handling on Flash ROM + * + * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 + * 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 . + * + */ + +/* + * We assume single DO size is less than 256. + * + * NOTE: "Card holder certificate" (which size is larger than 256) is + * not put into data pool, but is implemented by its own flash + * page(s). + */ + +#include +#include + +#include "config.h" + +#include "sys.h" +#include "gnuk.h" + +/* + * Flash memory map + * + * _text + * .text + * .ctors + * .dtors + * _etext + * .data + * _bss_start + * .bss + * _end + * + * ch_certificate_startp + * <2048 bytes> + * _keystore_pool + * Three flash pages for keystore + * a page contains a key data of: + * For RSA-2048: 512-byte (p, q and N) + * For RSA-4096: 1024-byte (p, q and N) + * For ECDSA/ECDH and EdDSA, there are padding after public key + * _data_pool + * + */ + +#define FLASH_DATA_POOL_HEADER_SIZE 2 +#define FLASH_DATA_POOL_SIZE (flash_page_size*2) + +static uint16_t flash_page_size; +static const uint8_t *data_pool; +static uint8_t *last_p; + +/* The first halfword is generation for the data page (little endian) */ +const uint8_t const flash_data[4] __attribute__ ((section (".gnuk_data"))) = { + 0x00, 0x00, 0xff, 0xff +}; + +#ifdef GNU_LINUX_EMULATION +extern uint8_t *flash_addr_key_storage_start; +extern uint8_t *flash_addr_data_storage_start; +#define FLASH_ADDR_KEY_STORAGE_START flash_addr_key_storage_start +#define FLASH_ADDR_DATA_STORAGE_START flash_addr_data_storage_start +#else +/* Linker sets these symbols */ +extern uint8_t _keystore_pool; +extern uint8_t _data_pool; +#define FLASH_ADDR_KEY_STORAGE_START ((&_keystore_pool)) +#define FLASH_ADDR_DATA_STORAGE_START ((&_data_pool)) +#endif + +static int key_available_at (const uint8_t *k, int key_size) +{ + int i; + + for (i = 0; i < key_size; i++) + if (k[i]) + break; + if (i == key_size) /* It's ZERO. Released key. */ + return 0; + + for (i = 0; i < key_size; i++) + if (k[i] != 0xff) + break; + if (i == key_size) /* It's FULL. Unused key. */ + return 0; + + return 1; +} + + +#define CHIP_ID_REG ((uint32_t *)0xe0042000) +void +flash_do_storage_init (const uint8_t **p_do_start, const uint8_t **p_do_end) +{ + uint16_t gen0, gen1; + uint16_t *gen0_p = (uint16_t *)FLASH_ADDR_DATA_STORAGE_START; + uint16_t *gen1_p; + + flash_page_size = 1024; +#if !defined (GNU_LINUX_EMULATION) + if (((*CHIP_ID_REG) & 0xfff) == 0x0414) + flash_page_size = 2048; +#endif + + gen1_p = (uint16_t *)(FLASH_ADDR_DATA_STORAGE_START + flash_page_size); + data_pool = FLASH_ADDR_DATA_STORAGE_START; + + /* Check data pool generation and choose the page */ + gen0 = *gen0_p; + gen1 = *gen1_p; + + if (gen0 == 0xffff && gen1 == 0xffff) + { + /* It's terminated. */ + *p_do_start = *p_do_end = NULL; + return; + } + + if (gen0 == 0xffff) + /* Use another page if a page is erased. */ + data_pool = FLASH_ADDR_DATA_STORAGE_START + flash_page_size; + else if (gen1 == 0xffff) + /* Or use different page if another page is erased. */ + data_pool = FLASH_ADDR_DATA_STORAGE_START; + else if ((gen0 == 0xfffe && gen1 == 0) || gen1 > gen0) + /* When both pages have valid header, use newer page. */ + data_pool = FLASH_ADDR_DATA_STORAGE_START + flash_page_size; + + *p_do_start = data_pool + FLASH_DATA_POOL_HEADER_SIZE; + *p_do_end = data_pool + flash_page_size; +} + +static uint8_t *flash_key_getpage (enum kind_of_key kk); + +void +flash_terminate (void) +{ + int i; + + for (i = 0; i < 3; i++) + flash_erase_page ((uintptr_t)flash_key_getpage (i)); + flash_erase_page ((uintptr_t)FLASH_ADDR_DATA_STORAGE_START); + flash_erase_page ((uintptr_t)(FLASH_ADDR_DATA_STORAGE_START + flash_page_size)); + data_pool = FLASH_ADDR_DATA_STORAGE_START; + last_p = FLASH_ADDR_DATA_STORAGE_START + FLASH_DATA_POOL_HEADER_SIZE; +#if defined(CERTDO_SUPPORT) + flash_erase_page ((uintptr_t)&ch_certificate_start); + if (FLASH_CH_CERTIFICATE_SIZE > flash_page_size) + flash_erase_page ((uintptr_t)(&ch_certificate_start + flash_page_size)); +#endif +} + +void +flash_activate (void) +{ + flash_program_halfword ((uintptr_t)FLASH_ADDR_DATA_STORAGE_START, 0); +} + + +void +flash_key_storage_init (void) +{ + const uint8_t *p; + int i; + + /* For each key, find its address. */ + p = FLASH_ADDR_KEY_STORAGE_START; + for (i = 0; i < 3; i++) + { + const uint8_t *k; + int key_size = gpg_get_algo_attr_key_size (i, GPG_KEY_STORAGE); + + kd[i].pubkey = NULL; + for (k = p; k < p + flash_page_size; k += key_size) + if (key_available_at (k, key_size)) + { + int prv_len = gpg_get_algo_attr_key_size (i, GPG_KEY_PRIVATE); + + kd[i].pubkey = k + prv_len; + break; + } + + p += flash_page_size; + } +} + +/* + * Flash data pool managenent + * + * Flash data pool consists of two parts: + * 2-byte header + * contents + * + * Flash data pool objects: + * Data Object (DO) (of smart card) + * Internal objects: + * NONE (0x0000) + * 123-counter + * 14-bit counter + * bool object + * small enum + * + * Format of a Data Object: + * NR: 8-bit tag_number + * LEN: 8-bit length + * DATA: data * LEN + * PAD: optional byte for 16-bit alignment + */ + +void +flash_set_data_pool_last (const uint8_t *p) +{ + last_p = (uint8_t *)p; +} + +/* + * We use two pages + */ +static int +flash_copying_gc (void) +{ + uint8_t *src, *dst; + uint16_t generation; + + if (data_pool == FLASH_ADDR_DATA_STORAGE_START) + { + src = FLASH_ADDR_DATA_STORAGE_START; + dst = FLASH_ADDR_DATA_STORAGE_START + flash_page_size; + } + else + { + src = FLASH_ADDR_DATA_STORAGE_START + flash_page_size; + dst = FLASH_ADDR_DATA_STORAGE_START; + } + + generation = *(uint16_t *)src; + data_pool = dst; + gpg_data_copy (data_pool + FLASH_DATA_POOL_HEADER_SIZE); + if (generation == 0xfffe) + generation = 0; + else + generation++; + flash_program_halfword ((uintptr_t)dst, generation); + flash_erase_page ((uintptr_t)src); + return 0; +} + +static int +is_data_pool_full (size_t size) +{ + return last_p + size > data_pool + flash_page_size; +} + +static uint8_t * +flash_data_pool_allocate (size_t size) +{ + uint8_t *p; + + size = (size + 1) & ~1; /* allocation unit is 1-halfword (2-byte) */ + + if (is_data_pool_full (size)) + if (flash_copying_gc () < 0 || /*still*/ is_data_pool_full (size)) + fatal (FATAL_FLASH); + + p = last_p; + last_p += size; + return p; +} + +void +flash_do_write_internal (const uint8_t *p, int nr, const uint8_t *data, int len) +{ + uint16_t hw; + uintptr_t addr; + int i; + + addr = (uintptr_t)p; + hw = nr | (len << 8); + if (flash_program_halfword (addr, hw) != 0) + flash_warning ("DO WRITE ERROR"); + addr += 2; + + for (i = 0; i < len/2; i++) + { + hw = data[i*2] | (data[i*2+1]<<8); + if (flash_program_halfword (addr, hw) != 0) + flash_warning ("DO WRITE ERROR"); + addr += 2; + } + + if ((len & 1)) + { + hw = data[i*2] | 0xff00; + if (flash_program_halfword (addr, hw) != 0) + flash_warning ("DO WRITE ERROR"); + } +} + +const uint8_t * +flash_do_write (uint8_t nr, const uint8_t *data, int len) +{ + const uint8_t *p; + + DEBUG_INFO ("flash DO\r\n"); + + p = flash_data_pool_allocate (2 + len); + if (p == NULL) + { + DEBUG_INFO ("flash data pool allocation failure.\r\n"); + return NULL; + } + + flash_do_write_internal (p, nr, data, len); + DEBUG_INFO ("flash DO...done\r\n"); + return p + 1; +} + +void +flash_warning (const char *msg) +{ + (void)msg; + DEBUG_INFO ("FLASH: "); + DEBUG_INFO (msg); + DEBUG_INFO ("\r\n"); +} + +void +flash_do_release (const uint8_t *do_data) +{ + uintptr_t addr = (uintptr_t)do_data - 1; + uintptr_t addr_tag = addr; + int i; + int len = do_data[0]; + + /* Don't filling zero for data in code (such as ds_count_initial_value) */ + if (do_data < FLASH_ADDR_DATA_STORAGE_START + || do_data > FLASH_ADDR_DATA_STORAGE_START + FLASH_DATA_POOL_SIZE) + return; + + addr += 2; + + /* Fill zero for content and pad */ + for (i = 0; i < len/2; i ++) + { + if (flash_program_halfword (addr, 0) != 0) + flash_warning ("fill-zero failure"); + addr += 2; + } + + if ((len & 1)) + { + if (flash_program_halfword (addr, 0) != 0) + flash_warning ("fill-zero pad failure"); + } + + /* Fill 0x0000 for "tag_number and length" word */ + if (flash_program_halfword (addr_tag, 0) != 0) + flash_warning ("fill-zero tag_nr failure"); +} + + +static uint8_t * +flash_key_getpage (enum kind_of_key kk) +{ + /* There is a page for each KK. */ + return FLASH_ADDR_KEY_STORAGE_START + (flash_page_size * kk); +} + +uint8_t * +flash_key_alloc (enum kind_of_key kk) +{ + uint8_t *k, *k0 = flash_key_getpage (kk); + int i; + int key_size = gpg_get_algo_attr_key_size (kk, GPG_KEY_STORAGE); + + /* Seek free space in the page. */ + for (k = k0; k < k0 + flash_page_size; k += key_size) + { + const uint32_t *p = (const uint32_t *)k; + + for (i = 0; i < key_size/4; i++) + if (p[i] != 0xffffffff) + break; + + if (i == key_size/4) /* Yes, it's empty. */ + return k; + } + + /* Should not happen as we have enough free space all time, but just + in case. */ + return NULL; +} + +int +flash_key_write (uint8_t *key_addr, + const uint8_t *key_data, int key_data_len, + const uint8_t *pubkey, int pubkey_len) +{ + uint16_t hw; + uintptr_t addr; + int i; + + addr = (uintptr_t)key_addr; + for (i = 0; i < key_data_len/2; i ++) + { + hw = key_data[i*2] | (key_data[i*2+1]<<8); + if (flash_program_halfword (addr, hw) != 0) + return -1; + addr += 2; + } + + for (i = 0; i < pubkey_len/2; i ++) + { + hw = pubkey[i*2] | (pubkey[i*2+1]<<8); + if (flash_program_halfword (addr, hw) != 0) + return -1; + addr += 2; + } + + return 0; +} + +static int +flash_check_all_other_keys_released (const uint8_t *key_addr, int key_size) +{ + uintptr_t start = (uintptr_t)key_addr & ~(flash_page_size - 1); + const uint32_t *p = (const uint32_t *)start; + + while (p < (const uint32_t *)(start + flash_page_size)) + if (p == (const uint32_t *)key_addr) + p += key_size/4; + else + if (*p) + return 0; + else + p++; + + return 1; +} + +static void +flash_key_fill_zero_as_released (uint8_t *key_addr, int key_size) +{ + int i; + uintptr_t addr = (uintptr_t)key_addr; + + for (i = 0; i < key_size/2; i++) + flash_program_halfword (addr + i*2, 0); +} + +void +flash_key_release (uint8_t *key_addr, int key_size) +{ + if (flash_check_all_other_keys_released (key_addr, key_size)) + flash_erase_page (((uintptr_t)key_addr & ~(flash_page_size - 1))); + else + flash_key_fill_zero_as_released (key_addr, key_size); +} + +void +flash_key_release_page (enum kind_of_key kk) +{ + flash_erase_page ((uintptr_t)flash_key_getpage (kk)); +} + + +void +flash_clear_halfword (uintptr_t addr) +{ + flash_program_halfword (addr, 0); +} + + +void +flash_put_data_internal (const uint8_t *p, uint16_t hw) +{ + flash_program_halfword ((uintptr_t)p, hw); +} + +void +flash_put_data (uint16_t hw) +{ + uint8_t *p; + + p = flash_data_pool_allocate (2); + if (p == NULL) + { + DEBUG_INFO ("data allocation failure.\r\n"); + } + + flash_program_halfword ((uintptr_t)p, hw); +} + + +void +flash_bool_clear (const uint8_t **addr_p) +{ + const uint8_t *p; + + if ((p = *addr_p) == NULL) + return; + + flash_program_halfword ((uintptr_t)p, 0); + *addr_p = NULL; +} + +void +flash_bool_write_internal (const uint8_t *p, int nr) +{ + flash_program_halfword ((uintptr_t)p, nr); +} + +const uint8_t * +flash_bool_write (uint8_t nr) +{ + uint8_t *p; + uint16_t hw = nr; + + p = flash_data_pool_allocate (2); + if (p == NULL) + { + DEBUG_INFO ("bool allocation failure.\r\n"); + return NULL; + } + + flash_program_halfword ((uintptr_t)p, hw); + return p; +} + + +void +flash_enum_clear (const uint8_t **addr_p) +{ + flash_bool_clear (addr_p); +} + +void +flash_enum_write_internal (const uint8_t *p, int nr, uint8_t v) +{ + uint16_t hw = nr | (v << 8); + + flash_program_halfword ((uintptr_t)p, hw); +} + +const uint8_t * +flash_enum_write (uint8_t nr, uint8_t v) +{ + uint8_t *p; + uint16_t hw = nr | (v << 8); + + p = flash_data_pool_allocate (2); + if (p == NULL) + { + DEBUG_INFO ("enum allocation failure.\r\n"); + return NULL; + } + + flash_program_halfword ((uintptr_t)p, hw); + return p; +} + + +int +flash_cnt123_get_value (const uint8_t *p) +{ + if (p == NULL) + return 0; + else + { + uint8_t v = *p; + + /* + * After erase, a halfword in flash memory becomes 0xffff. + * The halfword can be programmed to any value. + * Then, the halfword can be programmed to zero. + * + * Thus, we can represent value 1, 2, and 3. + */ + if (v == 0xff) + return 1; + else if (v == 0x00) + return 3; + else + return 2; + } +} + +void +flash_cnt123_write_internal (const uint8_t *p, int which, int v) +{ + uint16_t hw; + + hw = NR_COUNTER_123 | (which << 8); + flash_program_halfword ((uintptr_t)p, hw); + + if (v == 1) + return; + else if (v == 2) + flash_program_halfword ((uintptr_t)p+2, 0xc3c3); + else /* v == 3 */ + flash_program_halfword ((uintptr_t)p+2, 0); +} + +void +flash_cnt123_increment (uint8_t which, const uint8_t **addr_p) +{ + const uint8_t *p; + uint16_t hw; + + if ((p = *addr_p) == NULL) + { + p = flash_data_pool_allocate (4); + if (p == NULL) + { + DEBUG_INFO ("cnt123 allocation failure.\r\n"); + return; + } + hw = NR_COUNTER_123 | (which << 8); + flash_program_halfword ((uintptr_t)p, hw); + *addr_p = p + 2; + } + else + { + uint8_t v = *p; + + if (v == 0) + return; + + if (v == 0xff) + hw = 0xc3c3; + else + hw = 0; + + flash_program_halfword ((uintptr_t)p, hw); + } +} + +void +flash_cnt123_clear (const uint8_t **addr_p) +{ + const uint8_t *p; + + if ((p = *addr_p) == NULL) + return; + + flash_program_halfword ((uintptr_t)p, 0); + p -= 2; + flash_program_halfword ((uintptr_t)p, 0); + *addr_p = NULL; +} + + +#if defined(CERTDO_SUPPORT) +int +flash_erase_binary (uint8_t file_id) +{ + if (file_id == FILEID_CH_CERTIFICATE) + { + const uint8_t *p = &ch_certificate_start; + if (flash_check_blank (p, FLASH_CH_CERTIFICATE_SIZE) == 0) + { + flash_erase_page ((uintptr_t)p); + if (FLASH_CH_CERTIFICATE_SIZE > flash_page_size) + flash_erase_page ((uintptr_t)p + flash_page_size); + } + + return 0; + } + + return -1; +} +#endif + + +int +flash_write_binary (uint8_t file_id, const uint8_t *data, + uint16_t len, uint16_t offset) +{ + uint16_t maxsize; + const uint8_t *p; + + if (file_id == FILEID_SERIAL_NO) + { + maxsize = 6; + p = &openpgpcard_aid[8]; + } +#ifdef FLASH_UPGRADE_SUPPORT + else if (file_id >= FILEID_UPDATE_KEY_0 && file_id <= FILEID_UPDATE_KEY_3) + { + maxsize = FIRMWARE_UPDATE_KEY_CONTENT_LEN; + p = gpg_get_firmware_update_key (file_id - FILEID_UPDATE_KEY_0); + if (len == 0 && offset == 0) + { /* This means removal of update key. */ + if (flash_program_halfword ((uintptr_t)p, 0) != 0) + flash_warning ("DO WRITE ERROR"); + return 0; + } + } +#endif +#if defined(CERTDO_SUPPORT) + else if (file_id == FILEID_CH_CERTIFICATE) + { + maxsize = FLASH_CH_CERTIFICATE_SIZE; + p = &ch_certificate_start; + } +#endif + else + return -1; + + if (offset + len > maxsize || (offset&1) || (len&1)) + return -1; + else + { + uint16_t hw; + uintptr_t addr; + int i; + + if (flash_check_blank (p + offset, len) == 0) + return -1; + + addr = (uintptr_t)p + offset; + for (i = 0; i < len/2; i++) + { + hw = data[i*2] | (data[i*2+1]<<8); + if (flash_program_halfword (addr, hw) != 0) + flash_warning ("DO WRITE ERROR"); + addr += 2; + } + + return 0; + } +} diff --git a/gnuk/src/gnuk-malloc.h b/gnuk/src/gnuk-malloc.h new file mode 100644 index 0000000000..e9c55d2ea1 --- /dev/null +++ b/gnuk/src/gnuk-malloc.h @@ -0,0 +1,16 @@ +/* + * Gnuk uses its own malloc functions. + * + * The intention is no-dependency to C library. But, we provide + * malloc and free here, since RSA routines uses malloc/free + * internally. + * + */ + +#include /* NULL and size_t */ + +#define malloc(size) gnuk_malloc (size) +#define free(p) gnuk_free (p) + +void *gnuk_malloc (size_t); +void gnuk_free (void *); diff --git a/gnuk/src/gnuk.h b/gnuk/src/gnuk.h new file mode 100644 index 0000000000..77189a4306 --- /dev/null +++ b/gnuk/src/gnuk.h @@ -0,0 +1,464 @@ +/* + * Application layer <-> CCID layer data structure + */ +struct apdu { + uint8_t seq; + + /* command APDU */ + uint8_t *cmd_apdu_head; /* CLS INS P1 P2 [ internal Lc ] */ + uint8_t *cmd_apdu_data; + uint16_t cmd_apdu_data_len; /* Nc, calculated by Lc field */ + uint16_t expected_res_size; /* Ne, calculated by Le field */ + + /* response APDU */ + uint16_t sw; + uint16_t res_apdu_data_len; + uint8_t *res_apdu_data; +}; + +extern struct apdu apdu; + +#define CARD_CHANGE_INSERT 0 +#define CARD_CHANGE_REMOVE 1 +#define CARD_CHANGE_TOGGLE 2 +void ccid_card_change_signal (int how); +void ccid_usb_reset (int); + +/* CCID thread */ +#define EV_RX_DATA_READY 1 /* USB Rx data available */ +#define EV_EXEC_FINISHED 2 /* OpenPGP Execution finished */ +#define EV_TX_FINISHED 4 /* CCID Tx finished */ +#define EV_CARD_CHANGE 8 +#define EV_USB_SET_INTERFACE 16 +#define EV_USB_DEVICE_RESET 32 + +/* OpenPGPcard thread */ +#define EV_PINPAD_INPUT_DONE 1 +#define EV_EXIT 2 +#define EV_CMD_AVAILABLE 4 +#define EV_VERIFY_CMD_AVAILABLE 8 +#define EV_MODIFY_CMD_AVAILABLE 16 + +/* Maximum cmd apdu data is key import 24+4+256+256 (proc_key_import) */ +#define MAX_CMD_APDU_DATA_SIZE (24+4+256+256) /* without header */ +/* Maximum res apdu data is public key 5+9+512 (gpg_do_public_key) */ +#define MAX_RES_APDU_DATA_SIZE (5+9+512) /* without trailer */ + +#define CCID_MSG_HEADER_SIZE 10 + +#define res_APDU apdu.res_apdu_data +#define res_APDU_size apdu.res_apdu_data_len + +/* USB buffer size of LL (Low-level): size of single Bulk transaction */ +#define USB_LL_BUF_SIZE 64 + +enum ccid_state { + CCID_STATE_NOCARD, /* No card available */ + CCID_STATE_START, /* Initial */ + CCID_STATE_WAIT, /* Waiting APDU */ + /* Busy1, Busy2, Busy3, Busy5 */ + CCID_STATE_EXECUTE, /* Busy4 */ + CCID_STATE_RECEIVE, /* APDU Received Partially */ + CCID_STATE_SEND, /* APDU Sent Partially */ + + CCID_STATE_EXITED, /* ICC Thread Terminated */ + CCID_STATE_EXEC_REQUESTED, /* Exec requested */ +}; + + +extern enum ccid_state *const ccid_state_p; + +extern volatile uint8_t auth_status; +#define AC_NONE_AUTHORIZED 0x00 +#define AC_PSO_CDS_AUTHORIZED 0x01 /* PW1 with 0x81 verified */ +#define AC_OTHER_AUTHORIZED 0x02 /* PW1 with 0x82 verified */ +#define AC_ADMIN_AUTHORIZED 0x04 /* PW3 verified */ +#define AC_NEVER 0x80 +#define AC_ALWAYS 0xFF + +#define PW_ERR_PW1 0 +#define PW_ERR_RC 1 +#define PW_ERR_PW3 2 +int gpg_pw_get_retry_counter (int who); +int gpg_pw_locked (uint8_t which); +void gpg_pw_reset_err_counter (uint8_t which); +void gpg_pw_increment_err_counter (uint8_t which); + +int ac_check_status (uint8_t ac_flag); +int verify_pso_cds (const uint8_t *pw, int pw_len); +int verify_other (const uint8_t *pw, int pw_len); +int verify_user_0 (uint8_t access, const uint8_t *pw, int buf_len, + int pw_len_known, const uint8_t *ks_pw1, int saveks); +int verify_admin (const uint8_t *pw, int pw_len); +int verify_admin_0 (const uint8_t *pw, int buf_len, int pw_len_known, + const uint8_t *ks_pw3, int saveks); + +void ac_reset_pso_cds (void); +void ac_reset_other (void); +void ac_reset_admin (void); +void ac_fini (void); + + +void set_res_sw (uint8_t sw1, uint8_t sw2); +extern uint8_t file_selection; +extern const uint8_t historical_bytes[]; +extern uint16_t data_objects_number_of_bytes; + +#define CHALLENGE_LEN 32 + +void gpg_data_scan (const uint8_t *start, const uint8_t *end); +void gpg_data_copy (const uint8_t *p); +void gpg_do_terminate (void); +void gpg_do_get_data (uint16_t tag, int with_tag); +void gpg_do_put_data (uint16_t tag, const uint8_t *data, int len); +void gpg_do_public_key (uint8_t kk_byte); +void gpg_do_keygen (uint8_t *buf); + +const uint8_t *gpg_get_firmware_update_key (uint8_t keyno); + +/* Constants: algo+size */ +#define ALGO_RSA4K 0 +#define ALGO_NISTP256R1 1 +#define ALGO_SECP256K1 2 +#define ALGO_ED25519 3 +#define ALGO_CURVE25519 4 +#define ALGO_RSA2K 255 + +enum kind_of_key { + GPG_KEY_FOR_SIGNING = 0, + GPG_KEY_FOR_DECRYPTION, + GPG_KEY_FOR_AUTHENTICATION, +}; + +enum size_of_key { + GPG_KEY_STORAGE = 0, /* PUBKEY + PRVKEY rounded to 2^N */ + GPG_KEY_PUBLIC, + GPG_KEY_PRIVATE, +}; + +int gpg_get_algo_attr (enum kind_of_key kk); +int gpg_get_algo_attr_key_size (enum kind_of_key kk, enum size_of_key s); + +void flash_do_storage_init (const uint8_t **, const uint8_t **); +void flash_terminate (void); +void flash_activate (void); +void flash_key_storage_init (void); +void flash_do_release (const uint8_t *); +const uint8_t *flash_do_write (uint8_t nr, const uint8_t *data, int len); +uint8_t *flash_key_alloc (enum kind_of_key); +void flash_key_release (uint8_t *, int); +void flash_key_release_page (enum kind_of_key); +int flash_key_write (uint8_t *key_addr, + const uint8_t *key_data, int key_data_len, + const uint8_t *pubkey, int pubkey_len); +void flash_set_data_pool_last (const uint8_t *p); +void flash_clear_halfword (uintptr_t addr); +void flash_increment_counter (uint8_t counter_tag_nr); +void flash_reset_counter (uint8_t counter_tag_nr); + +#define FILEID_SERIAL_NO 0 +#define FILEID_UPDATE_KEY_0 1 +#define FILEID_UPDATE_KEY_1 2 +#define FILEID_UPDATE_KEY_2 3 +#define FILEID_UPDATE_KEY_3 4 +#define FILEID_CH_CERTIFICATE 5 +int flash_erase_binary (uint8_t file_id); +int flash_write_binary (uint8_t file_id, const uint8_t *data, + uint16_t len, uint16_t offset); + +#define FLASH_CH_CERTIFICATE_SIZE 2048 + +/* Linker set these two symbols */ +extern uint8_t ch_certificate_start; + +#define FIRMWARE_UPDATE_KEY_CONTENT_LEN 256 /* RSA-2048 (p and q) */ + +#define INITIAL_VECTOR_SIZE 16 +#define DATA_ENCRYPTION_KEY_SIZE 16 + +#define MAX_PRVKEY_LEN 512 /* Maximum is the case for RSA 4096-bit. */ + +struct key_data { + const uint8_t *pubkey; /* Pointer to public key */ + uint8_t data[MAX_PRVKEY_LEN]; /* decrypted private key data content */ +}; + +struct prvkey_data { + /* + * IV: Initial Vector + */ + uint8_t iv[INITIAL_VECTOR_SIZE]; + /* + * Checksum + */ + uint8_t checksum_encrypted[DATA_ENCRYPTION_KEY_SIZE]; + /* + * DEK (Data Encryption Key) encrypted + */ + uint8_t dek_encrypted_1[DATA_ENCRYPTION_KEY_SIZE]; /* For user */ + uint8_t dek_encrypted_2[DATA_ENCRYPTION_KEY_SIZE]; /* For resetcode */ + uint8_t dek_encrypted_3[DATA_ENCRYPTION_KEY_SIZE]; /* For admin */ +}; + +#define BY_USER 1 +#define BY_RESETCODE 2 +#define BY_ADMIN 3 + +/* + * Maximum length of pass phrase is 127. + * We use the top bit (0x80) to encode if keystring is available within DO. + */ +#define PW_LEN_MAX 127 +#define PW_LEN_MASK 0x7f +#define PW_LEN_KEYSTRING_BIT 0x80 + +#define SALT_SIZE 8 + +void s2k (const unsigned char *salt, size_t slen, + const unsigned char *input, size_t ilen, unsigned char output[32]); + +#define KEYSTRING_PASSLEN_SIZE 1 +#define KEYSTRING_SALT_SIZE SALT_SIZE +#define KEYSTRING_MD_SIZE 32 +#define KEYSTRING_SIZE (KEYSTRING_PASSLEN_SIZE + KEYSTRING_SALT_SIZE \ + + KEYSTRING_MD_SIZE) +#define KS_META_SIZE (KEYSTRING_PASSLEN_SIZE + KEYSTRING_SALT_SIZE) +#define KS_GET_SALT(ks) (ks + KEYSTRING_PASSLEN_SIZE) +#define KS_GET_KEYSTRING(ks) (ks + KS_META_SIZE) + +void gpg_do_clear_prvkey (enum kind_of_key kk); +int gpg_do_load_prvkey (enum kind_of_key kk, int who, const uint8_t *keystring); +int gpg_do_chks_prvkey (enum kind_of_key kk, + int who_old, const uint8_t *old_ks, + int who_new, const uint8_t *new_ks); + +int gpg_change_keystring (int who_old, const uint8_t *old_ks, + int who_new, const uint8_t *new_ks); + +extern struct key_data kd[3]; + +#ifdef DEBUG +void stdout_init (void); +#define DEBUG_MORE 1 +/* + * Debug functions in debug.c + */ +void put_byte (uint8_t b); +void put_byte_with_no_nl (uint8_t b); +void put_short (uint16_t x); +void put_word (uint32_t x); +void put_int (uint32_t x); +void put_string (const char *s); +void put_binary (const char *s, int len); + +#define DEBUG_INFO(msg) put_string (msg) +#define DEBUG_WORD(w) put_word (w) +#define DEBUG_SHORT(h) put_short (h) +#define DEBUG_BYTE(b) put_byte (b) +#define DEBUG_BINARY(s,len) put_binary ((const char *)s,len) +#else +#define DEBUG_INFO(msg) +#define DEBUG_WORD(w) +#define DEBUG_SHORT(h) +#define DEBUG_BYTE(b) +#define DEBUG_BINARY(s,len) +#endif + +int rsa_sign (const uint8_t *, uint8_t *, int, struct key_data *, int); +int modulus_calc (const uint8_t *, int, uint8_t *); +int rsa_decrypt (const uint8_t *, uint8_t *, int, struct key_data *, + unsigned int *); +int rsa_verify (const uint8_t *, int, const uint8_t *, const uint8_t *); +int rsa_genkey (int, uint8_t *, uint8_t *); + +int ecdsa_sign_p256r1 (const uint8_t *hash, uint8_t *output, + const uint8_t *key_data); +int ecc_compute_public_p256r1 (const uint8_t *key_data, uint8_t *); +int ecc_check_secret_p256r1 (const uint8_t *d0, uint8_t *d1); +int ecdh_decrypt_p256r1 (const uint8_t *input, uint8_t *output, + const uint8_t *key_data); + +int ecdsa_sign_p256k1 (const uint8_t *hash, uint8_t *output, + const uint8_t *key_data); +int ecc_compute_public_p256k1 (const uint8_t *key_data, uint8_t *); +int ecc_check_secret_p256k1 (const uint8_t *d0, uint8_t *d1); +int ecdh_decrypt_p256k1 (const uint8_t *input, uint8_t *output, + const uint8_t *key_data); + +int eddsa_sign_25519 (const uint8_t *input, size_t ilen, uint32_t *output, + const uint8_t *sk_a, const uint8_t *seed, + const uint8_t *pk); +void eddsa_compute_public_25519 (const uint8_t *a, uint8_t *); +void ecdh_compute_public_25519 (const uint8_t *a, uint8_t *); +int ecdh_decrypt_curve25519 (const uint8_t *input, uint8_t *output, + const uint8_t *key_data); + +const uint8_t *gpg_do_read_simple (uint8_t); +void gpg_do_write_simple (uint8_t, const uint8_t *, int); +void gpg_increment_digital_signature_counter (void); + + +void fatal (uint8_t code) __attribute__ ((noreturn)); +#define FATAL_FLASH 1 +#define FATAL_RANDOM 2 +#define FATAL_HEAP 3 + +extern uint8_t keystring_md_pw3[KEYSTRING_MD_SIZE]; +extern uint8_t admin_authorized; + +/*** Flash memory tag values ***/ +/* Data objects */ +/* + * Representation of data object: + * + * <-1 halfword-> <--len/2 halfwords-> + * <-tag-><-len-> <---data content---> + */ +#define NR_DO_SEX 0x00 +#define NR_DO_FP_SIG 0x01 +#define NR_DO_FP_DEC 0x02 +#define NR_DO_FP_AUT 0x03 +#define NR_DO_CAFP_1 0x04 +#define NR_DO_CAFP_2 0x05 +#define NR_DO_CAFP_3 0x06 +#define NR_DO_KGTIME_SIG 0x07 +#define NR_DO_KGTIME_DEC 0x08 +#define NR_DO_KGTIME_AUT 0x09 +#define NR_DO_LOGIN_DATA 0x0a +#define NR_DO_URL 0x0b +#define NR_DO_NAME 0x0c +#define NR_DO_LANGUAGE 0x0d +#define NR_DO_PRVKEY_SIG 0x0e +#define NR_DO_PRVKEY_DEC 0x0f +#define NR_DO_PRVKEY_AUT 0x10 +#define NR_DO_KEYSTRING_PW1 0x11 +#define NR_DO_KEYSTRING_RC 0x12 +#define NR_DO_KEYSTRING_PW3 0x13 +#define NR_DO__LAST__ 20 /* == 0x14 */ +/* 14-bit counter for DS: Recorded in flash memory by 1-halfword (2-byte). */ +/* + * Representation of 14-bit counter: + * 0: 0x8000 + * 1: 0x8001 + * ... + * 16383: 0xbfff + */ +#define NR_COUNTER_DS 0x80 /* ..0xbf */ +/* 10-bit counter for DS: Recorded in flash memory by 1-halfword (2-byte). */ +/* + * Representation of 10-bit counter: + * 0: 0xc000 + * 1: 0xc001 + * ... + * 1023: 0xc3ff + */ +#define NR_COUNTER_DS_LSB 0xc0 /* ..0xc3 */ +/* + * Boolean object, small enum, or 8-bit integer: + * Recorded in flash memory by 1-halfword (2-byte) + */ +/* + * Representation of Boolean object: + * 0: No record in flash memory + * 1: 0xf000 + */ +#define NR_BOOL_PW1_LIFETIME 0xf0 +/* + * Representation of algorithm attribute object: + * RSA-2048: No record in flash memory + * RSA-4096: 0xf?00 + * ECC p256r1: 0xf?01 + * ECC p256k1: 0xf?02 + * ECC Ed25519: 0xf?03 + * ECC Curve25519: 0xf?04 + * where == 1 (signature), 2 (decryption) or 3 (authentication) + */ +#define NR_KEY_ALGO_ATTR_SIG 0xf1 +#define NR_KEY_ALGO_ATTR_DEC 0xf2 +#define NR_KEY_ALGO_ATTR_AUT 0xf3 +/* + * NR_UINT_SOMETHING could be here... Use 0xf[456789abcd] + */ +/* 123-counters: Recorded in flash memory by 2-halfword (4-byte). */ +/* + * Representation of 123-counters: + * 0: No record in flash memory + * 1: 0xfe?? 0xffff + * 2: 0xfe?? 0xc3c3 + * 3: 0xfe?? 0x0000 + * where is placed at second byte + */ +#define NR_COUNTER_123 0xfe +#define NR_EMPTY 0xff + +#define SIZE_PW_STATUS_BYTES 7 + + +#define NUM_ALL_PRV_KEYS 3 /* SIG, DEC and AUT */ + +#if !defined(OPENPGP_CARD_INITIAL_PW1) +#define OPENPGP_CARD_INITIAL_PW1 "123456" +#endif + +#if !defined(OPENPGP_CARD_INITIAL_PW3) +#define OPENPGP_CARD_INITIAL_PW3 "12345678" +#endif + +extern const uint8_t openpgpcard_aid[14]; + +void flash_bool_clear (const uint8_t **addr_p); +const uint8_t *flash_bool_write (uint8_t nr); +void flash_enum_clear (const uint8_t **addr_p); +const uint8_t *flash_enum_write (uint8_t nr, uint8_t v); +int flash_cnt123_get_value (const uint8_t *p); +void flash_cnt123_increment (uint8_t which, const uint8_t **addr_p); +void flash_cnt123_clear (const uint8_t **addr_p); +void flash_put_data (uint16_t hw); +void flash_warning (const char *msg); + +void flash_put_data_internal (const uint8_t *p, uint16_t hw); +void flash_bool_write_internal (const uint8_t *p, int nr); +void flash_enum_write_internal (const uint8_t *p, int nr, uint8_t v); +void flash_cnt123_write_internal (const uint8_t *p, int which, int v); +void flash_do_write_internal (const uint8_t *p, int nr, + const uint8_t *data, int len); + +extern const uint8_t gnuk_string_serial[]; + +#define LED_ONESHOT 1 +#define LED_TWOSHOTS 2 +#define LED_SHOW_STATUS 4 +#define LED_FATAL 8 +#define LED_SYNC 16 +#define LED_GNUK_EXEC 32 +#define LED_START_COMMAND 64 +#define LED_FINISH_COMMAND 128 +void led_blink (int spec); + +#if defined(PINPAD_SUPPORT) +# if defined(PINPAD_CIR_SUPPORT) +void cir_init (void); +# elif defined(PINPAD_DIAL_SUPPORT) +void dial_sw_disable (void); +void dial_sw_enable (void); +# elif defined(PINPAD_DND_SUPPORT) +void msc_init (void); +void msc_media_insert_change (int available); +int msc_scsi_write (uint32_t lba, const uint8_t *buf, size_t size); +int msc_scsi_read (uint32_t lba, const uint8_t **sector_p); +void msc_scsi_stop (uint8_t code); +# endif +#define PIN_INPUT_CURRENT 1 +#define PIN_INPUT_NEW 2 +#define PIN_INPUT_CONFIRM 3 +#define MAX_PIN_CHARS 32 +extern uint8_t pin_input_buffer[MAX_PIN_CHARS]; +extern uint8_t pin_input_len; + +int pinpad_getline (int msg_code, uint32_t timeout_usec); + +#endif + +extern uint8_t _regnual_start, __heap_end__[]; + +uint8_t * sram_address (uint32_t offset); diff --git a/gnuk/src/gnuk.ld b/gnuk/src/gnuk.ld new file mode 100644 index 0000000000..8cb83dba03 --- /dev/null +++ b/gnuk/src/gnuk.ld @@ -0,0 +1,145 @@ +/* + * ST32F103 memory setup. + */ +MEMORY +{ + flash0 : org = 0x08000000, len = 4k + flash : org = 0x08000000+0x1000, len = 128k - 4k + ram : org = 0x20000000, len = 20k +} + +__ram_start__ = ORIGIN(ram); +__ram_size__ = LENGTH(ram); +__ram_end__ = __ram_start__ + __ram_size__; + +SECTIONS +{ + . = 0; + + .sys : ALIGN(4) SUBALIGN(4) + { + _sys = .; + KEEP(*(.vectors)) + . = ALIGN(16); + KEEP(*(.sys.version)) + KEEP(*(.sys.board_id)) + KEEP(*(.sys.board_name)) + build/sys-*.o(.text) + build/sys-*.o(.text.*) + build/sys-*.o(.rodata) + build/sys-*.o(.rodata.*) + . = ALIGN(1024); + *(.sys.0) + *(.sys.1) + *(.sys.2) + } > flash0 + + _text = .; + + .startup : ALIGN(128) SUBALIGN(128) + { + KEEP(*(.startup.vectors)) + . = ALIGN (16); + } > flash =0xffffffff + + .text : ALIGN(16) SUBALIGN(16) + { + *(.text.startup.*) + *(.text) + *(.text.*) + *(.rodata) + *(.rodata.*) + *(.glue_7t) + *(.glue_7) + *(.gcc*) + . = ALIGN(8); + } > flash + + .ARM.extab : {*(.ARM.extab* .gnu.linkonce.armextab.*)} > flash + + .ARM.exidx : { + PROVIDE(__exidx_start = .); + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + PROVIDE(__exidx_end = .); + } > flash + + .eh_frame_hdr : {*(.eh_frame_hdr)} > flash + + .eh_frame : ONLY_IF_RO {*(.eh_frame)} > flash + + .textalign : ONLY_IF_RO { . = ALIGN(8); } > flash + + _etext = .; + _textdata = _etext; + + .stacks (NOLOAD) : + { + . = ALIGN(8); + *(.main_stack) + *(.process_stack.0) + *(.process_stack.1) + *(.process_stack.2) + *(.process_stack.3) + *(.process_stack.4) + *(.process_stack.5) + *(.process_stack.6) + *(.process_stack.7) + . = ALIGN(8); + } > ram + + .data : + { + . = ALIGN(4); + PROVIDE(_data = .); + *(.data) + . = ALIGN(4); + *(.data.*) + . = ALIGN(4); + *(.ramtext) + . = ALIGN(4); + PROVIDE(_edata = .); + } > ram AT > flash + + .bss : + { + . = ALIGN(4); + PROVIDE(_bss_start = .); + *(.bss) + . = ALIGN(4); + *(.bss.*) + . = ALIGN(4); + *(COMMON) + . = ALIGN(4); + PROVIDE(_bss_end = .); + } > ram + + . = ALIGN(16); + PROVIDE(end = .); + _end = .; + . = ALIGN(512); + + _regnual_start = .; + + + .gnuk_flash : + { + . = ALIGN (1024); + _keystore_pool = .; + . += 1024; + . = ALIGN(1024); + . += 1024; + . = ALIGN(1024); + . += 1024; + . = ALIGN(1024); + _updatekey_store = .; + . += 1024; + . = ALIGN(1024); + _data_pool = .; + KEEP(*(.gnuk_data)) + . = ALIGN(1024); + . += 1024; + } > flash =0xffffffff +} + +__heap_base__ = _end; +__heap_end__ = __ram_end__; diff --git a/gnuk/src/gnuk.ld.in b/gnuk/src/gnuk.ld.in new file mode 100644 index 0000000000..70042c5cd6 --- /dev/null +++ b/gnuk/src/gnuk.ld.in @@ -0,0 +1,155 @@ +/* + * ST32F103 memory setup. + */ +MEMORY +{ + flash0 : org = @ORIGIN@, len = 4k + flash : org = @ORIGIN@+0x1000, len = @FLASH_SIZE@k - 4k + ram : org = 0x20000000, len = @MEMORY_SIZE@k +} + +__ram_start__ = ORIGIN(ram); +__ram_size__ = LENGTH(ram); +__ram_end__ = __ram_start__ + __ram_size__; + +SECTIONS +{ + . = 0; + + .sys : ALIGN(4) SUBALIGN(4) + { + _sys = .; + KEEP(*(.vectors)) + . = ALIGN(16); + KEEP(*(.sys.version)) + KEEP(*(.sys.board_id)) + KEEP(*(.sys.board_name)) + build/sys-*.o(.text) + build/sys-*.o(.text.*) + build/sys-*.o(.rodata) + build/sys-*.o(.rodata.*) + . = ALIGN(1024); + *(.sys.0) + *(.sys.1) + *(.sys.2) + } > flash0 + + _text = .; + + .startup : ALIGN(128) SUBALIGN(128) + { + KEEP(*(.startup.vectors)) + . = ALIGN (16); + } > flash =0xffffffff + + .text : ALIGN(16) SUBALIGN(16) + { + *(.text.startup.*) + *(.text) + *(.text.*) + *(.rodata) + *(.rodata.*) + *(.glue_7t) + *(.glue_7) + *(.gcc*) + . = ALIGN(8); + } > flash + + .ARM.extab : {*(.ARM.extab* .gnu.linkonce.armextab.*)} > flash + + .ARM.exidx : { + PROVIDE(__exidx_start = .); + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + PROVIDE(__exidx_end = .); + } > flash + + .eh_frame_hdr : {*(.eh_frame_hdr)} > flash + + .eh_frame : ONLY_IF_RO {*(.eh_frame)} > flash + + .textalign : ONLY_IF_RO { . = ALIGN(8); } > flash + + _etext = .; + _textdata = _etext; + + .stacks (NOLOAD) : + { + . = ALIGN(8); + *(.main_stack) + *(.process_stack.0) + *(.process_stack.1) + *(.process_stack.2) + *(.process_stack.3) + *(.process_stack.4) + *(.process_stack.5) + *(.process_stack.6) + *(.process_stack.7) + . = ALIGN(8); + } > ram + + .data : + { + . = ALIGN(4); + PROVIDE(_data = .); + *(.data) + . = ALIGN(4); + *(.data.*) + . = ALIGN(4); + *(.ramtext) + . = ALIGN(4); + PROVIDE(_edata = .); + } > ram AT > flash + + .bss : + { + . = ALIGN(4); + PROVIDE(_bss_start = .); + *(.bss) + . = ALIGN(4); + *(.bss.*) + . = ALIGN(4); + *(COMMON) + . = ALIGN(4); + PROVIDE(_bss_end = .); + } > ram + + . = ALIGN(16); + PROVIDE(end = .); + _end = .; + . = ALIGN(512); + + _regnual_start = .; + +@CERTDO_SUPPORT_START@ + .gnuk_ch_certificate : + { + . = ALIGN (@FLASH_PAGE_SIZE@); + ch_certificate_start = .; + LONG(0xffffffff); + . += 1920; + . = ALIGN (@FLASH_PAGE_SIZE@); + } > flash =0xffffffff +@CERTDO_SUPPORT_END@ + + .gnuk_flash : + { + . = ALIGN (@FLASH_PAGE_SIZE@); + _keystore_pool = .; + . += 1024; + . = ALIGN(@FLASH_PAGE_SIZE@); + . += 1024; + . = ALIGN(@FLASH_PAGE_SIZE@); + . += 1024; + . = ALIGN(@FLASH_PAGE_SIZE@); + _updatekey_store = .; + . += 1024; + . = ALIGN(@FLASH_PAGE_SIZE@); + _data_pool = .; + KEEP(*(.gnuk_data)) + . = ALIGN(@FLASH_PAGE_SIZE@); + . += @FLASH_PAGE_SIZE@; + } > flash =0xffffffff +} + +__heap_base__ = _end; +__heap_end__ = __ram_end__; diff --git a/gnuk/src/jpc-ac_p256k1.h b/gnuk/src/jpc-ac_p256k1.h new file mode 100644 index 0000000000..d8624bd091 --- /dev/null +++ b/gnuk/src/jpc-ac_p256k1.h @@ -0,0 +1,14 @@ +/** + * @brief Jacobian projective coordinates + */ +typedef struct +{ + bn256 x[1]; + bn256 y[1]; + bn256 z[1]; +} jpc; + +void jpc_double_p256k1 (jpc *X, const jpc *A); +void jpc_add_ac_p256k1 (jpc *X, const jpc *A, const ac *B); +void jpc_add_ac_signed_p256k1 (jpc *X, const jpc *A, const ac *B, int minus); +int jpc_to_ac_p256k1 (ac *X, const jpc *A); diff --git a/gnuk/src/jpc-ac_p256r1.h b/gnuk/src/jpc-ac_p256r1.h new file mode 100644 index 0000000000..e2350892f1 --- /dev/null +++ b/gnuk/src/jpc-ac_p256r1.h @@ -0,0 +1,14 @@ +/** + * @brief Jacobian projective coordinates + */ +typedef struct +{ + bn256 x[1]; + bn256 y[1]; + bn256 z[1]; +} jpc; + +void jpc_double_p256r1 (jpc *X, const jpc *A); +void jpc_add_ac_p256r1 (jpc *X, const jpc *A, const ac *B); +void jpc_add_ac_signed_p256r1 (jpc *X, const jpc *A, const ac *B, int minus); +int jpc_to_ac_p256r1 (ac *X, const jpc *A); diff --git a/gnuk/src/jpc.c b/gnuk/src/jpc.c new file mode 100644 index 0000000000..e6a1f7d26d --- /dev/null +++ b/gnuk/src/jpc.c @@ -0,0 +1,199 @@ +/* + * jpc.c -- arithmetic on Jacobian projective coordinates. + * + * Copyright (C) 2011, 2013 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 "field-group-select.h" + +/** + * @brief X = 2 * A + * + * @param X Destination JPC + * @param A JPC + */ +void +FUNC(jpc_double) (jpc *X, const jpc *A) +{ + bn256 a[1], b[1], c[1], tmp0[1]; + bn256 *d; + + if (bn256_is_zero (A->z)) /* A is infinite */ + return; + + d = X->x; + MFNC(sqr) (a, A->y); + memcpy (b, a, sizeof (bn256)); + MFNC(mul) (a, a, A->x); + MFNC(shift) (a, a, 2); + + MFNC(sqr) (b, b); + MFNC(shift) (b, b, 3); + +#if defined(COEFFICIENT_A_IS_MINUS_3) + MFNC(sqr) (tmp0, A->z); + MFNC(sub) (c, A->x, tmp0); + MFNC(add) (tmp0, tmp0, A->x); + MFNC(mul) (tmp0, tmp0, c); + MFNC(shift) (c, tmp0, 1); + MFNC(add) (c, c, tmp0); +#elif defined (COEFFICIENT_A_IS_ZERO) + MFNC(sqr) (tmp0, A->x); + MFNC(shift) (c, tmp0, 1); + MFNC(add) (c, c, tmp0); +#else +#error "not supported." +#endif + + MFNC(sqr) (d, c); + MFNC(shift) (tmp0, a, 1); + MFNC(sub) (d, d, tmp0); + + MFNC(mul) (X->z, A->y, A->z); + MFNC(shift) (X->z, X->z, 1); + + MFNC(sub) (tmp0, a, d); + MFNC(mul) (tmp0, c, tmp0); + MFNC(sub) (X->y, tmp0, b); +} + +/** + * @brief X = A + B + * + * @param X Destination JPC + * @param A JPC + * @param B AC + * @param MINUS if 1 subtraction, addition otherwise. + */ +void +FUNC(jpc_add_ac_signed) (jpc *X, const jpc *A, const ac *B, int minus) +{ + bn256 a[1], b[1], c[1], d[1], tmp[1]; +#define minus_B_y c +#define c_sqr a +#define c_cube b +#define x1_c_sqr c +#define x1_c_sqr_2 c +#define c_cube_plus_x1_c_sqr_2 c +#define x1_c_sqr_copy a +#define y3_tmp c +#define y1_c_cube a + + if (bn256_is_zero (A->z)) /* A is infinite */ + { + memcpy (X->x, B->x, sizeof (bn256)); + if (minus) + { + memcpy (tmp, B->y, sizeof (bn256)); + bn256_sub (X->y, CONST_P256, B->y); + } + else + { + memcpy (X->y, B->y, sizeof (bn256)); + bn256_sub (tmp, CONST_P256, B->y); + } + memset (X->z, 0, sizeof (bn256)); + X->z->word[0] = 1; + return; + } + + MFNC(sqr) (a, A->z); + memcpy (b, a, sizeof (bn256)); + MFNC(mul) (a, a, B->x); + + MFNC(mul) (b, b, A->z); + if (minus) + { + bn256_sub (minus_B_y, CONST_P256, B->y); + MFNC(mul) (b, b, minus_B_y); + } + else + { + bn256_sub (tmp, CONST_P256, B->y); + MFNC(mul) (b, b, B->y); + } + + if (bn256_cmp (A->x, a) == 0 && bn256_cmp (A->y, b) == 0) + { + FUNC(jpc_double) (X, A); + return; + } + + MFNC(sub) (c, a, A->x); + MFNC(sub) (d, b, A->y); + + MFNC(mul) (X->z, A->z, c); + + MFNC(sqr) (c_sqr, c); + MFNC(mul) (c_cube, c_sqr, c); + + MFNC(mul) (x1_c_sqr, A->x, c_sqr); + + MFNC(sqr) (X->x, d); + memcpy (x1_c_sqr_copy, x1_c_sqr, sizeof (bn256)); + MFNC(shift) (x1_c_sqr_2, x1_c_sqr, 1); + MFNC(add) (c_cube_plus_x1_c_sqr_2, x1_c_sqr_2, c_cube); + MFNC(sub) (X->x, X->x, c_cube_plus_x1_c_sqr_2); + + MFNC(sub) (y3_tmp, x1_c_sqr_copy, X->x); + MFNC(mul) (y3_tmp, y3_tmp, d); + MFNC(mul) (y1_c_cube, A->y, c_cube); + MFNC(sub) (X->y, y3_tmp, y1_c_cube); +} + +/** + * @brief X = A + B + * + * @param X Destination JPC + * @param A JPC + * @param B AC + */ +void +FUNC(jpc_add_ac) (jpc *X, const jpc *A, const ac *B) +{ + FUNC(jpc_add_ac_signed) (X, A, B, 0); +} + +/** + * @brief X = convert A + * + * @param X Destination AC + * @param A JPC + * + * Return -1 on error (infinite). + * Return 0 on success. + */ +int +FUNC(jpc_to_ac) (ac *X, const jpc *A) +{ + bn256 z_inv[1], z_inv_sqr[1]; + + if (bn256_is_zero (A->z)) + return -1; + + mod_inv (z_inv, A->z, CONST_P256); + + MFNC(sqr) (z_inv_sqr, z_inv); + MFNC(mul) (z_inv, z_inv, z_inv_sqr); + + MFNC(mul) (X->x, A->x, z_inv_sqr); + MFNC(mul) (X->y, A->y, z_inv); + return 0; +} diff --git a/gnuk/src/jpc_p256k1.c b/gnuk/src/jpc_p256k1.c new file mode 100644 index 0000000000..a903b12921 --- /dev/null +++ b/gnuk/src/jpc_p256k1.c @@ -0,0 +1,36 @@ +/* + * jpc_p256k1.c -- arithmetic on Jacobian projective coordinates for p256k1. + * + * Copyright (C) 2014 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 +#include +#include "bn.h" +#include "mod.h" +#include "modp256k1.h" +#include "affine.h" +#include "jpc-ac_p256k1.h" + +#define FIELD p256k1 +#define CONST_P256 P256K1 +#define COEFFICIENT_A_IS_ZERO 1 + +#include "jpc.c" diff --git a/gnuk/src/jpc_p256r1.c b/gnuk/src/jpc_p256r1.c new file mode 100644 index 0000000000..1196945987 --- /dev/null +++ b/gnuk/src/jpc_p256r1.c @@ -0,0 +1,36 @@ +/* + * jpc_p256r1.c -- arithmetic on Jacobian projective coordinates for p256r1. + * + * Copyright (C) 2014 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 +#include +#include "bn.h" +#include "mod.h" +#include "modp256r1.h" +#include "affine.h" +#include "jpc-ac_p256r1.h" + +#define FIELD p256r1 +#define CONST_P256 P256R1 +#define COEFFICIENT_A_IS_MINUS_3 1 + +#include "jpc.c" diff --git a/gnuk/src/main.c b/gnuk/src/main.c new file mode 100644 index 0000000000..4c62d9d185 --- /dev/null +++ b/gnuk/src/main.c @@ -0,0 +1,615 @@ +/* + * main.c - main routine of Gnuk + * + * Copyright (C) 2010, 2011, 2012, 2013, 2015, 2016, 2017 + * 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 +#include +#include +#include + +#include "config.h" + +#include "sys.h" +#include "adc.h" +#include "gnuk.h" +#include "usb_lld.h" +#include "usb-cdc.h" +#include "random.h" +#ifdef GNU_LINUX_EMULATION +#include +#include +#define main emulated_main +#else +#include "mcu/stm32f103.h" +#endif + +/* + * main thread does 1-bit LED display output + */ +#define LED_TIMEOUT_INTERVAL (75*1000) +#define LED_TIMEOUT_ZERO (25*1000) +#define LED_TIMEOUT_ONE (100*1000) +#define LED_TIMEOUT_STOP (200*1000) + + +#ifdef GNU_LINUX_EMULATION +uint8_t *flash_addr_key_storage_start; +uint8_t *flash_addr_data_storage_start; +#else +#define ID_OFFSET (2+SERIALNO_STR_LEN*2) +static void +device_initialize_once (void) +{ + const uint8_t *p = &gnuk_string_serial[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 () + 8; + int i; + + for (i = 0; i < 4; i++) + { + uint8_t b = u[3-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); + } + } +} +#endif + + +static volatile uint8_t fatal_code; +static struct eventflag led_event; + +static void display_fatal_code (void) +{ + while (1) + { + set_led (1); + chopstx_usec_wait (LED_TIMEOUT_ZERO); + set_led (0); + chopstx_usec_wait (LED_TIMEOUT_INTERVAL); + set_led (1); + chopstx_usec_wait (LED_TIMEOUT_ZERO); + set_led (0); + chopstx_usec_wait (LED_TIMEOUT_INTERVAL); + set_led (1); + chopstx_usec_wait (LED_TIMEOUT_ZERO); + set_led (0); + chopstx_usec_wait (LED_TIMEOUT_STOP); + set_led (1); + if (fatal_code & 1) + chopstx_usec_wait (LED_TIMEOUT_ONE); + else + chopstx_usec_wait (LED_TIMEOUT_ZERO); + set_led (0); + chopstx_usec_wait (LED_TIMEOUT_INTERVAL); + set_led (1); + if (fatal_code & 2) + chopstx_usec_wait (LED_TIMEOUT_ONE); + else + chopstx_usec_wait (LED_TIMEOUT_ZERO); + set_led (0); + chopstx_usec_wait (LED_TIMEOUT_INTERVAL); + set_led (1); + chopstx_usec_wait (LED_TIMEOUT_STOP); + set_led (0); + chopstx_usec_wait (LED_TIMEOUT_INTERVAL*10); + } +} + +static uint8_t led_inverted; + +static void emit_led (int on_time, int off_time) +{ + set_led (!led_inverted); + chopstx_usec_wait (on_time); + set_led (led_inverted); + chopstx_usec_wait (off_time); +} + +static void display_status_code (void) +{ + enum ccid_state ccid_state = *ccid_state_p; + + if (ccid_state == CCID_STATE_START) + emit_led (LED_TIMEOUT_ONE, LED_TIMEOUT_STOP); + else + /* OpenPGP card thread is running */ + { + emit_led ((auth_status & AC_ADMIN_AUTHORIZED)? + LED_TIMEOUT_ONE : LED_TIMEOUT_ZERO, LED_TIMEOUT_INTERVAL); + emit_led ((auth_status & AC_OTHER_AUTHORIZED)? + LED_TIMEOUT_ONE : LED_TIMEOUT_ZERO, LED_TIMEOUT_INTERVAL); + emit_led ((auth_status & AC_PSO_CDS_AUTHORIZED)? + LED_TIMEOUT_ONE : LED_TIMEOUT_ZERO, LED_TIMEOUT_INTERVAL); + + if (ccid_state == CCID_STATE_WAIT) + chopstx_usec_wait (LED_TIMEOUT_STOP * 2); + else + { + chopstx_usec_wait (LED_TIMEOUT_INTERVAL); + emit_led (ccid_state == CCID_STATE_RECEIVE? + LED_TIMEOUT_ONE : LED_TIMEOUT_ZERO, LED_TIMEOUT_STOP); + } + } +} + +void +led_blink (int spec) +{ + if (spec == LED_START_COMMAND || spec == LED_FINISH_COMMAND) + { + led_inverted = (spec == LED_START_COMMAND); + spec = LED_SYNC; + } + + eventflag_signal (&led_event, spec); +} + +#ifdef FLASH_UPGRADE_SUPPORT +/* + * In Gnuk 1.0.[12], reGNUal was not relocatable. + * Now, it's relocatable, but we need to calculate its entry address + * based on it's pre-defined address. + */ +#define REGNUAL_START_ADDRESS_COMPATIBLE 0x20001400 +static uintptr_t +calculate_regnual_entry_address (const uint8_t *addr) +{ + const uint8_t *p = addr + 4; + uintptr_t v = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24); + + v -= REGNUAL_START_ADDRESS_COMPATIBLE; + v += (uintptr_t)addr; + return v; +} +#endif + +#define STACK_MAIN +#define STACK_PROCESS_1 +#include "stack-def.h" +#define STACK_ADDR_CCID ((uintptr_t)process1_base) +#define STACK_SIZE_CCID (sizeof process1_base) + +#define PRIO_CCID 3 +#define PRIO_MAIN 5 + +extern void *ccid_thread (void *arg); + +static void gnuk_malloc_init (void); + + +extern uint32_t bDeviceState; + +/* + * Entry point. + */ +int +main (int argc, const char *argv[]) +{ +#ifdef GNU_LINUX_EMULATION + uintptr_t flash_addr; + const char *flash_image_path; + char *path_string = NULL; +#endif +#ifdef FLASH_UPGRADE_SUPPORT + uintptr_t entry; +#endif + chopstx_t ccid_thd; + + gnuk_malloc_init (); + +#ifdef GNU_LINUX_EMULATION +#define FLASH_IMAGE_NAME ".gnuk-flash-image" + + if (argc >= 4 || (argc == 2 && !strcmp (argv[1], "--help"))) + { + fprintf (stdout, "Usage: %s [--vidpid=Vxxx:Pxxx] [flash-image-file]", + argv[0]); + exit (0); + } + + if (argc >= 2 && !strncmp (argv[1], "--debug=", 8)) + { + debug = strtol (&argv[1][8], NULL, 10); + argc--; + argv++; + } + + if (argc >= 2 && !strncmp (argv[1], "--vidpid=", 9)) + { + extern uint8_t device_desc[]; + uint32_t id; + char *p; + + id = (uint32_t)strtol (&argv[1][9], &p, 16); + device_desc[8] = (id & 0xff); + device_desc[9] = (id >> 8); + + if (p && p[0] == ':') + { + id = (uint32_t)strtol (&p[1], NULL, 16); + device_desc[10] = (id & 0xff); + device_desc[11] = (id >> 8); + } + + argc--; + argv++; + } + + if (argc == 1) + { + char *p = getenv ("HOME"); + + if (p == NULL) + { + fprintf (stderr, "Can't find $HOME\n"); + exit (1); + } + + path_string = malloc (strlen (p) + strlen (FLASH_IMAGE_NAME) + 2); + + p = stpcpy (path_string, p); + *p++ = '/'; + strcpy (p, FLASH_IMAGE_NAME); + flash_image_path = path_string; + } + else + flash_image_path = argv[1]; + + flash_addr = flash_init (flash_image_path); + flash_addr_key_storage_start = (uint8_t *)flash_addr; + flash_addr_data_storage_start = (uint8_t *)flash_addr + 4096; +#else + (void)argc; + (void)argv; +#endif + + flash_unlock (); + +#ifdef GNU_LINUX_EMULATION + if (path_string) + free (path_string); +#else + device_initialize_once (); +#endif + + adc_init (); + + eventflag_init (&led_event); + + random_init (); + +#ifdef DEBUG + stdout_init (); +#endif + + ccid_thd = chopstx_create (PRIO_CCID, STACK_ADDR_CCID, STACK_SIZE_CCID, + ccid_thread, NULL); + +#ifdef PINPAD_CIR_SUPPORT + cir_init (); +#endif +#ifdef PINPAD_DND_SUPPORT + msc_init (); +#endif + + chopstx_setpriority (PRIO_MAIN); + + while (1) + { + if (bDeviceState != UNCONNECTED) + break; + + chopstx_usec_wait (250*1000); + } + + while (1) + { + eventmask_t m; + + m = eventflag_wait (&led_event); + switch (m) + { + case LED_ONESHOT: + emit_led (100*1000, LED_TIMEOUT_STOP); + break; + case LED_TWOSHOTS: + emit_led (50*1000, 50*1000); + emit_led (50*1000, LED_TIMEOUT_STOP); + break; + case LED_SHOW_STATUS: + display_status_code (); + break; + case LED_FATAL: + display_fatal_code (); + break; + case LED_SYNC: + set_led (led_inverted); + break; + case LED_GNUK_EXEC: + goto exec; + default: + emit_led (LED_TIMEOUT_ZERO, LED_TIMEOUT_STOP); + break; + } + } + + exec: + random_fini (); + + set_led (1); + usb_lld_shutdown (); + + /* Finish application. */ + chopstx_join (ccid_thd, NULL); + +#ifdef FLASH_UPGRADE_SUPPORT + /* Set vector */ + SCB->VTOR = (uintptr_t)&_regnual_start; + entry = calculate_regnual_entry_address (&_regnual_start); +#ifdef DFU_SUPPORT +#define FLASH_SYS_START_ADDR 0x08000000 +#define FLASH_SYS_END_ADDR (0x08000000+0x1000) +#define CHIP_ID_REG ((uint32_t *)0xE0042000) + { + extern uint8_t _sys; + uintptr_t addr; + handler *new_vector = (handler *)FLASH_SYS_START_ADDR; + void (*func) (void (*)(void)) = (void (*)(void (*)(void)))new_vector[9]; + uint32_t flash_page_size = 1024; /* 1KiB default */ + + if ((*CHIP_ID_REG)&0x07 == 0x04) /* High dencity device. */ + flash_page_size = 2048; /* It's 2KiB. */ + + /* 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))entry); + for (;;); + } +#else + /* Leave Gnuk to exec reGNUal */ + flash_erase_all_and_exec ((void (*)(void))entry); +#endif +#else + exit (0); +#endif + + /* Never reached */ + return 0; +} + +void +fatal (uint8_t code) +{ + extern void _write (const char *s, int len); + + fatal_code = code; + eventflag_signal (&led_event, LED_FATAL); + _write ("fatal\r\n", 7); + for (;;); +} + +/* + * Malloc for Gnuk. + * + * Each memory chunk has header with size information. + * The size of chunk is at least 16. + * + * Free memory is managed by FREE_LIST. + * + * When it is managed in FREE_LIST, three pointers, ->NEXT, ->PREV, + * and ->NEIGHBOR is used. NEXT and PREV is to implement doubly + * linked list. NEIGHBOR is to link adjacent memory chunk to be + * reclaimed to system. + */ + +#ifdef GNU_LINUX_EMULATION +#define HEAP_SIZE (32*1024) +uint8_t __heap_base__[HEAP_SIZE]; + +#define HEAP_START __heap_base__ +#define HEAP_END (__heap_base__ + HEAP_SIZE) +#define HEAP_ALIGNMENT 32 +#else +extern uint8_t __heap_base__[]; +extern uint8_t __heap_end__[]; + +#define HEAP_START __heap_base__ +#define HEAP_END (__heap_end__) +#define HEAP_ALIGNMENT 16 +#define HEAP_SIZE ((uintptr_t)__heap_end__ - (uintptr_t)__heap_base__) +#endif + +#define HEAP_ALIGN(n) (((n) + HEAP_ALIGNMENT - 1) & ~(HEAP_ALIGNMENT - 1)) + +static uint8_t *heap_p; +static chopstx_mutex_t malloc_mtx; + +struct mem_head { + uintptr_t size; + /**/ + struct mem_head *next, *prev; /* free list chain */ + struct mem_head *neighbor; /* backlink to neighbor */ +}; + +#define MEM_HEAD_IS_CORRUPT(x) \ + ((x)->size != HEAP_ALIGN((x)->size) || (x)->size > HEAP_SIZE) +#define MEM_HEAD_CHECK(x) if (MEM_HEAD_IS_CORRUPT(x)) fatal (FATAL_HEAP) + +static struct mem_head *free_list; + +static void +gnuk_malloc_init (void) +{ + chopstx_mutex_init (&malloc_mtx); + heap_p = HEAP_START; + free_list = NULL; +} + +static void * +sbrk (size_t size) +{ + void *p = (void *)heap_p; + + if ((size_t)(HEAP_END - heap_p) < size) + return NULL; + + heap_p += size; + return p; +} + +static void +remove_from_free_list (struct mem_head *m) +{ + if (m->prev) + m->prev->next = m->next; + else + free_list = m->next; + if (m->next) + m->next->prev = m->prev; +} + + +void * +gnuk_malloc (size_t size) +{ + struct mem_head *m; + struct mem_head *m0; + + size = HEAP_ALIGN (size + sizeof (uintptr_t)); + + chopstx_mutex_lock (&malloc_mtx); + DEBUG_INFO ("malloc: "); + DEBUG_SHORT (size); + m = free_list; + + while (1) + { + if (m == NULL) + { + m = (struct mem_head *)sbrk (size); + if (m) + m->size = size; + break; + } + MEM_HEAD_CHECK (m); + if (m->size == size) + { + remove_from_free_list (m); + m0 = free_list; + while (m0) + if (m0->neighbor == m) + m0->neighbor = NULL; + else + m0 = m0->next; + break; + } + + m = m->next; + } + + chopstx_mutex_unlock (&malloc_mtx); + if (m == NULL) + { + DEBUG_WORD (0); + return m; + } + else + { + DEBUG_WORD ((uintptr_t)m + sizeof (uintptr_t)); + return (void *)m + sizeof (uintptr_t); + } +} + + +void +gnuk_free (void *p) +{ + struct mem_head *m = (struct mem_head *)((void *)p - sizeof (uintptr_t)); + struct mem_head *m0; + + if (p == NULL) + return; + + chopstx_mutex_lock (&malloc_mtx); + m0 = free_list; + DEBUG_INFO ("free: "); + DEBUG_SHORT (m->size); + DEBUG_WORD ((uintptr_t)p); + + MEM_HEAD_CHECK (m); + m->neighbor = NULL; + while (m0) + { + MEM_HEAD_CHECK (m0); + if ((void *)m + m->size == (void *)m0) + m0->neighbor = m; + else if ((void *)m0 + m0->size == (void *)m) + m->neighbor = m0; + + m0 = m0->next; + } + + if ((void *)m + m->size == heap_p) + { + struct mem_head *mn = m->neighbor; + + heap_p -= m->size; + while (mn) + { + MEM_HEAD_CHECK (mn); + heap_p -= mn->size; + remove_from_free_list (mn); + mn = mn->neighbor; + } + } + else + { + m->next = free_list; + m->prev = NULL; + if (free_list) + free_list->prev = m; + free_list = m; + } + + chopstx_mutex_unlock (&malloc_mtx); +} diff --git a/gnuk/src/mcu-stm32f103.c b/gnuk/src/mcu-stm32f103.c new file mode 100644 index 0000000000..8dbc9ad2c8 --- /dev/null +++ b/gnuk/src/mcu-stm32f103.c @@ -0,0 +1,32 @@ +/* + * mcu-stm32f103.c - STM32F103 specific routines + * + * Copyright (C) 2017 + * 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 +#include "mcu/stm32f103.h" + +uint8_t * +sram_address (uint32_t offset) +{ + return ((uint8_t *)0x20000000) + offset; +} diff --git a/gnuk/src/mod.c b/gnuk/src/mod.c new file mode 100644 index 0000000000..340fc16a9a --- /dev/null +++ b/gnuk/src/mod.c @@ -0,0 +1,352 @@ +/* + * mod.c -- modulo arithmetic + * + * Copyright (C) 2011, 2014 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 +#include +#include "bn.h" + +/** + * @brief X = A mod B (using MU=(1<<(256)+MU_lower)) (Barret reduction) + * + */ +void +mod_reduce (bn256 *X, const bn512 *A, const bn256 *B, const bn256 *MU_lower) +{ + bn256 q[1]; + bn512 q_big[1], tmp[1]; + uint32_t carry; +#define borrow carry + + memset (q, 0, sizeof (bn256)); + q->word[0] = A->word[15]; + bn256_mul (tmp, q, MU_lower); + tmp->word[8] += A->word[15]; + carry = (tmp->word[8] < A->word[15]); + tmp->word[9] += carry; + + q->word[7] = A->word[14]; + q->word[6] = A->word[13]; + q->word[5] = A->word[12]; + q->word[4] = A->word[11]; + q->word[3] = A->word[10]; + q->word[2] = A->word[9]; + q->word[1] = A->word[8]; + q->word[0] = A->word[7]; + bn256_mul (q_big, q, MU_lower); + bn256_add ((bn256 *)&q_big->word[8], (bn256 *)&q_big->word[8], q); + + q->word[0] = q_big->word[9] + tmp->word[1]; + carry = (q->word[0] < tmp->word[1]); + + q->word[1] = q_big->word[10] + carry; + carry = (q->word[1] < carry); + q->word[1] += tmp->word[2]; + carry += (q->word[1] < tmp->word[2]); + + q->word[2] = q_big->word[11] + carry; + carry = (q->word[2] < carry); + q->word[2] += tmp->word[3]; + carry += (q->word[2] < tmp->word[3]); + + q->word[3] = q_big->word[12] + carry; + carry = (q->word[3] < carry); + q->word[3] += tmp->word[4]; + carry += (q->word[3] < tmp->word[4]); + + q->word[4] = q_big->word[13] + carry; + carry = (q->word[4] < carry); + q->word[4] += tmp->word[5]; + carry += (q->word[4] < tmp->word[5]); + + q->word[5] = q_big->word[14] + carry; + carry = (q->word[5] < carry); + q->word[5] += tmp->word[6]; + carry += (q->word[5] < tmp->word[6]); + + q->word[6] = q_big->word[15] + carry; + carry = (q->word[6] < carry); + q->word[6] += tmp->word[7]; + carry += (q->word[6] < tmp->word[7]); + + q->word[7] = carry; + q->word[7] += tmp->word[8]; + carry = (q->word[7] < tmp->word[8]); + + memset (q_big, 0, sizeof (bn512)); + q_big->word[8] = A->word[8]; + q_big->word[7] = A->word[7]; + q_big->word[6] = A->word[6]; + q_big->word[5] = A->word[5]; + q_big->word[4] = A->word[4]; + q_big->word[3] = A->word[3]; + q_big->word[2] = A->word[2]; + q_big->word[1] = A->word[1]; + q_big->word[0] = A->word[0]; + + bn256_mul (tmp, q, B); + tmp->word[8] += carry * B->word[0]; + tmp->word[15] = tmp->word[14] = tmp->word[13] = tmp->word[12] + = tmp->word[11] = tmp->word[10] = tmp->word[9] = 0; + + borrow = bn256_sub (X, (bn256 *)&q_big->word[0], (bn256 *)&tmp->word[0]); + q_big->word[8] -= borrow; + q_big->word[8] -= tmp->word[8]; + + carry = q_big->word[8]; + if (carry) + carry -= bn256_sub (X, X, B); + else + bn256_sub (q, X, B); + + if (carry) + bn256_sub (X, X, B); + else + bn256_sub (q, X, B); + + borrow = bn256_sub (q, X, B); + if (borrow) + memcpy (q, X, sizeof (bn256)); + else + memcpy (X, q, sizeof (bn256)); +#undef borrow +} + +/* + * Reference: + * Donald E. Knuth, The Art of Computer Programming, Vol. 2: + * Seminumerical Algorithms, 3rd ed. Reading, MA: Addison-Wesley, 1998 + * + * Max loop: X=0x8000...0000 and N=0xffff...ffff + */ +#define MAX_GCD_STEPS_BN256 (3*256-2) + +/** + * @brief C = X^(-1) mod N + * + * Assume X and N are co-prime (or N is prime). + * NOTE: If X==0, it return 0. + * + */ +void +mod_inv (bn256 *C, const bn256 *X, const bn256 *N) +{ + bn256 u[1], v[1], tmp[1]; + bn256 A[1] = { { { 1, 0, 0, 0, 0, 0, 0, 0 } } }; + uint32_t carry; +#define borrow carry + int n = MAX_GCD_STEPS_BN256; + + memset (tmp, 0, sizeof (bn256)); + memset (C, 0, sizeof (bn256)); + memcpy (u, X, sizeof (bn256)); + memcpy (v, N, sizeof (bn256)); + + while (n--) + { + int c = (bn256_is_even (u) << 1) + bn256_is_even (v); + + switch (c) + { + case 3: + bn256_shift (u, u, -1); + if (bn256_is_even (A)) + { + bn256_add (tmp, A, N); + carry = 0; + } + else + carry = bn256_add (A, A, N); + + bn256_shift (A, A, -1); + A->word[7] |= carry * 0x80000000; + + bn256_shift (v, v, -1); + if (bn256_is_even (C)) + { + bn256_add (tmp, C, N); + carry = 0; + } + else + carry = bn256_add (C, C, N); + + bn256_shift (C, C, -1); + C->word[7] |= carry * 0x80000000; + + if (bn256_is_ge (tmp, tmp)) + { + bn256_sub (tmp, tmp, tmp); + borrow = bn256_sub (tmp, tmp, tmp); + if (borrow) + bn256_add (tmp, tmp, tmp); + else + bn256_add (tmp, A, N); + } + else + { + bn256_sub (tmp, tmp, tmp); + borrow = bn256_sub (tmp, tmp, tmp); + if (borrow) + bn256_add (tmp, tmp, tmp); + else + bn256_add (tmp, tmp, N); + } + break; + + case 1: + bn256_shift (tmp, tmp, -1); + if (bn256_is_even (tmp)) + { + bn256_add (tmp, tmp, N); + carry = 0; + } + else + carry = bn256_add (tmp, tmp, N); + + bn256_shift (tmp, tmp, -1); + tmp->word[7] |= carry * 0x80000000; + + bn256_shift (v, v, -1); + if (bn256_is_even (C)) + { + bn256_add (tmp, C, N); + carry = 0; + } + else + carry = bn256_add (C, C, N); + + bn256_shift (C, C, -1); + C->word[7] |= carry * 0x80000000; + + if (bn256_is_ge (tmp, tmp)) + { + bn256_sub (tmp, tmp, tmp); + borrow = bn256_sub (tmp, tmp, tmp); + if (borrow) + bn256_add (tmp, tmp, tmp); + else + bn256_add (tmp, A, N); + } + else + { + bn256_sub (tmp, tmp, tmp); + borrow = bn256_sub (tmp, tmp, tmp); + if (borrow) + bn256_add (tmp, tmp, tmp); + else + bn256_add (tmp, tmp, N); + } + break; + + case 2: + bn256_shift (u, u, -1); + if (bn256_is_even (A)) + { + bn256_add (tmp, A, N); + carry = 0; + } + else + carry = bn256_add (A, A, N); + + bn256_shift (A, A, -1); + A->word[7] |= carry * 0x80000000; + + bn256_shift (tmp, tmp, -1); + if (bn256_is_even (tmp)) + { + bn256_add (tmp, tmp, N); + carry = 0; + } + else + carry = bn256_add (tmp, tmp, N); + + bn256_shift (tmp, tmp, -1); + tmp->word[7] |= carry * 0x80000000; + + if (bn256_is_ge (tmp, tmp)) + { + bn256_sub (tmp, tmp, tmp); + borrow = bn256_sub (tmp, tmp, tmp); + if (borrow) + bn256_add (tmp, tmp, tmp); + else + bn256_add (tmp, A, N); + } + else + { + bn256_sub (tmp, tmp, tmp); + borrow = bn256_sub (tmp, tmp, tmp); + if (borrow) + bn256_add (tmp, tmp, tmp); + else + bn256_add (tmp, tmp, N); + } + break; + + case 0: + bn256_shift (tmp, tmp, -1); + if (bn256_is_even (tmp)) + { + bn256_add (tmp, tmp, N); + carry = 0; + } + else + carry = bn256_add (tmp, tmp, N); + + bn256_shift (tmp, tmp, -1); + tmp->word[7] |= carry * 0x80000000; + + bn256_shift (tmp, tmp, -1); + if (bn256_is_even (tmp)) + { + bn256_add (tmp, tmp, N); + carry = 0; + } + else + carry = bn256_add (tmp, tmp, N); + + bn256_shift (tmp, tmp, -1); + tmp->word[7] |= carry * 0x80000000; + + if (bn256_is_ge (u, v)) + { + bn256_sub (u, u, v); + borrow = bn256_sub (A, A, C); + if (borrow) + bn256_add (A, A, N); + else + bn256_add (tmp, A, N); + } + else + { + bn256_sub (v, v, u); + borrow = bn256_sub (C, C, A); + if (borrow) + bn256_add (C, C, N); + else + bn256_add (tmp, C, N); + } + break; + } + } +#undef borrow +} diff --git a/gnuk/src/mod.h b/gnuk/src/mod.h new file mode 100644 index 0000000000..117bcc7f61 --- /dev/null +++ b/gnuk/src/mod.h @@ -0,0 +1,3 @@ +void mod_reduce (bn256 *X, const bn512 *A, const bn256 *B, + const bn256 *MU_lower); +void mod_inv (bn256 *X, const bn256 *A, const bn256 *N); diff --git a/gnuk/src/mod25638.c b/gnuk/src/mod25638.c new file mode 100644 index 0000000000..9b0777a6d3 --- /dev/null +++ b/gnuk/src/mod25638.c @@ -0,0 +1,287 @@ +/* + * mod25638.c -- modulo arithmetic of 2^256-38 for 2^255-19 field + * + * Copyright (C) 2014 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 . + * + */ + +/* + * The field is \Z/(2^255-19) + * + * We use radix-32. During computation, it's not reduced to 2^255-19, + * but it is represented in 256-bit (it is redundant representation), + * that is, something like 2^256-38. + * + * The idea is, keeping within 256-bit until it will be converted to + * affine coordinates. + */ + +#include +#include + +#include "bn.h" +#include "mod25638.h" + +#ifndef BN256_C_IMPLEMENTATION +#define ASM_IMPLEMENTATION 1 +#endif + +#if ASM_IMPLEMENTATION +#include "muladd_256.h" +#define ADDWORD_256(d_,s_,w_,c_) \ + asm ( "ldmia %[s]!, { r4, r5, r6, r7 } \n\t" \ + "adds r4, r4, %[w] \n\t" \ + "adcs r5, r5, #0 \n\t" \ + "adcs r6, r6, #0 \n\t" \ + "adcs r7, r7, #0 \n\t" \ + "stmia %[d]!, { r4, r5, r6, r7 }\n\t" \ + "ldmia %[s]!, { r4, r5, r6, r7 } \n\t" \ + "adcs r4, r4, #0 \n\t" \ + "adcs r5, r5, #0 \n\t" \ + "adcs r6, r6, #0 \n\t" \ + "adcs r7, r7, #0 \n\t" \ + "stmia %[d]!, { r4, r5, r6, r7 }\n\t" \ + "mov %[c], #0 \n\t" \ + "adc %[c], %[c], #0" \ + : [s] "=&r" (s_), [d] "=&r" (d_), [c] "=&r" (c_) \ + : "[s]" (s_), "[d]" (d_), [w] "r" (w_) \ + : "r4", "r5", "r6", "r7", "memory", "cc" ) +#endif + +/* +256 224 192 160 128 96 64 32 0 +2^256 + 1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 +2^256 - 16 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffff0 +2^256 - 16 - 2 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffee +2^256 - 16 - 2 - 1 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffed +*/ +const bn256 p25519[1] = { + {{ 0xffffffed, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff }} }; + + +/* + * Implementation Note. + * + * It's not always modulo n25638. The representation is redundant + * during computation. For example, when we add the number - 1 and 1, + * it won't overflow to 2^256, and the result is represented within + * 256-bit. + */ + + +/** + * @brief X = (A + B) mod 2^256-38 + */ +void +mod25638_add (bn256 *X, const bn256 *A, const bn256 *B) +{ + uint32_t carry; + + carry = bn256_add (X, A, B); + carry = bn256_add_uint (X, X, carry*38); + X->word[0] += carry * 38; +} + +/** + * @brief X = (A - B) mod 2^256-38 + */ +void +mod25638_sub (bn256 *X, const bn256 *A, const bn256 *B) +{ + uint32_t borrow; + + borrow = bn256_sub (X, A, B); + borrow = bn256_sub_uint (X, X, borrow*38); + X->word[0] -= borrow * 38; +} + + +/** + * @brief X = A mod 2^256-38 + * + * Note that the second argument is not "const bn512 *". + * A is modified during the computation of modulo. + * + * It's not precisely modulo 2^256-38 for all cases, + * but result may be redundant. + */ +static void +mod25638_reduce (bn256 *X, bn512 *A) +{ + const uint32_t *s; + uint32_t *d; + uint32_t w; + +#if ASM_IMPLEMENTATION + uint32_t c, c0; + + s = &A->word[8]; d = &A->word[0]; w = 38; MULADD_256 (s, d, w, c); + c0 = A->word[8] * 38; + d = &X->word[0]; + s = &A->word[0]; + ADDWORD_256 (d, s, c0, c); + X->word[0] += c * 38; +#else + s = &A->word[8]; d = &A->word[0]; w = 38; + { + int i; + uint64_t r; + uint32_t carry; + + r = 0; + for (i = 0; i < BN256_WORDS; i++) + { + uint64_t uv; + + r += d[i]; + carry = (r < d[i]); + + uv = ((uint64_t)s[i])*w; + r += uv; + carry += (r < uv); + + d[i] = (uint32_t)r; + r = ((r >> 32) | ((uint64_t)carry << 32)); + } + + carry = bn256_add_uint (X, (bn256 *)A, r * 38); + X->word[0] += carry * 38; + } +#endif +} + +/** + * @brief X = (A * B) mod 2^256-38 + */ +void +mod25638_mul (bn256 *X, const bn256 *A, const bn256 *B) +{ + bn512 tmp[1]; + + bn256_mul (tmp, A, B); + mod25638_reduce (X, tmp); +} + +/** + * @brief X = A * A mod 2^256-38 + */ +void +mod25638_sqr (bn256 *X, const bn256 *A) +{ + bn512 tmp[1]; + + bn256_sqr (tmp, A); + mod25638_reduce (X, tmp); +} + + +/** + * @brief X = (A << shift) mod 2^256-38 + * @note shift < 32 + */ +void +mod25638_shift (bn256 *X, const bn256 *A, int shift) +{ + uint32_t carry; + bn256 tmp[1]; + + carry = bn256_shift (X, A, shift); + if (shift < 0) + return; + + memset (tmp, 0, sizeof (bn256)); + tmp->word[0] = (carry << 1); + /* tmp->word[1] = (carry >> 31); always zero. */ + tmp->word[0] = tmp->word[0] + (carry << 2); + tmp->word[1] = (tmp->word[0] < (carry << 2)) + (carry >> 30); + tmp->word[0] = tmp->word[0] + (carry << 5); + tmp->word[1] = tmp->word[1] + (tmp->word[0] < (carry << 5)) + (carry >> 27); + + mod25638_add (X, X, tmp); +} + + +/* + * @brief X = A mod 2^255-19 + * + * It's precisely modulo 2^255-19 (unlike mod25638_reduce). + */ +void +mod25519_reduce (bn256 *X) +{ + uint32_t q; + bn256 r0[1], r1[1]; + int flag; + + memcpy (r0, X, sizeof (bn256)); + q = (r0->word[7] >> 31); + r0->word[7] &= 0x7fffffff; + if (q) + { + bn256_add_uint (r0, r0, 19); + q = (r0->word[7] >> 31); + r0->word[7] &= 0x7fffffff; + if (q) + { + bn256_add_uint (r1, r0, 19); + q = (r1->word[7] >> 31); + r1->word[7] &= 0x7fffffff; + flag = 0; + } + else + flag = 1; + } + else + { + bn256_add_uint (r1, r0, 19); + q = (r1->word[7] >> 31); /* dummy */ + r1->word[7] &= 0x7fffffff; /* dummy */ + if (q) + flag = 2; + else + flag = 3; + } + + if (flag) + { + bn256_add_uint (r1, r0, 19); + q = (r1->word[7] >> 31); + r1->word[7] &= 0x7fffffff; + if (q) + memcpy (X, r1, sizeof (bn256)); + else + memcpy (X, r0, sizeof (bn256)); + } + else + { + if (q) + { + asm volatile ("" : : "r" (q) : "memory"); + memcpy (X, r1, sizeof (bn256)); + asm volatile ("" : : "r" (q) : "memory"); + } + else + memcpy (X, r1, sizeof (bn256)); + } +} diff --git a/gnuk/src/mod25638.h b/gnuk/src/mod25638.h new file mode 100644 index 0000000000..8773611568 --- /dev/null +++ b/gnuk/src/mod25638.h @@ -0,0 +1,7 @@ +extern const bn256 p25519[1]; + +void mod25638_add (bn256 *X, const bn256 *A, const bn256 *B); +void mod25638_sub (bn256 *X, const bn256 *A, const bn256 *B); +void mod25638_mul (bn256 *X, const bn256 *A, const bn256 *B); +void mod25638_sqr (bn256 *X, const bn256 *A); +void mod25519_reduce (bn256 *X); diff --git a/gnuk/src/modp256k1.c b/gnuk/src/modp256k1.c new file mode 100644 index 0000000000..c296453399 --- /dev/null +++ b/gnuk/src/modp256k1.c @@ -0,0 +1,318 @@ +/* + * modp256k1.c -- modulo arithmetic for p256k1 + * + * Copyright (C) 2014, 2016 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 . + * + */ + +/* + * p256k1 = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1 + */ +#include +#include + +#include "bn.h" +#include "modp256k1.h" + +/* +256 224 192 160 128 96 64 32 0 +2^256 + 1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 +2^256 - 2^32 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff 00000000 +2^256 - 2^32 - 2^9 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffe00 +2^256 - 2^32 - 2^9 - 2^8 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffd00 +2^256 - 2^32 - 2^9 - 2^8 - 2^7 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc80 +2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc40 +2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc30 +2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f +*/ +const bn256 p256k1 = { {0xfffffc2f, 0xfffffffe, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff } }; + +/* + * Implementation Note. + * + * It's always modulo p256k1. + * + * Once, I tried redundant representation which caused wrong + * calculation. Implementation could be correct with redundant + * representation, but it found that it's more expensive. + * + */ + +/** + * @brief X = (A + B) mod p256k1 + */ +void +modp256k1_add (bn256 *X, const bn256 *A, const bn256 *B) +{ + uint32_t cond; + bn256 tmp[1]; + + cond = (bn256_add (X, A, B) == 0); + cond &= bn256_sub (tmp, X, P256K1); + if (cond) + /* No-carry AND borrow */ + memcpy (tmp, tmp, sizeof (bn256)); + else + memcpy (X, tmp, sizeof (bn256)); +} + +/** + * @brief X = (A - B) mod p256 + */ +void +modp256k1_sub (bn256 *X, const bn256 *A, const bn256 *B) +{ + uint32_t borrow; + bn256 tmp[1]; + + borrow = bn256_sub (X, A, B); + bn256_add (tmp, X, P256K1); + if (borrow) + memcpy (X, tmp, sizeof (bn256)); + else + memcpy (tmp, tmp, sizeof (bn256)); +} + +/** + * @brief X = A mod p256k1 + */ +void +modp256k1_reduce (bn256 *X, const bn512 *A) +{ + bn256 tmp[1]; + uint32_t carry; +#define borrow carry + uint32_t s0, s1; +#define s00 tmp->word[0] +#define s01 tmp->word[1] +#define s02 tmp->word[2] + +#define W0 X +#define W1 tmp +#define W2 tmp +#define W3 tmp +#define W4 tmp +#define W5 tmp +#define W6 tmp +#define W7 tmp +#define S tmp + + /* + * Suppose: P256K1 = 2^256 - CONST + * Then, compute: W = A_low + A_high * CONST + * 256-bit W0 = W mod 2^256 + * 64-bit (S1, S0) = W / 2^256 + * where: CONST = 2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1 + */ + + /* W0 = A_low */ + /* W7 = A_high */ + /* W0 += W7 */ + carry = bn256_add (W0, (const bn256 *)&A->word[8], (const bn256 *)A); + + /* W6 = W7 << 4 */ + /* W0 += W6 */ + bn256_shift (W6, (const bn256 *)&A->word[8], 4); + carry += bn256_add (W0, W0, W6); + + /* W5 = W6 << 2 */ + /* W0 += W5 */ + bn256_shift (W5, W6, 2); + carry += bn256_add (W0, W0, W5); + + /* W4 = W5 << 1 */ + /* W0 += W4 */ + bn256_shift (W4, W5, 1); + carry += bn256_add (W0, W0, W4); + + /* W3 = W4 << 1 */ + /* W0 += W3 */ + bn256_shift (W3, W4, 1); + carry += bn256_add (W0, W0, W3); + + /* W2 = W3 << 1 */ + /* W0 += W2 */ + bn256_shift (W2, W3, 1); + carry += bn256_add (W0, W0, W2); + + /* W1 = A_high << 32 */ + /* W0 += W1 */ + W1->word[7] = A->word[14]; + W1->word[6] = A->word[13]; + W1->word[5] = A->word[12]; + W1->word[4] = A->word[11]; + W1->word[3] = A->word[10]; + W1->word[2] = A->word[9]; + W1->word[1] = A->word[8]; + W1->word[0] = 0; + carry += bn256_add (W0, W0, W1); + + /* (S1, S0) = W / 2^256 */ + s0 = A->word[15]; + carry += (s0 >> 28) + (s0 >> 26) + (s0 >> 25) + (s0 >> 24) + (s0 >> 23); + carry += s0; + s1 = (carry < s0) ? 1 : 0; + s0 = carry; + + /* + * Compute: S:=(S02, S01, S00), S = (S1,S0)*CONST + */ + S->word[7] = S->word[6] = S->word[5] = S->word[4] = S->word[3] = 0; + + /* (S02, S01, S00) = (S1, S0) + (S1, S0)*2^32 */ + s00 = s0; + s01 = s0 + s1; + s02 = s1 + ((s01 < s0)? 1 : 0); + + /* (S02, S01, S00) += (S1, S0)*2^9 */ + carry = (s0 >> 23) + s01; + s02 += (s1 >> 23) + ((carry < s01)? 1 : 0); + s01 = (s1 << 9) + carry; + s02 += ((s01 < carry)? 1 : 0); + s00 += (s0 << 9); + carry = ((s00 < (s0 << 9))? 1 : 0); + s01 += carry; + s02 += ((s01 < carry)? 1 : 0); + + /* (S02, S01, S00) += (S1, S0)*2^8 */ + carry = (s0 >> 24) + s01; + s02 += (s1 >> 24) + ((carry < s01)? 1 : 0); + s01 = (s1 << 8) + carry; + s02 += ((s01 < carry)? 1 : 0); + s00 += (s0 << 8); + carry = ((s00 < (s0 << 8))? 1 : 0); + s01 += carry; + s02 += ((s01 < carry)? 1 : 0); + + /* (S02, S01, S00) += (S1, S0)*2^7 */ + carry = (s0 >> 25) + s01; + s02 += (s1 >> 25) + ((carry < s01)? 1 : 0); + s01 = (s1 << 7) + carry; + s02 += ((s01 < carry)? 1 : 0); + s00 += (s0 << 7); + carry = ((s00 < (s0 << 7))? 1 : 0); + s01 += carry; + s02 += ((s01 < carry)? 1 : 0); + + /* (S02, S01, S00) += (S1, S0)*2^6 */ + carry = (s0 >> 26) + s01; + s02 += (s1 >> 26) + ((carry < s01)? 1 : 0); + s01 = (s1 << 6) + carry; + s02 += ((s01 < carry)? 1 : 0); + s00 += (s0 << 6); + carry = ((s00 < (s0 << 6))? 1 : 0); + s01 += carry; + s02 += ((s01 < carry)? 1 : 0); + + /* (S02, S01, S00) += (S1, S0)*2^4 */ + carry = (s0 >> 28) + s01; + s02 += (s1 >> 28) + ((carry < s01)? 1 : 0); + s01 = (s1 << 4) + carry; + s02 += ((s01 < carry)? 1 : 0); + s00 += (s0 << 4); + carry = ((s00 < (s0 << 4))? 1 : 0); + s01 += carry; + s02 += ((s01 < carry)? 1 : 0); + + /* W0 += S */ + modp256k1_add (W0, W0, S); + + borrow = bn256_sub (tmp, W0, P256K1); + if (borrow) + memcpy (tmp, W0, sizeof (bn256)); + else + memcpy (W0, tmp, sizeof (bn256)); + +#undef W0 +#undef W1 +#undef W2 +#undef W3 +#undef W4 +#undef W5 +#undef W6 +#undef W7 +#undef S +#undef s00 +#undef s01 +#undef s02 +#undef borrow +} + +/** + * @brief X = (A * B) mod p256k1 + */ +void +modp256k1_mul (bn256 *X, const bn256 *A, const bn256 *B) +{ + bn512 AB[1]; + + bn256_mul (AB, A, B); + modp256k1_reduce (X, AB); +} + +/** + * @brief X = A * A mod p256k1 + */ +void +modp256k1_sqr (bn256 *X, const bn256 *A) +{ + bn512 AA[1]; + + bn256_sqr (AA, A); + modp256k1_reduce (X, AA); +} + + +/** + * @brief X = (A << shift) mod p256k1 + * @note shift < 32 + */ +void +modp256k1_shift (bn256 *X, const bn256 *A, int shift) +{ + uint32_t carry; + bn256 tmp[1]; + + carry = bn256_shift (X, A, shift); + if (shift < 0) + return; + + memset (tmp, 0, sizeof (bn256)); + tmp->word[0] = carry + (carry << 9); + tmp->word[1] = carry + (tmp->word[0] < (carry << 9)) + (carry >> 23); + tmp->word[0] = tmp->word[0] + (carry << 8); + tmp->word[1] = tmp->word[1] + (tmp->word[0] < (carry << 8)) + (carry >> 24); + tmp->word[0] = tmp->word[0] + (carry << 7); + tmp->word[1] = tmp->word[1] + (tmp->word[0] < (carry << 7)) + (carry >> 25); + tmp->word[0] = tmp->word[0] + (carry << 6); + tmp->word[1] = tmp->word[1] + (tmp->word[0] < (carry << 6)) + (carry >> 26); + tmp->word[0] = tmp->word[0] + (carry << 4); + tmp->word[1] = tmp->word[1] + (tmp->word[0] < (carry << 4)) + (carry >> 28); + + modp256k1_add (X, X, tmp); +} diff --git a/gnuk/src/modp256k1.h b/gnuk/src/modp256k1.h new file mode 100644 index 0000000000..3e20ab5d94 --- /dev/null +++ b/gnuk/src/modp256k1.h @@ -0,0 +1,9 @@ +extern const bn256 p256k1; +#define P256K1 (&p256k1) + +void modp256k1_add (bn256 *X, const bn256 *A, const bn256 *B); +void modp256k1_sub (bn256 *X, const bn256 *A, const bn256 *B); +void modp256k1_reduce (bn256 *X, const bn512 *A); +void modp256k1_mul (bn256 *X, const bn256 *A, const bn256 *B); +void modp256k1_sqr (bn256 *X, const bn256 *A); +void modp256k1_shift (bn256 *X, const bn256 *A, int shift); diff --git a/gnuk/src/modp256r1.c b/gnuk/src/modp256r1.c new file mode 100644 index 0000000000..90518c564f --- /dev/null +++ b/gnuk/src/modp256r1.c @@ -0,0 +1,300 @@ +/* + * modp256r1.c -- modulo arithmetic for p256r1 + * + * Copyright (C) 2011, 2013, 2014, 2016 + * 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 . + * + */ + +/* + * p256 = 2^256 - 2^224 + 2^192 + 2^96 - 1 + */ +#include +#include + +#include "bn.h" +#include "modp256r1.h" + +/* +256 224 192 160 128 96 64 32 0 +2^256 + 1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 +2^256 - 2^224 + 0 ffffffff 00000000 00000000 00000000 00000000 00000000 00000000 00000000 +2^256 - 2^224 + 2^192 + 0 ffffffff 00000001 00000000 00000000 00000000 00000000 00000000 00000000 +2^256 - 2^224 + 2^192 + 2^96 + 0 ffffffff 00000001 00000000 00000000 00000001 00000000 00000000 00000000 +2^256 - 2^224 + 2^192 + 2^96 - 1 + 0 ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff ffffffff +*/ +const bn256 p256r1 = { {0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0x00000000, 0x00000001, 0xffffffff} }; + +/* + * Implementation Note. + * + * It's always modulo p256r1. + * + * Once, I tried redundant representation which caused wrong + * calculation. Implementation could be correct with redundant + * representation, but it found that it's more expensive. + * + */ + +/** + * @brief X = (A + B) mod p256r1 + */ +void +modp256r1_add (bn256 *X, const bn256 *A, const bn256 *B) +{ + uint32_t cond; + bn256 tmp[1]; + + cond = (bn256_add (X, A, B) == 0); + cond &= bn256_sub (tmp, X, P256R1); + if (cond) + /* No-carry AND borrow */ + memcpy (tmp, tmp, sizeof (bn256)); + else + memcpy (X, tmp, sizeof (bn256)); +} + +/** + * @brief X = (A - B) mod p256r1 + */ +void +modp256r1_sub (bn256 *X, const bn256 *A, const bn256 *B) +{ + uint32_t borrow; + bn256 tmp[1]; + + borrow = bn256_sub (X, A, B); + bn256_add (tmp, X, P256R1); + if (borrow) + memcpy (X, tmp, sizeof (bn256)); + else + memcpy (tmp, tmp, sizeof (bn256)); +} + +/** + * @brief X = A mod p256r1 + */ +void +modp256r1_reduce (bn256 *X, const bn512 *A) +{ + bn256 tmp[1], tmp0[1]; + uint32_t borrow; + +#define S1 X +#define S2 tmp +#define S3 tmp +#define S4 tmp +#define S5 tmp +#define S6 tmp +#define S7 tmp +#define S8 tmp +#define S9 tmp + + S1->word[7] = A->word[7]; + S1->word[6] = A->word[6]; + S1->word[5] = A->word[5]; + S1->word[4] = A->word[4]; + S1->word[3] = A->word[3]; + S1->word[2] = A->word[2]; + S1->word[1] = A->word[1]; + S1->word[0] = A->word[0]; + borrow = bn256_sub (tmp0, S1, P256R1); + if (borrow) + memcpy (tmp0, tmp0, sizeof (bn256)); + else + memcpy (S1, tmp0, sizeof (bn256)); + /* X = S1 */ + + S2->word[7] = A->word[15]; + S2->word[6] = A->word[14]; + S2->word[5] = A->word[13]; + S2->word[4] = A->word[12]; + S2->word[3] = A->word[11]; + S2->word[2] = S2->word[1] = S2->word[0] = 0; + /* X += 2 * S2 */ + modp256r1_add (X, X, S2); + modp256r1_add (X, X, S2); + + S3->word[7] = 0; + S3->word[6] = A->word[15]; + S3->word[5] = A->word[14]; + S3->word[4] = A->word[13]; + S3->word[3] = A->word[12]; + S3->word[2] = S3->word[1] = S3->word[0] = 0; + /* X += 2 * S3 */ + modp256r1_add (X, X, S3); + modp256r1_add (X, X, S3); + + S4->word[7] = A->word[15]; + S4->word[6] = A->word[14]; + S4->word[5] = S4->word[4] = S4->word[3] = 0; + S4->word[2] = A->word[10]; + S4->word[1] = A->word[9]; + S4->word[0] = A->word[8]; + /* X += S4 */ + modp256r1_add (X, X, S4); + + S5->word[7] = A->word[8]; + S5->word[6] = A->word[13]; + S5->word[5] = A->word[15]; + S5->word[4] = A->word[14]; + S5->word[3] = A->word[13]; + S5->word[2] = A->word[11]; + S5->word[1] = A->word[10]; + S5->word[0] = A->word[9]; + borrow = bn256_sub (tmp0, S5, P256R1); + if (borrow) + memcpy (tmp0, tmp0, sizeof (bn256)); + else + memcpy (S5, tmp0, sizeof (bn256)); + /* X += S5 */ + modp256r1_add (X, X, S5); + + S6->word[7] = A->word[10]; + S6->word[6] = A->word[8]; + S6->word[5] = S6->word[4] = S6->word[3] = 0; + S6->word[2] = A->word[13]; + S6->word[1] = A->word[12]; + S6->word[0] = A->word[11]; + borrow = bn256_sub (tmp0, S6, P256R1); + if (borrow) + memcpy (tmp0, tmp0, sizeof (bn256)); + else + memcpy (S6, tmp0, sizeof (bn256)); + /* X -= S6 */ + modp256r1_sub (X, X, S6); + + S7->word[7] = A->word[11]; + S7->word[6] = A->word[9]; + S7->word[5] = S7->word[4] = 0; + S7->word[3] = A->word[15]; + S7->word[2] = A->word[14]; + S7->word[1] = A->word[13]; + S7->word[0] = A->word[12]; + borrow = bn256_sub (tmp0, S7, P256R1); + if (borrow) + memcpy (tmp0, tmp0, sizeof (bn256)); + else + memcpy (S7, tmp0, sizeof (bn256)); + /* X -= S7 */ + modp256r1_sub (X, X, S7); + + S8->word[7] = A->word[12]; + S8->word[6] = 0; + S8->word[5] = A->word[10]; + S8->word[4] = A->word[9]; + S8->word[3] = A->word[8]; + S8->word[2] = A->word[15]; + S8->word[1] = A->word[14]; + S8->word[0] = A->word[13]; + /* X -= S8 */ + modp256r1_sub (X, X, S8); + + S9->word[7] = A->word[13]; + S9->word[6] = 0; + S9->word[5] = A->word[11]; + S9->word[4] = A->word[10]; + S9->word[3] = A->word[9]; + S9->word[2] = 0; + S9->word[1] = A->word[15]; + S9->word[0] = A->word[14]; + /* X -= S9 */ + modp256r1_sub (X, X, S9); + + borrow = bn256_sub (tmp, X, P256R1); + if (borrow) + memcpy (tmp, X, sizeof (bn256)); + else + memcpy (X, tmp, sizeof (bn256)); + +#undef S1 +#undef S2 +#undef S3 +#undef S4 +#undef S5 +#undef S6 +#undef S7 +#undef S8 +#undef S9 +} + +/** + * @brief X = (A * B) mod p256r1 + */ +void +modp256r1_mul (bn256 *X, const bn256 *A, const bn256 *B) +{ + bn512 AB[1]; + + bn256_mul (AB, A, B); + modp256r1_reduce (X, AB); +} + +/** + * @brief X = A * A mod p256r1 + */ +void +modp256r1_sqr (bn256 *X, const bn256 *A) +{ + bn512 AA[1]; + + bn256_sqr (AA, A); + modp256r1_reduce (X, AA); +} + + +/** + * @brief X = (A << shift) mod p256r1 + * @note shift < 32 + */ +void +modp256r1_shift (bn256 *X, const bn256 *A, int shift) +{ + uint32_t carry; +#define borrow carry + bn256 tmp[1]; + + carry = bn256_shift (X, A, shift); + if (shift < 0) + return; + + memset (tmp, 0, sizeof (bn256)); + tmp->word[7] = carry; + tmp->word[0] = carry; + modp256r1_add (X, X, tmp); + + tmp->word[7] = 0; + tmp->word[0] = 0; + tmp->word[6] = carry; + tmp->word[3] = carry; + modp256r1_sub (X, X, tmp); + + borrow = bn256_sub (tmp, X, P256R1); + if (borrow) + memcpy (tmp, X, sizeof (bn256)); + else + memcpy (X, tmp, sizeof (bn256)); +#undef borrow +} diff --git a/gnuk/src/modp256r1.h b/gnuk/src/modp256r1.h new file mode 100644 index 0000000000..f7dcf941b9 --- /dev/null +++ b/gnuk/src/modp256r1.h @@ -0,0 +1,9 @@ +extern const bn256 p256r1; +#define P256R1 (&p256r1) + +void modp256r1_add (bn256 *X, const bn256 *A, const bn256 *B); +void modp256r1_sub (bn256 *X, const bn256 *A, const bn256 *B); +void modp256r1_reduce (bn256 *X, const bn512 *A); +void modp256r1_mul (bn256 *X, const bn256 *A, const bn256 *B); +void modp256r1_sqr (bn256 *X, const bn256 *A); +void modp256r1_shift (bn256 *X, const bn256 *A, int shift); diff --git a/gnuk/src/muladd_256.h b/gnuk/src/muladd_256.h new file mode 100644 index 0000000000..f37d976020 --- /dev/null +++ b/gnuk/src/muladd_256.h @@ -0,0 +1,50 @@ +#define MULADD_256_ASM(s_,d_,w_,c_) \ + asm ( "ldmia %[s]!, { r8, r9, r10 } \n\t" \ + "ldmia %[d], { r5, r6, r7 } \n\t" \ + "umull r4, r8, %[w], r8 \n\t" \ + "adds r5, r5, r4 \n\t" \ + "adcs r6, r6, r8 \n\t" \ + "umull r4, r8, %[w], r9 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r6, r6, r4 \n\t" \ + "adcs r7, r7, %[c] \n\t" \ + "umull r4, r8, %[w], r10 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r7, r7, r4 \n\t" \ + "stmia %[d]!, { r5, r6, r7 } \n\t" \ + "ldmia %[s]!, { r8, r9, r10 } \n\t" \ + "ldmia %[d], { r5, r6, r7 } \n\t" \ + "adcs r5, r5, %[c] \n\t" \ + "umull r4, r8, %[w], r8 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r5, r5, r4 \n\t" \ + "adcs r6, r6, %[c] \n\t" \ + "umull r4, r8, %[w], r9 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r6, r6, r4 \n\t" \ + "adcs r7, r7, %[c] \n\t" \ + "umull r4, r8, %[w], r10 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r7, r7, r4 \n\t" \ + "stmia %[d]!, { r5, r6, r7 } \n\t" \ + "ldmia %[s]!, { r8, r9 } \n\t" \ + "ldmia %[d], { r5, r6 } \n\t" \ + "adcs r5, r5, %[c] \n\t" \ + "umull r4, r8, %[w], r8 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r5, r5, r4 \n\t" \ + "adcs r6, r6, %[c] \n\t" \ + "umull r4, r8, %[w], r9 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r6, r6, r4 \n\t" \ + "adc %[c], %[c], #0 \n\t" \ + "stmia %[d]!, { r5, r6 }" \ + : [s] "=&r" (s_), [d] "=&r" (d_), [c] "=&r" (c_) \ + : "[s]" (s_), "[d]" (d_), [w] "r" (w_) \ + : "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "memory", "cc" ) + +#define MULADD_256(s__,d__,w__,c__) do { \ + MULADD_256_ASM(s__,d__,w__,c__); \ + *d__ = c__; \ +} while (0) diff --git a/gnuk/src/neug.c b/gnuk/src/neug.c new file mode 100644 index 0000000000..805911c24c --- /dev/null +++ b/gnuk/src/neug.c @@ -0,0 +1,915 @@ +/* + * neug.c - true random number generation + * + * Copyright (C) 2011, 2012, 2013, 2016, 2017 + * Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of NeuG, a True Random Number Generator + * implementation based on quantization error of ADC (for STM32F103). + * + * NeuG 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. + * + * NeuG 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 +#include +#include + +#include "sys.h" +#include "neug.h" +#ifndef GNU_LINUX_EMULATION +#include "mcu/stm32f103.h" +#endif +#include "adc.h" +#include "sha256.h" + +#ifdef GNU_LINUX_EMULATION +static const uint32_t crc32_rv_table[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, + 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, + 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, + 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, + 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, + 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, + 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, + 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, + 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, + 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, + 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, + 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, + 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, + 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, + 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, + 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, + 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +static uint32_t crc; + +void +crc32_rv_reset (void) +{ + crc = 0xffffffff; +} + +void +crc32_rv_step (uint32_t v) +{ + crc = crc32_rv_table[(crc ^ (v << 0)) >> 24] ^ (crc << 8); + crc = crc32_rv_table[(crc ^ (v << 8)) >> 24] ^ (crc << 8); + crc = crc32_rv_table[(crc ^ (v << 16)) >> 24] ^ (crc << 8); + crc = crc32_rv_table[(crc ^ (v << 24)) >> 24] ^ (crc << 8); +} + +uint32_t +crc32_rv_get (void) +{ + return crc; +} + +uint32_t +rbit (uint32_t v) +{ + v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1); + v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2); + v = ((v >> 4) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 4); + v = ((v >> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8); + v = ( v >> 16 ) | ( v << 16); + return v; +} +#else +void +crc32_rv_reset (void) +{ + RCC->AHBENR |= RCC_AHBENR_CRCEN; + CRC->CR = CRC_CR_RESET; +} + +void +crc32_rv_step (uint32_t v) +{ + CRC->DR = v; +} + +uint32_t +crc32_rv_get (void) +{ + return CRC->DR; +} + +uint32_t +rbit (uint32_t v) +{ + uint32_t r; + + asm ("rbit %0, %1" : "=r" (r) : "r" (v)); + return r; +} +#endif + +static chopstx_mutex_t mode_mtx; +static chopstx_cond_t mode_cond; + +static sha256_context sha256_ctx_data; +static uint32_t sha256_output[SHA256_DIGEST_SIZE/sizeof (uint32_t)]; + +/* + * To be a full entropy source, the requirement is to have N samples + * for output of 256-bit, where: + * + * N = (256 * 2) / + * + * For example, N should be more than 103 for min-entropy = 5.0. + * + * On the other hand, in the section 6.2 "Full Entropy Source + * Requirements", it says: + * + * At least twice the block size of the underlying cryptographic + * primitive shall be provided as input to the conditioning + * function to produce full entropy output. + * + * For us, cryptographic primitive is SHA-256 and its blocksize is + * 512-bit (64-byte), thus, N >= 128. + * + * We chose N=140. Note that we have "additional bits" of 16-byte for + * last block (feedback from previous output of SHA-256) to feed + * hash_df function of SHA-256, together with sample data of 140-byte. + * + * N=140 corresponds to min-entropy >= 3.68. + * + */ +#define NUM_NOISE_INPUTS 140 + +#define EP_ROUND_0 0 /* initial-five-byte and 3-byte, then 56-byte-input */ +#define EP_ROUND_1 1 /* 64-byte-input */ +#define EP_ROUND_2 2 /* 17-byte-input */ +#define EP_ROUND_RAW 3 /* 32-byte-input */ +#define EP_ROUND_RAW_DATA 4 /* 32-byte-input */ + +#define EP_ROUND_0_INPUTS 56 +#define EP_ROUND_1_INPUTS 64 +#define EP_ROUND_2_INPUTS 17 +#define EP_ROUND_RAW_INPUTS 32 +#define EP_ROUND_RAW_DATA_INPUTS 32 + +static uint8_t ep_round; + +static void noise_source_continuous_test (uint8_t noise); +static void noise_source_continuous_test_word (uint8_t b0, uint8_t b1, + uint8_t b2, uint8_t b3); + +/* + * Hash_df initial string: + * + * Initial five bytes are: + * 1, : counter = 1 + * 0, 0, 1, 0 : no_of_bits_returned (in big endian) + * + * Then, three-byte from noise source follows. + * + * One-byte was used in the previous turn, and we have three bytes in + * CRC32. + */ +static void ep_fill_initial_string (void) +{ + uint32_t v = crc32_rv_get (); + uint8_t b1, b2, b3; + + b3 = v >> 24; + b2 = v >> 16; + b1 = v >> 8; + + noise_source_continuous_test (b1); + noise_source_continuous_test (b2); + noise_source_continuous_test (b3); + + adc_buf[0] = 0x01000001; + adc_buf[1] = (v & 0xffffff00); +} + +static void ep_init (int mode) +{ + if (mode == NEUG_MODE_RAW) + { + ep_round = EP_ROUND_RAW; + adc_start_conversion (0, EP_ROUND_RAW_INPUTS); + } + else if (mode == NEUG_MODE_RAW_DATA) + { + ep_round = EP_ROUND_RAW_DATA; + adc_start_conversion (0, EP_ROUND_RAW_DATA_INPUTS / 4); + } + else + { + ep_round = EP_ROUND_0; + ep_fill_initial_string (); + adc_start_conversion (2, EP_ROUND_0_INPUTS); + } +} + + +static void ep_fill_wbuf_v (int i, int test, uint32_t v) +{ + if (test) + { + uint8_t b0, b1, b2, b3; + + b3 = v >> 24; + b2 = v >> 16; + b1 = v >> 8; + b0 = v; + + noise_source_continuous_test_word (b0, b1, b2, b3); + } + + sha256_ctx_data.wbuf[i] = v; +} + +/* Here, we assume a little endian architecture. */ +static int ep_process (int mode) +{ + int i, n; + uint32_t v; + + if (ep_round == EP_ROUND_0) + { + sha256_start (&sha256_ctx_data); + sha256_ctx_data.wbuf[0] = adc_buf[0]; + sha256_ctx_data.wbuf[1] = adc_buf[1]; + for (i = 0; i < EP_ROUND_0_INPUTS / 4; i++) + { + crc32_rv_step (adc_buf[i*4 + 2]); + crc32_rv_step (adc_buf[i*4 + 3]); + crc32_rv_step (adc_buf[i*4 + 4]); + crc32_rv_step (adc_buf[i*4 + 5]); + v = crc32_rv_get (); + ep_fill_wbuf_v (i+2, 1, v); + } + + adc_start_conversion (0, EP_ROUND_1_INPUTS); + sha256_process (&sha256_ctx_data); + ep_round++; + return 0; + } + else if (ep_round == EP_ROUND_1) + { + for (i = 0; i < EP_ROUND_1_INPUTS / 4; i++) + { + crc32_rv_step (adc_buf[i*4]); + crc32_rv_step (adc_buf[i*4 + 1]); + crc32_rv_step (adc_buf[i*4 + 2]); + crc32_rv_step (adc_buf[i*4 + 3]); + v = crc32_rv_get (); + ep_fill_wbuf_v (i, 1, v); + } + + adc_start_conversion (0, EP_ROUND_2_INPUTS + 3); + sha256_process (&sha256_ctx_data); + ep_round++; + return 0; + } + else if (ep_round == EP_ROUND_2) + { + for (i = 0; i < EP_ROUND_2_INPUTS / 4; i++) + { + crc32_rv_step (adc_buf[i*4]); + crc32_rv_step (adc_buf[i*4 + 1]); + crc32_rv_step (adc_buf[i*4 + 2]); + crc32_rv_step (adc_buf[i*4 + 3]); + v = crc32_rv_get (); + ep_fill_wbuf_v (i, 1, v); + } + + crc32_rv_step (adc_buf[i*4]); + crc32_rv_step (adc_buf[i*4 + 1]); + crc32_rv_step (adc_buf[i*4 + 2]); + crc32_rv_step (adc_buf[i*4 + 3]); + v = crc32_rv_get () & 0xff; /* First byte of CRC32 is used here. */ + noise_source_continuous_test (v); + sha256_ctx_data.wbuf[i] = v; + ep_init (NEUG_MODE_CONDITIONED); /* The rest three-byte of + CRC32 is used here. */ + n = SHA256_DIGEST_SIZE / 2; + memcpy (((uint8_t *)sha256_ctx_data.wbuf) + EP_ROUND_2_INPUTS, + sha256_output, n); + sha256_ctx_data.total[0] = 5 + NUM_NOISE_INPUTS + n; + sha256_finish (&sha256_ctx_data, (uint8_t *)sha256_output); + return SHA256_DIGEST_SIZE / sizeof (uint32_t); + } + else if (ep_round == EP_ROUND_RAW) + { + for (i = 0; i < EP_ROUND_RAW_INPUTS / 4; i++) + { + crc32_rv_step (adc_buf[i*4]); + crc32_rv_step (adc_buf[i*4 + 1]); + crc32_rv_step (adc_buf[i*4 + 2]); + crc32_rv_step (adc_buf[i*4 + 3]); + v = crc32_rv_get (); + ep_fill_wbuf_v (i, 1, v); + } + + ep_init (mode); + return EP_ROUND_RAW_INPUTS / 4; + } + else if (ep_round == EP_ROUND_RAW_DATA) + { + for (i = 0; i < EP_ROUND_RAW_DATA_INPUTS / 4; i++) + { + v = adc_buf[i]; + ep_fill_wbuf_v (i, 0, v); + } + + ep_init (mode); + return EP_ROUND_RAW_DATA_INPUTS / 4; + } + + return 0; +} + + +static const uint32_t *ep_output (int mode) +{ + if (mode) + return sha256_ctx_data.wbuf; + else + return sha256_output; +} + +#define REPETITION_COUNT 1 +#define ADAPTIVE_PROPORTION_64 2 +#define ADAPTIVE_PROPORTION_4096 4 + +uint8_t neug_err_state; +uint16_t neug_err_cnt; +uint16_t neug_err_cnt_rc; +uint16_t neug_err_cnt_p64; +uint16_t neug_err_cnt_p4k; + +uint16_t neug_rc_max; +uint16_t neug_p64_max; +uint16_t neug_p4k_max; + +static void noise_source_cnt_max_reset (void) +{ + neug_err_cnt = neug_err_cnt_rc = neug_err_cnt_p64 = neug_err_cnt_p4k = 0; + neug_rc_max = neug_p64_max = neug_p4k_max = 0; +} + +static void noise_source_error_reset (void) +{ + neug_err_state = 0; +} + +static void noise_source_error (uint32_t err) +{ + neug_err_state |= err; + neug_err_cnt++; + + if ((err & REPETITION_COUNT)) + neug_err_cnt_rc++; + if ((err & ADAPTIVE_PROPORTION_64)) + neug_err_cnt_p64++; + if ((err & ADAPTIVE_PROPORTION_4096)) + neug_err_cnt_p4k++; +} + +/* + * For health tests, we assume that the device noise source has + * min-entropy >= 4.2. Observing raw data stream (before CRC-32) has + * more than 4.2 bit/byte entropy. When the data stream after CRC-32 + * filter will be less than 4.2 bit/byte entropy, that must be + * something wrong. Note that even we observe < 4.2, we still have + * some margin, since we use NUM_NOISE_INPUTS=140. + * + */ + +/* Cuttoff = 9, when min-entropy = 4.2, W= 2^-30 */ +/* ceiling of (1+30/4.2) */ +#define REPITITION_COUNT_TEST_CUTOFF 9 + +static uint8_t rct_a; +static uint8_t rct_b; + +static void repetition_count_test (uint8_t sample) +{ + if (rct_a == sample) + { + rct_b++; + if (rct_b >= REPITITION_COUNT_TEST_CUTOFF) + noise_source_error (REPETITION_COUNT); + if (rct_b > neug_rc_max) + neug_rc_max = rct_b; + } + else + { + rct_a = sample; + rct_b = 1; + } +} + +static void repetition_count_test_word (uint8_t b0, uint8_t b1, + uint8_t b2, uint8_t b3) +{ + if (rct_a == b0) + rct_b++; + else + { + rct_a = b0; + rct_b = 1; + } + + if (rct_a == b1) + rct_b++; + else + { + rct_a = b1; + rct_b = 1; + } + + if (rct_a == b2) + rct_b++; + else + { + rct_a = b2; + rct_b = 1; + } + + if (rct_a == b3) + rct_b++; + else + { + rct_a = b3; + rct_b = 1; + } + + if (rct_b >= REPITITION_COUNT_TEST_CUTOFF) + noise_source_error (REPETITION_COUNT); + if (rct_b > neug_rc_max) + neug_rc_max = rct_b; +} + +/* Cuttoff = 18, when min-entropy = 4.2, W= 2^-30 */ +/* With R, qbinom(1-2^-30,64,2^-4.2) */ +#define ADAPTIVE_PROPORTION_64_TEST_CUTOFF 18 + +static uint8_t ap64t_a; +static uint8_t ap64t_b; +static uint8_t ap64t_s; + +static void adaptive_proportion_64_test (uint8_t sample) +{ + if (ap64t_s++ >= 64) + { + ap64t_a = sample; + ap64t_s = 1; + ap64t_b = 0; + } + else + if (ap64t_a == sample) + { + ap64t_b++; + if (ap64t_b > ADAPTIVE_PROPORTION_64_TEST_CUTOFF) + noise_source_error (ADAPTIVE_PROPORTION_64); + if (ap64t_b > neug_p64_max) + neug_p64_max = ap64t_b; + } +} + +static void adaptive_proportion_64_test_word (uint8_t b0, uint8_t b1, + uint8_t b2, uint8_t b3) +{ + if (ap64t_s >= 64) + { + ap64t_a = b0; + ap64t_s = 4; + ap64t_b = 0; + } + else + { + ap64t_s += 4; + + if (ap64t_a == b0) + ap64t_b++; + } + + if (ap64t_a == b1) + ap64t_b++; + + if (ap64t_a == b2) + ap64t_b++; + + if (ap64t_a == b3) + ap64t_b++; + + if (ap64t_b > ADAPTIVE_PROPORTION_64_TEST_CUTOFF) + noise_source_error (ADAPTIVE_PROPORTION_64); + if (ap64t_b > neug_p64_max) + neug_p64_max = ap64t_b; +} + +/* Cuttoff = 315, when min-entropy = 4.2, W= 2^-30 */ +/* With R, qbinom(1-2^-30,4096,2^-4.2) */ +#define ADAPTIVE_PROPORTION_4096_TEST_CUTOFF 315 + +static uint8_t ap4096t_a; +static uint16_t ap4096t_b; +static uint16_t ap4096t_s; + +static void adaptive_proportion_4096_test (uint8_t sample) +{ + if (ap4096t_s++ >= 4096) + { + ap4096t_a = sample; + ap4096t_s = 1; + ap4096t_b = 0; + } + else + if (ap4096t_a == sample) + { + ap4096t_b++; + if (ap4096t_b > ADAPTIVE_PROPORTION_4096_TEST_CUTOFF) + noise_source_error (ADAPTIVE_PROPORTION_4096); + if (ap4096t_b > neug_p4k_max) + neug_p4k_max = ap4096t_b; + } +} + +static void adaptive_proportion_4096_test_word (uint8_t b0, uint8_t b1, + uint8_t b2, uint8_t b3) +{ + if (ap4096t_s >= 4096) + { + ap4096t_a = b0; + ap4096t_s = 4; + ap4096t_b = 0; + } + else + { + ap4096t_s += 4; + + if (ap4096t_a == b0) + ap4096t_b++; + } + + if (ap4096t_a == b1) + ap4096t_b++; + + if (ap4096t_a == b2) + ap4096t_b++; + + if (ap4096t_a == b3) + ap4096t_b++; + + if (ap4096t_b > ADAPTIVE_PROPORTION_4096_TEST_CUTOFF) + noise_source_error (ADAPTIVE_PROPORTION_4096); + if (ap4096t_b > neug_p4k_max) + neug_p4k_max = ap4096t_b; +} + + +static void noise_source_continuous_test (uint8_t noise) +{ + repetition_count_test (noise); + adaptive_proportion_64_test (noise); + adaptive_proportion_4096_test (noise); +} + +static void noise_source_continuous_test_word (uint8_t b0, uint8_t b1, + uint8_t b2, uint8_t b3) +{ + repetition_count_test_word (b0, b1, b2, b3); + adaptive_proportion_64_test_word (b0, b1, b2, b3); + adaptive_proportion_4096_test_word (b0, b1, b2, b3); +} + +/* + * Ring buffer, filled by generator, consumed by neug_get routine. + */ +struct rng_rb { + uint32_t *buf; + chopstx_mutex_t m; + chopstx_cond_t data_available; + chopstx_cond_t space_available; + uint8_t head, tail; + uint8_t size; + unsigned int full :1; + unsigned int empty :1; +}; + +static void rb_init (struct rng_rb *rb, uint32_t *p, uint8_t size) +{ + rb->buf = p; + rb->size = size; + chopstx_mutex_init (&rb->m); + chopstx_cond_init (&rb->data_available); + chopstx_cond_init (&rb->space_available); + rb->head = rb->tail = 0; + rb->full = 0; + rb->empty = 1; +} + +static void rb_add (struct rng_rb *rb, uint32_t v) +{ + rb->buf[rb->tail++] = v; + if (rb->tail == rb->size) + rb->tail = 0; + if (rb->tail == rb->head) + rb->full = 1; + rb->empty = 0; +} + +static uint32_t rb_del (struct rng_rb *rb) +{ + uint32_t v = rb->buf[rb->head++]; + + if (rb->head == rb->size) + rb->head = 0; + if (rb->head == rb->tail) + rb->empty = 1; + rb->full = 0; + + return v; +} + +uint8_t neug_mode; +static int rng_should_terminate; +static chopstx_t rng_thread; + + +/** + * @brief Random number generation thread. + */ +static void * +rng (void *arg) +{ + struct rng_rb *rb = (struct rng_rb *)arg; + int mode = neug_mode; + + rng_should_terminate = 0; + chopstx_mutex_init (&mode_mtx); + chopstx_cond_init (&mode_cond); + + /* Enable ADCs */ + adc_start (); + + ep_init (mode); + while (!rng_should_terminate) + { + int err; + int n; + + err = adc_wait_completion (); + + chopstx_mutex_lock (&mode_mtx); + if (err || mode != neug_mode) + { + mode = neug_mode; + + noise_source_cnt_max_reset (); + + /* Discarding data available, re-initiate from the start. */ + ep_init (mode); + chopstx_cond_signal (&mode_cond); + chopstx_mutex_unlock (&mode_mtx); + continue; + } + else + chopstx_mutex_unlock (&mode_mtx); + + if ((n = ep_process (mode))) + { + int i; + const uint32_t *vp; + + if (neug_err_state != 0 + && (mode == NEUG_MODE_CONDITIONED || mode == NEUG_MODE_RAW)) + { + /* Don't use the result and do it again. */ + noise_source_error_reset (); + continue; + } + + vp = ep_output (mode); + + chopstx_mutex_lock (&rb->m); + while (rb->full) + chopstx_cond_wait (&rb->space_available, &rb->m); + + for (i = 0; i < n; i++) + { + rb_add (rb, *vp++); + if (rb->full) + break; + } + + chopstx_cond_signal (&rb->data_available); + chopstx_mutex_unlock (&rb->m); + } + } + + adc_stop (); + + return NULL; +} + +static struct rng_rb the_ring_buffer; + +#define STACK_PROCESS_2 +#include "stack-def.h" +#define STACK_ADDR_RNG ((uintptr_t)process2_base) +#define STACK_SIZE_RNG (sizeof process2_base) + +#define PRIO_RNG 2 + +/** + * @brief Initialize NeuG. + */ +void +neug_init (uint32_t *buf, uint8_t size) +{ + const uint32_t *u = (const uint32_t *)unique_device_id (); + struct rng_rb *rb = &the_ring_buffer; + int i; + + crc32_rv_reset (); + + /* + * This initialization ensures that it generates different sequence + * even if all physical conditions are same. + */ + for (i = 0; i < 3; i++) + crc32_rv_step (*u++); + + neug_mode = NEUG_MODE_CONDITIONED; + rb_init (rb, buf, size); + + rng_thread = chopstx_create (PRIO_RNG, STACK_ADDR_RNG, STACK_SIZE_RNG, + rng, rb); +} + +/** + * @breif Flush random bytes. + */ +void +neug_flush (void) +{ + struct rng_rb *rb = &the_ring_buffer; + + chopstx_mutex_lock (&rb->m); + while (!rb->empty) + (void)rb_del (rb); + chopstx_cond_signal (&rb->space_available); + chopstx_mutex_unlock (&rb->m); +} + + +/** + * @brief Wakes up RNG thread to generate random numbers. + */ +void +neug_kick_filling (void) +{ + struct rng_rb *rb = &the_ring_buffer; + + chopstx_mutex_lock (&rb->m); + if (!rb->full) + chopstx_cond_signal (&rb->space_available); + chopstx_mutex_unlock (&rb->m); +} + +/** + * @brief Get random word (32-bit) from NeuG. + * @detail With NEUG_KICK_FILLING, it wakes up RNG thread. + * With NEUG_NO_KICK, it doesn't wake up RNG thread automatically, + * it is needed to call neug_kick_filling later. + */ +uint32_t +neug_get (int kick) +{ + struct rng_rb *rb = &the_ring_buffer; + uint32_t v; + + chopstx_mutex_lock (&rb->m); + while (rb->empty) + chopstx_cond_wait (&rb->data_available, &rb->m); + v = rb_del (rb); + if (kick) + chopstx_cond_signal (&rb->space_available); + chopstx_mutex_unlock (&rb->m); + + return v; +} + +int +neug_get_nonblock (uint32_t *p) +{ + struct rng_rb *rb = &the_ring_buffer; + int r = 0; + + chopstx_mutex_lock (&rb->m); + if (rb->empty) + { + r = -1; + chopstx_cond_signal (&rb->space_available); + } + else + *p = rb_del (rb); + chopstx_mutex_unlock (&rb->m); + + return r; +} + +int neug_consume_random (void (*proc) (uint32_t, int)) +{ + int i = 0; + struct rng_rb *rb = &the_ring_buffer; + + chopstx_mutex_lock (&rb->m); + while (!rb->empty) + { + uint32_t v; + + v = rb_del (rb); + proc (v, i); + i++; + } + chopstx_cond_signal (&rb->space_available); + chopstx_mutex_unlock (&rb->m); + + return i; +} + +void +neug_wait_full (void) +{ + struct rng_rb *rb = &the_ring_buffer; + + chopstx_mutex_lock (&rb->m); + while (!rb->full) + chopstx_cond_wait (&rb->data_available, &rb->m); + chopstx_mutex_unlock (&rb->m); +} + +void +neug_fini (void) +{ + rng_should_terminate = 1; + neug_get (1); + chopstx_join (rng_thread, NULL); +} + +void +neug_mode_select (uint8_t mode) +{ + if (neug_mode == mode) + return; + + neug_wait_full (); + + chopstx_mutex_lock (&mode_mtx); + neug_mode = mode; + neug_flush (); + chopstx_cond_wait (&mode_cond, &mode_mtx); + chopstx_mutex_unlock (&mode_mtx); + + neug_wait_full (); + neug_flush (); +} diff --git a/gnuk/src/neug.h b/gnuk/src/neug.h new file mode 100644 index 0000000000..38aadc0f98 --- /dev/null +++ b/gnuk/src/neug.h @@ -0,0 +1,33 @@ +#define NEUG_NO_KICK 0 +#define NEUG_KICK_FILLING 1 + +#define NEUG_PRE_LOOP 32 + +#define NEUG_MODE_CONDITIONED 0 /* Conditioned data. */ +#define NEUG_MODE_RAW 1 /* CRC-32 filtered sample data. */ +#define NEUG_MODE_RAW_DATA 2 /* Sample data directly. */ + +extern uint8_t neug_mode; +extern uint16_t neug_err_cnt; +extern uint16_t neug_err_cnt_rc; +extern uint16_t neug_err_cnt_p64; +extern uint16_t neug_err_cnt_p4k; +extern uint16_t neug_rc_max; +extern uint16_t neug_p64_max; +extern uint16_t neug_p4k_max; + +void neug_init (uint32_t *buf, uint8_t size); +uint32_t neug_get (int kick); +int neug_get_nonblock (uint32_t *p); +void neug_kick_filling (void); +void neug_flush (void); +void neug_wait_full (void); +void neug_fini (void); +void neug_mode_select (uint8_t mode); + +int neug_consume_random (void (*proc) (uint32_t, int)); + +void crc32_rv_reset (void); +void crc32_rv_step (uint32_t v); +uint32_t crc32_rv_get (void); +uint32_t rbit (uint32_t v); diff --git a/gnuk/src/openpgp-do.c b/gnuk/src/openpgp-do.c new file mode 100644 index 0000000000..7c34bf4867 --- /dev/null +++ b/gnuk/src/openpgp-do.c @@ -0,0 +1,2190 @@ +/* + * openpgp-do.c -- OpenPGP card Data Objects (DO) handling + * + * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 + * 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 +#include + +#include "config.h" + +#include "sys.h" +#include "gnuk.h" +#include "status-code.h" +#include "random.h" +#include "polarssl/config.h" +#include "polarssl/aes.h" +#include "sha512.h" + +/* Forward declaration */ +#define CLEAN_PAGE_FULL 1 +#define CLEAN_SINGLE 0 +static void gpg_do_delete_prvkey (enum kind_of_key kk, int clean_page_full); +static void gpg_reset_digital_signature_counter (void); + +#define PASSWORD_ERRORS_MAX 3 /* >= errors, it will be locked */ +static const uint8_t *pw_err_counter_p[3]; + +static int +gpg_pw_get_err_counter (uint8_t which) +{ + return flash_cnt123_get_value (pw_err_counter_p[which]); +} + +int +gpg_pw_get_retry_counter (int who) +{ + if (who == 0x81 || who == 0x82) + return PASSWORD_ERRORS_MAX - gpg_pw_get_err_counter (PW_ERR_PW1); + else if (who == 0x83) + return PASSWORD_ERRORS_MAX - gpg_pw_get_err_counter (PW_ERR_PW3); + else + return PASSWORD_ERRORS_MAX - gpg_pw_get_err_counter (PW_ERR_RC); +} + +int +gpg_pw_locked (uint8_t which) +{ + if (gpg_pw_get_err_counter (which) >= PASSWORD_ERRORS_MAX) + return 1; + else + return 0; +} + +void +gpg_pw_reset_err_counter (uint8_t which) +{ + flash_cnt123_clear (&pw_err_counter_p[which]); + if (pw_err_counter_p[which] != NULL) + GPG_MEMORY_FAILURE (); +} + +void +gpg_pw_increment_err_counter (uint8_t which) +{ + flash_cnt123_increment (which, &pw_err_counter_p[which]); +} + + +uint16_t data_objects_number_of_bytes; + +/* + * Compile time vars: + * Historical Bytes, Extended Capabilities. + */ + +/* Historical Bytes */ +const uint8_t historical_bytes[] __attribute__ ((aligned (1))) = { + 10, + 0x00, + 0x31, 0x84, /* Full DF name, GET DATA, MF */ + 0x73, + 0x80, 0x01, 0x80, /* Full DF name */ + /* 1-byte */ + /* Command chaining, No extended Lc and Le */ +#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT + 0x05, +#else + 0x00, +#endif + 0x90, 0x00 /* Status info */ +}; + +/* Extended Capabilities */ +static const uint8_t extended_capabilities[] __attribute__ ((aligned (1))) = { + 10, + 0x74, /* + * No Secure Messaging supported + * GET CHALLENGE supported + * Key import supported + * PW status byte can be put + * No private_use_DO + * Algorithm attrs are changable + */ + 0, /* Secure Messaging Algorithm: N/A (TDES=0, AES=1) */ + 0x00, CHALLENGE_LEN, /* Max size of GET CHALLENGE */ +#ifdef CERTDO_SUPPORT + 0x08, 0x00, /* max. length of cardholder certificate (2KiB) */ +#else + 0x00, 0x00, +#endif + /* Max. length of command APDU data */ + 0x00, 0xff, + /* Max. length of response APDU data */ + 0x01, 0x00, +}; + +/* Algorithm Attributes */ +#define OPENPGP_ALGO_RSA 0x01 +#define OPENPGP_ALGO_ECDH 0x12 +#define OPENPGP_ALGO_ECDSA 0x13 +#define OPENPGP_ALGO_EDDSA 0x16 /* It catches 22, finally. */ + +static const uint8_t algorithm_attr_rsa2k[] __attribute__ ((aligned (1))) = { + 6, + OPENPGP_ALGO_RSA, + 0x08, 0x00, /* Length modulus (in bit): 2048 */ + 0x00, 0x20, /* Length exponent (in bit): 32 */ + 0x00 /* 0: Acceptable format is: P and Q */ +}; + +static const uint8_t algorithm_attr_rsa4k[] __attribute__ ((aligned (1))) = { + 6, + OPENPGP_ALGO_RSA, + 0x10, 0x00, /* Length modulus (in bit): 4096 */ + 0x00, 0x20, /* Length exponent (in bit): 32 */ + 0x00 /* 0: Acceptable format is: P and Q */ +}; + +static const uint8_t algorithm_attr_p256r1[] __attribute__ ((aligned (1))) = { + 9, + OPENPGP_ALGO_ECDSA, + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 /* OID of NIST curve P-256 */ +}; + +static const uint8_t algorithm_attr_p256k1[] __attribute__ ((aligned (1))) = { + 6, + OPENPGP_ALGO_ECDSA, + 0x2b, 0x81, 0x04, 0x00, 0x0a /* OID of curve secp256k1 */ +}; + +static const uint8_t algorithm_attr_ed25519[] __attribute__ ((aligned (1))) = { + 10, + OPENPGP_ALGO_EDDSA, + /* OID of the curve Ed25519 */ + 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01 +}; + +static const uint8_t algorithm_attr_cv25519[] __attribute__ ((aligned (1))) = { + 11, + OPENPGP_ALGO_ECDH, + /* OID of the curve Curve25519 */ + 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01 +}; + + +/* + * Representation of PW1_LIFETIME: + * 0: PW1_LIEFTIME_P == NULL : PW1 is valid for single PSO:CDS command + * 1: PW1_LIEFTIME_P != NULL : PW1 is valid for several PSO:CDS commands + * + * The address in the variable PW1_LIEFTIME_P is used when filling zero + * in flash memory + */ +static const uint8_t *pw1_lifetime_p; + +static int +gpg_get_pw1_lifetime (void) +{ + if (pw1_lifetime_p == NULL) + return 0; + else + return 1; +} + + +/* + * Representation of algorithm attributes: + * 0: ALGO_ATTR_<>_P == NULL : RSA-2048 + * N: ALGO_ATTR_<>_P != NULL : + * + */ +static const uint8_t *algo_attr_sig_p; +static const uint8_t *algo_attr_dec_p; +static const uint8_t *algo_attr_aut_p; + +static const uint8_t ** +get_algo_attr_pointer (enum kind_of_key kk) +{ + if (kk == GPG_KEY_FOR_SIGNING) + return &algo_attr_sig_p; + else if (kk == GPG_KEY_FOR_DECRYPTION) + return &algo_attr_dec_p; + else + return &algo_attr_aut_p; +} + +static int +kk_to_nr (enum kind_of_key kk) +{ + int nr; + + if (kk == GPG_KEY_FOR_SIGNING) + nr = NR_KEY_ALGO_ATTR_SIG; + else if (kk == GPG_KEY_FOR_DECRYPTION) + nr = NR_KEY_ALGO_ATTR_DEC; + else + nr = NR_KEY_ALGO_ATTR_AUT; + + return nr; +} + +int +gpg_get_algo_attr (enum kind_of_key kk) +{ + const uint8_t *algo_attr_p = *get_algo_attr_pointer (kk); + + if (algo_attr_p == NULL) + return ALGO_RSA2K; + + return algo_attr_p[1]; +} + +static void +gpg_reset_algo_attr (enum kind_of_key kk) +{ + gpg_do_delete_prvkey (kk, CLEAN_PAGE_FULL); + if (kk == GPG_KEY_FOR_SIGNING) + { + gpg_reset_digital_signature_counter (); + gpg_do_write_simple (NR_DO_FP_SIG, NULL, 0); + gpg_do_write_simple (NR_DO_KGTIME_SIG, NULL, 0); + } + else if (kk == GPG_KEY_FOR_DECRYPTION) + { + gpg_do_write_simple (NR_DO_FP_DEC, NULL, 0); + gpg_do_write_simple (NR_DO_KGTIME_DEC, NULL, 0); + } + else + { + gpg_do_write_simple (NR_DO_FP_AUT, NULL, 0); + gpg_do_write_simple (NR_DO_KGTIME_AUT, NULL, 0); + } +} + +static const uint8_t * +get_algo_attr_data_object (enum kind_of_key kk) +{ + const uint8_t *algo_attr_p = *get_algo_attr_pointer (kk); + + if (algo_attr_p == NULL) + return algorithm_attr_rsa2k; + + switch (algo_attr_p[1]) + { + case ALGO_RSA4K: + return algorithm_attr_rsa4k; + case ALGO_NISTP256R1: + return algorithm_attr_p256r1; + case ALGO_SECP256K1: + return algorithm_attr_p256k1; + case ALGO_ED25519: + return algorithm_attr_ed25519; + case ALGO_CURVE25519: + return algorithm_attr_cv25519; + default: + return algorithm_attr_rsa2k; + } +} + +int +gpg_get_algo_attr_key_size (enum kind_of_key kk, enum size_of_key s) +{ + const uint8_t *algo_attr_p = *get_algo_attr_pointer (kk); + + if (algo_attr_p == NULL) /* RSA-2048 */ + goto rsa2k; + + switch (algo_attr_p[1]) + { + case ALGO_RSA4K: + if (s == GPG_KEY_STORAGE) + return 1024; + else + return 512; + case ALGO_NISTP256R1: + case ALGO_SECP256K1: + if (s == GPG_KEY_STORAGE) + return 128; + else if (s == GPG_KEY_PUBLIC) + return 64; + else + return 32; + case ALGO_ED25519: + if (s == GPG_KEY_STORAGE) + return 128; + else if (s == GPG_KEY_PUBLIC) + return 32; + else + return 64; + case ALGO_CURVE25519: + if (s == GPG_KEY_STORAGE) + return 64; + else + return 32; + default: + rsa2k: + if (s == GPG_KEY_STORAGE) + return 512; + else + return 256; + } +} + + +static uint32_t digital_signature_counter; + +static const uint8_t * +gpg_write_digital_signature_counter (const uint8_t *p, uint32_t dsc) +{ + uint16_t hw0, hw1; + + if ((dsc >> 10) == 0) + { /* no upper bits */ + hw1 = NR_COUNTER_DS_LSB | ((dsc & 0x0300) >> 8) | ((dsc & 0x00ff) << 8); + flash_put_data_internal (p, hw1); + return p+2; + } + else + { + hw0 = NR_COUNTER_DS | ((dsc & 0xfc0000) >> 18) | ((dsc & 0x03fc00) >> 2); + hw1 = NR_COUNTER_DS_LSB | ((dsc & 0x0300) >> 8) | ((dsc & 0x00ff) << 8); + flash_put_data_internal (p, hw0); + flash_put_data_internal (p+2, hw1); + return p+4; + } +} + +static void +gpg_reset_digital_signature_counter (void) +{ + if (digital_signature_counter != 0) + { + flash_put_data (NR_COUNTER_DS); + flash_put_data (NR_COUNTER_DS_LSB); + digital_signature_counter = 0; + } +} + +void +gpg_increment_digital_signature_counter (void) +{ + uint16_t hw0, hw1; + uint32_t dsc = (digital_signature_counter + 1) & 0x00ffffff; + + if ((dsc & 0x03ff) == 0) + { /* carry occurs from l10 to h14 */ + hw0 = NR_COUNTER_DS | ((dsc & 0xfc0000) >> 18) | ((dsc & 0x03fc00) >> 2); + hw1 = NR_COUNTER_DS_LSB; /* zero */ + flash_put_data (hw0); + flash_put_data (hw1); + } + else + { + hw1 = NR_COUNTER_DS_LSB | ((dsc & 0x0300) >> 8) | ((dsc & 0x00ff) << 8); + flash_put_data (hw1); + } + + digital_signature_counter = dsc; + + if (gpg_get_pw1_lifetime () == 0) + ac_reset_pso_cds (); +} + + +#define SIZE_FINGER_PRINT 20 +#define SIZE_KEYGEN_TIME 4 /* RFC4880 */ + +enum do_type { + DO_FIXED, + DO_VAR, + DO_CMP_READ, + DO_PROC_READ, + DO_PROC_WRITE, + DO_PROC_READWRITE, +}; + +struct do_table_entry { + uint16_t tag; + enum do_type do_type; + uint8_t ac_read; + uint8_t ac_write; + const void *obj; +}; + +static uint8_t *res_p; + +static void copy_do_1 (uint16_t tag, const uint8_t *do_data, int with_tag); +static const struct do_table_entry *get_do_entry (uint16_t tag); + +#define GPG_DO_AID 0x004f +#define GPG_DO_NAME 0x005b +#define GPG_DO_LOGIN_DATA 0x005e +#define GPG_DO_CH_DATA 0x0065 +#define GPG_DO_APP_DATA 0x006e +#define GPG_DO_DISCRETIONARY 0x0073 +#define GPG_DO_SS_TEMP 0x007a +#define GPG_DO_DS_COUNT 0x0093 +#define GPG_DO_EXTCAP 0x00c0 +#define GPG_DO_ALG_SIG 0x00c1 +#define GPG_DO_ALG_DEC 0x00c2 +#define GPG_DO_ALG_AUT 0x00c3 +#define GPG_DO_PW_STATUS 0x00c4 +#define GPG_DO_FP_ALL 0x00c5 +#define GPG_DO_CAFP_ALL 0x00c6 +#define GPG_DO_FP_SIG 0x00c7 +#define GPG_DO_FP_DEC 0x00c8 +#define GPG_DO_FP_AUT 0x00c9 +#define GPG_DO_CAFP_1 0x00ca +#define GPG_DO_CAFP_2 0x00cb +#define GPG_DO_CAFP_3 0x00cc +#define GPG_DO_KGTIME_ALL 0x00cd +#define GPG_DO_KGTIME_SIG 0x00ce +#define GPG_DO_KGTIME_DEC 0x00cf +#define GPG_DO_KGTIME_AUT 0x00d0 +#define GPG_DO_RESETTING_CODE 0x00d3 +#define GPG_DO_KEY_IMPORT 0x3fff +#define GPG_DO_LANGUAGE 0x5f2d +#define GPG_DO_SEX 0x5f35 +#define GPG_DO_URL 0x5f50 +#define GPG_DO_HIST_BYTES 0x5f52 +#define GPG_DO_CH_CERTIFICATE 0x7f21 + +static const uint8_t *do_ptr[NR_DO__LAST__]; + +static int +do_tag_to_nr (uint16_t tag) +{ + switch (tag) + { + case GPG_DO_SEX: + return NR_DO_SEX; + case GPG_DO_FP_SIG: + return NR_DO_FP_SIG; + case GPG_DO_FP_DEC: + return NR_DO_FP_DEC; + case GPG_DO_FP_AUT: + return NR_DO_FP_AUT; + case GPG_DO_CAFP_1: + return NR_DO_CAFP_1; + case GPG_DO_CAFP_2: + return NR_DO_CAFP_2; + case GPG_DO_CAFP_3: + return NR_DO_CAFP_3; + case GPG_DO_KGTIME_SIG: + return NR_DO_KGTIME_SIG; + case GPG_DO_KGTIME_DEC: + return NR_DO_KGTIME_DEC; + case GPG_DO_KGTIME_AUT: + return NR_DO_KGTIME_AUT; + case GPG_DO_LOGIN_DATA: + return NR_DO_LOGIN_DATA; + case GPG_DO_URL: + return NR_DO_URL; + case GPG_DO_NAME: + return NR_DO_NAME; + case GPG_DO_LANGUAGE: + return NR_DO_LANGUAGE; + default: + return -1; + } +} + +static void +copy_tag (uint16_t tag) +{ + if (tag < 0x0100) + *res_p++ = (tag & 0xff); + else + { + *res_p++ = (tag >> 8); + *res_p++ = (tag & 0xff); + } +} + + +#define SIZE_FP 20 +#define SIZE_KGTIME 4 + +static int +do_fp_all (uint16_t tag, int with_tag) +{ + const uint8_t *data; + + if (with_tag) + { + copy_tag (tag); + *res_p++ = SIZE_FP*3; + } + + data = gpg_do_read_simple (NR_DO_FP_SIG); + if (data) + memcpy (res_p, data, SIZE_FP); + else + memset (res_p, 0, SIZE_FP); + res_p += SIZE_FP; + + data = gpg_do_read_simple (NR_DO_FP_DEC); + if (data) + memcpy (res_p, data, SIZE_FP); + else + memset (res_p, 0, SIZE_FP); + res_p += SIZE_FP; + + data = gpg_do_read_simple (NR_DO_FP_AUT); + if (data) + memcpy (res_p, data, SIZE_FP); + else + memset (res_p, 0, SIZE_FP); + res_p += SIZE_FP; + return 1; +} + +static int +do_cafp_all (uint16_t tag, int with_tag) +{ + const uint8_t *data; + + if (with_tag) + { + copy_tag (tag); + *res_p++ = SIZE_FP*3; + } + + data = gpg_do_read_simple (NR_DO_CAFP_1); + if (data) + memcpy (res_p, data, SIZE_FP); + else + memset (res_p, 0, SIZE_FP); + res_p += SIZE_FP; + + data = gpg_do_read_simple (NR_DO_CAFP_2); + if (data) + memcpy (res_p, data, SIZE_FP); + else + memset (res_p, 0, SIZE_FP); + res_p += SIZE_FP; + + data = gpg_do_read_simple (NR_DO_CAFP_2); + if (data) + memcpy (res_p, data, SIZE_FP); + else + memset (res_p, 0, SIZE_FP); + res_p += SIZE_FP; + return 1; +} + +static int +do_kgtime_all (uint16_t tag, int with_tag) +{ + const uint8_t *data; + + if (with_tag) + { + copy_tag (tag); + *res_p++ = SIZE_KGTIME*3; + } + + data = gpg_do_read_simple (NR_DO_KGTIME_SIG); + if (data) + memcpy (res_p, data, SIZE_KGTIME); + else + memset (res_p, 0, SIZE_KGTIME); + res_p += SIZE_KGTIME; + + data = gpg_do_read_simple (NR_DO_KGTIME_DEC); + if (data) + memcpy (res_p, data, SIZE_KGTIME); + else + memset (res_p, 0, SIZE_KGTIME); + res_p += SIZE_KGTIME; + + data = gpg_do_read_simple (NR_DO_KGTIME_AUT); + if (data) + memcpy (res_p, data, SIZE_KGTIME); + else + memset (res_p, 0, SIZE_KGTIME); + res_p += SIZE_KGTIME; + return 1; +} + +const uint8_t openpgpcard_aid[] = { + 0xd2, 0x76, /* D: National, 276: DEU ISO 3166-1 */ + 0x00, 0x01, 0x24, /* Registered Application Provider Identifier */ + 0x01, /* Application: OpenPGPcard */ + 0x02, 0x00, /* Version 2.0 */ + /* v. id */ /* serial number */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* To be overwritten */ +}; + +static int +do_openpgpcard_aid (uint16_t tag, int with_tag) +{ + const volatile uint8_t *p = openpgpcard_aid; + uint16_t vid = (p[8] << 8) | p[9]; + + if (with_tag) + { + copy_tag (tag); + *res_p++ = 16; + } + + if (vid == 0xffff || vid == 0x0000) + { + const uint8_t *u = unique_device_id () + 8; + + memcpy (res_p, openpgpcard_aid, 8); + res_p += 8; + + /* vid == 0xfffe: serial number is four random bytes */ + *res_p++ = 0xff; + *res_p++ = 0xfe; + + *res_p++ = u[3]; + *res_p++ = u[2]; + *res_p++ = u[1]; + *res_p++ = u[0]; + } + else + { + memcpy (res_p, openpgpcard_aid, 14); + res_p += 14; + } + + *res_p++ = 0; + *res_p++ = 0; + + return 1; +} + +static int +do_ds_count (uint16_t tag, int with_tag) +{ + if (with_tag) + { + copy_tag (tag); + *res_p++ = 3; + } + + *res_p++ = (digital_signature_counter >> 16) & 0xff; + *res_p++ = (digital_signature_counter >> 8) & 0xff; + *res_p++ = digital_signature_counter & 0xff; + return 1; +} + +static int +rw_pw_status (uint16_t tag, int with_tag, + const uint8_t *data, int len, int is_write) +{ + if (is_write) + { + if (len != 1) + return 0; /* Failure */ + + /* The first byte of DATA specifies the lifetime. */ + if (data[0] == 0 && pw1_lifetime_p != NULL) + { + flash_bool_clear (&pw1_lifetime_p); + if (pw1_lifetime_p != NULL) /* No change after update */ + return 0; + } + else if (pw1_lifetime_p == NULL) + { + pw1_lifetime_p = flash_bool_write (NR_BOOL_PW1_LIFETIME); + if (pw1_lifetime_p == NULL) /* No change after update */ + return 0; + } + + return 1; /* Success */ + } + else + { + if (with_tag) + { + copy_tag (tag); + *res_p++ = SIZE_PW_STATUS_BYTES; + } + + *res_p++ = gpg_get_pw1_lifetime (); + *res_p++ = PW_LEN_MAX; + *res_p++ = PW_LEN_MAX; + *res_p++ = PW_LEN_MAX; + *res_p++ = PASSWORD_ERRORS_MAX - gpg_pw_get_err_counter (PW_ERR_PW1); + *res_p++ = PASSWORD_ERRORS_MAX - gpg_pw_get_err_counter (PW_ERR_RC); + *res_p++ = PASSWORD_ERRORS_MAX - gpg_pw_get_err_counter (PW_ERR_PW3); + return 1; + } +} + +static int +rw_algorithm_attr (uint16_t tag, int with_tag, + const uint8_t *data, int len, int is_write) +{ + enum kind_of_key kk; + + if (tag == GPG_DO_ALG_SIG) + kk = GPG_KEY_FOR_SIGNING; + else if (tag == GPG_DO_ALG_DEC) + kk = GPG_KEY_FOR_DECRYPTION; + else + kk = GPG_KEY_FOR_AUTHENTICATION; + + if (is_write) + { + int algo = -1; + const uint8_t **algo_attr_pp = get_algo_attr_pointer (kk); + + if (len == 6) + { + if (memcmp (data, algorithm_attr_rsa2k+1, 6) == 0) + algo = ALGO_RSA2K; + else if (memcmp (data, algorithm_attr_rsa4k+1, 6) == 0) + algo = ALGO_RSA4K; + else if ((tag != GPG_DO_ALG_DEC + && memcmp (data, algorithm_attr_p256k1+1, 6) == 0) + || (tag == GPG_DO_ALG_DEC && data[0]==OPENPGP_ALGO_ECDH + && memcmp (data+1, algorithm_attr_p256k1+2, 5) == 0)) + algo = ALGO_SECP256K1; + } + else if (len == 9 + && ((tag != GPG_DO_ALG_DEC + && memcmp (data, algorithm_attr_p256r1+1, 9) == 0) + || (tag == GPG_DO_ALG_DEC && data[0]==OPENPGP_ALGO_ECDH + && memcmp (data+1, algorithm_attr_p256r1+2, 8) == 0))) + algo = ALGO_NISTP256R1; + else if (len == 10 && memcmp (data, algorithm_attr_ed25519+1, 10) == 0) + algo = ALGO_ED25519; + else if (len == 11 && memcmp (data, algorithm_attr_cv25519+1, 11) == 0) + algo = ALGO_CURVE25519; + + if (algo < 0) + return 0; /* Error. */ + else if (algo == ALGO_RSA2K && *algo_attr_pp != NULL) + { + gpg_reset_algo_attr (kk); + flash_enum_clear (algo_attr_pp); + if (*algo_attr_pp != NULL) + return 0; + } + else if ((algo != ALGO_RSA2K && *algo_attr_pp == NULL) || + (*algo_attr_pp != NULL && (*algo_attr_pp)[1] != algo)) + { + gpg_reset_algo_attr (kk); + *algo_attr_pp = flash_enum_write (kk_to_nr (kk), algo); + if (*algo_attr_pp == NULL) + return 0; + } + + return 1; + } + else + { + const uint8_t *algo_attr_do = get_algo_attr_data_object (kk); + + copy_do_1 (tag, algo_attr_do, with_tag); + /* Override the byte when GPG_DO_ALG_DEC. */ + if (tag == GPG_DO_ALG_DEC && algo_attr_do[1] == OPENPGP_ALGO_ECDSA) + *(res_p - algo_attr_do[0]) = OPENPGP_ALGO_ECDH; + return 1; + } +} + +static int +proc_resetting_code (const uint8_t *data, int len) +{ + const uint8_t *old_ks = keystring_md_pw3; + uint8_t new_ks0[KEYSTRING_SIZE]; + uint8_t *new_ks = KS_GET_KEYSTRING (new_ks0); + const uint8_t *newpw; + int newpw_len; + int r; + uint8_t *salt = KS_GET_SALT (new_ks0); + + DEBUG_INFO ("Resetting Code!\r\n"); + + newpw_len = len; + newpw = data; + new_ks0[0] = newpw_len; + random_get_salt (salt); + s2k (salt, SALT_SIZE, newpw, newpw_len, new_ks); + r = gpg_change_keystring (admin_authorized, old_ks, BY_RESETCODE, new_ks); + if (r <= -2) + { + DEBUG_INFO ("memory error.\r\n"); + return 0; + } + else if (r < 0) + { + DEBUG_INFO ("security error.\r\n"); + return 0; + } + else if (r == 0) + { + DEBUG_INFO ("error (no prvkey).\r\n"); + return 0; + } + else + { + DEBUG_INFO ("done.\r\n"); + gpg_do_write_simple (NR_DO_KEYSTRING_RC, new_ks0, KS_META_SIZE); + } + + gpg_pw_reset_err_counter (PW_ERR_RC); + return 1; +} + +static void +encrypt (const uint8_t *key, const uint8_t *iv, uint8_t *data, int len) +{ + aes_context aes; + uint8_t iv0[INITIAL_VECTOR_SIZE]; + size_t iv_offset; + + DEBUG_INFO ("ENC\r\n"); + DEBUG_BINARY (data, len); + + aes_setkey_enc (&aes, key, 128); + memcpy (iv0, iv, INITIAL_VECTOR_SIZE); + iv_offset = 0; + aes_crypt_cfb128 (&aes, AES_ENCRYPT, len, &iv_offset, iv0, data, data); +} + +/* For three keys: Signing, Decryption, and Authentication */ +struct key_data kd[3]; + +static void +decrypt (const uint8_t *key, const uint8_t *iv, uint8_t *data, int len) +{ + aes_context aes; + uint8_t iv0[INITIAL_VECTOR_SIZE]; + size_t iv_offset; + + aes_setkey_enc (&aes, key, 128); /* This is setkey_enc, because of CFB. */ + memcpy (iv0, iv, INITIAL_VECTOR_SIZE); + iv_offset = 0; + aes_crypt_cfb128 (&aes, AES_DECRYPT, len, &iv_offset, iv0, data, data); + + DEBUG_INFO ("DEC\r\n"); + DEBUG_BINARY (data, len); +} + +static void +encrypt_dek (const uint8_t *key_string, uint8_t *dek) +{ + aes_context aes; + + aes_setkey_enc (&aes, key_string, 128); + aes_crypt_ecb (&aes, AES_ENCRYPT, dek, dek); +} + +static void +decrypt_dek (const uint8_t *key_string, uint8_t *dek) +{ + aes_context aes; + + aes_setkey_dec (&aes, key_string, 128); + aes_crypt_ecb (&aes, AES_DECRYPT, dek, dek); +} + +static uint8_t +get_do_ptr_nr_for_kk (enum kind_of_key kk) +{ + switch (kk) + { + case GPG_KEY_FOR_SIGNING: + return NR_DO_PRVKEY_SIG; + case GPG_KEY_FOR_DECRYPTION: + return NR_DO_PRVKEY_DEC; + case GPG_KEY_FOR_AUTHENTICATION: + return NR_DO_PRVKEY_AUT; + } + return NR_DO_PRVKEY_SIG; +} + +void +gpg_do_clear_prvkey (enum kind_of_key kk) +{ + memset (kd[kk].data, 0, MAX_PRVKEY_LEN); +} + + +#define CHECKSUM_ADDR(kdi,prvkey_len) \ + (&(kdi).data[prvkey_len / sizeof (uint32_t)]) +#define kdi_len(prvkey_len) (prvkey_len+DATA_ENCRYPTION_KEY_SIZE) +struct key_data_internal { + uint32_t data[(MAX_PRVKEY_LEN+DATA_ENCRYPTION_KEY_SIZE) / sizeof (uint32_t)]; + /* + * Secret key data. + * RSA: p and q, ECDSA/ECDH: d, EdDSA: a+seed + */ + /* Checksum */ +}; + +#define CKDC_CALC 0 +#define CKDC_CHECK 1 +static int +compute_key_data_checksum (struct key_data_internal *kdi, int prvkey_len, + int check_or_calc) +{ + unsigned int i; + uint32_t d[4] = { 0, 0, 0, 0 }; + uint32_t *checksum = CHECKSUM_ADDR (*kdi, prvkey_len); + + for (i = 0; i < prvkey_len / sizeof (uint32_t); i++) + d[i&3] ^= kdi->data[i]; + + if (check_or_calc == CKDC_CALC) /* store */ + { + memcpy (checksum, d, DATA_ENCRYPTION_KEY_SIZE); + return 0; + } + else /* check */ + return memcmp (checksum, d, DATA_ENCRYPTION_KEY_SIZE) == 0; +} + +/* + * Return 1 on success, + * 0 if none, + * -1 on error, + */ +int +gpg_do_load_prvkey (enum kind_of_key kk, int who, const uint8_t *keystring) +{ + uint8_t nr = get_do_ptr_nr_for_kk (kk); + int prvkey_len = gpg_get_algo_attr_key_size (kk, GPG_KEY_PRIVATE); + const uint8_t *do_data = do_ptr[nr]; + const uint8_t *key_addr; + uint8_t dek[DATA_ENCRYPTION_KEY_SIZE]; + const uint8_t *iv; + struct key_data_internal kdi; + + DEBUG_INFO ("Loading private key: "); + DEBUG_BYTE (kk); + + if (do_data == NULL) + return 0; + + key_addr = kd[kk].pubkey - prvkey_len; + memcpy (kdi.data, key_addr, prvkey_len); + iv = &do_data[1]; + memcpy (CHECKSUM_ADDR (kdi, prvkey_len), + iv + INITIAL_VECTOR_SIZE, DATA_ENCRYPTION_KEY_SIZE); + + memcpy (dek, iv + DATA_ENCRYPTION_KEY_SIZE*(who+1), DATA_ENCRYPTION_KEY_SIZE); + decrypt_dek (keystring, dek); + + decrypt (dek, iv, (uint8_t *)&kdi, kdi_len (prvkey_len)); + memset (dek, 0, DATA_ENCRYPTION_KEY_SIZE); + if (!compute_key_data_checksum (&kdi, prvkey_len, CKDC_CHECK)) + { + DEBUG_INFO ("gpg_do_load_prvkey failed.\r\n"); + return -1; + } + + memcpy (kd[kk].data, kdi.data, prvkey_len); + DEBUG_BINARY (kd[kk].data, prvkey_len); + return 1; +} + + +static int8_t num_prv_keys; + +static void +gpg_do_delete_prvkey (enum kind_of_key kk, int clean_page_full) +{ + uint8_t nr = get_do_ptr_nr_for_kk (kk); + const uint8_t *do_data = do_ptr[nr]; + uint8_t *key_addr; + int prvkey_len = gpg_get_algo_attr_key_size (kk, GPG_KEY_PRIVATE); + int key_size = gpg_get_algo_attr_key_size (kk, GPG_KEY_STORAGE); + + if (do_data == NULL) + { + if (clean_page_full) + flash_key_release_page (kk); + return; + } + + do_ptr[nr] = NULL; + flash_do_release (do_data); + key_addr = (uint8_t *)kd[kk].pubkey - prvkey_len; + kd[kk].pubkey = NULL; + if (clean_page_full) + flash_key_release_page (kk); + else + flash_key_release (key_addr, key_size); + + if (admin_authorized == BY_ADMIN && kk == GPG_KEY_FOR_SIGNING) + { /* Recover admin keystring DO. */ + const uint8_t *ks_pw3 = gpg_do_read_simple (NR_DO_KEYSTRING_PW3); + + if (ks_pw3 != NULL) + { + uint8_t ks0[KEYSTRING_SIZE]; + + ks0[0] = ks_pw3[0] | PW_LEN_KEYSTRING_BIT; + memcpy (KS_GET_SALT (ks0), KS_GET_SALT (ks_pw3), SALT_SIZE); + memcpy (KS_GET_KEYSTRING (ks0), keystring_md_pw3, KEYSTRING_MD_SIZE); + gpg_do_write_simple (NR_DO_KEYSTRING_PW3, ks0, KEYSTRING_SIZE); + } + } + + if (--num_prv_keys == 0) + { + /* 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 (); + } +} + +void +gpg_do_terminate (void) +{ + int i; + + for (i = 0; i < 3; i++) + kd[i].pubkey = NULL; + + for (i = 0; i < NR_DO__LAST__; i++) + do_ptr[i] = NULL; + + num_prv_keys = 0; + data_objects_number_of_bytes = 0; + digital_signature_counter = 0; + + pw1_lifetime_p = NULL; + pw_err_counter_p[PW_ERR_PW1] = NULL; + pw_err_counter_p[PW_ERR_RC] = NULL; + pw_err_counter_p[PW_ERR_PW3] = NULL; + algo_attr_sig_p = algo_attr_dec_p = algo_attr_aut_p = NULL; +} + +static int +gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, + int prvkey_len, const uint8_t *keystring_admin, + const uint8_t *pubkey) +{ + uint8_t nr = get_do_ptr_nr_for_kk (kk); + int attr = gpg_get_algo_attr (kk);; + const uint8_t *p; + int r; + struct prvkey_data prv; + struct prvkey_data *pd = &prv; + uint8_t *key_addr; + const uint8_t *dek, *iv; + struct key_data_internal kdi; + int pubkey_len; + uint8_t ks[KEYSTRING_MD_SIZE]; + enum kind_of_key kk0; + + DEBUG_INFO ("Key import\r\n"); + DEBUG_SHORT (prvkey_len); + + /* Delete it first, if any. */ + gpg_do_delete_prvkey (kk, CLEAN_SINGLE); + + if (attr == ALGO_NISTP256R1 || attr == ALGO_SECP256K1) + { + pubkey_len = prvkey_len * 2; + if (prvkey_len != 32) + return -1; + } + else if (attr == ALGO_ED25519) + { + pubkey_len = prvkey_len / 2; + if (prvkey_len != 64) + return -1; + } + else if (attr == ALGO_CURVE25519) + { + pubkey_len = prvkey_len; + if (prvkey_len != 32) + return -1; + } + else /* RSA */ + { + int key_size = gpg_get_algo_attr_key_size (kk, GPG_KEY_STORAGE); + + pubkey_len = prvkey_len; + if (prvkey_len + pubkey_len != key_size) + return -1; + } + + DEBUG_INFO ("Getting keystore address...\r\n"); + key_addr = flash_key_alloc (kk); + if (key_addr == NULL) + return -1; + + kd[kk].pubkey = key_addr + prvkey_len; + + num_prv_keys++; + + DEBUG_INFO ("key_addr: "); + DEBUG_WORD ((uint32_t)key_addr); + + memcpy (kdi.data, key_data, prvkey_len); + memset ((uint8_t *)kdi.data + prvkey_len, 0, MAX_PRVKEY_LEN - prvkey_len); + + compute_key_data_checksum (&kdi, prvkey_len, CKDC_CALC); + + dek = random_bytes_get (); /* 32-byte random bytes */ + iv = dek + DATA_ENCRYPTION_KEY_SIZE; + memcpy (pd->dek_encrypted_1, dek, DATA_ENCRYPTION_KEY_SIZE); + memcpy (pd->dek_encrypted_2, dek, DATA_ENCRYPTION_KEY_SIZE); + memcpy (pd->dek_encrypted_3, dek, DATA_ENCRYPTION_KEY_SIZE); + + s2k (NULL, 0, (const uint8_t *)OPENPGP_CARD_INITIAL_PW1, + strlen (OPENPGP_CARD_INITIAL_PW1), ks); + + /* Handle existing keys and keystring DOs. */ + gpg_do_write_simple (NR_DO_KEYSTRING_PW1, NULL, 0); + gpg_do_write_simple (NR_DO_KEYSTRING_RC, NULL, 0); + for (kk0 = 0; kk0 <= GPG_KEY_FOR_AUTHENTICATION; kk0++) + if (kk0 != kk) + { + gpg_do_chks_prvkey (kk0, admin_authorized, keystring_md_pw3, + BY_USER, ks); + gpg_do_chks_prvkey (kk0, BY_RESETCODE, NULL, 0, NULL); + } + + encrypt (dek, iv, (uint8_t *)&kdi, kdi_len (prvkey_len)); + + r = flash_key_write (key_addr, (const uint8_t *)kdi.data, prvkey_len, + pubkey, pubkey_len); + if (r < 0) + { + random_bytes_free (dek); + memset (pd, 0, sizeof (struct prvkey_data)); + return r; + } + + memcpy (pd->iv, iv, INITIAL_VECTOR_SIZE); + memcpy (pd->checksum_encrypted, CHECKSUM_ADDR (kdi, prvkey_len), + DATA_ENCRYPTION_KEY_SIZE); + + encrypt_dek (ks, pd->dek_encrypted_1); + + memset (pd->dek_encrypted_2, 0, DATA_ENCRYPTION_KEY_SIZE); + + if (keystring_admin) + encrypt_dek (keystring_admin, pd->dek_encrypted_3); + else + memset (pd->dek_encrypted_3, 0, DATA_ENCRYPTION_KEY_SIZE); + + p = flash_do_write (nr, (const uint8_t *)pd, sizeof (struct prvkey_data)); + do_ptr[nr] = p; + + random_bytes_free (dek); + memset (pd, 0, sizeof (struct prvkey_data)); + if (p == NULL) + return -1; + + if (keystring_admin && kk == GPG_KEY_FOR_SIGNING) + { + const uint8_t *ks_admin = gpg_do_read_simple (NR_DO_KEYSTRING_PW3); + uint8_t ks_info[KS_META_SIZE]; + + if (ks_admin != NULL && (ks_admin[0] & PW_LEN_KEYSTRING_BIT)) + { + ks_info[0] = ks_admin[0] & PW_LEN_MASK; + memcpy (KS_GET_SALT (ks_info), KS_GET_SALT (ks_admin), SALT_SIZE); + gpg_do_write_simple (NR_DO_KEYSTRING_PW3, ks_info, KS_META_SIZE); + } + else + { + DEBUG_INFO ("No admin keystring!\r\n"); + } + } + + return 0; +} + +int +gpg_do_chks_prvkey (enum kind_of_key kk, + int who_old, const uint8_t *old_ks, + int who_new, const uint8_t *new_ks) +{ + uint8_t nr = get_do_ptr_nr_for_kk (kk); + const uint8_t *do_data = do_ptr[nr]; + uint8_t dek[DATA_ENCRYPTION_KEY_SIZE]; + struct prvkey_data prv; + struct prvkey_data *pd = &prv; + uint8_t *dek_p; + int update_needed = 0; + int r = 1; /* Success */ + + if (do_data == NULL) + return 0; /* No private key */ + + memcpy (pd, &do_data[1], sizeof (struct prvkey_data)); + + dek_p = ((uint8_t *)pd) + INITIAL_VECTOR_SIZE + + DATA_ENCRYPTION_KEY_SIZE * who_old; + memcpy (dek, dek_p, DATA_ENCRYPTION_KEY_SIZE); + if (who_new == 0) /* Remove */ + { + int i; + + for (i = 0; i < DATA_ENCRYPTION_KEY_SIZE; i++) + if (dek_p[i] != 0) + { + update_needed = 1; + dek_p[i] = 0; + } + } + else + { + decrypt_dek (old_ks, dek); + encrypt_dek (new_ks, dek); + dek_p += DATA_ENCRYPTION_KEY_SIZE * (who_new - who_old); + if (memcmp (dek_p, dek, DATA_ENCRYPTION_KEY_SIZE) != 0) + { + memcpy (dek_p, dek, DATA_ENCRYPTION_KEY_SIZE); + update_needed = 1; + } + } + + if (update_needed) + { + const uint8_t *p; + + flash_do_release (do_data); + do_ptr[nr] = NULL; + p = flash_do_write (nr, (const uint8_t *)pd, sizeof (struct prvkey_data)); + do_ptr[nr] = p; + if (p == NULL) + r = -1; + } + + memset (pd, 0, sizeof (struct prvkey_data)); + + return r; +} + + +static enum kind_of_key +kkb_to_kk (uint8_t kk_byte) +{ + enum kind_of_key kk; + + if (kk_byte == 0xb6) + kk = GPG_KEY_FOR_SIGNING; + else if (kk_byte == 0xb8) + kk = GPG_KEY_FOR_DECRYPTION; + else /* 0xa4 */ + kk = GPG_KEY_FOR_AUTHENTICATION; + return kk; +} + +/* + * RSA-2048: + * 4d, xx, xx, xx: Extended Header List + * b6 00 (SIG) / b8 00 (DEC) / a4 00 (AUT) + * 7f48, xx: cardholder private key template + * 91 L: 91=tag of E, L: length of E + * 92 Lh

Ll

: 92=tag of P, L

: length of P + * 93 Lh Ll: 93=tag of Q, L: length of Q + * 5f48, xx xx xx: cardholder private key + * , , + * + * RSA-4096: + * 4d, 82, 02, 18: Extended Header List + * b6 00 (SIG) / b8 00 (DEC) / a4 00 (AUT) + * 7f48, 0a: cardholder private key template + * 91 L: 91=tag of E, L: length of E + * 92 82 Lh

Ll

: 92=tag of P, L

: length of P + * 93 82 Lh Ll: 93=tag of Q, L: length of Q + * 5f48, 82 02 04: cardholder private key + * , , + * + * ECDSA / ECDH / EdDSA: + * 4d, 2a: Extended Header List + * b6 00 (SIG) / b8 00 (DEC) / a4 00 (AUT) + * 7f48, 02: cardholder private key template + * 9x LEN: 9x=tag of private key d, LEN=length of d + * 5f48, 20: cardholder private key + * + */ +static int +proc_key_import (const uint8_t *data, int len) +{ + int r = -1; + enum kind_of_key kk; + const uint8_t *keystring_admin; + int attr; + const uint8_t *p = data; + uint8_t pubkey[512]; + + if (admin_authorized == BY_ADMIN) + keystring_admin = keystring_md_pw3; + else + keystring_admin = NULL; + + DEBUG_BINARY (data, len); + + if (*p++ != 0x4d) + return 0; + + /* length field */ + if (*p == 0x82) + p += 3; + else if (*p == 0x81) + p += 2; + else + p += 1; + + kk = kkb_to_kk (*p); + if (kk == GPG_KEY_FOR_SIGNING) + { + ac_reset_pso_cds (); + gpg_reset_digital_signature_counter (); + } + else + ac_reset_other (); + + attr = gpg_get_algo_attr (kk); + + if ((len <= 12 && (attr == ALGO_NISTP256R1 || attr == ALGO_SECP256K1 + || attr == ALGO_ED25519 || attr == ALGO_CURVE25519)) + || (len <= 22 && attr == ALGO_RSA2K) || (len <= 24 && attr == ALGO_RSA4K)) + { /* Deletion of the key */ + gpg_do_delete_prvkey (kk, CLEAN_SINGLE); + return 1; + } + + if (attr == ALGO_RSA2K) + { + /* It should starts with 00 01 00 01 (E), skiping E (4-byte) */ + r = modulus_calc (&data[26], len - 26, pubkey); + if (r >= 0) + r = gpg_do_write_prvkey (kk, &data[26], len - 26, keystring_admin, + pubkey); + } + else if (attr == ALGO_RSA4K) + { + /* It should starts with 00 01 00 01 (E), skiping E (4-byte) */ + r = modulus_calc (&data[28], len - 28, pubkey); + if (r >= 0) + r = gpg_do_write_prvkey (kk, &data[28], len - 28, keystring_admin, + pubkey); + } + else if (attr == ALGO_NISTP256R1) + { + r = ecc_compute_public_p256r1 (&data[12], pubkey); + if (r >= 0) + r = gpg_do_write_prvkey (kk, &data[12], len - 12, keystring_admin, + pubkey); + } + else if (attr == ALGO_SECP256K1) + { + r = ecc_compute_public_p256k1 (&data[12], pubkey); + if (r >= 0) + r = gpg_do_write_prvkey (kk, &data[12], len - 12, keystring_admin, + pubkey); + } + else if (attr == ALGO_ED25519) + { + uint8_t hash[64]; + + if (len - 12 != 32) + return 0; /* Error. */ + + sha512 (&data[12], 32, hash); + hash[0] &= 248; + hash[31] &= 127; + hash[31] |= 64; + eddsa_compute_public_25519 (hash, pubkey); + r = gpg_do_write_prvkey (kk, hash, 64, keystring_admin, pubkey); + } + else if (attr == ALGO_CURVE25519) + { + uint8_t priv[32]; + int i; + + if (len - 12 != 32) + return 0; /* Error. */ + + for (i = 0; i < 32; i++) + priv[31-i] = data[12+i]; + ecdh_compute_public_25519 (priv, pubkey); + r = gpg_do_write_prvkey (kk, priv, 32, keystring_admin, pubkey); + } + + if (r < 0) + return 0; + else + return 1; +} + +static const uint16_t cmp_ch_data[] = { + 3, + GPG_DO_NAME, + GPG_DO_LANGUAGE, + GPG_DO_SEX, +}; + +static const uint16_t cmp_app_data[] = { + 3, + GPG_DO_AID, + GPG_DO_HIST_BYTES, + GPG_DO_DISCRETIONARY, +}; + +static const uint16_t cmp_discretionary[] = { + 8, + GPG_DO_EXTCAP, + GPG_DO_ALG_SIG, GPG_DO_ALG_DEC, GPG_DO_ALG_AUT, + GPG_DO_PW_STATUS, + GPG_DO_FP_ALL, GPG_DO_CAFP_ALL, GPG_DO_KGTIME_ALL +}; + +static const uint16_t cmp_ss_temp[] = { 1, GPG_DO_DS_COUNT }; + +static const struct do_table_entry +gpg_do_table[] = { + /* Variables: Fixed size */ + { GPG_DO_SEX, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[0] }, + { GPG_DO_FP_SIG, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[1] }, + { GPG_DO_FP_DEC, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[2] }, + { GPG_DO_FP_AUT, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[3] }, + { GPG_DO_CAFP_1, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[4] }, + { GPG_DO_CAFP_2, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[5] }, + { GPG_DO_CAFP_3, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[6] }, + { GPG_DO_KGTIME_SIG, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[7] }, + { GPG_DO_KGTIME_DEC, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[8] }, + { GPG_DO_KGTIME_AUT, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[9] }, + /* Variables: Variable size */ + { GPG_DO_LOGIN_DATA, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[10] }, + { GPG_DO_URL, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[11] }, + { GPG_DO_NAME, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[12] }, + { GPG_DO_LANGUAGE, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[13] }, + /* Pseudo DO READ: calculated */ + { GPG_DO_FP_ALL, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_fp_all }, + { GPG_DO_CAFP_ALL, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_cafp_all }, + { GPG_DO_KGTIME_ALL, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_kgtime_all }, + /* Pseudo DO READ: calculated, not changeable by user */ + { GPG_DO_DS_COUNT, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_ds_count }, + { GPG_DO_AID, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_openpgpcard_aid }, + /* Pseudo DO READ/WRITE: calculated */ + { GPG_DO_PW_STATUS, DO_PROC_READWRITE, AC_ALWAYS, AC_ADMIN_AUTHORIZED, + rw_pw_status }, + { GPG_DO_ALG_SIG, DO_PROC_READWRITE, AC_ALWAYS, AC_ADMIN_AUTHORIZED, + rw_algorithm_attr }, + { GPG_DO_ALG_DEC, DO_PROC_READWRITE, AC_ALWAYS, AC_ADMIN_AUTHORIZED, + rw_algorithm_attr }, + { GPG_DO_ALG_AUT, DO_PROC_READWRITE, AC_ALWAYS, AC_ADMIN_AUTHORIZED, + rw_algorithm_attr }, + /* Fixed data */ + { GPG_DO_HIST_BYTES, DO_FIXED, AC_ALWAYS, AC_NEVER, historical_bytes }, + { GPG_DO_EXTCAP, DO_FIXED, AC_ALWAYS, AC_NEVER, extended_capabilities }, + /* Compound data: Read access only */ + { GPG_DO_CH_DATA, DO_CMP_READ, AC_ALWAYS, AC_NEVER, cmp_ch_data }, + { GPG_DO_APP_DATA, DO_CMP_READ, AC_ALWAYS, AC_NEVER, cmp_app_data }, + { GPG_DO_DISCRETIONARY, DO_CMP_READ, AC_ALWAYS, AC_NEVER, cmp_discretionary }, + { GPG_DO_SS_TEMP, DO_CMP_READ, AC_ALWAYS, AC_NEVER, cmp_ss_temp }, + /* Simple data: write access only */ + { GPG_DO_RESETTING_CODE, DO_PROC_WRITE, AC_NEVER, AC_ADMIN_AUTHORIZED, + proc_resetting_code }, + /* Compound data: Write access only */ + { GPG_DO_KEY_IMPORT, DO_PROC_WRITE, AC_NEVER, AC_ADMIN_AUTHORIZED, + proc_key_import }, +#if 0 + /* Card holder certificate is handled in special way, as its size is big */ + { GPG_DO_CH_CERTIFICATE, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, NULL }, +#endif +}; + +#define NUM_DO_ENTRIES (int)(sizeof (gpg_do_table) \ + / sizeof (struct do_table_entry)) + +/* + * Reading data from Flash ROM, initialize DO_PTR, PW_ERR_COUNTERS, etc. + */ +void +gpg_data_scan (const uint8_t *do_start, const uint8_t *do_end) +{ + const uint8_t *p; + int i; + const uint8_t *dsc_h14_p, *dsc_l10_p; + int dsc_h14, dsc_l10; + + dsc_h14_p = dsc_l10_p = NULL; + pw1_lifetime_p = NULL; + pw_err_counter_p[PW_ERR_PW1] = NULL; + pw_err_counter_p[PW_ERR_RC] = NULL; + pw_err_counter_p[PW_ERR_PW3] = NULL; + algo_attr_sig_p = algo_attr_dec_p = algo_attr_aut_p = NULL; + digital_signature_counter = 0; + + /* When the card is terminated no data objects are valid. */ + if (do_start == NULL) + return; + + /* Traverse DO, counters, etc. in DATA pool */ + p = do_start; + while (p < do_end && *p != NR_EMPTY) + { + uint8_t nr = *p++; + uint8_t second_byte = *p; + + if (nr == 0x00 && second_byte == 0x00) + p++; /* Skip released word */ + else + { + if (nr < 0x80) + { + /* It's Data Object */ + if (nr < NR_DO__LAST__) + do_ptr[nr] = p; + + p += second_byte + 1; /* second_byte has length */ + + if (((uintptr_t)p & 1)) + p++; + } + else if (nr >= 0x80 && nr <= 0xbf) + /* Encoded data of Digital Signature Counter: upper 14-bit */ + { + dsc_h14_p = p - 1; + p++; + } + else if (nr >= 0xc0 && nr <= 0xc3) + /* Encoded data of Digital Signature Counter: lower 10-bit */ + { + dsc_l10_p = p - 1; + p++; + } + else + switch (nr) + { + case NR_BOOL_PW1_LIFETIME: + pw1_lifetime_p = p - 1; + p++; + break; + case NR_KEY_ALGO_ATTR_SIG: + algo_attr_sig_p = p - 1; + p++; + break; + case NR_KEY_ALGO_ATTR_DEC: + algo_attr_dec_p = p - 1; + p++; + break; + case NR_KEY_ALGO_ATTR_AUT: + algo_attr_aut_p = p - 1; + p++; + break; + case NR_COUNTER_123: + p++; + if (second_byte <= PW_ERR_PW3) + pw_err_counter_p[second_byte] = p; + p += 2; + break; + default: + /* Something going wrong. ignore this word. */ + p++; + break; + } + } + } + + flash_set_data_pool_last (p); + + num_prv_keys = 0; + if (do_ptr[NR_DO_PRVKEY_SIG] != NULL) + num_prv_keys++; + if (do_ptr[NR_DO_PRVKEY_DEC] != NULL) + num_prv_keys++; + if (do_ptr[NR_DO_PRVKEY_AUT] != NULL) + num_prv_keys++; + + data_objects_number_of_bytes = 0; + for (i = 0; i < NR_DO__LAST__; i++) + if (do_ptr[i] != NULL) + data_objects_number_of_bytes += *do_ptr[i]; + + if (dsc_l10_p == NULL) + dsc_l10 = 0; + else + dsc_l10 = ((*dsc_l10_p - 0xc0) << 8) | *(dsc_l10_p + 1); + + if (dsc_h14_p == NULL) + dsc_h14 = 0; + else + { + dsc_h14 = ((*dsc_h14_p - 0x80) << 8) | *(dsc_h14_p + 1); + if (dsc_l10_p == NULL) + DEBUG_INFO ("something wrong in DSC\r\n"); /* weird??? */ + else if (dsc_l10_p < dsc_h14_p) + /* Possibly, power off during writing dsc_l10 */ + dsc_l10 = 0; + } + + digital_signature_counter = (dsc_h14 << 10) | dsc_l10; +} + +/* + * Write all data to newly allocated Flash ROM page (from P_START), + * updating PW1_LIFETIME_P, PW_ERR_COUNTER_P, and DO_PTR. + * Called by flash_copying_gc. + */ +void +gpg_data_copy (const uint8_t *p_start) +{ + const uint8_t *p; + int i; + int v; + + p = gpg_write_digital_signature_counter (p_start, digital_signature_counter); + + if (pw1_lifetime_p != NULL) + { + flash_bool_write_internal (p, NR_BOOL_PW1_LIFETIME); + pw1_lifetime_p = p; + p += 2; + } + + if (algo_attr_sig_p != NULL) + { + flash_enum_write_internal (p, NR_KEY_ALGO_ATTR_SIG, algo_attr_sig_p[1]); + algo_attr_sig_p = p; + p += 2; + } + + if (algo_attr_dec_p != NULL) + { + flash_enum_write_internal (p, NR_KEY_ALGO_ATTR_DEC, algo_attr_dec_p[1]); + algo_attr_dec_p = p; + p += 2; + } + + if (algo_attr_aut_p != NULL) + { + flash_enum_write_internal (p, NR_KEY_ALGO_ATTR_AUT, algo_attr_aut_p[1]); + algo_attr_aut_p = p; + p += 2; + } + + for (i = 0; i < 3; i++) + if ((v = flash_cnt123_get_value (pw_err_counter_p[i])) != 0) + { + flash_cnt123_write_internal (p, i, v); + pw_err_counter_p[i] = p + 2; + p += 4; + } + + data_objects_number_of_bytes = 0; + for (i = 0; i < NR_DO__LAST__; i++) + if (do_ptr[i] != NULL) + { + const uint8_t *do_data = do_ptr[i]; + int len = do_data[0]; + + flash_do_write_internal (p, i, &do_data[1], len); + do_ptr[i] = p + 1; + p += 2 + ((len + 1) & ~1); + data_objects_number_of_bytes += len; + } + + flash_set_data_pool_last (p); +} + +static const struct do_table_entry * +get_do_entry (uint16_t tag) +{ + int i; + + for (i = 0; i < NUM_DO_ENTRIES; i++) + if (gpg_do_table[i].tag == tag) + return &gpg_do_table[i]; + + return NULL; +} + +static void +copy_do_1 (uint16_t tag, const uint8_t *do_data, int with_tag) +{ + int len; + + if (with_tag) + { + copy_tag (tag); + + if (do_data[0] >= 128) + *res_p++ = 0x81; + + len = do_data[0] + 1; + } + else + { + len = do_data[0]; + do_data++; + } + + memcpy (res_p, do_data, len); + res_p += len; +} + +static int +copy_do (const struct do_table_entry *do_p, int with_tag) +{ + if (do_p == NULL) + return 0; + + if (!ac_check_status (do_p->ac_read)) + return -1; + + switch (do_p->do_type) + { + case DO_FIXED: + { + const uint8_t *do_data = (const uint8_t *)do_p->obj; + if (do_data == NULL) + return 0; + else + copy_do_1 (do_p->tag, do_data, with_tag); + break; + } + case DO_VAR: + { + const uint8_t *do_data = *(const uint8_t **)do_p->obj; + if (do_data == NULL) + return 0; + else + copy_do_1 (do_p->tag, do_data, with_tag); + break; + } + case DO_CMP_READ: + { + int i; + const uint16_t *cmp_data = (const uint16_t *)do_p->obj; + int num_components = cmp_data[0]; + uint8_t *len_p = NULL; + + if (with_tag) + { + copy_tag (do_p->tag); + *res_p++ = 0x81; /* Assume it's less than 256 */ + len_p = res_p; + *res_p++ = 0; /* for now */ + } + + for (i = 0; i < num_components; i++) + { + uint16_t tag0; + const struct do_table_entry *do0_p; + + tag0 = cmp_data[i+1]; + do0_p = get_do_entry (tag0); + if (copy_do (do0_p, 1) < 0) + return -1; + } + + if (len_p) + *len_p = res_p - len_p - 1; + break; + } + case DO_PROC_READ: + { + int (*do_func)(uint16_t, int) = (int (*)(uint16_t, int))do_p->obj; + + return do_func (do_p->tag, with_tag); + } + case DO_PROC_READWRITE: + { + 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); + } + case DO_PROC_WRITE: + return -1; + } + + return 1; +} + +/* + * Process GET_DATA request on Data Object specified by TAG + * Call write_res_adpu to fill data returned + */ +void +gpg_do_get_data (uint16_t tag, int with_tag) +{ +#if defined(CERTDO_SUPPORT) + if (tag == GPG_DO_CH_CERTIFICATE) + { + apdu.res_apdu_data = &ch_certificate_start; + apdu.res_apdu_data_len = ((apdu.res_apdu_data[2] << 8) | apdu.res_apdu_data[3]); + if (apdu.res_apdu_data_len == 0xffff) + { + apdu.res_apdu_data_len = 0; + GPG_NO_RECORD (); + } + else + /* Add length of (tag+len) */ + apdu.res_apdu_data_len += 4; + } + else +#endif + { + const struct do_table_entry *do_p = get_do_entry (tag); + + res_p = res_APDU; + + DEBUG_INFO (" "); + DEBUG_SHORT (tag); + + if (do_p) + { + if (copy_do (do_p, with_tag) < 0) + /* Overwriting partially written result */ + GPG_SECURITY_FAILURE (); + else + { + res_APDU_size = res_p - res_APDU; + GPG_SUCCESS (); + } + } + else + GPG_NO_RECORD (); + } +} + +void +gpg_do_put_data (uint16_t tag, const uint8_t *data, int len) +{ + const struct do_table_entry *do_p = get_do_entry (tag); + + DEBUG_INFO (" "); + DEBUG_SHORT (tag); + + if (do_p) + { + if (!ac_check_status (do_p->ac_write)) + { + GPG_SECURITY_FAILURE (); + return; + } + + switch (do_p->do_type) + { + case DO_FIXED: + case DO_CMP_READ: + case DO_PROC_READ: + GPG_SECURITY_FAILURE (); + break; + case DO_VAR: + { + const uint8_t **do_data_p = (const uint8_t **)do_p->obj; + + if (*do_data_p) + flash_do_release (*do_data_p); + + if (len == 0) + { + /* make DO empty */ + *do_data_p = NULL; + GPG_SUCCESS (); + } + else if (len > 255) + GPG_MEMORY_FAILURE (); + else + { + int nr = do_tag_to_nr (tag); + + if (nr < 0) + GPG_MEMORY_FAILURE (); + else + { + *do_data_p = NULL; + *do_data_p = flash_do_write (nr, data, len); + if (*do_data_p) + GPG_SUCCESS (); + else + GPG_MEMORY_FAILURE (); + } + } + break; + } + case DO_PROC_READWRITE: + { + int (*rw_func)(uint16_t, int, const uint8_t *, int, int) + = (int (*)(uint16_t, int, const uint8_t *, int, int))do_p->obj; + + if (rw_func (tag, 0, data, len, 1)) + GPG_SUCCESS (); + else + GPG_ERROR (); + break; + } + case DO_PROC_WRITE: + { + int (*proc_func)(const uint8_t *, int) + = (int (*)(const uint8_t *, int))do_p->obj; + + if (proc_func (data, len)) + GPG_SUCCESS (); + else + GPG_ERROR (); + break; + } + } + } + else + GPG_NO_RECORD (); +} + +void +gpg_do_public_key (uint8_t kk_byte) +{ + enum kind_of_key kk = kkb_to_kk (kk_byte); + int attr = gpg_get_algo_attr (kk); + int pubkey_len = gpg_get_algo_attr_key_size (kk, GPG_KEY_PUBLIC); + const uint8_t *pubkey = kd[kk].pubkey; + + DEBUG_INFO ("Public key\r\n"); + DEBUG_BYTE (kk_byte); + + if (pubkey == NULL) + { + DEBUG_INFO ("none.\r\n"); + GPG_NO_RECORD (); + return; + } + + res_p = res_APDU; + + /* TAG */ + *res_p++ = 0x7f; *res_p++ = 0x49; + + if (attr == ALGO_NISTP256R1 || attr == ALGO_SECP256K1) + { /* ECDSA or ECDH */ + /* LEN */ + *res_p++ = 2 + 1 + 64; + { + /*TAG*/ /* LEN = 1+64 */ + *res_p++ = 0x86; *res_p++ = 0x41; + *res_p++ = 0x04; /* No compression of EC point. */ + /* 64-byte binary (big endian): X || Y */ + memcpy (res_p, pubkey, 64); + res_p += 64; + } + } + else if (attr == ALGO_ED25519 || attr == ALGO_CURVE25519) + { /* EdDSA or ECDH on curve25519 */ + /* LEN */ + *res_p++ = 2 + 32; + { + /*TAG*/ /* LEN = 32 */ + *res_p++ = 0x86; *res_p++ = 0x20; + /* 32-byte binary (little endian): Y with parity or X*/ + memcpy (res_p, pubkey, 32); + res_p += 32; + } + } + else + { /* RSA */ + /* LEN = 9+256or512 */ + *res_p++ = 0x82; *res_p++ = pubkey_len > 256? 0x02: 0x01; *res_p++ = 0x09; + + { + /*TAG*/ /* LEN = 256or512 */ + *res_p++ = 0x81; + *res_p++ = 0x82; *res_p++ = pubkey_len > 256? 0x02: 0x01;*res_p++ = 0x00; + /* PUBKEY_LEN-byte binary (big endian) */ + memcpy (res_p, pubkey, pubkey_len); + res_p += pubkey_len; + } + { + /*TAG*/ /* LEN= 3 */ + *res_p++ = 0x82; *res_p++ = 3; + /* 3-byte E=0x10001 (big endian) */ + *res_p++ = 0x01; *res_p++ = 0x00; *res_p++ = 0x01; + } + } + + /* Success */ + res_APDU_size = res_p - res_APDU; + GPG_SUCCESS (); + + DEBUG_INFO ("done.\r\n"); + return; +} + +const uint8_t * +gpg_do_read_simple (uint8_t nr) +{ + const uint8_t *do_data; + + do_data = do_ptr[nr]; + if (do_data == NULL) + return NULL; + + return do_data+1; +} + +void +gpg_do_write_simple (uint8_t nr, const uint8_t *data, int size) +{ + const uint8_t **do_data_p; + + do_data_p = (const uint8_t **)&do_ptr[nr]; + if (*do_data_p) + flash_do_release (*do_data_p); + + 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"); + } + else + *do_data_p = NULL; +} + +void +gpg_do_keygen (uint8_t *buf) +{ + uint8_t kk_byte = buf[0]; + enum kind_of_key kk = kkb_to_kk (kk_byte); + int attr = gpg_get_algo_attr (kk);; + int prvkey_len = gpg_get_algo_attr_key_size (kk, GPG_KEY_PRIVATE); + const uint8_t *prv; + const uint8_t *rnd; + int r = 0; +#define p_q buf +#define d buf +#define d1 (&buf[64]) +#define pubkey (&buf[256]) + + DEBUG_INFO ("Keygen\r\n"); + DEBUG_BYTE (kk_byte); + + if (attr == ALGO_RSA2K || attr == ALGO_RSA4K) + { + if (rsa_genkey (prvkey_len, pubkey, p_q) < 0) + { + GPG_MEMORY_FAILURE (); + return; + } + + prv = p_q; + } + else if (attr == ALGO_NISTP256R1 || attr == ALGO_SECP256K1) + { + const uint8_t *p; + int i, r; + + rnd = NULL; + do + { + if (rnd) + random_bytes_free (rnd); + rnd = random_bytes_get (); + if (attr == ALGO_NISTP256R1) + r = ecc_check_secret_p256r1 (rnd, d1); + else + r = ecc_check_secret_p256k1 (rnd, d1); + } + while (r == 0); + + /* Convert it to big endian */ + + if (r < 0) + p = (const uint8_t *)d1; + else + p = rnd; + for (i = 0; i < 32; i++) + d[32 - i - 1] = p[i]; + + random_bytes_free (rnd); + + prv = d; + if (attr == ALGO_SECP256K1) + r = ecc_compute_public_p256k1 (prv, pubkey); + else if (attr == ALGO_NISTP256R1) + r = ecc_compute_public_p256r1 (prv, pubkey); + } + else if (attr == ALGO_ED25519) + { + rnd = random_bytes_get (); + sha512 (rnd, 32, d); + random_bytes_free (rnd); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + prv = d; + eddsa_compute_public_25519 (d, pubkey); + } + else if (attr == ALGO_CURVE25519) + { + rnd = random_bytes_get (); + memcpy (d, rnd, 32); + random_bytes_free (rnd); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + prv = d; + ecdh_compute_public_25519 (prv, pubkey); + } + else + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + if (r >= 0) + { + const uint8_t *keystring_admin; + + if (admin_authorized == BY_ADMIN) + keystring_admin = keystring_md_pw3; + else + keystring_admin = NULL; + + r = gpg_do_write_prvkey (kk, prv, prvkey_len, keystring_admin, pubkey); + } + + /* Clear private key data in the buffer. */ + memset (buf, 0, 256); + + if (r < 0) + { + GPG_ERROR (); + return; + } + + DEBUG_INFO ("Calling gpg_do_public_key...\r\n"); + + if (kk == GPG_KEY_FOR_SIGNING) + { + const uint8_t *pw = (const uint8_t *)OPENPGP_CARD_INITIAL_PW1; + uint8_t keystring[KEYSTRING_MD_SIZE]; + + /* GnuPG expects it's ready for signing. */ + /* Don't call ac_reset_pso_cds here, but load the private key */ + + gpg_reset_digital_signature_counter (); + s2k (NULL, 0, pw, strlen (OPENPGP_CARD_INITIAL_PW1), keystring); + gpg_do_load_prvkey (GPG_KEY_FOR_SIGNING, BY_USER, keystring); + } + else + ac_reset_other (); + + gpg_do_public_key (kk_byte); +} diff --git a/gnuk/src/openpgp.c b/gnuk/src/openpgp.c new file mode 100644 index 0000000000..f0d8d7b3f9 --- /dev/null +++ b/gnuk/src/openpgp.c @@ -0,0 +1,1573 @@ +/* + * openpgp.c -- OpenPGP card protocol support + * + * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 + * 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 +#include +#include +#include + +#include "config.h" + +#include "gnuk.h" +#include "sys.h" +#include "status-code.h" +#include "sha256.h" +#include "random.h" + +static struct eventflag *openpgp_comm; + +#define ADMIN_PASSWD_MINLEN 8 + +#define CLS(a) a.cmd_apdu_head[0] +#define INS(a) a.cmd_apdu_head[1] +#define P1(a) a.cmd_apdu_head[2] +#define P2(a) a.cmd_apdu_head[3] + +#define INS_VERIFY 0x20 +#define INS_CHANGE_REFERENCE_DATA 0x24 +#define INS_PSO 0x2a +#define INS_RESET_RETRY_COUNTER 0x2c +#define INS_ACTIVATE_FILE 0x44 +#define INS_PGP_GENERATE_ASYMMETRIC_KEY_PAIR 0x47 +#define INS_EXTERNAL_AUTHENTICATE 0x82 +#define INS_GET_CHALLENGE 0x84 +#define INS_INTERNAL_AUTHENTICATE 0x88 +#define INS_SELECT_FILE 0xa4 +#define INS_READ_BINARY 0xb0 +#define INS_GET_DATA 0xca +#define INS_WRITE_BINARY 0xd0 +#define INS_UPDATE_BINARY 0xd6 +#define INS_PUT_DATA 0xda +#define INS_PUT_DATA_ODD 0xdb /* For key import */ +#define INS_TERMINATE_DF 0xe6 + +static const uint8_t *challenge; /* Random bytes */ + +static const uint8_t +select_file_TOP_result[] __attribute__ ((aligned (1))) = { + 0x00, 0x00, /* unused */ + 0x00, 0x00, /* number of bytes in this directory: to be filled */ + 0x3f, 0x00, /* field of selected file: MF, 3f00 */ + 0x38, /* it's DF */ + 0xff, /* unused */ + 0xff, 0x44, 0x44, /* access conditions */ + 0x01, /* status of the selected file (OK, unblocked) */ + 0x05, /* number of bytes of data follow */ + 0x03, /* Features: unused */ + 0x01, /* number of subdirectories (OpenPGP) */ + 0x01, /* number of elementary files (SerialNo) */ + 0x00, /* number of secret codes */ + 0x00, /* Unused */ + 0x00, 0x00 /* PIN status: OK, PIN blocked?: No */ +}; + +void +set_res_sw (uint8_t sw1, uint8_t sw2) +{ + apdu.sw = (sw1 << 8) | sw2; +} + +#define FILE_NONE 0 +#define FILE_DF_OPENPGP 1 +#define FILE_MF 2 +#define FILE_EF_DIR 3 +#define FILE_EF_SERIAL_NO 4 +#define FILE_EF_UPDATE_KEY_0 5 +#define FILE_EF_UPDATE_KEY_1 6 +#define FILE_EF_UPDATE_KEY_2 7 +#define FILE_EF_UPDATE_KEY_3 8 +#define FILE_EF_CH_CERTIFICATE 9 +#define FILE_CARD_TERMINATED 255 + +uint8_t file_selection; + +static void +gpg_init (void) +{ + const uint8_t *flash_do_start; + const uint8_t *flash_do_end; + + flash_do_storage_init (&flash_do_start, &flash_do_end); + + if (flash_do_start == NULL) + file_selection = FILE_CARD_TERMINATED; + else + file_selection = FILE_NONE; + + gpg_data_scan (flash_do_start, flash_do_end); + flash_key_storage_init (); +} + +static void +gpg_fini (void) +{ + ac_fini (); +} + +#if defined(PINPAD_SUPPORT) +/* + * Let user input PIN string. + * Return length of the string. + * The string itself is in PIN_INPUT_BUFFER. + */ +static int +get_pinpad_input (int msg_code) +{ + int r; + + led_blink (LED_START_COMMAND); + r = pinpad_getline (msg_code, 8000000); + led_blink (LED_FINISH_COMMAND); + return r; +} +#endif + +static void +cmd_verify (void) +{ + int len; + uint8_t p1 = P1 (apdu); + uint8_t p2 = P2 (apdu); + int r; + const uint8_t *pw; + + DEBUG_INFO (" - VERIFY\r\n"); + DEBUG_BYTE (p2); + + len = apdu.cmd_apdu_data_len; + pw = apdu.cmd_apdu_data; + + if (len == 0) + { + if (p1 == 0) + { /* This is to examine status. */ + if (p2 == 0x81) + r = ac_check_status (AC_PSO_CDS_AUTHORIZED); + else if (p2 == 0x82) + r = ac_check_status (AC_OTHER_AUTHORIZED); + else + r = ac_check_status (AC_ADMIN_AUTHORIZED); + + if (r) + GPG_SUCCESS (); /* If authentication done already, return success. */ + else + { /* If not, return retry counter, encoded. */ + r = gpg_pw_get_retry_counter (p2); + set_res_sw (0x63, 0xc0 | (r&0x0f)); + } + } + else if (p1 == 0xff) + { /* Reset the status. */ + if (p2 == 0x81) + ac_reset_pso_cds (); + else if (p2 == 0x82) + ac_reset_other (); + else + ac_reset_admin (); + GPG_SUCCESS (); + } + else + GPG_BAD_P1_P2 (); + return; + } + + /* This is real authentication. */ + if (p2 == 0x81) + r = verify_pso_cds (pw, len); + else if (p2 == 0x82) + r = verify_other (pw, len); + else + r = verify_admin (pw, len); + + if (r < 0) + { + DEBUG_INFO ("failed\r\n"); + GPG_SECURITY_FAILURE (); + } + else if (r == 0) + { + DEBUG_INFO ("blocked\r\n"); + GPG_SECURITY_AUTH_BLOCKED (); + } + else + { + DEBUG_INFO ("good\r\n"); + GPG_SUCCESS (); + } +} + +int +gpg_change_keystring (int who_old, const uint8_t *old_ks, + int who_new, const uint8_t *new_ks) +{ + int r; + int prv_keys_exist = 0; + + r = gpg_do_load_prvkey (GPG_KEY_FOR_SIGNING, who_old, old_ks); + if (r < 0) + return r; + + if (r > 0) + prv_keys_exist++; + + r = gpg_do_chks_prvkey (GPG_KEY_FOR_SIGNING, who_old, old_ks, + who_new, new_ks); + if (r < 0) + return -2; + + r = gpg_do_load_prvkey (GPG_KEY_FOR_DECRYPTION, who_old, old_ks); + if (r < 0) + return r; + + if (r > 0) + prv_keys_exist++; + + r = gpg_do_chks_prvkey (GPG_KEY_FOR_DECRYPTION, who_old, old_ks, + who_new, new_ks); + if (r < 0) + return -2; + + r = gpg_do_load_prvkey (GPG_KEY_FOR_AUTHENTICATION, who_old, old_ks); + if (r < 0) + return r; + + if (r > 0) + prv_keys_exist++; + + r = gpg_do_chks_prvkey (GPG_KEY_FOR_AUTHENTICATION, who_old, old_ks, + who_new, new_ks); + if (r < 0) + return -2; + + if (prv_keys_exist) + return 1; + else + return 0; +} + +static void +cmd_change_password (void) +{ + uint8_t old_ks[KEYSTRING_MD_SIZE]; + uint8_t new_ks0[KEYSTRING_SIZE]; + uint8_t *new_salt = KS_GET_SALT (new_ks0); + int newsalt_len = SALT_SIZE; + uint8_t *new_ks = KS_GET_KEYSTRING (new_ks0); + uint8_t p1 = P1 (apdu); /* 0: change (old+new), 1: exchange (new) */ + uint8_t p2 = P2 (apdu); + int len; + uint8_t *pw, *newpw; + int pw_len, newpw_len; + int who = p2 - 0x80; + int who_old; + int r; + int pw3_null = 0; + const uint8_t *salt; + int salt_len; + const uint8_t *ks_pw3; + + DEBUG_INFO ("Change PW\r\n"); + DEBUG_BYTE (who); + + len = apdu.cmd_apdu_data_len; + pw = apdu.cmd_apdu_data; + + if (p1 != 0) + { + GPG_FUNCTION_NOT_SUPPORTED (); + return; + } + + if (who == BY_USER) /* PW1 */ + { + const uint8_t *ks_pw1 = gpg_do_read_simple (NR_DO_KEYSTRING_PW1); + + who_old = who; + pw_len = verify_user_0 (AC_PSO_CDS_AUTHORIZED, pw, len, -1, ks_pw1, 0); + + if (ks_pw1 == NULL) + { + salt = NULL; + salt_len = 0; + } + else + { + salt = KS_GET_SALT (ks_pw1); + salt_len = SALT_SIZE; + } + + if (pw_len < 0) + { + DEBUG_INFO ("permission denied.\r\n"); + GPG_SECURITY_FAILURE (); + return; + } + else if (pw_len == 0) + { + DEBUG_INFO ("blocked.\r\n"); + GPG_SECURITY_AUTH_BLOCKED (); + return; + } + else + { + newpw = pw + pw_len; + newpw_len = len - pw_len; + ks_pw3 = gpg_do_read_simple (NR_DO_KEYSTRING_PW3); + + /* Check length of password for admin-less mode. */ + if (ks_pw3 == NULL && newpw_len < ADMIN_PASSWD_MINLEN) + { + DEBUG_INFO ("new password length is too short."); + GPG_CONDITION_NOT_SATISFIED (); + return; + } + } + } + else /* PW3 (0x83) */ + { + ks_pw3 = gpg_do_read_simple (NR_DO_KEYSTRING_PW3); + pw_len = verify_admin_0 (pw, len, -1, ks_pw3, 0); + + if (ks_pw3 == NULL) + { + salt = NULL; + salt_len = 0; + } + else + { + salt = KS_GET_SALT (ks_pw3); + salt_len = SALT_SIZE; + } + + if (pw_len < 0) + { + DEBUG_INFO ("permission denied.\r\n"); + GPG_SECURITY_FAILURE (); + return; + } + else if (pw_len == 0) + { + DEBUG_INFO ("blocked.\r\n"); + GPG_SECURITY_AUTH_BLOCKED (); + return; + } + else + { + newpw = pw + pw_len; + newpw_len = len - pw_len; + if (newpw_len == 0 && admin_authorized == BY_ADMIN) + { + newpw_len = strlen (OPENPGP_CARD_INITIAL_PW3); + memcpy (newpw, OPENPGP_CARD_INITIAL_PW3, newpw_len); + newsalt_len = 0; + pw3_null = 1; + } + + who_old = admin_authorized; + } + } + + if (newsalt_len != 0) + random_get_salt (new_salt); + s2k (salt, salt_len, pw, pw_len, old_ks); + s2k (new_salt, newsalt_len, newpw, newpw_len, new_ks); + new_ks0[0] = newpw_len; + + r = gpg_change_keystring (who_old, old_ks, who, 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 && who == BY_USER) /* no prvkey */ + { + DEBUG_INFO ("user pass change not supported with no keys.\r\n"); + GPG_CONDITION_NOT_SATISFIED (); + } + else if (r > 0 && who == BY_USER) + { + gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, KS_META_SIZE); + 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 (); + } + else if (r > 0 && who == BY_ADMIN) + { + if (pw3_null) + gpg_do_write_simple (NR_DO_KEYSTRING_PW3, NULL, 0); + else + gpg_do_write_simple (NR_DO_KEYSTRING_PW3, new_ks0, KS_META_SIZE); + + ac_reset_admin (); + DEBUG_INFO ("Changed length of DO_KEYSTRING_PW3.\r\n"); + GPG_SUCCESS (); + } + else /* r == 0 && who == BY_ADMIN */ /* no prvkey */ + { + if (pw3_null) + gpg_do_write_simple (NR_DO_KEYSTRING_PW3, NULL, 0); + else + { + new_ks0[0] |= PW_LEN_KEYSTRING_BIT; + gpg_do_write_simple (NR_DO_KEYSTRING_PW3, new_ks0, KEYSTRING_SIZE); + } + DEBUG_INFO ("Changed DO_KEYSTRING_PW3.\r\n"); + ac_reset_admin (); + GPG_SUCCESS (); + } +} + + +#ifndef S2KCOUNT +/* + * OpenPGP uses the value 65535 for the key on disk. + * Given the condition that the access to flash ROM is harder than disk, + * that is, the threat model is different, we chose the default value 192. + */ +#define S2KCOUNT 192 +#endif +void +s2k (const unsigned char *salt, size_t slen, + const unsigned char *input, size_t ilen, unsigned char output[32]) +{ + sha256_context ctx; + size_t count = S2KCOUNT; + const uint8_t *unique = unique_device_id (); + + sha256_start (&ctx); + + sha256_update (&ctx, unique, 12); + + while (count > slen + ilen) + { + if (slen) + sha256_update (&ctx, salt, slen); + sha256_update (&ctx, input, ilen); + count -= slen + ilen; + } + + if (count <= slen) + sha256_update (&ctx, salt, count); + else + { + if (slen) + { + sha256_update (&ctx, salt, slen); + count -= slen; + } + sha256_update (&ctx, input, count); + } + + sha256_finish (&ctx, output); +} + + +static void +cmd_reset_user_password (void) +{ + uint8_t p1 = P1 (apdu); + int len; + const uint8_t *pw; + const uint8_t *newpw; + int pw_len, newpw_len; + int r; + uint8_t new_ks0[KEYSTRING_SIZE]; + uint8_t *new_ks = KS_GET_KEYSTRING (new_ks0); + uint8_t *new_salt = KS_GET_SALT (new_ks0); + const uint8_t *salt; + int salt_len; + + DEBUG_INFO ("Reset PW1\r\n"); + DEBUG_BYTE (p1); + + len = apdu.cmd_apdu_data_len; + pw = apdu.cmd_apdu_data; + + if (p1 == 0x00) /* by User with Reseting Code */ + { + const uint8_t *ks_rc = gpg_do_read_simple (NR_DO_KEYSTRING_RC); + uint8_t old_ks[KEYSTRING_MD_SIZE]; + + if (gpg_pw_locked (PW_ERR_RC)) + { + DEBUG_INFO ("blocked.\r\n"); + GPG_SECURITY_AUTH_BLOCKED (); + return; + } + + if (ks_rc == NULL) + { + DEBUG_INFO ("security error.\r\n"); + GPG_SECURITY_FAILURE (); + return; + } + + pw_len = ks_rc[0] & PW_LEN_MASK; + salt = KS_GET_SALT (ks_rc); + salt_len = SALT_SIZE; + newpw = pw + pw_len; + newpw_len = len - pw_len; + random_get_salt (new_salt); + s2k (salt, salt_len, pw, pw_len, old_ks); + s2k (new_salt, SALT_SIZE, 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) + { + DEBUG_INFO ("memory error.\r\n"); + GPG_MEMORY_FAILURE (); + } + else if (r < 0) + { + DEBUG_INFO ("failed.\r\n"); + gpg_pw_increment_err_counter (PW_ERR_RC); + GPG_SECURITY_FAILURE (); + } + else if (r == 0) + { + DEBUG_INFO ("user pass change not supported with no keys.\r\n"); + GPG_CONDITION_NOT_SATISFIED (); + } + else + { + DEBUG_INFO ("done.\r\n"); + gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, KS_META_SIZE); + 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 (); + } + } + 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; + random_get_salt (new_salt); + s2k (new_salt, SALT_SIZE, 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) + { + 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 ("user pass change not supported with no keys.\r\n"); + GPG_CONDITION_NOT_SATISFIED (); + } + else + { + DEBUG_INFO ("done.\r\n"); + gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, KS_META_SIZE); + 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 (); + } + } +} + +static void +cmd_put_data (void) +{ + uint8_t *data; + uint16_t tag; + int len; + + DEBUG_INFO (" - PUT DATA\r\n"); + + tag = ((P1 (apdu)<<8) | P2 (apdu)); + len = apdu.cmd_apdu_data_len; + data = apdu.cmd_apdu_data; + gpg_do_put_data (tag, data, len); +} + +static void +cmd_pgp_gakp (void) +{ + DEBUG_INFO (" - Generate Asymmetric Key Pair\r\n"); + DEBUG_BYTE (P1 (apdu)); + + if (P1 (apdu) == 0x81) + /* Get public key */ + gpg_do_public_key (apdu.cmd_apdu_data[0]); + else + { + if (!ac_check_status (AC_ADMIN_AUTHORIZED)) + GPG_SECURITY_FAILURE (); + gpg_do_keygen (&apdu.cmd_apdu_data[0]); + } +} + +#ifdef FLASH_UPGRADE_SUPPORT +const uint8_t * +gpg_get_firmware_update_key (uint8_t keyno) +{ + extern uint8_t _updatekey_store; + const uint8_t *p; + + p = &_updatekey_store + keyno * FIRMWARE_UPDATE_KEY_CONTENT_LEN; + return p; +} +#endif + +#ifdef CERTDO_SUPPORT +#define FILEID_CH_CERTIFICATE_IS_VALID 1 +#else +#define FILEID_CH_CERTIFICATE_IS_VALID 0 +#endif + +static void +cmd_read_binary (void) +{ + int is_short_EF = (P1 (apdu) & 0x80) != 0; + uint8_t file_id; + uint16_t offset; + + DEBUG_INFO (" - Read binary\r\n"); + + if (is_short_EF) + file_id = (P1 (apdu) & 0x1f); + else + file_id = file_selection - FILE_EF_SERIAL_NO + FILEID_SERIAL_NO; + + if (is_short_EF) + { + file_selection = file_id - FILEID_SERIAL_NO + FILE_EF_SERIAL_NO; + offset = P2 (apdu); + } + else + offset = (P1 (apdu) << 8) | P2 (apdu); + + if (file_id == FILEID_SERIAL_NO) + { + if (offset != 0) + GPG_BAD_P1_P2 (); + else + { + gpg_do_get_data (0x004f, 1); /* Get AID... */ + res_APDU[0] = 0x5a; /* ... and overwrite the first byte of data. */ + } + return; + } +#ifdef FLASH_UPGRADE_SUPPORT + else if (file_id >= FILEID_UPDATE_KEY_0 && file_id <= FILEID_UPDATE_KEY_3) + { + if (offset != 0) + GPG_MEMORY_FAILURE (); + else + { + const uint8_t *p; + + p = gpg_get_firmware_update_key (file_id - FILEID_UPDATE_KEY_0); + res_APDU_size = FIRMWARE_UPDATE_KEY_CONTENT_LEN; + memcpy (res_APDU, p, FIRMWARE_UPDATE_KEY_CONTENT_LEN); + GPG_SUCCESS (); + } + } +#endif +#if defined(CERTDO_SUPPORT) + else if (file_id == FILEID_CH_CERTIFICATE) + { + const uint8_t *p; + uint16_t len = 256; + + p = &ch_certificate_start; + if (offset >= FLASH_CH_CERTIFICATE_SIZE) + GPG_MEMORY_FAILURE (); + else + { + if (offset + len >= FLASH_CH_CERTIFICATE_SIZE) + len = FLASH_CH_CERTIFICATE_SIZE - offset; + + res_APDU_size = len; + memcpy (res_APDU, p + offset, len); + GPG_SUCCESS (); + } + } +#endif + else + { + GPG_NO_FILE (); + return; + } +} + +static void +cmd_select_file (void) +{ + if (P1 (apdu) == 4) /* Selection by DF name */ + { + DEBUG_INFO (" - select DF by name\r\n"); + + /* name = D2 76 00 01 24 01 */ + if (apdu.cmd_apdu_data_len != 6 + || memcmp (openpgpcard_aid, apdu.cmd_apdu_data, 6) != 0) + { + DEBUG_SHORT (apdu.cmd_apdu_data_len); + DEBUG_BINARY (apdu.cmd_apdu_data, apdu.cmd_apdu_data_len); + + GPG_NO_FILE (); + return; + } + + if (file_selection == FILE_CARD_TERMINATED) + { + GPG_APPLICATION_TERMINATED (); + return; + } + + file_selection = FILE_DF_OPENPGP; + + /* Behave just like original OpenPGP card. */ + GPG_SUCCESS (); + } + else if (apdu.cmd_apdu_data_len == 2 + && apdu.cmd_apdu_data[0] == 0x2f && apdu.cmd_apdu_data[1] == 0x02) + { + DEBUG_INFO (" - select 0x2f02 EF\r\n"); + /* + * MF.EF-GDO -- Serial number of the card and name of the owner + */ + GPG_SUCCESS (); + file_selection = FILE_EF_SERIAL_NO; + } + else if (apdu.cmd_apdu_data_len == 2 + && apdu.cmd_apdu_data[0] == 0x3f && apdu.cmd_apdu_data[1] == 0x00) + { + DEBUG_INFO (" - select ROOT MF\r\n"); + if (P2 (apdu) == 0x0c) + { + GPG_SUCCESS (); + } + else + { + int len = sizeof (select_file_TOP_result); + + res_APDU_size = len; + memcpy (res_APDU, select_file_TOP_result, len); + res_APDU[2] = (data_objects_number_of_bytes & 0xff); + res_APDU[3] = (data_objects_number_of_bytes >> 8); + GPG_SUCCESS (); + } + + file_selection = FILE_MF; + ac_fini (); /* Reset authentication */ + } + else + { + DEBUG_INFO (" - select ?? \r\n"); + + file_selection = FILE_NONE; + GPG_NO_FILE (); + } +} + +static void +cmd_get_data (void) +{ + uint16_t tag = ((P1 (apdu)<<8) | P2 (apdu)); + + DEBUG_INFO (" - Get Data\r\n"); + + gpg_do_get_data (tag, 0); +} + +#define ECDSA_HASH_LEN 32 +#define ECDSA_SIGNATURE_LENGTH 64 + +#define EDDSA_HASH_LEN_MAX 256 +#define EDDSA_SIGNATURE_LENGTH 64 + +#define ECC_CIPHER_DO_HEADER_SIZE 7 + +static void +cmd_pso (void) +{ + int len = apdu.cmd_apdu_data_len; + int r = -1; + int attr; + int pubkey_len; + unsigned int result_len = 0; + int cs; + + DEBUG_INFO (" - PSO: "); + DEBUG_WORD ((uint32_t)&r); + DEBUG_BINARY (apdu.cmd_apdu_data, apdu.cmd_apdu_data_len); + DEBUG_SHORT (len); + + if (P1 (apdu) == 0x9e && P2 (apdu) == 0x9a) + { + attr = gpg_get_algo_attr (GPG_KEY_FOR_SIGNING); + pubkey_len = gpg_get_algo_attr_key_size (GPG_KEY_FOR_SIGNING, + GPG_KEY_PUBLIC); + + if (!ac_check_status (AC_PSO_CDS_AUTHORIZED)) + { + DEBUG_INFO ("security error."); + GPG_SECURITY_FAILURE (); + return; + } + + if (attr == ALGO_RSA2K || attr == ALGO_RSA4K) + { + /* Check size of digestInfo */ + if (len != 34 /* MD5 */ + && len != 35 /* SHA1 / RIPEMD-160 */ + && len != 47 /* SHA224 */ + && len != 51 /* SHA256 */ + && len != 67 /* SHA384 */ + && len != 83) /* SHA512 */ + { + DEBUG_INFO (" wrong length"); + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + DEBUG_BINARY (kd[GPG_KEY_FOR_SIGNING].data, pubkey_len); + + result_len = pubkey_len; + r = rsa_sign (apdu.cmd_apdu_data, res_APDU, len, + &kd[GPG_KEY_FOR_SIGNING], pubkey_len); + } + else if (attr == ALGO_NISTP256R1 || attr == ALGO_SECP256K1) + { + /* ECDSA with p256r1/p256k1 for signature */ + if (len != ECDSA_HASH_LEN) + { + DEBUG_INFO (" wrong length"); + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + cs = chopstx_setcancelstate (0); + result_len = ECDSA_SIGNATURE_LENGTH; + if (attr == ALGO_NISTP256R1) + r = ecdsa_sign_p256r1 (apdu.cmd_apdu_data, res_APDU, + kd[GPG_KEY_FOR_SIGNING].data); + else /* ALGO_SECP256K1 */ + r = ecdsa_sign_p256k1 (apdu.cmd_apdu_data, res_APDU, + kd[GPG_KEY_FOR_SIGNING].data); + chopstx_setcancelstate (cs); + } + else if (attr == ALGO_ED25519) + { + uint32_t output[64/4]; /* Require 4-byte alignment. */ + + if (len > EDDSA_HASH_LEN_MAX) + { + DEBUG_INFO ("wrong hash length."); + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + cs = chopstx_setcancelstate (0); + result_len = EDDSA_SIGNATURE_LENGTH; + r = eddsa_sign_25519 (apdu.cmd_apdu_data, len, output, + kd[GPG_KEY_FOR_SIGNING].data, + kd[GPG_KEY_FOR_SIGNING].data+32, + kd[GPG_KEY_FOR_SIGNING].pubkey); + chopstx_setcancelstate (cs); + memcpy (res_APDU, output, EDDSA_SIGNATURE_LENGTH); + } + else + { + DEBUG_INFO ("unknown algo."); + GPG_FUNCTION_NOT_SUPPORTED (); + return; + } + + if (r == 0) + { + res_APDU_size = result_len; + gpg_increment_digital_signature_counter (); + } + else /* Failure */ + ac_reset_pso_cds (); + } + else if (P1 (apdu) == 0x80 && P2 (apdu) == 0x86) + { + attr = gpg_get_algo_attr (GPG_KEY_FOR_DECRYPTION); + pubkey_len = gpg_get_algo_attr_key_size (GPG_KEY_FOR_DECRYPTION, + GPG_KEY_PUBLIC); + + DEBUG_BINARY (kd[GPG_KEY_FOR_DECRYPTION].data, pubkey_len); + + if (!ac_check_status (AC_OTHER_AUTHORIZED)) + { + DEBUG_INFO ("security error."); + GPG_SECURITY_FAILURE (); + return; + } + + if (attr == ALGO_RSA2K || attr == ALGO_RSA4K) + { + /* Skip padding 0x00 */ + len--; + if (len != pubkey_len) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + r = rsa_decrypt (apdu.cmd_apdu_data+1, res_APDU, len, + &kd[GPG_KEY_FOR_DECRYPTION], &result_len); + } + else if (attr == ALGO_NISTP256R1 || attr == ALGO_SECP256K1) + { + int header = ECC_CIPHER_DO_HEADER_SIZE; + + /* Format is in big endian MPI: 04 || x || y */ + if (len != 65 + ECC_CIPHER_DO_HEADER_SIZE + || apdu.cmd_apdu_data[header] != 0x04) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + cs = chopstx_setcancelstate (0); + result_len = 65; + if (attr == ALGO_NISTP256R1) + r = ecdh_decrypt_p256r1 (apdu.cmd_apdu_data + header, res_APDU, + kd[GPG_KEY_FOR_DECRYPTION].data); + else + r = ecdh_decrypt_p256k1 (apdu.cmd_apdu_data + header, res_APDU, + kd[GPG_KEY_FOR_DECRYPTION].data); + chopstx_setcancelstate (cs); + } + else if (attr == ALGO_CURVE25519) + { + int header = ECC_CIPHER_DO_HEADER_SIZE; + + if (len != 32 + ECC_CIPHER_DO_HEADER_SIZE) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + cs = chopstx_setcancelstate (0); + result_len = 32; + r = ecdh_decrypt_curve25519 (apdu.cmd_apdu_data + header, res_APDU, + kd[GPG_KEY_FOR_DECRYPTION].data); + chopstx_setcancelstate (cs); + } + else + { + DEBUG_INFO ("unknown algo."); + GPG_FUNCTION_NOT_SUPPORTED (); + return; + } + + if (r == 0) + res_APDU_size = result_len; + } + + if (r < 0) + { + DEBUG_INFO (" - ??"); + DEBUG_BYTE (P1 (apdu)); + DEBUG_INFO (" - ??"); + DEBUG_BYTE (P2 (apdu)); + GPG_ERROR (); + } + + DEBUG_INFO ("PSO done.\r\n"); +} + + +#define MAX_RSA_DIGEST_INFO_LEN 102 /* 40% */ +static void +cmd_internal_authenticate (void) +{ + int attr = gpg_get_algo_attr (GPG_KEY_FOR_AUTHENTICATION); + int pubkey_len = gpg_get_algo_attr_key_size (GPG_KEY_FOR_AUTHENTICATION, + GPG_KEY_PUBLIC); + int len = apdu.cmd_apdu_data_len; + int r = -1; + unsigned int result_len = 0; + int cs; + + DEBUG_INFO (" - INTERNAL AUTHENTICATE\r\n"); + + if (P1 (apdu) != 0x00 || P2 (apdu) != 0x00) + { + DEBUG_INFO (" - ??"); + DEBUG_BYTE (P1 (apdu)); + DEBUG_INFO (" - ??"); + DEBUG_BYTE (P2 (apdu)); + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + DEBUG_SHORT (len); + if (!ac_check_status (AC_OTHER_AUTHORIZED)) + { + DEBUG_INFO ("security error."); + GPG_SECURITY_FAILURE (); + return; + } + + if (attr == ALGO_RSA2K || attr == ALGO_RSA4K) + { + if (len > MAX_RSA_DIGEST_INFO_LEN) + { + DEBUG_INFO ("input is too long."); + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + result_len = pubkey_len; + r = rsa_sign (apdu.cmd_apdu_data, res_APDU, len, + &kd[GPG_KEY_FOR_AUTHENTICATION], pubkey_len); + } + else if (attr == ALGO_NISTP256R1) + { + if (len != ECDSA_HASH_LEN) + { + DEBUG_INFO ("wrong hash length."); + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + cs = chopstx_setcancelstate (0); + result_len = ECDSA_SIGNATURE_LENGTH; + r = ecdsa_sign_p256r1 (apdu.cmd_apdu_data, res_APDU, + kd[GPG_KEY_FOR_AUTHENTICATION].data); + chopstx_setcancelstate (cs); + } + else if (attr == ALGO_SECP256K1) + { + if (len != ECDSA_HASH_LEN) + { + DEBUG_INFO ("wrong hash length."); + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + cs = chopstx_setcancelstate (0); + result_len = ECDSA_SIGNATURE_LENGTH; + r = ecdsa_sign_p256k1 (apdu.cmd_apdu_data, res_APDU, + kd[GPG_KEY_FOR_AUTHENTICATION].data); + chopstx_setcancelstate (cs); + } + else if (attr == ALGO_ED25519) + { + uint32_t output[64/4]; /* Require 4-byte alignment. */ + + if (len > EDDSA_HASH_LEN_MAX) + { + DEBUG_INFO ("wrong hash length."); + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + cs = chopstx_setcancelstate (0); + result_len = EDDSA_SIGNATURE_LENGTH; + r = eddsa_sign_25519 (apdu.cmd_apdu_data, len, output, + kd[GPG_KEY_FOR_AUTHENTICATION].data, + kd[GPG_KEY_FOR_AUTHENTICATION].data+32, + kd[GPG_KEY_FOR_AUTHENTICATION].pubkey); + chopstx_setcancelstate (cs); + memcpy (res_APDU, output, EDDSA_SIGNATURE_LENGTH); + } + + if (r == 0) + res_APDU_size = result_len; + else + GPG_ERROR (); + + DEBUG_INFO ("INTERNAL AUTHENTICATE done.\r\n"); +} + + +#define MBD_OPRATION_WRITE 0 +#define MBD_OPRATION_UPDATE 1 + +static void +modify_binary (uint8_t op, uint8_t p1, uint8_t p2, int len) +{ + uint8_t file_id; + uint16_t offset; + int is_short_EF = (p1 & 0x80) != 0; + int r; + + if (!ac_check_status (AC_ADMIN_AUTHORIZED)) + { + DEBUG_INFO ("security error."); + GPG_SECURITY_FAILURE (); + return; + } + + if (is_short_EF) + file_id = (p1 & 0x1f); + else + file_id = file_selection - FILE_EF_SERIAL_NO + FILEID_SERIAL_NO; + + if (!FILEID_CH_CERTIFICATE_IS_VALID && file_id == FILEID_CH_CERTIFICATE) + { + GPG_NO_FILE (); + return; + } + + if (op == MBD_OPRATION_UPDATE && file_id != FILEID_CH_CERTIFICATE) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + if (file_id > FILEID_CH_CERTIFICATE) + { + GPG_NO_FILE (); + return; + } + + if (is_short_EF) + { + file_selection = file_id - FILEID_SERIAL_NO + FILE_EF_SERIAL_NO; + offset = p2; + + if (op == MBD_OPRATION_UPDATE) + { + r = flash_erase_binary (file_id); + if (r < 0) + { + DEBUG_INFO ("memory error.\r\n"); + GPG_MEMORY_FAILURE (); + return; + } + } + } + else + offset = (p1 << 8) | p2; + + DEBUG_SHORT (len); + DEBUG_SHORT (offset); + + if (file_id == FILEID_CH_CERTIFICATE && (len&1)) + /* It's OK the size of last write is odd. */ + apdu.cmd_apdu_data[len++] = 0xff; + + r = flash_write_binary (file_id, apdu.cmd_apdu_data, len, offset); + if (r < 0) + { + DEBUG_INFO ("memory error.\r\n"); + GPG_MEMORY_FAILURE (); + return; + } + +#ifdef FLASH_UPGRADE_SUPPORT + if (file_id >= FILEID_UPDATE_KEY_0 && file_id <= FILEID_UPDATE_KEY_3 + && len == 0 && offset == 0) + { + int i; + const uint8_t *p; + + for (i = 0; i < 4; i++) + { + p = gpg_get_firmware_update_key (i); + if (p[0] != 0x00 || p[1] != 0x00) /* still valid */ + break; + } + + if (i == 4) /* all update keys are removed */ + { + p = gpg_get_firmware_update_key (0); + flash_erase_page ((uintptr_t)p); + } + } +#endif + + GPG_SUCCESS (); +} + + +#if defined(CERTDO_SUPPORT) +static void +cmd_update_binary (void) +{ + int len = apdu.cmd_apdu_data_len; + + DEBUG_INFO (" - UPDATE BINARY\r\n"); + modify_binary (MBD_OPRATION_UPDATE, P1 (apdu), P2 (apdu), len); + DEBUG_INFO ("UPDATE BINARY done.\r\n"); +} +#endif + + +static void +cmd_write_binary (void) +{ + int len = apdu.cmd_apdu_data_len; + + DEBUG_INFO (" - WRITE BINARY\r\n"); + modify_binary (MBD_OPRATION_WRITE, P1 (apdu), P2 (apdu), len); + DEBUG_INFO ("WRITE BINARY done.\r\n"); +} + + +#ifdef FLASH_UPGRADE_SUPPORT +static void +cmd_external_authenticate (void) +{ + const uint8_t *pubkey; + const uint8_t *signature = apdu.cmd_apdu_data; + int len = apdu.cmd_apdu_data_len; + uint8_t keyno = P2 (apdu); + int r; + + DEBUG_INFO (" - EXTERNAL AUTHENTICATE\r\n"); + + if (keyno >= 4) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + pubkey = gpg_get_firmware_update_key (keyno); + if (len != 256 + || (pubkey[0] == 0xff && pubkey[1] == 0xff) /* not registered */ + || (pubkey[0] == 0x00 && pubkey[1] == 0x00) /* removed */) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + r = rsa_verify (pubkey, FIRMWARE_UPDATE_KEY_CONTENT_LEN, + challenge, signature); + random_bytes_free (challenge); + challenge = NULL; + + if (r < 0) + { + GPG_SECURITY_FAILURE (); + return; + } + + eventflag_signal (openpgp_comm, EV_EXIT); /* signal to self. */ + set_res_sw (0xff, 0xff); + DEBUG_INFO ("EXTERNAL AUTHENTICATE done.\r\n"); +} +#endif + +static void +cmd_get_challenge (void) +{ + int len = apdu.expected_res_size; + + DEBUG_INFO (" - GET CHALLENGE\r\n"); + + if (len > CHALLENGE_LEN) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + else if (len == 0) + /* Le is not specified. Return full-sized challenge by GET_RESPONSE. */ + len = CHALLENGE_LEN; + + if (challenge) + random_bytes_free (challenge); + + challenge = random_bytes_get (); + memcpy (res_APDU, challenge, len); + res_APDU_size = len; + GPG_SUCCESS (); + DEBUG_INFO ("GET CHALLENGE done.\r\n"); +} + + +#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT +static void +cmd_activate_file (void) +{ + if (file_selection != FILE_CARD_TERMINATED) + { + GPG_NO_RECORD (); + return; + } + + flash_activate (); + file_selection = FILE_DF_OPENPGP; + GPG_SUCCESS (); +} + +static void +cmd_terminate_df (void) +{ + const uint8_t *ks_pw3; + + uint8_t p1 = P1 (apdu); + uint8_t p2 = P2 (apdu); + + if (file_selection != FILE_DF_OPENPGP) + { + GPG_NO_RECORD (); + return; + } + + if (p1 != 0 || p2 != 0) + { + GPG_BAD_P1_P2(); + return; + } + + if (apdu.cmd_apdu_data_len != 0) + { + GPG_WRONG_LENGTH(); + return; + } + + ks_pw3 = gpg_do_read_simple (NR_DO_KEYSTRING_PW3); + + if (!ac_check_status (AC_ADMIN_AUTHORIZED) + && !((ks_pw3 && gpg_pw_locked (PW_ERR_PW3)) + || (ks_pw3 == NULL && gpg_pw_locked (PW_ERR_PW1)))) + { + /* Only allow the case admin authorized, or, admin pass is locked. */ + GPG_SECURITY_FAILURE(); + return; + } + + ac_reset_admin (); + ac_reset_pso_cds (); + ac_reset_other (); + gpg_do_terminate (); + flash_terminate (); + file_selection = FILE_CARD_TERMINATED; + GPG_SUCCESS (); +} +#endif + + +struct command +{ + uint8_t command; + void (*cmd_handler) (void); +}; + +const struct command cmds[] = { + { INS_VERIFY, cmd_verify }, + { INS_CHANGE_REFERENCE_DATA, cmd_change_password }, + { INS_PSO, cmd_pso }, + { INS_RESET_RETRY_COUNTER, cmd_reset_user_password }, +#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT + { INS_ACTIVATE_FILE, cmd_activate_file }, +#endif + { INS_PGP_GENERATE_ASYMMETRIC_KEY_PAIR, cmd_pgp_gakp }, +#ifdef FLASH_UPGRADE_SUPPORT + { INS_EXTERNAL_AUTHENTICATE, /* Not in OpenPGP card protocol */ + cmd_external_authenticate }, +#endif + { INS_GET_CHALLENGE, cmd_get_challenge }, /* Not in OpenPGP card protocol */ + { INS_INTERNAL_AUTHENTICATE, cmd_internal_authenticate }, + { INS_SELECT_FILE, cmd_select_file }, + { INS_READ_BINARY, cmd_read_binary }, /* Not in OpenPGP card protocol */ + { INS_GET_DATA, cmd_get_data }, + { INS_WRITE_BINARY, cmd_write_binary}, /* Not in OpenPGP card protocol */ +#if defined(CERTDO_SUPPORT) + { INS_UPDATE_BINARY, cmd_update_binary }, /* Not in OpenPGP card protocol */ +#endif + { INS_PUT_DATA, cmd_put_data }, + { INS_PUT_DATA_ODD, cmd_put_data }, +#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT + { INS_TERMINATE_DF, cmd_terminate_df}, +#endif +}; +#define NUM_CMDS ((int)(sizeof (cmds) / sizeof (struct command))) + +static void +process_command_apdu (void) +{ + int i; + uint8_t cmd = INS (apdu); + + for (i = 0; i < NUM_CMDS; i++) + if (cmds[i].command == cmd) + break; + + if (i < NUM_CMDS) + { + if (file_selection == FILE_CARD_TERMINATED + && cmd != INS_SELECT_FILE && cmd != INS_ACTIVATE_FILE + && cmd != INS_GET_CHALLENGE && cmd != INS_EXTERNAL_AUTHENTICATE) + GPG_APPLICATION_TERMINATED (); + else if (file_selection != FILE_DF_OPENPGP + && cmd != INS_SELECT_FILE && cmd != INS_ACTIVATE_FILE + && cmd != INS_GET_CHALLENGE && cmd != INS_EXTERNAL_AUTHENTICATE + && cmd != INS_WRITE_BINARY && cmd != INS_UPDATE_BINARY + && cmd != INS_READ_BINARY) + GPG_NO_RECORD (); + else + { + chopstx_setcancelstate (1); + cmds[i].cmd_handler (); + chopstx_setcancelstate (0); + } + } + else + { + DEBUG_INFO (" - ??"); + DEBUG_BYTE (cmd); + GPG_NO_INS (); + } +} + +void * +openpgp_card_thread (void *arg) +{ + struct eventflag *ccid_comm = (struct eventflag *)arg; + + openpgp_comm = ccid_comm + 1; + + gpg_init (); + + while (1) + { +#if defined(PINPAD_SUPPORT) + int len, pw_len, newpw_len; +#endif + eventmask_t m = eventflag_wait (openpgp_comm); + + DEBUG_INFO ("GPG!: "); + + if (m == EV_VERIFY_CMD_AVAILABLE) + { +#if defined(PINPAD_SUPPORT) + if (INS (apdu) != INS_VERIFY) + { + GPG_CONDITION_NOT_SATISFIED (); + goto done; + } + + pw_len = get_pinpad_input (PIN_INPUT_CURRENT); + if (pw_len < 0) + { + GPG_ERROR (); + goto done; + } + memcpy (apdu.cmd_apdu_data, pin_input_buffer, pw_len); + apdu.cmd_apdu_data_len = pw_len; +#else + GPG_ERROR (); + goto done; +#endif + } + else if (m == EV_MODIFY_CMD_AVAILABLE) + { +#if defined(PINPAD_SUPPORT) + uint8_t bConfirmPIN = apdu.cmd_apdu_data[0]; + uint8_t *p = apdu.cmd_apdu_data; + + if (INS (apdu) != INS_CHANGE_REFERENCE_DATA + && INS (apdu) != INS_RESET_RETRY_COUNTER + && INS (apdu) != INS_PUT_DATA) + { + GPG_CONDITION_NOT_SATISFIED (); + goto done; + } + + if ((bConfirmPIN & 2)) /* Require old PIN */ + { + pw_len = get_pinpad_input (PIN_INPUT_CURRENT); + if (pw_len < 0) + { + GPG_ERROR (); + goto done; + } + memcpy (p, pin_input_buffer, pw_len); + p += pw_len; + } + else + pw_len = 0; + + newpw_len = get_pinpad_input (PIN_INPUT_NEW); + if (newpw_len < 0) + { + GPG_ERROR (); + goto done; + } + memcpy (p, pin_input_buffer, newpw_len); + + if ((bConfirmPIN & 1)) /* New PIN twice */ + { + len = get_pinpad_input (PIN_INPUT_CONFIRM); + if (len < 0) + { + GPG_ERROR (); + goto done; + } + + if (len != newpw_len || memcmp (p, pin_input_buffer, len) != 0) + { + GPG_SECURITY_FAILURE (); + goto done; + } + } + + apdu.cmd_apdu_data_len = pw_len + newpw_len; +#else + GPG_ERROR (); + goto done; +#endif + } + else if (m == EV_EXIT) + break; + + led_blink (LED_START_COMMAND); + process_command_apdu (); + led_blink (LED_FINISH_COMMAND); + done: + eventflag_signal (ccid_comm, EV_EXEC_FINISHED); + } + + gpg_fini (); + return NULL; +} diff --git a/gnuk/src/pin-cir.c b/gnuk/src/pin-cir.c new file mode 100644 index 0000000000..f92cebf8e2 --- /dev/null +++ b/gnuk/src/pin-cir.c @@ -0,0 +1,1065 @@ +/* + * pin-cir.c -- PIN input device support (Consumer Infra-Red) + * + * Copyright (C) 2010, 2011, 2013 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 +#include +#include + +#include "config.h" +#include "board.h" +#include "gnuk.h" +#include "mcu/stm32f103.h" + +#ifdef DEBUG +#define DEBUG_CIR 1 +#endif + +static int +cir_ext_disable (void) +{ + int rcvd = (EXTI->PR & EXTI_PR) != 0; + + EXTI->IMR &= ~EXTI_IMR; + EXTI->PR = EXTI_PR; + return rcvd; +} + +static void +cir_ext_enable (void) +{ + EXTI->PR = EXTI_PR; + EXTI->IMR |= EXTI_IMR; +} + +static chopstx_mutex_t cir_input_mtx; +static chopstx_cond_t cir_input_cnd; +static int input_avail; + +uint8_t pin_input_buffer[MAX_PIN_CHARS]; +uint8_t pin_input_len; + +/* + * Supported/tested TV controllers: + * + * Controller of Toshiba REGZA + * Controller of Sony BRAVIA + * Controller of Sharp AQUOS + * Dell Wireless Travel Remote MR425 + * + * The code supports RC-5 protocol in fact, but I don't have any + * controller at hand which I can test with, so I don't have any data + * for controller of RC-5. + * + * Current code assumes following mapping: + * + * -------------------------------------- + * Protocol Controller + * -------------------------------------- + * RC-6 Dell MR425 + * NEC Toshiba REGZA + * Sharp Sharp AQUOS + * Sony Sony BRAVIA + * -------------------------------------- + * + * In future, when I will have other controllers, this mapping will be + * (should be) configurable, at compile time at least, preferably at + * runtime. + */ + +/* + * Philips RC-5 Protocol: 14-bit (MSB first) + * + * Philips RC-6 Protocol: (START + 1 + MODE + TR + 32-bit / 16-bit) (MSB first) + * 3-bit (mode 6 / mode 0) + * Example: Controller of DELL (mode 6) + * + * NEC Protocol: (START + 32-bit + STOP) + (START + STOP) x N (LSB first) + * 32-bit: 8-bit command inverted + 8-bit command + 16-bit address + * Example: Controller of Toshiba REGZA + * Example: Controller of Ceiling Light by NEC + * + * Sony Protocol: (START + 12-bit) or (START + 15-bit) (LSB first) + * 12-bit: 5-bit address + 7-bit command + * Example: Controller of Sony BRAVIA + * + * Sharp Protocol: (START + 48-bit + STOP) x N (LSB first) + * 48-bit: 32-bit address + 12-bit command + 4-bit parity + * Example: Controller of Sharp AQUOS + * Example: Controller of Ceiling Light by Mitsubishi + * + * Unsupported: + * + * Unknown Protocol 2: (START + 112-bit + STOP) (LSB first) + * Example: Controller of Mitsubishi air conditioner + * + * Unknown Protocol 3: (START + 128-bit + STOP) (LSB first) + * Example: Controller of Fujitsu air conditioner + * + * Unknown Protocol 4: (START + 152-bit + STOP) (LSB first) + * Example: Controller of Sanyo air conditioner + * + */ + +/* + * The implementation note of CIR signal decoding (on STM32). + * + * (1) Use EXTI interrupt to detect the first edge of signal. + * (2) Use Timer (with PWM input mode) to measure timings of square wave. + * + */ + +/* + * Timer settings. + * + * See THE reference manual (RM0008) section 15.3.6 PWM input mode. + * + * 72MHz + * Prescaler = 72 + * + * 1us + * + * TIMx_CR1 + * CKD = 00 (tDTS = tCK_INT) + * ARPE = 1 (buffered) + * CMS = 00 (up counter) + * DIR = 0 (up counter) + * OPM = 0 (up counter) + * URS = 1 (update request source: overflow only) + * UDIS = 0 (UEV (update event) enabled) + * CEN = 1 (counter enable) + * + * TIMx_CR2 + * TI1S = 1 (TI1 is XOR of ch1, ch2, ch3) + * MMS = 000 (TRGO at Reset) + * CCDS = 0 (DMA on capture) + * RSVD = 000 + * + * TIMx_SMCR + * ETP = 0 + * ECE = 0 + * ETPS = 00 + * ETF = 0000 + * MSM = 0 + * TS = 101 (TI1FP1 selected) + * RSVD = 0 + * SMS = 100 (Reset-mode) + * + * TIMx_DIER + * + * TIMx_SR + * + * TIMx_EGR + * + * TIMx_CCMR1 + * CC1S = 01 (TI1 selected) + * CC2S = 10 (TI1 selected) + * IC1F = 1001 (fSAMPLING=fDTS/8, N=8) + * IC2F = 1001 (fSAMPLING=fDTS/8, N=8) + * + * TIMx_CCMR2 + * + * TIMx_CCER + * CC2P = 1 (polarity = falling edge: TI1FP1) + * CC2E = 1 + * CC1P = 0 (polarity = rising edge: TI1FP1) + * CC1E = 1 + * + * TIMx_CNT + * TIMx_PSC = 71 + * TIMx_ARR = 18000 + * + * TIMx_CCR1 period + * TIMx_CCR2 duty + * + * TIMx_DCR + * TIMx_DMAR + */ + +#if defined(DEBUG_CIR) +static uint16_t intr_ext; +static uint16_t intr_trg; +static uint16_t intr_ovf; +static uint16_t intr_err; + +#define MAX_CIRINPUT_BIT 512 +static uint16_t cirinput[MAX_CIRINPUT_BIT]; +static uint16_t *cirinput_p; +#endif + +static uint32_t cir_data; +static uint16_t cir_data_more; +static uint8_t cir_proto; +#define CIR_PROTO_RC5 1 +#define CIR_PROTO_RC6 2 +#define CIR_PROTO_OTHER 3 +#define CIR_PROTO_SONY 4 +#define CIR_PROTO_NEC 5 +#define CIR_PROTO_SHARP 6 + + +/* CIR_DATA_ZERO: Used for zero-bit handling of RC-5/RC-6 */ +static uint8_t cir_data_zero; + +static uint8_t cir_seq; + +static void +cir_ll_init (void) +{ + cir_data = 0; + cir_seq = 0; + /* Don't touch cir_proto here */ + cir_ext_enable (); +} + + +#define CH_RETURN 0x0d +#define CH_BACKSPACE 0x08 + +struct codetable { + uint16_t cir_code; + uint8_t char_code; +}; + +/* NOTE: no way to input '0' */ +static const struct codetable +cir_codetable_dell_mr425[] = { + {0x10, '7' }, /* Speaker Louder */ + {0x11, '8' }, /* Speaker Quieter */ + {0x0d, '9' }, /* Speaker Mute */ + {0xce, 'a' }, /* Black triangle UP */ + {0xcf, 'b' }, /* Black triangle DOWN */ + {0x58, 'c' }, /* White triangle UP */ + {0x5a, 'd' }, /* White triangle LEFT */ + {0x5c, CH_RETURN }, /* Check */ + {0x5b, 'e' }, /* White triangle RIGHT */ + {0xa4, CH_BACKSPACE }, /* Back */ + {0x59, 'f' }, /* White triangle DOWN */ + {0x2f, '1' }, /* Rewind */ + {0x2c, '2' }, /* Play / Pause */ + {0x2e, '3' }, /* Forward */ + {0x21, '4' }, /* Skip backward */ + {0x31, '5' }, /* Stop */ + {0x20, '6' }, /* Skip forward */ + + {0, 0} /* <> */ +}; + +#define CIR_ADDR_SHARP_AQUOS 0x028f +static const struct codetable +cir_codetable_aquos[] = { + { 0x0116, ' ' }, /* Power */ + { 0x025e, '0' }, /* d */ + { 0x024e, '1' }, /* 1 */ + { 0x024f, '2' }, /* 2 */ + { 0x0250, '3' }, /* 3 */ + { 0x0251, '4' }, /* 4 */ + { 0x0252, '5' }, /* 5 */ + { 0x0253, '6' }, /* 6 */ + { 0x0254, '7' }, /* 7 */ + { 0x0255, '8' }, /* 8 */ + { 0x0256, '9' }, /* 9 */ + { 0x0257, 'a' }, /* 10/0 */ + { 0x0258, 'b' }, /* 11 */ + { 0x0259, 'c' }, /* 12 */ + { 0x0111, 'd' }, /* Ch ^ */ + { 0x0112, 'e' }, /* Ch v */ + { 0x0114, 'f' }, /* Vol + */ + { 0x0115, 'g' }, /* Vol - */ + { 0x0117, 'h' }, /* Mute */ + { 0x0280, 'i' }, /* BLUE */ + { 0x0281, 'j' }, /* RED */ + { 0x0282, 'k' }, /* GREEN */ + { 0x0283, 'l' }, /* YELLOW */ + { 0x011b, 'm' }, /* DISPLAY CONTROL (gamen hyouji) */ + { 0x01d5, 'n' }, /* DISPLAY SIZE */ + { 0x0157, 'o' }, /* UP */ + { 0x01d7, 'p' }, /* LEFT */ + { 0x01d8, 'q' }, /* RIGHT */ + { 0x0120, 'r' }, /* DOWN */ + { 0x0152, CH_RETURN }, /* Commit (kettei) */ + { 0x01e4, CH_BACKSPACE }, /* Back (modoru) */ + { 0x01f5, 's' }, /* Quit (shuuryou) */ + { 0x0b03, 't' }, /* Rewind (hayamodoshi) */ + { 0x0b01, 'u' }, /* Play (saisei) */ + { 0x0b04, 'v' }, /* Forward (hayaokuri) */ + { 0x0b02, 'w' }, /* Stop (teishi) */ + { 0x028a, 'x' }, /* BS */ + { 0x028b, 'y' }, /* CS */ + { 0x025f, 'z' }, /* Program information (bangumi jouhou) */ + { 0x0260, '\\' }, /* Program table (bangumi hyou) */ + { 0x0118, '|' }, /* Sound channel (onsei kirikae) */ + { 0x028e, '[' }, /* Ground Analog (chijou A) */ + { 0x0289, ']' }, /* Ground Digital (chijou D) */ + + { 0x0b07, '\"' }, /* Feature select (kinou sentaku) */ + { 0x026b, '.' }, /* TV/Radio/Data (terebi/rajio/data) */ + { 0x025a, ',' }, /* 3 code input (3 keta nyuuryoku) */ + { 0x0267, ':' }, /* subtitle (jimaku) */ + { 0x0159, ';' }, /* hold (seishi) */ + + { 0x01c4, 'A' }, /* Menu */ + { 0x011a, 'B' }, /* Off timer */ + { 0x0121, 'C' }, /* CATV */ + { 0x0b05, 'D' }, /* Record */ + { 0x0b06, 'E' }, /* Recording stop */ + { 0x0113, 'F' }, /* Inputs (nyuuryoku kirikae) */ + { 0x0275, 'G' }, /* other programs (ura bangumi) */ + { 0x0266, 'H' }, /* signal control (eizou kirikae) */ + { 0x01e7, 'I' }, /* AV position */ + { 0x027f, 'J' }, /* i.LINK */ + { 0x0b00, 'K' }, /* Recorder power */ + { 0x028f, 'L' }, /* as you like it (okonomi senkyoku) */ + + {0, 0} /* <> */ +}; + +#define CIR_ADDR_TOSHIBA_REGZA 0xbf40 +static const struct codetable +cir_codetable_regza[] = { + { 0x12, ' ' }, /* Power */ + { 0x14, '0' }, /* d (data) */ + { 0x01, '1' }, /* 1 */ + { 0x02, '2' }, /* 2 */ + { 0x03, '3' }, /* 3 */ + { 0x04, '4' }, /* 4 */ + { 0x05, '5' }, /* 5 */ + { 0x06, '6' }, /* 6 */ + { 0x07, '7' }, /* 7 */ + { 0x08, '8' }, /* 8 */ + { 0x09, '9' }, /* 9 */ + { 0x0a, 'a' }, /* 10 */ + { 0x0b, 'b' }, /* 11 */ + { 0x0c, 'c' }, /* 12 */ + { 0x1b, 'd' }, /* Ch ^ */ + { 0x1f, 'e' }, /* Ch v */ + { 0x1a, 'f' }, /* Vol + */ + { 0x1e, 'g' }, /* Vol - */ + { 0x10, 'h' }, /* Mute */ + { 0x73, 'i' }, /* BLUE */ + { 0x74, 'j' }, /* RED */ + { 0x75, 'k' }, /* GREEN */ + { 0x76, 'l' }, /* YELLOW */ + { 0x1c, 'm' }, /* Display control */ + { 0x2b, 'n' }, /* Display size */ + { 0x3e, 'o' }, /* UP */ + { 0x5f, 'p' }, /* LEFT */ + { 0x5b, 'q' }, /* RIGHT */ + { 0x3f, 'r' }, /* DOWN */ + { 0x3d, CH_RETURN }, /* Commit (kettei) */ + { 0x3b, CH_BACKSPACE }, /* Back (modoru) */ + { 0x3c, 's' }, /* Quit (shuuryou) */ + { 0x2c, 't' }, /* << (Rewind) */ + { 0x2d, 'u' }, /* >/|| (Play/Stop) */ + { 0x2e, 'v' }, /* >> (Forward) */ + { 0x2b, 'w' }, /* Stop (teishi) */ + { 0x7c, 'x' }, /* BS */ + { 0x7d, 'y' }, /* CS */ + { 0x71, 'z' }, /* Program information (bangumi setsumei) */ + { 0x77, '\\' }, /* Mini program table (mini bangumihyou) */ + { 0x13, '|' }, /* Sound (onta kirikae) */ + { 0x7a, '[' }, /* Ground Digital (chideji) */ + { 0x7b, ']' }, /* Ground Analog (chiana) */ + + { 0xd0, '\"' }, /* Settings Menu (settei menu) */ + { 0x6d, '.' }, /* Radio/Data (rajio/data) */ + { 0x60, ',' }, /* CH 10-key input (search) */ + { 0x52, ':' }, /* subtitle (jimaku) */ + { 0x50, ';' }, /* hold (seishi) */ + + { 0x3a, 'A' }, /* Input- (nyuuryokukirikae-) */ + { 0x0f, 'B' }, /* Input+ (nyuuryokukirikae+) */ + { 0x29, 'C' }, /* Two screens (nigamen) */ + { 0x25, 'D' }, /* Broadband */ + { 0x27, 'E' }, /* |<< Skip backward */ + { 0x26, 'F' }, /* >>| Skip forward */ + { 0x61, '!' }, /* 1 NHK1 */ + { 0x62, '@' }, /* 2 NHK2 */ + { 0x63, '#' }, /* 3 NHKh */ + { 0x64, '$' }, /* 4 BS Nihon TV */ + { 0x65, '%' }, /* 5 BS Asahi */ + { 0x66, '^' }, /* 6 BS-i */ + { 0x67, '&' }, /* 7 BSJ */ + { 0x68, '*' }, /* 8 BS Fuji */ + { 0x69, '(' }, /* 9 WOW */ + { 0x6a, ')' }, /* 10 Star */ + { 0x6b, '-' }, /* 11 BS11 */ + { 0x6c, '+' }, /* 12 TwellV */ + { 0x27, '=' }, /* Quick (Delete) */ + { 0x34, '<' }, /* REGZA link */ + { 0x6e, '>' }, /* Program Table */ + { 0x20, '/' }, /* ^^ */ + { 0x22, '\'' }, /* << */ + { 0x23, '?' }, /* >> */ + { 0x21, '_' }, /* vv */ + + {0, 0} /* <> */ +}; + +static const struct codetable +cir_codetable_bravia[] = { + { 0x15, ' ' }, /* Power */ + { 0x95, '0' }, /* d (16-bit: 0x4b) */ + { 0x00, '1' }, /* 1 */ + { 0x01, '2' }, /* 2 */ + { 0x02, '3' }, /* 3 */ + { 0x03, '4' }, /* 4 */ + { 0x04, '5' }, /* 5 */ + { 0x05, '6' }, /* 6 */ + { 0x06, '7' }, /* 7 */ + { 0x07, '8' }, /* 8 */ + { 0x08, '9' }, /* 9 */ + { 0x09, 'a' }, /* 10 */ + { 0x0a, 'b' }, /* 11 */ + { 0x0b, 'c' }, /* 12 */ + { 0x10, 'd' }, /* CH+ */ + { 0x11, 'd' }, /* CH- */ + { 0x12, 'f' }, /* Vol+ */ + { 0x13, 'g' }, /* Vol- */ + { 0x14, 'h' }, /* Mute */ + { 0xa4, 'i' }, /* BLUE (16-bit: 0x4b) */ + { 0xa5, 'j' }, /* RED (16-bit: 0x4b) */ + { 0xa6, 'k' }, /* GREEN (16-bit: 0x4b) */ + { 0xa7, 'l' }, /* YELLOW (16-bit: 0x4b) */ + { 0x3a, 'm' }, /* DISPLAY control (gamen hyouji) */ + { 0x3d, 'n' }, /* Display Wide (waido kirikae) */ + { 0x74, 'o' }, /* UP */ + { 0x75, 'p' }, /* DOWN */ + { 0x33, 'q' }, /* RIGHT */ + { 0x34, 'r' }, /* LEFT */ + { 0x65, CH_RETURN }, /* Commit (kettei) */ + { 0xa3, CH_BACKSPACE }, /* Back (modoru) (16-bit: 0x4b) */ + { 0xac, 's' }, /* BS (16-bit: 0x4b) */ + { 0xab, 't' }, /* CS (16-bit: 0x4b) */ + { 0x5b, 'u' }, /* Program table (bangumi hyou) (16-bit: 0x52) */ + { 0x17, 'v' }, /* Sound channel (onsei kirikae) */ + { 0xa8, 'w' }, /* subtitle (jimaku) (16-bit: 0x4b) */ + { 0x5c, 'x' }, /* hold (memo) */ + { 0xb6, 'y' }, /* Tool (16-bit: 0x4b) */ + { 0x8c, 'z' }, /* 10 key input (10ki-) (16-bit: 0x4b) */ + { 0x60, '!' }, /* Menu */ + { 0xae, '@' }, /* Analog (16-bit: 0x4b) */ + { 0xb2, '#' }, /* Digital (16-bit: 0x4b) */ + { 0x25, '$' }, /* Input (nyuuryoku kirikae) */ + + {0, 0} /* <> */, +}; + +static int +ch_is_backspace (int ch) +{ + return ch == CH_BACKSPACE; +} + +static int +ch_is_enter (int ch) +{ + return ch == CH_RETURN; +} + +/* liner search is good enough for this small amount of data */ +static uint8_t +find_char_codetable (uint32_t cir_code, const struct codetable *ctp) +{ + while (ctp->cir_code != 0x0000 || ctp->char_code != 0x00) + if (ctp->cir_code == cir_code) + return ctp->char_code; + else + ctp++; + + /* Not found */ + return cir_code & 0xff; +} + +static int +hex (int x) +{ + if (x < 10) + return x + '0'; + else + return (x - 10) + 'a'; +} + +static int +check_input (void *arg) +{ + (void)arg; + return input_avail; +} + +static int +cir_getchar (uint32_t timeout) +{ + chopstx_poll_cond_t poll_desc; + struct chx_poll_head *pd_array[1] = { (struct chx_poll_head *)&poll_desc }; + uint16_t cir_addr; +#if defined(DEBUG_CIR) + uint16_t *p; +#endif + +#if defined(DEBUG_CIR) + cirinput_p = cirinput; +#endif + + cir_ll_init (); + + poll_desc.type = CHOPSTX_POLL_COND; + poll_desc.ready = 0; + poll_desc.cond = &cir_input_cnd; + poll_desc.mutex = &cir_input_mtx; + poll_desc.check = check_input; + poll_desc.arg = NULL; + + input_avail = 0; + if (chopstx_poll (&timeout, 1, pd_array) == 0) + return -1; + + /* Sleep 200ms to avoid detecting chatter inputs. */ + chopstx_usec_wait (200 * 1000); + +#if defined(DEBUG_CIR) + DEBUG_INFO ("****\r\n"); + DEBUG_SHORT (intr_ext); + DEBUG_SHORT (intr_trg); + DEBUG_SHORT (intr_ovf); + DEBUG_INFO ("----\r\n"); + for (p = cirinput; p < cirinput_p; p++) + { + DEBUG_SHORT (*p); + } + DEBUG_INFO ("====\r\n"); + + cirinput_p = cirinput; + + DEBUG_INFO ("**** CIR data:"); + DEBUG_WORD (cir_data); + if (cir_seq > 48) + { + DEBUG_SHORT (cir_data_more); + } + DEBUG_BYTE (cir_seq); +#endif + + switch (cir_proto) + { + case CIR_PROTO_RC5: + cir_data &= 0x003f; + goto err; + case CIR_PROTO_RC6: + cir_addr = cir_data >> 8; /* in case of cir_seq == 16. 32??? */ + cir_data &= 0x00ff; + return find_char_codetable (cir_data, cir_codetable_dell_mr425); + case CIR_PROTO_NEC: + cir_addr = cir_data&0xffff; + if (cir_addr == CIR_ADDR_TOSHIBA_REGZA) + { + cir_data = (cir_data >> 16) & 0x00ff; + return find_char_codetable (cir_data, cir_codetable_regza); + } + else + goto err; + case CIR_PROTO_SHARP: + cir_addr = cir_data&0x0fff; + if (cir_addr == CIR_ADDR_SHARP_AQUOS) + { + cir_data = (cir_data>>16)&0x0fff; + return find_char_codetable (cir_data, cir_codetable_aquos); + } + else + goto err; + case CIR_PROTO_SONY: + /* Remove ADDRESS bits and filter COMMAND bits */ + if (cir_seq == 1 + 12) + { + cir_addr = cir_data >> 7; + cir_data = cir_data & 0x007f; + /* ADDRESS = 0x01 (5-bit) */ + } + else + { + cir_addr = cir_data >> 8; + cir_data = cir_data & 0x00ff; + /* ADDRESS = 0x4b or 0x52 (7-bit) */ + } + return find_char_codetable (cir_data, cir_codetable_bravia); + err: + default: + /* encode debug information */ + pin_input_len = 16; + pin_input_buffer[0] = hex (cir_proto >> 4); + pin_input_buffer[1] = hex (cir_proto & 0x0f); + pin_input_buffer[2] = ':'; + pin_input_buffer[3] = hex ((cir_data >> 28) & 0x0f); + pin_input_buffer[4] = hex ((cir_data >> 24) & 0x0f); + pin_input_buffer[5] = hex ((cir_data >> 20) & 0x0f); + pin_input_buffer[6] = hex ((cir_data >> 16) & 0x0f); + pin_input_buffer[7] = hex ((cir_data >> 12) & 0x0f); + pin_input_buffer[8] = hex ((cir_data >> 8) & 0x0f); + pin_input_buffer[9] = hex ((cir_data >> 4) & 0x0f); + pin_input_buffer[10] = hex (cir_data & 0x0f); + pin_input_buffer[11] = ':'; + pin_input_buffer[12] = hex ((cir_data_more >> 12) & 0x0f); + pin_input_buffer[13] = hex ((cir_data_more >> 8) & 0x0f); + pin_input_buffer[14] = hex ((cir_data_more >> 4) & 0x0f); + pin_input_buffer[15] = hex (cir_data_more & 0x0f); + return CH_RETURN; + } +} + +/* + * RC-5 protocol doesn't have a start bit, while any other protocols + * have the one. + */ +#define CIR_BIT_START_RC5_DETECT 1600 /* RC-5: 889us, Sony start: 2400us */ + +#define CIR_BIT_START_RC5_LENGTH (889 + 889/2) +#define CIR_BIT_PERIOD_RC6 444 +#define CIR_BIT_PERIOD 1500 +#define CIR_BIT_SIRC_PERIOD_ON 1000 + +/* + * Let user input PIN string. + * Return length of the string. + * The string itself is in PIN_INPUT_BUFFER. + */ +int +pinpad_getline (int msg_code, uint32_t timeout) +{ + (void)msg_code; + + DEBUG_INFO (">>>\r\n"); + + pin_input_len = 0; + while (1) + { + int ch; + + ch = cir_getchar (timeout); + if (ch < 0) + return 0; /* timeout */ + + if (ch_is_backspace (ch)) + { + led_blink (LED_TWOSHOTS); + if (pin_input_len > 0) + pin_input_len--; + } + else if (ch_is_enter (ch)) + break; + else if (pin_input_len < MAX_PIN_CHARS) + { + led_blink (LED_ONESHOT); + pin_input_buffer[pin_input_len++] = ch; + } + } + + cir_ext_disable (); + + return pin_input_len; +} + +/** + * @brief Interrupt handler of EXTI. + * @note This handler will be invoked at the beginning of signal. + * Setup timer to measure period and duty using PWM input mode. + */ +static void +cir_ext_interrupt (void) +{ + int rcvd = cir_ext_disable (); + + if (!rcvd) + return; + +#if defined(DEBUG_CIR) + intr_ext++; + if (cirinput_p - cirinput < MAX_CIRINPUT_BIT) + *cirinput_p++ = 0x0000; +#endif + + TIMx->EGR = TIM_EGR_UG; /* Generate UEV to load PSC and ARR */ + /* Enable Timer */ + TIMx->SR &= ~(TIM_SR_UIF + | TIM_SR_CC1IF | TIM_SR_CC2IF + | TIM_SR_TIF + | TIM_SR_CC1OF | TIM_SR_CC2OF); + TIMx->DIER = TIM_DIER_UIE /*overflow*/ | TIM_DIER_TIE /*trigger*/; + TIMx->CR1 |= TIM_CR1_CEN; +} + +#define CIR_PERIOD_ON_RC5_OR_RC6 (((cir_proto == CIR_PROTO_RC5) ? 2 : 1) \ + * CIR_BIT_PERIOD_RC6 * 3 / 2) + +/** + * @brief Interrupt handler of timer. + * @note Timer is PWM input mode, this handler will be invoked on each cycle + */ +static void +cir_timer_interrupt (void) +{ + uint16_t period, on, off; + + period = TIMx->CCR1; + on = TIMx->CCR2; + off = period - on; + + if ((TIMx->SR & TIM_SR_TIF)) + { + if (cir_seq == 0) + { + if (on >= CIR_BIT_START_RC5_DETECT) + { + cir_proto = CIR_PROTO_OTHER; + cir_data_zero = 0; + } + else + { + cir_proto = CIR_PROTO_RC5; + cir_data = 1; + if (on >= CIR_BIT_START_RC5_LENGTH) + { + cir_data <<= 1; + cir_seq++; + if (off >= CIR_BIT_START_RC5_LENGTH) + cir_data_zero = 0; + else + cir_data_zero = 1; + } + else + cir_data_zero = 0; + } + } + else if (cir_proto == CIR_PROTO_OTHER) + { + if (cir_seq == 1 + 16) + cir_data_more = (uint16_t)(cir_data >> 16); + + cir_data >>= 1; + cir_data |= (period >= CIR_BIT_PERIOD) ? 0x80000000 : 0; + + /* Detection of RC-6 protocol */ + if (cir_data_zero && on > CIR_BIT_PERIOD_RC6*3/2) + /* TR-bit 0 is detected */ + { + cir_proto = CIR_PROTO_RC6; + cir_seq = 0; + cir_data = 0; /* ignore MODE bits */ + if (on > CIR_BIT_PERIOD_RC6*5/2) + { + cir_data = 1; + if (off > CIR_BIT_PERIOD_RC6*3/2) + cir_data_zero = 1; + else + cir_data_zero = 0; + } + else /* Off must be short */ + { + cir_data_zero = 1; + } + } + else if ((!cir_data_zero + && on > CIR_BIT_PERIOD_RC6*3/2 && off > CIR_BIT_PERIOD_RC6*3/2)) + /* TR-bit 1 is detected */ + { + cir_proto = CIR_PROTO_RC6; + cir_seq = 0; + cir_data = 0; /* ignore MODE bits */ + cir_data_zero = 0; + } + else + { + /* Check if it looks like TR-bit of RC6 protocol */ + if (off <= CIR_BIT_PERIOD_RC6*3/2) + cir_data_zero = 0; + else + cir_data_zero = 1; + } + } + else if (cir_proto == CIR_PROTO_RC5 || cir_proto == CIR_PROTO_RC6) + { + if (cir_data_zero) + { + cir_data <<= 1; + + if (on > CIR_PERIOD_ON_RC5_OR_RC6) + { + cir_data <<= 1; + cir_data |= 1; + cir_seq++; + if (off > CIR_PERIOD_ON_RC5_OR_RC6) + cir_data_zero = 1; + else + cir_data_zero = 0; + } + else /* Off must be short */ + cir_data_zero = 1; + } + else + { + cir_data <<= 1; + cir_data |= 1; + + /* On must be short */ + if (off > CIR_PERIOD_ON_RC5_OR_RC6) + cir_data_zero = 1; + else + cir_data_zero = 0; + } + } + + cir_seq++; + +#if defined(DEBUG_CIR) + if (cirinput_p - cirinput < MAX_CIRINPUT_BIT) + { + *cirinput_p++ = on; + *cirinput_p++ = off; + } + intr_trg++; +#endif + + TIMx->EGR = TIM_EGR_UG; /* Generate UEV */ + TIMx->SR &= ~TIM_SR_TIF; + } + else if ((TIMx->SR & TIM_SR_UIF)) + /* overflow occurred */ + { + TIMx->SR &= ~TIM_SR_UIF; + + if (on > 0) + { + uint8_t ignore_input = 0; + + /* Disable the timer */ + TIMx->CR1 &= ~TIM_CR1_CEN; + TIMx->DIER = 0; + + if (cir_seq == 12 || cir_seq == 15) + { +#if defined(DEBUG_CIR) + if (cirinput_p - cirinput < MAX_CIRINPUT_BIT) + { + *cirinput_p++ = on; + *cirinput_p++ = 0xffff; + } +#endif + cir_proto = CIR_PROTO_SONY; + cir_data >>= 1; + cir_data |= (on >= CIR_BIT_SIRC_PERIOD_ON) ? 0x80000000 : 0; + cir_data >>= (32 - cir_seq); + cir_seq++; + } + else + { + if (cir_proto == CIR_PROTO_RC5 || cir_proto == CIR_PROTO_RC6) + { + cir_data <<= 1; + cir_seq++; + if (cir_data_zero) + { + if (on > CIR_PERIOD_ON_RC5_OR_RC6) + { + cir_data <<= 1; + cir_data |= 1; + cir_seq++; + } + } + else + cir_data |= 1; + } + /* Or else, it must be the stop bit, just ignore */ + } + + if (cir_proto == CIR_PROTO_SONY) + { + if (cir_seq != 1 + 12 && cir_seq != 1 + 15) + ignore_input = 1; + } + else if (cir_proto == CIR_PROTO_OTHER) + { + if (cir_seq == 1 + 32) + { + if (((cir_data >> 16) & 0xff) == ((cir_data >> 24) ^ 0xff)) + cir_proto = CIR_PROTO_NEC; + else + ignore_input = 1; + } + else if (cir_seq == 1 + 48) + { + if ((cir_data >> 28) == + (((cir_data_more >> 12) & 0x0f) + ^ ((cir_data_more >> 8) & 0x0f) + ^ ((cir_data_more >> 4) & 0x0f) + ^ (cir_data_more & 0x0f) + ^ ((cir_data >> 24) & 0x0f) + ^ ((cir_data >> 20) & 0x0f) ^ ((cir_data >> 16) & 0x0f) + ^ ((cir_data >> 12) & 0x0f) ^ ((cir_data >> 8) & 0x0f) + ^ ((cir_data >> 4) & 0x0f) ^ (cir_data & 0x0f))) + cir_proto = CIR_PROTO_SHARP; + else + ignore_input = 1; + } + else + ignore_input = 1; + } + else if (cir_proto == CIR_PROTO_RC6) + { + if (cir_seq != 16 && cir_seq != 32) + ignore_input = 1; + } + else if (cir_proto == CIR_PROTO_RC5) + { + if (cir_seq != 14) + ignore_input = 1; + } + else + ignore_input = 1; + + if (ignore_input) + /* Ignore data received and enable CIR again */ + cir_ll_init (); + else + { + /* + * Notify the thread, when it's waiting the input. + * If else, the input is thrown away. + */ + chopstx_mutex_lock (&cir_input_mtx); + input_avail = 1; + chopstx_cond_signal (&cir_input_cnd); + chopstx_mutex_unlock (&cir_input_mtx); + } + +#if defined(DEBUG_CIR) + if (cirinput_p - cirinput < MAX_CIRINPUT_BIT) + *cirinput_p++ = 0xffff; + + intr_ovf++; +#endif + } + } +#if defined(DEBUG_CIR) + else + intr_err++; +#endif +} + + +#define STACK_PROCESS_6 +#define STACK_PROCESS_7 +#include "stack-def.h" +#define STACK_ADDR_TIM ((uintptr_t)process6_base) +#define STACK_SIZE_TIM (sizeof process6_base) +#define STACK_ADDR_EXT ((uintptr_t)process7_base) +#define STACK_SIZE_EXT (sizeof process7_base) + +#define PRIO_TIM 4 + +static void * +tim_main (void *arg) +{ + chopstx_intr_t interrupt; + + (void)arg; + chopstx_claim_irq (&interrupt, INTR_REQ_TIM); + + while (1) + { + chopstx_intr_wait (&interrupt); + cir_timer_interrupt (); + } + + return NULL; +} + + +#define PRIO_EXT 4 + +static void * +ext_main (void *arg) +{ + chopstx_intr_t interrupt; + + (void)arg; + chopstx_claim_irq (&interrupt, INTR_REQ_EXTI); + + while (1) + { + chopstx_intr_wait (&interrupt); + cir_ext_interrupt (); + } + + return NULL; +} + + +void +cir_init (void) +{ + chopstx_mutex_init (&cir_input_mtx); + chopstx_cond_init (&cir_input_cnd); + + /* + * We use XOR function for three signals: TIMx_CH1, TIMx_CH2, and TIMx_CH3. + * + * This is because we want to invert the signal (of Vout from CIR + * receiver module) for timer. + * + * For FST-01, TIM2_CH3 is the signal. We set TIM2_CH1 = 1 and + * TIM2_CH2 = 0. + * + * For STM8S, TIM2_CH2 is the signal. We set TIM2_CH1 = 1 and + * TIMx_CH3 = 0. + */ + + /* EXTIx <= Py */ + AFIO->EXTICR[AFIO_EXTICR_INDEX] = AFIO_EXTICR1_EXTIx_Py; + EXTI->IMR = 0; + EXTI->FTSR = EXTI_FTSR_TR; + + /* TIM */ +#ifdef ENABLE_RCC_APB1 + RCC->APB1ENR |= RCC_APBnENR_TIMxEN; + RCC->APB1RSTR = RCC_APBnRSTR_TIMxRST; + RCC->APB1RSTR = 0; +#elif ENABLE_RCC_APB2 + RCC->APB2ENR |= RCC_APBnENR_TIMxEN; + RCC->APB2RSTR = RCC_APBnRSTR_TIMxRST; + RCC->APB2RSTR = 0; +#endif + + TIMx->CR1 = TIM_CR1_URS | TIM_CR1_ARPE; + TIMx->CR2 = TIM_CR2_TI1S; + TIMx->SMCR = TIM_SMCR_TS_0 | TIM_SMCR_TS_2 | TIM_SMCR_SMS_2; + TIMx->DIER = 0; /* Disable interrupt for now */ + TIMx->CCMR1 = TIM_CCMR1_CC1S_0 | TIM_CCMR1_IC1F_0 | TIM_CCMR1_IC1F_3 + | TIM_CCMR1_CC2S_1 | TIM_CCMR1_IC2F_0 | TIM_CCMR1_IC2F_3; + TIMx->CCMR2 = 0; + TIMx->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC2P; + TIMx->PSC = 72 - 1; /* 1 MHz */ + TIMx->ARR = 18000; /* 18 ms */ + /* Generate UEV to upload PSC and ARR */ + TIMx->EGR = TIM_EGR_UG; + + chopstx_create (PRIO_TIM, STACK_ADDR_TIM, STACK_SIZE_TIM, tim_main, NULL); + chopstx_create (PRIO_EXT, STACK_ADDR_EXT, STACK_SIZE_EXT, ext_main, NULL); +} diff --git a/gnuk/src/pin-dnd.c b/gnuk/src/pin-dnd.c new file mode 100644 index 0000000000..50ae59e3dd --- /dev/null +++ b/gnuk/src/pin-dnd.c @@ -0,0 +1,374 @@ +/* + * pin-dnd.c -- PIN input support (Drag and Drop with File Manager) + * + * Copyright (C) 2011, 2013 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 +#include +#include + +#include "config.h" +#include "board.h" +#include "gnuk.h" +#include "usb-msc.h" + +struct folder { + uint8_t parent; + uint8_t children[7]; +}; + +static struct folder folders[8]; +static const struct folder folder_ini = { 0, { 1, 2, 3, 4, 5, 6, 7 } }; + + +uint8_t pin_input_buffer[MAX_PIN_CHARS]; +uint8_t pin_input_len; + +static uint8_t msg; + +/* + * Let user input PIN string. + * Return length of the string. + * The string itself is in PIN_INPUT_BUFFER. + */ +int +pinpad_getline (int msg_code, uint32_t timeout) +{ + uint8_t msg_received; + + (void)msg_code; + (void)timeout; + + DEBUG_INFO (">>>\r\n"); + + pin_input_len = 0; + + msc_media_insert_change (1); + + memset (folders, 0, sizeof folders); + memcpy (folders, &folder_ini, sizeof folder_ini); + + while (1) + { + chopstx_mutex_lock (pinpad_mutex); + chopstx_cond_wait (pinpad_cond, pinpad_mutex); + msg_received = msg; + chopstx_mutex_unlock (pinpad_mutex); + + led_blink (LED_ONESHOT); + if (msg_received != 0) + break; + } + + msc_media_insert_change (0); + + if (msg == 1) + return pin_input_len; + else + return -1; /* cancel */ +} + +static void pinpad_input (void) +{ + chopstx_mutex_lock (pinpad_mutex); + msg = 0; + chopstx_cond_signal (pinpad_cond); + chopstx_mutex_unlock (pinpad_mutex); +} + +static void pinpad_finish_entry (int cancel) +{ + chopstx_mutex_lock (pinpad_mutex); + if (cancel) + msg = 2; + else + msg = 1; + chopstx_cond_signal (pinpad_cond); + chopstx_mutex_unlock (pinpad_mutex); +} + +#define TOTAL_SECTOR 68 + +/* + +blk=0: master boot record sector +blk=1: fat0 +blk=2: fat1 +blk=3: root directory +blk=4: fat cluster #2 +... +blk=4+63: fat cluster #2+63 +*/ + +static const uint8_t d0_0_sector[] = { + 0xeb, 0x3c, /* Jump instruction */ + 0x90, /* NOP */ + + 0x6d, 0x6b, 0x64, 0x6f, 0x73, 0x66, 0x73, 0x20, /* "mkdosfs " */ + + 0x00, 0x02, /* Bytes per sector: 512 */ + + 0x01, /* sectors per cluster: 1 */ + 0x01, 0x00, /* reserved sector count: 1 */ + 0x02, /* Number of FATs: 2 */ + 0x10, 0x00, /* Max. root directory entries: 16 (1 sector) */ + TOTAL_SECTOR, 0x00, /* total sectors: 68 */ + 0xf8, /* media descriptor: fixed disk */ + 0x01, 0x00, /* sectors per FAT: 1 */ + 0x04, 0x00, /* sectors per track: 4 */ + 0x01, 0x00, /* number of heads: 1 */ + 0x00, 0x00, 0x00, 0x00, /* hidden sectors: 0 */ + 0x00, 0x00, 0x00, 0x00, /* total sectors (long) */ + 0x00, /* drive number */ + 0x00, /* reserved */ + 0x29, /* extended boot signature */ + 0xbf, 0x86, 0x75, 0xea, /* Volume ID (serial number) (Little endian) */ + + /* Volume label: DNDpinentry */ + 'D', 'n', 'D', 'p', 'i', 'n', 'e', 'n', 't', 'r', 'y', + + 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, /* FAT12 */ + + 0x0e, /* push cs */ + 0x1f, /* pop ds */ + 0xbe, 0x5b, 0x7c, /* mov si, offset message_txt */ + 0xac, /* 1: lodsb */ + 0x22, 0xc0, /* and al, al */ + 0x74, 0x0b, /* jz 2f */ + 0x56, /* push si */ + 0xb4, 0x0e, /* mov ah, 0eh */ + 0xbb, 0x07, 0x00, /* mov bx, 0007h */ + 0xcd, 0x10, /* int 10h ; output char color=white */ + 0x5e, /* pop si */ + 0xeb, 0xf0, /* jmp 1b */ + 0x32, 0xe4, /* 2: xor ah, ah */ + 0xcd, 0x16, /* int 16h; key input */ + 0xcd, 0x19, /* int 19h; load OS */ + 0xeb, 0xfe, /* 3: jmp 3b */ + + /* "This is not a bootable disk... \r\n" */ + 0x54, 0x68, 0x69, 0x73, 0x20, + 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, + 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x20, 0x64, 0x69, 0x73, 0x6b, 0x2e, 0x20, + 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, + 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, + 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x70, 0x70, 0x79, + 0x20, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x70, 0x72, + 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20, + 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74, + 0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, + 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x0d, 0x0a, 0x00, +}; + +static const uint8_t d0_fat0_sector[] = { + 0xf8, 0xff, 0xff, /* Media descriptor: fixed disk */ /* EOC */ + 0xff, 0xff, 0xff, /* cluster 2: used */ /* cluster 3: used */ + 0xff, 0xff, 0xff, /* cluster 4: used */ /* cluster 5: used */ + 0xff, 0xff, 0xff, /* cluster 6: used */ /* cluster 7: used */ + 0xff, 0x0f, 0x00, /* cluster 8: used */ /* cluster 9: free */ +}; + +static uint8_t the_sector[512]; + +#define FOLDER_INDEX_TO_CLUSTER_NO(i) (i+1) +#define CLUSTER_NO_TO_FOLDER_INDEX(n) (n-1) +#define FOLDER_INDEX_TO_LBA(i) (i+3) +#define LBA_TO_FOLDER_INDEX(lba) (lba-3) +#define FOLDER_INDEX_TO_DIRCHAR(i) ('A'+i-1) +#define DIRCHAR_TO_FOLDER_INDEX(c) (c - 'A' + 1) + +static uint8_t *fill_file_entry (uint8_t *p, const uint8_t *filename, + uint16_t cluster_no) +{ + memcpy (p, filename, 8+3); + p += 11; + *p++ = 0x10; /* directory */ + *p++ = 0x00; /* reserved */ + memcpy (p, "\x64\x3b\xa7\x61\x3f", 5); /* create time */ + p += 5; + memcpy (p, "\x61\x3f", 2); /* last access */ + p += 2; + *p++ = 0x00; *p++ = 0x00; /* ea-index */ + memcpy (p, "\x3b\xa7\x61\x3f", 4); /* last modified */ + p += 4; + memcpy (p, &cluster_no, 2); /* cluster # */ + p += 2; + *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; /* file size */ + return p; +} + +static void build_directory_sector (uint8_t *p, uint8_t index) +{ + uint16_t cluster_no = FOLDER_INDEX_TO_CLUSTER_NO (index); + int i; + uint8_t filename[11] = { 0x2e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20 }; + uint8_t child; + + memset (p, 0, 512); + + if (index != 0) + { + p = fill_file_entry (p, filename, cluster_no); + filename[1] = 0x2e; + p = fill_file_entry (p, filename, 0); + filename[1] = 0x20; + } + + for (i = 0; i < 7; i++) + if ((child = folders[index].children[i]) != 0) + { + filename[0] = FOLDER_INDEX_TO_DIRCHAR (child); + p = fill_file_entry (p, filename, FOLDER_INDEX_TO_CLUSTER_NO (child)); + } + else + break; +} + +int +msc_scsi_read (uint32_t lba, const uint8_t **sector_p) +{ + if (!media_available) + return SCSI_ERROR_NOT_READY; + + if (lba >= TOTAL_SECTOR) + return SCSI_ERROR_ILLEAGAL_REQUEST; + + switch (lba) + { + case 0: + *sector_p = the_sector; + memcpy (the_sector, d0_0_sector, sizeof d0_0_sector); + memset (the_sector + sizeof d0_0_sector, 0, 512 - sizeof d0_0_sector); + the_sector[510] = 0x55; + the_sector[511] = 0xaa; + return 0; + case 1: + case 2: + *sector_p = the_sector; + memcpy (the_sector, d0_fat0_sector, sizeof d0_fat0_sector); + memset (the_sector + sizeof d0_fat0_sector, 0, + 512 - sizeof d0_fat0_sector); + return 0; + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + *sector_p = the_sector; + build_directory_sector (the_sector, LBA_TO_FOLDER_INDEX (lba)); + return 0; + default: + *sector_p = the_sector; + memset (the_sector, 0, 512); + return 0; + } +} + + +static void parse_directory_sector (const uint8_t *p, uint8_t index) +{ + int i; + uint8_t child; + int input = 0; + int num_children = 0; + + if (index != 0) + { + uint16_t cluster_no; + uint8_t dest_index; + + p += 32; /* skip "." */ + + /* ".." */ + cluster_no = p[26] | (p[27] << 8); + dest_index = CLUSTER_NO_TO_FOLDER_INDEX (cluster_no); + + if (dest_index < 1 || dest_index > 7) + ; /* it can be 255 : root_dir */ + else + if (pin_input_len < MAX_PIN_CHARS - 2) + { + pin_input_buffer[pin_input_len++] + = FOLDER_INDEX_TO_DIRCHAR (index); + pin_input_buffer[pin_input_len++] + = FOLDER_INDEX_TO_DIRCHAR (dest_index); + input = 1; + } + + p += 32; + } + + for (i = 0; i < 7; i++) + { + if (*p >= 'A' && *p <= 'G') + { + child = DIRCHAR_TO_FOLDER_INDEX (*p); + folders[index].children[i] = child; + num_children++; + } + else + folders[index].children[i] = 0; + p += 32; + } + + if (index == 0 && num_children == 1) + pinpad_finish_entry (0); + else if (input) + pinpad_input (); +} + +int +msc_scsi_write (uint32_t lba, const uint8_t *buf, size_t size) +{ + (void)size; + + if (!media_available) + return SCSI_ERROR_NOT_READY; + + if (lba >= TOTAL_SECTOR) + return SCSI_ERROR_ILLEAGAL_REQUEST; + + if (lba == 1) + return 0; /* updating FAT, just ignore */ + + if (lba <= 2 || lba >= 11) + return SCSI_ERROR_DATA_PROTECT; + else + { + uint8_t index = LBA_TO_FOLDER_INDEX (lba); + + parse_directory_sector (buf, index); + return 0; + } +} + +void +msc_scsi_stop (uint8_t code) +{ + (void)code; + pinpad_finish_entry (1); +} diff --git a/gnuk/src/random.c b/gnuk/src/random.c new file mode 100644 index 0000000000..ec957a75d9 --- /dev/null +++ b/gnuk/src/random.c @@ -0,0 +1,120 @@ +/* + * random.c -- get random bytes + * + * Copyright (C) 2010, 2011, 2012, 2013, 2015 + * 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 +#include + +#include "gnuk.h" +#include "neug.h" + +#define RANDOM_BYTES_LENGTH 32 +static uint32_t random_word[RANDOM_BYTES_LENGTH/sizeof (uint32_t)]; + +void +random_init (void) +{ + int i; + + neug_init (random_word, RANDOM_BYTES_LENGTH/sizeof (uint32_t)); + + for (i = 0; i < NEUG_PRE_LOOP; i++) + (void)neug_get (NEUG_KICK_FILLING); +} + +void +random_fini (void) +{ + neug_fini (); +} + +/* + * Return pointer to random 32-byte + */ +const uint8_t * +random_bytes_get (void) +{ + neug_wait_full (); + return (const uint8_t *)random_word; +} + +/* + * Free pointer to random 32-byte + */ +void +random_bytes_free (const uint8_t *p) +{ + (void)p; + memset (random_word, 0, RANDOM_BYTES_LENGTH); + neug_flush (); +} + +/* + * Return 4-byte salt + */ +void +random_get_salt (uint8_t *p) +{ + uint32_t rnd; + + rnd = neug_get (NEUG_KICK_FILLING); + memcpy (p, &rnd, sizeof (uint32_t)); + rnd = neug_get (NEUG_KICK_FILLING); + memcpy (p + sizeof (uint32_t), &rnd, sizeof (uint32_t)); +} + + +/* + * Random byte iterator + */ +int +random_gen (void *arg, unsigned char *out, size_t out_len) +{ + uint8_t *index_p = (uint8_t *)arg; + uint8_t index = *index_p; + size_t n; + + while (out_len) + { + neug_wait_full (); + + n = RANDOM_BYTES_LENGTH - index; + if (n > out_len) + n = out_len; + + memcpy (out, ((unsigned char *)random_word) + index, n); + out += n; + out_len -= n; + index += n; + + if (index >= RANDOM_BYTES_LENGTH) + { + index = 0; + neug_flush (); + } + } + + *index_p = index; + + return 0; +} diff --git a/gnuk/src/random.h b/gnuk/src/random.h new file mode 100644 index 0000000000..026922a1b8 --- /dev/null +++ b/gnuk/src/random.h @@ -0,0 +1,12 @@ +void random_init (void); +void random_fini (void); + +/* 32-byte random bytes */ +const uint8_t *random_bytes_get (void); +void random_bytes_free (const uint8_t *p); + +/* 8-byte salt */ +void random_get_salt (uint8_t *p); + +/* iterator returning a byta at a time */ +int random_gen (void *arg, unsigned char *output, size_t output_len); diff --git a/gnuk/src/sha256.c b/gnuk/src/sha256.c new file mode 100644 index 0000000000..52e13a9a41 --- /dev/null +++ b/gnuk/src/sha256.c @@ -0,0 +1,225 @@ +/* + * sha256.c -- Compute SHA-256 hash + * + * Just for little endian architecture. + * + * Code taken from: + * http://gladman.plushost.co.uk/oldsite/cryptography_technology/sha/index.php + * + * File names are sha2.c, sha2.h, brg_types.h, brg_endian.h + * in the archive sha2-07-01-07.zip. + * + * Code is modified in the style of PolarSSL API. + * + * See original copyright notice below. + */ +/* + --------------------------------------------------------------------------- + Copyright (c) 2002, Dr Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The free distribution and use of this software in both source and binary + form is allowed (with or without changes) provided that: + + 1. distributions of this source code include the above copyright + notice, this list of conditions and the following disclaimer; + + 2. distributions in binary form include the above copyright + notice, this list of conditions and the following disclaimer + in the documentation and/or other associated materials; + + 3. the copyright holder's name is not used to endorse products + built using this software without specific written permission. + + ALTERNATIVELY, provided that this notice is retained in full, this product + may be distributed under the terms of the GNU General Public License (GPL), + in which case the provisions of the GPL apply INSTEAD OF those given above. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 01/08/2005 +*/ + +#include +#include +#include "sha256.h" + +#define SHA256_MASK (SHA256_BLOCK_SIZE - 1) + +static void memcpy_output_bswap32 (unsigned char *dst, const uint32_t *p) +{ + int i; + uint32_t q = 0; + + for (i = 0; i < 32; i++) + { + if ((i & 3) == 0) + q = __builtin_bswap32 (p[i >> 2]); /* bswap32 is GCC extention */ + dst[i] = q >> ((i & 3) * 8); + } +} + +#define rotr32(x,n) (((x) >> n) | ((x) << (32 - n))) + +#define ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z)))) +#define maj(x,y,z) (((x) & (y)) | ((z) & ((x) ^ (y)))) + +/* round transforms for SHA256 compression functions */ +#define vf(n,i) v[(n - i) & 7] + +#define hf(i) (p[i & 15] += \ + g_1(p[(i + 14) & 15]) + p[(i + 9) & 15] + g_0(p[(i + 1) & 15])) + +#define v_cycle0(i) \ + p[i] = __builtin_bswap32 (p[i]); \ + vf(7,i) += p[i] + k_0[i] \ + + s_1(vf(4,i)) + ch(vf(4,i),vf(5,i),vf(6,i)); \ + vf(3,i) += vf(7,i); \ + vf(7,i) += s_0(vf(0,i))+ maj(vf(0,i),vf(1,i),vf(2,i)) + +#define v_cycle(i, j) \ + vf(7,i) += hf(i) + k_0[i+j] \ + + s_1(vf(4,i)) + ch(vf(4,i),vf(5,i),vf(6,i)); \ + vf(3,i) += vf(7,i); \ + vf(7,i) += s_0(vf(0,i))+ maj(vf(0,i),vf(1,i),vf(2,i)) + +#define s_0(x) (rotr32((x), 2) ^ rotr32((x), 13) ^ rotr32((x), 22)) +#define s_1(x) (rotr32((x), 6) ^ rotr32((x), 11) ^ rotr32((x), 25)) +#define g_0(x) (rotr32((x), 7) ^ rotr32((x), 18) ^ ((x) >> 3)) +#define g_1(x) (rotr32((x), 17) ^ rotr32((x), 19) ^ ((x) >> 10)) +#define k_0 k256 + +static const uint32_t k256[64] = { + 0X428A2F98, 0X71374491, 0XB5C0FBCF, 0XE9B5DBA5, + 0X3956C25B, 0X59F111F1, 0X923F82A4, 0XAB1C5ED5, + 0XD807AA98, 0X12835B01, 0X243185BE, 0X550C7DC3, + 0X72BE5D74, 0X80DEB1FE, 0X9BDC06A7, 0XC19BF174, + 0XE49B69C1, 0XEFBE4786, 0X0FC19DC6, 0X240CA1CC, + 0X2DE92C6F, 0X4A7484AA, 0X5CB0A9DC, 0X76F988DA, + 0X983E5152, 0XA831C66D, 0XB00327C8, 0XBF597FC7, + 0XC6E00BF3, 0XD5A79147, 0X06CA6351, 0X14292967, + 0X27B70A85, 0X2E1B2138, 0X4D2C6DFC, 0X53380D13, + 0X650A7354, 0X766A0ABB, 0X81C2C92E, 0X92722C85, + 0XA2BFE8A1, 0XA81A664B, 0XC24B8B70, 0XC76C51A3, + 0XD192E819, 0XD6990624, 0XF40E3585, 0X106AA070, + 0X19A4C116, 0X1E376C08, 0X2748774C, 0X34B0BCB5, + 0X391C0CB3, 0X4ED8AA4A, 0X5B9CCA4F, 0X682E6FF3, + 0X748F82EE, 0X78A5636F, 0X84C87814, 0X8CC70208, + 0X90BEFFFA, 0XA4506CEB, 0XBEF9A3F7, 0XC67178F2, +}; + +void +sha256_process (sha256_context *ctx) +{ + uint32_t i; + uint32_t *p = ctx->wbuf; + uint32_t v[8]; + + memcpy (v, ctx->state, 8 * sizeof (uint32_t)); + + v_cycle0 ( 0); v_cycle0 ( 1); v_cycle0 ( 2); v_cycle0 ( 3); + v_cycle0 ( 4); v_cycle0 ( 5); v_cycle0 ( 6); v_cycle0 ( 7); + v_cycle0 ( 8); v_cycle0 ( 9); v_cycle0 (10); v_cycle0 (11); + v_cycle0 (12); v_cycle0 (13); v_cycle0 (14); v_cycle0 (15); + + for (i = 16; i < 64; i += 16) + { + v_cycle ( 0, i); v_cycle ( 1, i); v_cycle ( 2, i); v_cycle ( 3, i); + v_cycle ( 4, i); v_cycle ( 5, i); v_cycle ( 6, i); v_cycle ( 7, i); + v_cycle ( 8, i); v_cycle ( 9, i); v_cycle (10, i); v_cycle (11, i); + v_cycle (12, i); v_cycle (13, i); v_cycle (14, i); v_cycle (15, i); + } + + ctx->state[0] += v[0]; + ctx->state[1] += v[1]; + ctx->state[2] += v[2]; + ctx->state[3] += v[3]; + ctx->state[4] += v[4]; + ctx->state[5] += v[5]; + ctx->state[6] += v[6]; + ctx->state[7] += v[7]; +} + +void +sha256_update (sha256_context *ctx, const unsigned char *input, + unsigned int ilen) +{ + uint32_t left = (ctx->total[0] & SHA256_MASK); + uint32_t fill = SHA256_BLOCK_SIZE - left; + + ctx->total[0] += ilen; + if (ctx->total[0] < ilen) + ctx->total[1]++; + + while (ilen >= fill) + { + memcpy (((unsigned char*)ctx->wbuf) + left, input, fill); + sha256_process (ctx); + input += fill; + ilen -= fill; + left = 0; + fill = SHA256_BLOCK_SIZE; + } + + memcpy (((unsigned char*)ctx->wbuf) + left, input, ilen); +} + +void +sha256_finish (sha256_context *ctx, unsigned char output[32]) +{ + uint32_t last = (ctx->total[0] & SHA256_MASK); + + ctx->wbuf[last >> 2] = __builtin_bswap32 (ctx->wbuf[last >> 2]); + ctx->wbuf[last >> 2] &= 0xffffff80 << (8 * (~last & 3)); + ctx->wbuf[last >> 2] |= 0x00000080 << (8 * (~last & 3)); + ctx->wbuf[last >> 2] = __builtin_bswap32 (ctx->wbuf[last >> 2]); + + if (last > SHA256_BLOCK_SIZE - 9) + { + if (last < 60) + ctx->wbuf[15] = 0; + sha256_process (ctx); + last = 0; + } + else + last = (last >> 2) + 1; + + while (last < 14) + ctx->wbuf[last++] = 0; + + ctx->wbuf[14] = __builtin_bswap32 ((ctx->total[0] >> 29) | (ctx->total[1] << 3)); + ctx->wbuf[15] = __builtin_bswap32 (ctx->total[0] << 3); + sha256_process (ctx); + + memcpy_output_bswap32 (output, ctx->state); + memset (ctx, 0, sizeof (sha256_context)); +} + +static const uint32_t initial_state[8] = +{ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 +}; + +void +sha256_start (sha256_context *ctx) +{ + ctx->total[0] = ctx->total[1] = 0; + memcpy (ctx->state, initial_state, 8 * sizeof(uint32_t)); +} + +void +sha256 (const unsigned char *input, unsigned int ilen, + unsigned char output[32]) +{ + sha256_context ctx; + + sha256_start (&ctx); + sha256_update (&ctx, input, ilen); + sha256_finish (&ctx, output); +} diff --git a/gnuk/src/sha256.h b/gnuk/src/sha256.h new file mode 100644 index 0000000000..1454c3f508 --- /dev/null +++ b/gnuk/src/sha256.h @@ -0,0 +1,17 @@ +#define SHA256_DIGEST_SIZE 32 +#define SHA256_BLOCK_SIZE 64 + +typedef struct +{ + uint32_t total[2]; + uint32_t state[8]; + uint32_t wbuf[16]; +} sha256_context; + +void sha256 (const unsigned char *input, unsigned int ilen, + unsigned char output[32]); +void sha256_start (sha256_context *ctx); +void sha256_finish (sha256_context *ctx, unsigned char output[32]); +void sha256_update (sha256_context *ctx, const unsigned char *input, + unsigned int ilen); +void sha256_process (sha256_context *ctx); diff --git a/gnuk/src/sha512.c b/gnuk/src/sha512.c new file mode 100644 index 0000000000..4931a31796 --- /dev/null +++ b/gnuk/src/sha512.c @@ -0,0 +1,215 @@ +/* + * sha512.c -- Compute SHA-512 hash (for little endian architecture). + * + * This module is written by gniibe, following the API of sha256.c. + * + * Copyright (C) 2014 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 . + * + */ + +/* + * Reference: + * + * [1] FIPS PUB 180-4: Secure hash Standard (SHS), March, 2012. + * + */ + +#include +#include +#include "sha512.h" + +#define SHA512_MASK (SHA512_BLOCK_SIZE - 1) + +static void memcpy_output_bswap64 (unsigned char dst[64], const uint64_t *p) +{ + int i; + uint64_t q = 0; + + for (i = 0; i < 64; i++) + { + if ((i & 7) == 0) + q = __builtin_bswap64 (p[i >> 3]); /* bswap64 is GCC extention */ + dst[i] = q >> ((i & 7) * 8); + } +} + +#define rotr64(x,n) (((x) >> n) | ((x) << (64 - n))) + +#define ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z)))) +#define maj(x,y,z) (((x) & (y)) | ((z) & ((x) ^ (y)))) + +/* round transforms for SHA512 compression functions */ +#define vf(n,i) v[(n - i) & 7] + +#define hf(i) (p[i & 15] += \ + g_1(p[(i + 14) & 15]) + p[(i + 9) & 15] + g_0(p[(i + 1) & 15])) + +#define v_cycle0(i) \ + p[i] = __builtin_bswap64 (p[i]); \ + vf(7,i) += p[i] + k_0[i] \ + + s_1(vf(4,i)) + ch(vf(4,i),vf(5,i),vf(6,i)); \ + vf(3,i) += vf(7,i); \ + vf(7,i) += s_0(vf(0,i))+ maj(vf(0,i),vf(1,i),vf(2,i)) + +#define v_cycle(i, j) \ + vf(7,i) += hf(i) + k_0[i+j] \ + + s_1(vf(4,i)) + ch(vf(4,i),vf(5,i),vf(6,i)); \ + vf(3,i) += vf(7,i); \ + vf(7,i) += s_0(vf(0,i))+ maj(vf(0,i),vf(1,i),vf(2,i)) + +#define s_0(x) (rotr64((x), 28) ^ rotr64((x), 34) ^ rotr64((x), 39)) +#define s_1(x) (rotr64((x), 14) ^ rotr64((x), 18) ^ rotr64((x), 41)) +#define g_0(x) (rotr64((x), 1) ^ rotr64((x), 8) ^ ((x) >> 7)) +#define g_1(x) (rotr64((x), 19) ^ rotr64((x), 61) ^ ((x) >> 6)) +#define k_0 k512 + +/* Taken from section 4.2.3 of [1]. */ +static const uint64_t k512[80] = { +0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, +0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, +0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, +0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694, +0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, +0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, +0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, +0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70, +0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, +0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, +0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, +0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, +0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, +0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, +0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, +0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, +0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, +0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, +0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, +0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 +}; + +void +sha512_process (sha512_context *ctx) +{ + uint32_t i; + uint64_t *p = ctx->wbuf; + uint64_t v[8]; + + memcpy (v, ctx->state, 8 * sizeof (uint64_t)); + + v_cycle0 ( 0); v_cycle0 ( 1); v_cycle0 ( 2); v_cycle0 ( 3); + v_cycle0 ( 4); v_cycle0 ( 5); v_cycle0 ( 6); v_cycle0 ( 7); + v_cycle0 ( 8); v_cycle0 ( 9); v_cycle0 (10); v_cycle0 (11); + v_cycle0 (12); v_cycle0 (13); v_cycle0 (14); v_cycle0 (15); + + for (i = 16; i < 80; i += 16) + { + v_cycle ( 0, i); v_cycle ( 1, i); v_cycle ( 2, i); v_cycle ( 3, i); + v_cycle ( 4, i); v_cycle ( 5, i); v_cycle ( 6, i); v_cycle ( 7, i); + v_cycle ( 8, i); v_cycle ( 9, i); v_cycle (10, i); v_cycle (11, i); + v_cycle (12, i); v_cycle (13, i); v_cycle (14, i); v_cycle (15, i); + } + + ctx->state[0] += v[0]; + ctx->state[1] += v[1]; + ctx->state[2] += v[2]; + ctx->state[3] += v[3]; + ctx->state[4] += v[4]; + ctx->state[5] += v[5]; + ctx->state[6] += v[6]; + ctx->state[7] += v[7]; +} + +void +sha512_update (sha512_context *ctx, const unsigned char *input, + unsigned int ilen) +{ + uint32_t left = (ctx->total[0] & SHA512_MASK); + uint32_t fill = SHA512_BLOCK_SIZE - left; + + ctx->total[0] += ilen; + if (ctx->total[0] < ilen) + ctx->total[1]++; + + while (ilen >= fill) + { + memcpy (((unsigned char*)ctx->wbuf) + left, input, fill); + sha512_process (ctx); + input += fill; + ilen -= fill; + left = 0; + fill = SHA512_BLOCK_SIZE; + } + + memcpy (((unsigned char*)ctx->wbuf) + left, input, ilen); +} + +void +sha512_finish (sha512_context *ctx, unsigned char output[64]) +{ + uint32_t last = (ctx->total[0] & SHA512_MASK); + + ctx->wbuf[last >> 3] = __builtin_bswap64 (ctx->wbuf[last >> 3]); + ctx->wbuf[last >> 3] &= 0xffffffffffffff80LL << (8 * (~last & 7)); + ctx->wbuf[last >> 3] |= 0x0000000000000080LL << (8 * (~last & 7)); + ctx->wbuf[last >> 3] = __builtin_bswap64 (ctx->wbuf[last >> 3]); + + if (last > SHA512_BLOCK_SIZE - 17) + { + if (last < 120) + ctx->wbuf[15] = 0; + sha512_process (ctx); + last = 0; + } + else + last = (last >> 3) + 1; + + while (last < 14) + ctx->wbuf[last++] = 0; + + ctx->wbuf[14] = __builtin_bswap64 ((ctx->total[0] >> 61) | (ctx->total[1] << 3)); + ctx->wbuf[15] = __builtin_bswap64 (ctx->total[0] << 3); + sha512_process (ctx); + + memcpy_output_bswap64 (output, ctx->state); + memset (ctx, 0, sizeof (sha512_context)); +} + +/* Taken from section 5.3.5 of [1]. */ +static const uint64_t initial_state[8] = { +0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, +0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179 +}; + +void +sha512_start (sha512_context *ctx) +{ + ctx->total[0] = ctx->total[1] = 0; + memcpy (ctx->state, initial_state, 8 * sizeof(uint64_t)); +} + +void +sha512 (const unsigned char *input, unsigned int ilen, + unsigned char output[64]) +{ + sha512_context ctx; + + sha512_start (&ctx); + sha512_update (&ctx, input, ilen); + sha512_finish (&ctx, output); +} diff --git a/gnuk/src/sha512.h b/gnuk/src/sha512.h new file mode 100644 index 0000000000..221f81e6c8 --- /dev/null +++ b/gnuk/src/sha512.h @@ -0,0 +1,17 @@ +#define SHA512_DIGEST_SIZE 64 +#define SHA512_BLOCK_SIZE 128 + +typedef struct +{ + uint64_t total[2]; + uint64_t state[8]; + uint64_t wbuf[16]; +} sha512_context; + +void sha512 (const unsigned char *input, unsigned int ilen, + unsigned char output[64]); +void sha512_start (sha512_context *ctx); +void sha512_finish (sha512_context *ctx, unsigned char output[64]); +void sha512_update (sha512_context *ctx, const unsigned char *input, + unsigned int ilen); +void sha512_process (sha512_context *ctx); diff --git a/gnuk/src/stack-def.h b/gnuk/src/stack-def.h new file mode 100644 index 0000000000..421529005f --- /dev/null +++ b/gnuk/src/stack-def.h @@ -0,0 +1,65 @@ +#ifdef GNU_LINUX_EMULATION +#define SIZE_1 4096 +#define SIZE_2 4096 +#define SIZE_3 (5 * 4096) +#else +#define SIZE_0 0x0100 /* Main */ +#define SIZE_1 0x01a0 /* CCID */ +#define SIZE_2 0x0180 /* RNG */ +#if MEMORY_SIZE >= 32 +#define SIZE_3 0x4640 /* openpgp-card */ +#elif MEMORY_SIZE >= 24 +#define SIZE_3 0x2640 /* openpgp-card */ +#else +#define SIZE_3 0x1640 /* openpgp-card */ +#endif +#define SIZE_4 0x0000 /* --- */ +#define SIZE_5 0x0200 /* msc */ +#define SIZE_6 0x00c0 /* timer (cir) */ +#define SIZE_7 0x00c0 /* ext (cir) */ +#endif + +#if defined(STACK_MAIN) && !defined(GNU_LINUX_EMULATION) +/* Idle+Exception handlers */ +char __main_stack_end__[0] __attribute__ ((section(".main_stack"))); +char main_base[0x0080] __attribute__ ((section(".main_stack"))); + +/* Main program */ +char __process0_stack_end__[0] __attribute__ ((section(".process_stack.0"))); +char process0_base[SIZE_0] __attribute__ ((section(".process_stack.0"))); +#endif + +/* First thread program */ +#if defined(STACK_PROCESS_1) +char process1_base[SIZE_1] __attribute__ ((section(".process_stack.1"))); +#endif + +/* Second thread program */ +#if defined(STACK_PROCESS_2) +char process2_base[SIZE_2] __attribute__ ((section(".process_stack.2"))); +#endif + +/* Third thread program */ +#if defined(STACK_PROCESS_3) +char process3_base[SIZE_3] __attribute__ ((section(".process_stack.3"))); +#endif + +/* Fourth thread program */ +#if defined(STACK_PROCESS_4) +char process4_base[SIZE_4] __attribute__ ((section(".process_stack.4"))); +#endif + +/* Fifth thread program */ +#if defined(STACK_PROCESS_5) +char process5_base[SIZE_5] __attribute__ ((section(".process_stack.5"))); +#endif + +/* Sixth thread program */ +#if defined(STACK_PROCESS_6) +char process6_base[SIZE_6] __attribute__ ((section(".process_stack.6"))); +#endif + +/* Seventh thread program */ +#if defined(STACK_PROCESS_7) +char process7_base[SIZE_7] __attribute__ ((section(".process_stack.7"))); +#endif diff --git a/gnuk/src/status-code.h b/gnuk/src/status-code.h new file mode 100644 index 0000000000..537d56cfcd --- /dev/null +++ b/gnuk/src/status-code.h @@ -0,0 +1,14 @@ +#define GPG_APPLICATION_TERMINATED() set_res_sw (0x62, 0x85) +#define GPG_MEMORY_FAILURE() set_res_sw (0x65, 0x81) +#define GPG_WRONG_LENGTH() set_res_sw (0x67, 0x00) +#define GPG_SECURITY_FAILURE() set_res_sw (0x69, 0x82) +#define GPG_SECURITY_AUTH_BLOCKED() set_res_sw (0x69, 0x83) +#define GPG_CONDITION_NOT_SATISFIED() set_res_sw (0x69, 0x85) +#define GPG_COMMAND_NOT_ALLOWED() set_res_sw (0x69, 0x86) +#define GPG_FUNCTION_NOT_SUPPORTED() set_res_sw (0x6a, 0x81) +#define GPG_NO_FILE() set_res_sw (0x6a, 0x82) +#define GPG_NO_RECORD() set_res_sw (0x6a, 0x88) +#define GPG_BAD_P1_P2() set_res_sw (0x6b, 0x00) +#define GPG_NO_INS() set_res_sw (0x6d, 0x00) +#define GPG_ERROR() set_res_sw (0x6f, 0x00) +#define GPG_SUCCESS() set_res_sw (0x90, 0x00) diff --git a/gnuk/src/usb-ccid.c b/gnuk/src/usb-ccid.c new file mode 100644 index 0000000000..656f695c50 --- /dev/null +++ b/gnuk/src/usb-ccid.c @@ -0,0 +1,1890 @@ +/* + * usb-ccid.c -- USB CCID protocol handling + * + * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 + * 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 +#include +#include +#include + +#include "config.h" + +#ifdef DEBUG +#include "usb-cdc.h" +#include "debug.h" +struct stdout stdout; +#endif + +#include "gnuk.h" +#include "usb_lld.h" +#include "usb_conf.h" + +/* + * USB buffer size of USB-CCID driver + */ +#if MAX_RES_APDU_DATA_SIZE > MAX_CMD_APDU_DATA_SIZE +#define USB_BUF_SIZE (MAX_RES_APDU_DATA_SIZE+5) +#else +#define USB_BUF_SIZE (MAX_CMD_APDU_DATA_SIZE+5) +#endif + +struct apdu apdu; + +/* + * There are three layers in USB CCID implementation + * + * +-------------------+ + * | Application Layer | + * +-------------------+ + * ^ command APDU | + * | v response APDU + * +-------------------+ + * | CCID Layer | + * +-------------------+ + * ^ CCID PC_to_RDR | CCID RDR_to_PC + * | Message v Message + * +-------------------+ + * | USB Layer | + * +-------------------+ + * ^ USB | USB + * | Bulk-OUT Packet v Bulk-IN Packet + * + */ + +/* + * USB layer data structures + */ + +struct ep_in { + uint8_t ep_num; + uint8_t tx_done; + const uint8_t *buf; + size_t cnt; + size_t buf_len; + void *priv; + void (*next_buf) (struct ep_in *epi, size_t len); +}; + +static void epi_init (struct ep_in *epi, int ep_num, void *priv) +{ + epi->ep_num = ep_num; + epi->tx_done = 0; + epi->buf = NULL; + epi->cnt = 0; + epi->buf_len = 0; + epi->priv = priv; + epi->next_buf = NULL; +} + +struct ep_out { + uint8_t ep_num; + uint8_t err; + uint8_t *buf; + size_t cnt; + size_t buf_len; + void *priv; + void (*next_buf) (struct ep_out *epo, size_t len); + int (*end_rx) (struct ep_out *epo, size_t orig_len); +}; + +static struct ep_out endpoint_out; +static struct ep_in endpoint_in; + +static void epo_init (struct ep_out *epo, int ep_num, void *priv) +{ + epo->ep_num = ep_num; + epo->err = 0; + epo->buf = NULL; + epo->cnt = 0; + epo->buf_len = 0; + epo->priv = priv; + epo->next_buf = NULL; + epo->end_rx = NULL; +} + +/* + * CCID Layer + */ + +/* + * Buffer of USB communication: for both of RX and TX + * + * The buffer will be filled by multiple RX packets (Bulk-OUT) + * or will be used for multiple TX packets (Bulk-IN) + */ +static uint8_t ccid_buffer[USB_BUF_SIZE]; + +#define CCID_SET_PARAMS 0x61 /* non-ICCD command */ +#define CCID_POWER_ON 0x62 +#define CCID_POWER_OFF 0x63 +#define CCID_SLOT_STATUS 0x65 /* non-ICCD command */ +#define CCID_SECURE 0x69 /* non-ICCD command */ +#define CCID_GET_PARAMS 0x6C /* non-ICCD command */ +#define CCID_RESET_PARAMS 0x6D /* non-ICCD command */ +#define CCID_XFR_BLOCK 0x6F +#define CCID_DATA_BLOCK_RET 0x80 +#define CCID_SLOT_STATUS_RET 0x81 /* non-ICCD result */ +#define CCID_PARAMS_RET 0x82 /* non-ICCD result */ + +#define CCID_MSG_SEQ_OFFSET 6 +#define CCID_MSG_STATUS_OFFSET 7 +#define CCID_MSG_ERROR_OFFSET 8 +#define CCID_MSG_CHAIN_OFFSET 9 +#define CCID_MSG_DATA_OFFSET 10 /* == CCID_MSG_HEADER_SIZE */ +#define CCID_MAX_MSG_DATA_SIZE USB_BUF_SIZE + +#define CCID_STATUS_RUN 0x00 +#define CCID_STATUS_PRESENT 0x01 +#define CCID_STATUS_NOTPRESENT 0x02 +#define CCID_CMD_STATUS_OK 0x00 +#define CCID_CMD_STATUS_ERROR 0x40 +#define CCID_CMD_STATUS_TIMEEXT 0x80 + +#define CCID_ERROR_XFR_OVERRUN 0xFC + +/* + * Since command-byte is at offset 0, + * error with offset 0 means "command not supported". + */ +#define CCID_OFFSET_CMD_NOT_SUPPORTED 0 +#define CCID_OFFSET_DATA_LEN 1 +#define CCID_OFFSET_PARAM 8 + +struct ccid_header { + uint8_t msg_type; + uint32_t data_len; + uint8_t slot; + uint8_t seq; + uint8_t rsvd; + uint16_t param; +} __attribute__((packed)); + + +/* Data structure handled by CCID layer */ +struct ccid { + enum ccid_state ccid_state; + uint8_t state; + uint8_t err; + + uint8_t *p; + size_t len; + + struct ccid_header ccid_header; + + uint8_t sw1sw2[2]; + uint8_t chained_cls_ins_p1_p2[4]; + + /* lower layer */ + struct ep_out *epo; + struct ep_in *epi; + + /* from both layers */ + struct eventflag ccid_comm; + + /* upper layer */ + struct eventflag openpgp_comm; + chopstx_t application; + struct apdu *a; +}; + +/* + * APDU_STATE_WAIT_COMMAND +---------+ + * | | | | + * | v v | + * | APDU_STATE_COMMAND_CHAINING --+ + * | | + * v v + * APDU_STATE_COMMAND_RECEIVED + * | + * v + * =================== + * | Process COMMAND | + * =================== + * | + * v + * +-----+----------+ +---------+ + * | | | | + * v v v | + * APDU_STATE_RESULT <---- APDU_STATE_RESULT_GET_RESPONSE --+ + * | + * | + * v + * APDU_STATE_WAIT_COMMAND + */ + +#define APDU_STATE_WAIT_COMMAND 0 +#define APDU_STATE_COMMAND_CHAINING 1 +#define APDU_STATE_COMMAND_RECEIVED 2 +#define APDU_STATE_RESULT 3 +#define APDU_STATE_RESULT_GET_RESPONSE 4 + +static void ccid_reset (struct ccid *c) +{ + c->err = 0; + c->state = APDU_STATE_WAIT_COMMAND; + c->p = c->a->cmd_apdu_data; + c->len = MAX_CMD_APDU_DATA_SIZE; + c->a->cmd_apdu_data_len = 0; + c->a->expected_res_size = 0; +} + +static void ccid_init (struct ccid *c, struct ep_in *epi, struct ep_out *epo, + struct apdu *a) +{ + c->ccid_state = CCID_STATE_START; + c->state = APDU_STATE_WAIT_COMMAND; + c->p = a->cmd_apdu_data; + c->len = MAX_CMD_APDU_DATA_SIZE; + c->err = 0; + memset (&c->ccid_header, 0, sizeof (struct ccid_header)); + c->sw1sw2[0] = 0x90; + c->sw1sw2[1] = 0x00; + c->application = 0; + c->epi = epi; + c->epo = epo; + c->a = a; +} + +/* + * Application layer + */ + +/* + * USB-CCID communication could be considered "half duplex". + * + * While the device is sending something, there is no possibility for + * the device to receive anything. + * + * While the device is receiving something, there is no possibility + * for the device to send anything. + * + * Thus, the buffer can be shared for RX and TX. + * + * Exception: When we support ABORT of CCID, it is possible to receive + * ABORT Class Specific Request to control pipe while we are + * receiving/sending something at OUT/IN endpoint. + * + */ + +#define CMD_APDU_HEAD_SIZE 5 + +static void apdu_init (struct apdu *a) +{ + a->seq = 0; /* will be set by lower layer */ + a->cmd_apdu_head = &ccid_buffer[0]; + a->cmd_apdu_data = &ccid_buffer[5]; + a->cmd_apdu_data_len = 0; /* will be set by lower layer */ + a->expected_res_size = 0; /* will be set by lower layer */ + + a->sw = 0x9000; /* will be set by upper layer */ + a->res_apdu_data = &ccid_buffer[5]; /* will be set by upper layer */ + a->res_apdu_data_len = 0; /* will be set by upper layer */ +} + + +static void notify_tx (struct ep_in *epi) +{ + struct ccid *c = (struct ccid *)epi->priv; + + /* The sequence of Bulk-IN transactions finished */ + eventflag_signal (&c->ccid_comm, EV_TX_FINISHED); +} + +static void no_buf (struct ep_in *epi, size_t len) +{ + (void)len; + epi->buf = NULL; + epi->cnt = 0; + epi->buf_len = 0; +} + +static void set_sw1sw2 (struct ccid *c, size_t chunk_len) +{ + if (c->a->expected_res_size >= c->len) + { + c->sw1sw2[0] = 0x90; + c->sw1sw2[1] = 0x00; + } + else + { + c->sw1sw2[0] = 0x61; + if (c->len - chunk_len >= 256) + c->sw1sw2[1] = 0; + else + c->sw1sw2[1] = (uint8_t)(c->len - chunk_len); + } +} + +static void get_sw1sw2 (struct ep_in *epi, size_t len) +{ + struct ccid *c = (struct ccid *)epi->priv; + + (void)len; + epi->buf = c->sw1sw2; + epi->cnt = 0; + epi->buf_len = 2; + epi->next_buf = no_buf; +} + +#ifdef GNU_LINUX_EMULATION +static uint8_t endp1_tx_buf[64]; /* Only support single CCID interface. */ +#endif + +/* + * Tx done callback + */ +static void +ccid_tx_done (uint8_t ep_num, uint16_t len) +{ + /* + * If we support multiple CCID interfaces, we select endpoint object + * by EP_NUM. Because it has only single CCID interface now, it's + * hard-coded, here. + */ + struct ep_in *epi = &endpoint_in; + + (void)len; + if (epi->buf == NULL) + if (epi->tx_done) + notify_tx (epi); + else + { + epi->tx_done = 1; + /* send ZLP */ +#ifdef GNU_LINUX_EMULATION + usb_lld_tx_enable_buf (ep_num, endp1_tx_buf, 0); +#else + usb_lld_tx_enable (ep_num, 0); +#endif + } + else + { + int tx_size = 0; + size_t remain = USB_LL_BUF_SIZE; + int offset = 0; + + while (epi->buf) + if (epi->buf_len < remain) + { +#ifdef GNU_LINUX_EMULATION + memcpy (endp1_tx_buf+offset, epi->buf, epi->buf_len); +#else + usb_lld_txcpy (epi->buf, ep_num, offset, epi->buf_len); +#endif + offset += epi->buf_len; + remain -= epi->buf_len; + tx_size += epi->buf_len; + epi->next_buf (epi, remain); /* Update epi->buf, cnt, buf_len */ + } + else + { +#ifdef GNU_LINUX_EMULATION + memcpy (endp1_tx_buf+offset, epi->buf, remain); +#else + usb_lld_txcpy (epi->buf, ep_num, offset, remain); +#endif + epi->buf += remain; + epi->cnt += remain; + epi->buf_len -= remain; + tx_size += remain; + break; + } + + if (tx_size < USB_LL_BUF_SIZE) + epi->tx_done = 1; + +#ifdef GNU_LINUX_EMULATION + usb_lld_tx_enable_buf (ep_num, endp1_tx_buf, tx_size); +#else + usb_lld_tx_enable (ep_num, tx_size); +#endif + } +} + + +static void notify_icc (struct ep_out *epo) +{ + struct ccid *c = (struct ccid *)epo->priv; + + c->err = epo->err; + eventflag_signal (&c->ccid_comm, EV_RX_DATA_READY); +} + +static int end_ccid_rx (struct ep_out *epo, size_t orig_len) +{ + (void)orig_len; + if (epo->cnt < sizeof (struct ccid_header)) + /* short packet, just ignore */ + return 1; + + /* icc message with no abdata */ + return 0; +} + +static int end_abdata (struct ep_out *epo, size_t orig_len) +{ + struct ccid *c = (struct ccid *)epo->priv; + size_t len = epo->cnt; + + if (orig_len == USB_LL_BUF_SIZE && len < c->ccid_header.data_len) + /* more packet comes */ + return 1; + + if (len != c->ccid_header.data_len) + epo->err = 1; + + return 0; +} + +static int end_cmd_apdu_head (struct ep_out *epo, size_t orig_len) +{ + struct ccid *c = (struct ccid *)epo->priv; + + (void)orig_len; + + if (epo->cnt < 4 || epo->cnt != c->ccid_header.data_len) + { + epo->err = 1; + return 0; + } + + if ((c->state == APDU_STATE_COMMAND_CHAINING) + && (c->chained_cls_ins_p1_p2[0] != (c->a->cmd_apdu_head[0] & ~0x10) + || c->chained_cls_ins_p1_p2[1] != c->a->cmd_apdu_head[1] + || c->chained_cls_ins_p1_p2[2] != c->a->cmd_apdu_head[2] + || c->chained_cls_ins_p1_p2[3] != c->a->cmd_apdu_head[3])) + /* + * Handling exceptional request. + * + * Host stops sending command APDU using command chaining, + * and start another command APDU. + * + * Discard old one, and start handling new one. + */ + { + c->state = APDU_STATE_WAIT_COMMAND; + c->p = c->a->cmd_apdu_data; + c->len = MAX_CMD_APDU_DATA_SIZE; + } + + if (epo->cnt == 4) + /* No Lc and Le */ + c->a->expected_res_size = 0; + else if (epo->cnt == 5) + { + /* No Lc but Le */ + c->a->expected_res_size = c->a->cmd_apdu_head[4]; + if (c->a->expected_res_size == 0) + c->a->expected_res_size = 256; + c->a->cmd_apdu_head[4] = 0; + } + + c->a->cmd_apdu_data_len = 0; + return 0; +} + + +static int end_nomore_data (struct ep_out *epo, size_t orig_len) +{ + (void)epo; + if (orig_len == USB_LL_BUF_SIZE) + return 1; + else + return 0; +} + + +static int end_cmd_apdu_data (struct ep_out *epo, size_t orig_len) +{ + struct ccid *c = (struct ccid *)epo->priv; + size_t len = epo->cnt; + + if (orig_len == USB_LL_BUF_SIZE + && CMD_APDU_HEAD_SIZE + len < c->ccid_header.data_len) + /* more packet comes */ + return 1; + + if (CMD_APDU_HEAD_SIZE + len != c->ccid_header.data_len) + goto error; + + if (len == c->a->cmd_apdu_head[4]) + /* No Le field*/ + c->a->expected_res_size = 0; + else if (len == (size_t)c->a->cmd_apdu_head[4] + 1) + { + /* it has Le field*/ + c->a->expected_res_size = epo->buf[-1]; + if (c->a->expected_res_size == 0) + c->a->expected_res_size = 256; + len--; + } + else + { + error: + epo->err = 1; + return 0; + } + + c->a->cmd_apdu_data_len += len; + return 0; +} + + +static void nomore_data (struct ep_out *epo, size_t len) +{ + (void)len; + epo->err = 1; + epo->end_rx = end_nomore_data; + epo->buf = NULL; + epo->buf_len = 0; + epo->cnt = 0; + epo->next_buf = nomore_data; +} + +#define INS_GET_RESPONSE 0xc0 + +static void ccid_cmd_apdu_data (struct ep_out *epo, size_t len) +{ + struct ccid *c = (struct ccid *)epo->priv; + + (void)len; + if (c->state == APDU_STATE_RESULT_GET_RESPONSE + && c->a->cmd_apdu_head[1] != INS_GET_RESPONSE) + { + /* + * Handling exceptional request. + * + * Host didn't finish receiving the whole response APDU by GET RESPONSE, + * but initiates another command. + */ + + c->state = APDU_STATE_WAIT_COMMAND; + c->p = c->a->cmd_apdu_data; + c->len = MAX_CMD_APDU_DATA_SIZE; + } + else if (c->state == APDU_STATE_COMMAND_CHAINING) + { + if (c->chained_cls_ins_p1_p2[0] != (c->a->cmd_apdu_head[0] & ~0x10) + || c->chained_cls_ins_p1_p2[1] != c->a->cmd_apdu_head[1] + || c->chained_cls_ins_p1_p2[2] != c->a->cmd_apdu_head[2] + || c->chained_cls_ins_p1_p2[3] != c->a->cmd_apdu_head[3]) + /* + * Handling exceptional request. + * + * Host stops sending command APDU using command chaining, + * and start another command APDU. + * + * Discard old one, and start handling new one. + */ + { + c->state = APDU_STATE_WAIT_COMMAND; + c->p = c->a->cmd_apdu_data; + c->len = MAX_CMD_APDU_DATA_SIZE; + c->a->cmd_apdu_data_len = 0; + } + } + + epo->end_rx = end_cmd_apdu_data; + epo->buf = c->p; + epo->buf_len = c->len; + epo->cnt = 0; + epo->next_buf = nomore_data; +} + +static void ccid_abdata (struct ep_out *epo, size_t len) +{ + struct ccid *c = (struct ccid *)epo->priv; + + (void)len; + c->a->seq = c->ccid_header.seq; + if (c->ccid_header.msg_type == CCID_XFR_BLOCK) + { + c->a->seq = c->ccid_header.seq; + epo->end_rx = end_cmd_apdu_head; + epo->buf = c->a->cmd_apdu_head; + epo->buf_len = 5; + epo->cnt = 0; + epo->next_buf = ccid_cmd_apdu_data; + } + else + { + epo->end_rx = end_abdata; + epo->buf = c->p; + epo->buf_len = c->len; + epo->cnt = 0; + epo->next_buf = nomore_data; + } +} + +#ifdef GNU_LINUX_EMULATION +static uint8_t endp1_rx_buf[64]; /* Only support single CCID interface. */ +#endif + +static void +ccid_prepare_receive (struct ccid *c) +{ + c->epo->err = 0; + c->epo->buf = (uint8_t *)&c->ccid_header; + c->epo->buf_len = sizeof (struct ccid_header); + c->epo->cnt = 0; + c->epo->next_buf = ccid_abdata; + c->epo->end_rx = end_ccid_rx; +#ifdef GNU_LINUX_EMULATION + usb_lld_rx_enable_buf (c->epo->ep_num, endp1_rx_buf, 64); +#else + usb_lld_rx_enable (c->epo->ep_num); +#endif + DEBUG_INFO ("Rx ready\r\n"); +} + +/* + * Rx ready callback + */ +static void +ccid_rx_ready (uint8_t ep_num, uint16_t len) +{ + /* + * If we support multiple CCID interfaces, we select endpoint object + * by EP_NUM. Because it has only single CCID interface now, it's + * hard-coded, here. + */ + struct ep_out *epo = &endpoint_out; + int offset = 0; + int cont; + size_t orig_len = len; + + while (epo->err == 0) + if (len == 0) + break; + else if (len <= epo->buf_len) + { +#ifdef GNU_LINUX_EMULATION + memcpy (epo->buf, endp1_rx_buf + offset, len); +#else + usb_lld_rxcpy (epo->buf, ep_num, offset, len); +#endif + epo->buf += len; + epo->cnt += len; + epo->buf_len -= len; + break; + } + else /* len > buf_len */ + { +#ifdef GNU_LINUX_EMULATION + memcpy (epo->buf, endp1_rx_buf + offset, epo->buf_len); +#else + usb_lld_rxcpy (epo->buf, ep_num, offset, epo->buf_len); +#endif + len -= epo->buf_len; + offset += epo->buf_len; + epo->next_buf (epo, len); /* Update epo->buf, cnt, buf_len */ + } + + /* + * ORIG_LEN to distingush ZLP and the end of transaction + * (ORIG_LEN != USB_LL_BUF_SIZE) + */ + cont = epo->end_rx (epo, orig_len); + + if (cont) +#ifdef GNU_LINUX_EMULATION + usb_lld_rx_enable_buf (ep_num, endp1_rx_buf, 64); +#else + usb_lld_rx_enable (ep_num); +#endif + else + notify_icc (epo); +} + + +extern void EP6_IN_Callback (uint16_t len); + +#if defined(DEBUG) && defined(GNU_LINUX_EMULATION) +static uint8_t endp5_buf[VIRTUAL_COM_PORT_DATA_SIZE]; +#endif + +static void +usb_rx_ready (uint8_t ep_num, uint16_t len) +{ + if (ep_num == ENDP1) + ccid_rx_ready (ep_num, len); +#ifdef DEBUG + else if (ep_num == ENDP5) + { + chopstx_mutex_lock (&stdout.m_dev); +#ifdef GNU_LINUX_EMULATION + usb_lld_rx_enable (ep_num, endp5_buf, VIRTUAL_COM_PORT_DATA_SIZE); +#else + usb_lld_rx_enable (ep_num); +#endif + chopstx_mutex_unlock (&stdout.m_dev); + } +#endif +} + +static void +usb_tx_done (uint8_t ep_num, uint16_t len) +{ + if (ep_num == ENDP1) + ccid_tx_done (ep_num, len); + else if (ep_num == ENDP2) + { + /* INTERRUPT Transfer done */ + } +#ifdef DEBUG + else if (ep_num == ENDP3) + { + chopstx_mutex_lock (&stdout.m_dev); + chopstx_cond_signal (&stdout.cond_dev); + chopstx_mutex_unlock (&stdout.m_dev); + } +#endif +#ifdef PINPAD_DND_SUPPORT + else if (ep_num == ENDP6) + EP6_IN_Callback (len); +#endif +} + +/* + * ATR (Answer To Reset) string + * + * TS = 0x3b: Direct conversion + * T0 = 0xda: TA1, TC1 and TD1 follow, 10 historical bytes + * TA1 = 0x11: FI=1, DI=1 + * TC1 = 0xff + * TD1 = 0x81: TD2 follows, T=1 + * TD2 = 0xb1: TA3, TB3 and TD3 follow, T=1 + * TA3 = 0xFE: IFSC = 254 bytes + * TB3 = 0x55: BWI = 5, CWI = 5 (BWT timeout 3.2 sec) + * TD3 = 0x1f: TA4 follows, T=15 + * TA4 = 0x03: 5V or 3.3V + * + * Minimum: 0x3b, 0x8a, 0x80, 0x01 + * + */ +static const uint8_t ATR_head[] = { + 0x3b, 0xda, 0x11, 0xff, 0x81, 0xb1, 0xfe, 0x55, 0x1f, 0x03, +}; + +/* + * Send back error + */ +static void ccid_error (struct ccid *c, int offset) +{ + uint8_t ccid_reply[CCID_MSG_HEADER_SIZE]; + + ccid_reply[0] = CCID_SLOT_STATUS_RET; /* Any value should be OK */ + ccid_reply[1] = 0x00; + ccid_reply[2] = 0x00; + ccid_reply[3] = 0x00; + ccid_reply[4] = 0x00; + ccid_reply[5] = 0x00; /* Slot */ + ccid_reply[CCID_MSG_SEQ_OFFSET] = c->ccid_header.seq; + if (c->ccid_state == CCID_STATE_NOCARD) + ccid_reply[CCID_MSG_STATUS_OFFSET] = 2; /* 2: No ICC present */ + else if (c->ccid_state == CCID_STATE_START) + /* 1: ICC present but not activated */ + ccid_reply[CCID_MSG_STATUS_OFFSET] = 1; + else + ccid_reply[CCID_MSG_STATUS_OFFSET] = 0; /* An ICC is present and active */ + ccid_reply[CCID_MSG_STATUS_OFFSET] |= CCID_CMD_STATUS_ERROR; /* Failed */ + ccid_reply[CCID_MSG_ERROR_OFFSET] = offset; + ccid_reply[CCID_MSG_CHAIN_OFFSET] = 0x00; + + /* This is a single packet Bulk-IN transaction */ + c->epi->buf = NULL; + c->epi->tx_done = 1; +#ifdef GNU_LINUX_EMULATION + memcpy (endp1_tx_buf, ccid_reply, CCID_MSG_HEADER_SIZE); + usb_lld_tx_enable_buf (c->epi->ep_num, endp1_tx_buf, CCID_MSG_HEADER_SIZE); +#else + usb_lld_write (c->epi->ep_num, ccid_reply, CCID_MSG_HEADER_SIZE); +#endif +} + +extern void *openpgp_card_thread (void *arg); + +#define STACK_PROCESS_3 +#include "stack-def.h" +#define STACK_ADDR_GPG ((uintptr_t)process3_base) +#define STACK_SIZE_GPG (sizeof process3_base) + +#define PRIO_GPG 1 + + +/* Send back ATR (Answer To Reset) */ +static enum ccid_state +ccid_power_on (struct ccid *c) +{ + uint8_t p[CCID_MSG_HEADER_SIZE+1]; /* >= size of historical_bytes -1 */ + int hist_len = historical_bytes[0]; + size_t size_atr = sizeof (ATR_head) + hist_len + 1; + uint8_t xor_check = 0; + int i; + + if (c->application == 0) + c->application = chopstx_create (PRIO_GPG, STACK_ADDR_GPG, + STACK_SIZE_GPG, openpgp_card_thread, + (void *)&c->ccid_comm); + + p[0] = CCID_DATA_BLOCK_RET; + p[1] = size_atr; + p[2] = 0x00; + p[3] = 0x00; + p[4] = 0x00; + p[5] = 0x00; /* Slot */ + p[CCID_MSG_SEQ_OFFSET] = c->ccid_header.seq; + p[CCID_MSG_STATUS_OFFSET] = 0x00; + p[CCID_MSG_ERROR_OFFSET] = 0x00; + p[CCID_MSG_CHAIN_OFFSET] = 0x00; + +#ifdef GNU_LINUX_EMULATION + memcpy (endp1_tx_buf, p, CCID_MSG_HEADER_SIZE); + memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE, ATR_head, sizeof (ATR_head)); +#else + usb_lld_txcpy (p, c->epi->ep_num, 0, CCID_MSG_HEADER_SIZE); + usb_lld_txcpy (ATR_head, c->epi->ep_num, CCID_MSG_HEADER_SIZE, + sizeof (ATR_head)); +#endif + for (i = 1; i < (int)sizeof (ATR_head); i++) + xor_check ^= ATR_head[i]; + memcpy (p, historical_bytes + 1, hist_len); +#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT + if (file_selection == 255) + p[7] = 0x03; +#endif + for (i = 0; i < hist_len; i++) + xor_check ^= p[i]; + p[i] = xor_check; +#ifdef GNU_LINUX_EMULATION + memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE+sizeof (ATR_head), p, hist_len+1); +#else + usb_lld_txcpy (p, c->epi->ep_num, CCID_MSG_HEADER_SIZE + sizeof (ATR_head), + hist_len+1); +#endif + + /* This is a single packet Bulk-IN transaction */ + c->epi->buf = NULL; + c->epi->tx_done = 1; +#ifdef GNU_LINUX_EMULATION + usb_lld_tx_enable_buf (c->epi->ep_num, endp1_tx_buf, + CCID_MSG_HEADER_SIZE + size_atr); +#else + usb_lld_tx_enable (c->epi->ep_num, CCID_MSG_HEADER_SIZE + size_atr); +#endif + DEBUG_INFO ("ON\r\n"); + + return CCID_STATE_WAIT; +} + +static void +ccid_send_status (struct ccid *c) +{ + uint8_t ccid_reply[CCID_MSG_HEADER_SIZE]; + + ccid_reply[0] = CCID_SLOT_STATUS_RET; + ccid_reply[1] = 0x00; + ccid_reply[2] = 0x00; + ccid_reply[3] = 0x00; + ccid_reply[4] = 0x00; + ccid_reply[5] = 0x00; /* Slot */ + ccid_reply[CCID_MSG_SEQ_OFFSET] = c->ccid_header.seq; + if (c->ccid_state == CCID_STATE_NOCARD) + ccid_reply[CCID_MSG_STATUS_OFFSET] = 2; /* 2: No ICC present */ + else if (c->ccid_state == CCID_STATE_START) + /* 1: ICC present but not activated */ + ccid_reply[CCID_MSG_STATUS_OFFSET] = 1; + else + ccid_reply[CCID_MSG_STATUS_OFFSET] = 0; /* An ICC is present and active */ + ccid_reply[CCID_MSG_ERROR_OFFSET] = 0x00; + ccid_reply[CCID_MSG_CHAIN_OFFSET] = 0x00; + + /* This is a single packet Bulk-IN transaction */ + c->epi->buf = NULL; + c->epi->tx_done = 1; + +#ifdef GNU_LINUX_EMULATION + memcpy (endp1_tx_buf, ccid_reply, CCID_MSG_HEADER_SIZE); + usb_lld_tx_enable_buf (c->epi->ep_num, endp1_tx_buf, CCID_MSG_HEADER_SIZE); +#else + usb_lld_write (c->epi->ep_num, ccid_reply, CCID_MSG_HEADER_SIZE); +#endif + + led_blink (LED_SHOW_STATUS); +#ifdef DEBUG_MORE + DEBUG_INFO ("St\r\n"); +#endif +} + +static enum ccid_state +ccid_power_off (struct ccid *c) +{ + if (c->application) + { + eventflag_signal (&c->openpgp_comm, EV_EXIT); + chopstx_join (c->application, NULL); + c->application = 0; + } + + c->ccid_state = CCID_STATE_START; /* This status change should be here */ + ccid_send_status (c); + DEBUG_INFO ("OFF\r\n"); + return CCID_STATE_START; +} + +static void +ccid_send_data_block_internal (struct ccid *c, uint8_t status, uint8_t error) +{ + int tx_size = USB_LL_BUF_SIZE; + uint8_t p[CCID_MSG_HEADER_SIZE]; + size_t len; + + if (status == 0) + len = c->a->res_apdu_data_len + 2; + else + len = 0; + + p[0] = CCID_DATA_BLOCK_RET; + p[1] = len & 0xFF; + p[2] = (len >> 8)& 0xFF; + p[3] = (len >> 16)& 0xFF; + p[4] = (len >> 24)& 0xFF; + p[5] = 0x00; /* Slot */ + p[CCID_MSG_SEQ_OFFSET] = c->a->seq; + p[CCID_MSG_STATUS_OFFSET] = status; + p[CCID_MSG_ERROR_OFFSET] = error; + p[CCID_MSG_CHAIN_OFFSET] = 0; + +#ifdef GNU_LINUX_EMULATION + memcpy (endp1_tx_buf, p, CCID_MSG_HEADER_SIZE); +#else + usb_lld_txcpy (p, c->epi->ep_num, 0, CCID_MSG_HEADER_SIZE); +#endif + if (len == 0) + { +#ifdef GNU_LINUX_EMULATION + usb_lld_tx_enable_buf (c->epi->ep_num, endp1_tx_buf, + CCID_MSG_HEADER_SIZE); +#else + usb_lld_tx_enable (c->epi->ep_num, CCID_MSG_HEADER_SIZE); +#endif + return; + } + + if (CCID_MSG_HEADER_SIZE + len <= USB_LL_BUF_SIZE) + { +#ifdef GNU_LINUX_EMULATION + memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE, + c->a->res_apdu_data, c->a->res_apdu_data_len); + memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE+c->a->res_apdu_data_len, + c->sw1sw2, 2); +#else + usb_lld_txcpy (c->a->res_apdu_data, c->epi->ep_num, + CCID_MSG_HEADER_SIZE, c->a->res_apdu_data_len); + usb_lld_txcpy (c->sw1sw2, c->epi->ep_num, + CCID_MSG_HEADER_SIZE + c->a->res_apdu_data_len, 2); +#endif + c->epi->buf = NULL; + if (CCID_MSG_HEADER_SIZE + len < USB_LL_BUF_SIZE) + c->epi->tx_done = 1; + tx_size = CCID_MSG_HEADER_SIZE + len; + } + else if (CCID_MSG_HEADER_SIZE + len - 1 == USB_LL_BUF_SIZE) + { +#ifdef GNU_LINUX_EMULATION + memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE, + c->a->res_apdu_data, c->a->res_apdu_data_len); + memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE+c->a->res_apdu_data_len, + c->sw1sw2, 1); +#else + usb_lld_txcpy (c->a->res_apdu_data, c->epi->ep_num, + CCID_MSG_HEADER_SIZE, c->a->res_apdu_data_len); + usb_lld_txcpy (c->sw1sw2, c->epi->ep_num, + CCID_MSG_HEADER_SIZE + c->a->res_apdu_data_len, 1); +#endif + c->epi->buf = &c->sw1sw2[1]; + c->epi->cnt = 1; + c->epi->buf_len = 1; + c->epi->next_buf = no_buf; + } + else if (CCID_MSG_HEADER_SIZE + len - 2 == USB_LL_BUF_SIZE) + { +#ifdef GNU_LINUX_EMULATION + memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE, + c->a->res_apdu_data, c->a->res_apdu_data_len); +#else + usb_lld_txcpy (c->a->res_apdu_data, c->epi->ep_num, + CCID_MSG_HEADER_SIZE, c->a->res_apdu_data_len); +#endif + c->epi->buf = &c->sw1sw2[0]; + c->epi->cnt = 0; + c->epi->buf_len = 2; + c->epi->next_buf = no_buf; + } + else + { +#ifdef GNU_LINUX_EMULATION + memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE, + c->a->res_apdu_data, USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE); +#else + usb_lld_txcpy (c->a->res_apdu_data, c->epi->ep_num, CCID_MSG_HEADER_SIZE, + USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE); +#endif + c->epi->buf = c->a->res_apdu_data + USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE; + c->epi->cnt = USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE; + c->epi->buf_len = c->a->res_apdu_data_len + - (USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE); + c->epi->next_buf = get_sw1sw2; + } + +#ifdef GNU_LINUX_EMULATION + usb_lld_tx_enable_buf (c->epi->ep_num, endp1_tx_buf, tx_size); +#else + usb_lld_tx_enable (c->epi->ep_num, tx_size); +#endif +#ifdef DEBUG_MORE + DEBUG_INFO ("DATA\r\n"); +#endif +} + +static void +ccid_send_data_block (struct ccid *c) +{ + ccid_send_data_block_internal (c, 0, 0); +} + +static void +ccid_send_data_block_time_extension (struct ccid *c) +{ + ccid_send_data_block_internal (c, CCID_CMD_STATUS_TIMEEXT, 1); +} + +static void +ccid_send_data_block_0x9000 (struct ccid *c) +{ + uint8_t p[CCID_MSG_HEADER_SIZE+2]; + size_t len = 2; + + p[0] = CCID_DATA_BLOCK_RET; + p[1] = len & 0xFF; + p[2] = (len >> 8)& 0xFF; + p[3] = (len >> 16)& 0xFF; + p[4] = (len >> 24)& 0xFF; + p[5] = 0x00; /* Slot */ + p[CCID_MSG_SEQ_OFFSET] = c->a->seq; + p[CCID_MSG_STATUS_OFFSET] = 0; + p[CCID_MSG_ERROR_OFFSET] = 0; + p[CCID_MSG_CHAIN_OFFSET] = 0; + p[CCID_MSG_CHAIN_OFFSET+1] = 0x90; + p[CCID_MSG_CHAIN_OFFSET+2] = 0x00; + +#ifdef GNU_LINUX_EMULATION + memcpy (endp1_tx_buf, p, CCID_MSG_HEADER_SIZE + len); +#else + usb_lld_txcpy (p, c->epi->ep_num, 0, CCID_MSG_HEADER_SIZE + len); +#endif + c->epi->buf = NULL; + c->epi->tx_done = 1; + +#ifdef GNU_LINUX_EMULATION + usb_lld_tx_enable_buf (c->epi->ep_num, endp1_tx_buf, + CCID_MSG_HEADER_SIZE + len); +#else + usb_lld_tx_enable (c->epi->ep_num, CCID_MSG_HEADER_SIZE + len); +#endif +#ifdef DEBUG_MORE + DEBUG_INFO ("DATA\r\n"); +#endif +} + +/* + * Reply to the host for "GET RESPONSE". + */ +static void +ccid_send_data_block_gr (struct ccid *c, size_t chunk_len) +{ + int tx_size = USB_LL_BUF_SIZE; + uint8_t p[CCID_MSG_HEADER_SIZE]; + size_t len = chunk_len + 2; + + p[0] = CCID_DATA_BLOCK_RET; + p[1] = len & 0xFF; + p[2] = (len >> 8)& 0xFF; + p[3] = (len >> 16)& 0xFF; + p[4] = (len >> 24)& 0xFF; + p[5] = 0x00; /* Slot */ + p[CCID_MSG_SEQ_OFFSET] = c->a->seq; + p[CCID_MSG_STATUS_OFFSET] = 0; + p[CCID_MSG_ERROR_OFFSET] = 0; + p[CCID_MSG_CHAIN_OFFSET] = 0; + +#ifdef GNU_LINUX_EMULATION + memcpy (endp1_tx_buf, p, CCID_MSG_HEADER_SIZE); +#else + usb_lld_txcpy (p, c->epi->ep_num, 0, CCID_MSG_HEADER_SIZE); +#endif + + set_sw1sw2 (c, chunk_len); + + if (chunk_len <= USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE) + { + int size_for_sw; + + if (chunk_len <= USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE - 2) + size_for_sw = 2; + else if (chunk_len == USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE - 1) + size_for_sw = 1; + else + size_for_sw = 0; + +#ifdef GNU_LINUX_EMULATION + memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE, c->p, chunk_len); +#else + usb_lld_txcpy (c->p, c->epi->ep_num, CCID_MSG_HEADER_SIZE, chunk_len); +#endif + if (size_for_sw) +#ifdef GNU_LINUX_EMULATION + memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE+chunk_len, + c->sw1sw2, size_for_sw); +#else + usb_lld_txcpy (c->sw1sw2, c->epi->ep_num, + CCID_MSG_HEADER_SIZE + chunk_len, size_for_sw); +#endif + tx_size = CCID_MSG_HEADER_SIZE + chunk_len + size_for_sw; + if (size_for_sw == 2) + { + c->epi->buf = NULL; + if (tx_size < USB_LL_BUF_SIZE) + c->epi->tx_done = 1; + /* Don't set epi->tx_done = 1, when it requires ZLP */ + } + else + { + c->epi->buf = c->sw1sw2 + size_for_sw; + c->epi->cnt = size_for_sw; + c->epi->buf_len = 2 - size_for_sw; + c->epi->next_buf = no_buf; + } + } + else + { +#ifdef GNU_LINUX_EMULATION + memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE, + c->p, USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE); +#else + usb_lld_txcpy (c->p, c->epi->ep_num, CCID_MSG_HEADER_SIZE, + USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE); +#endif + c->epi->buf = c->p + USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE; + c->epi->cnt = 0; + c->epi->buf_len = chunk_len - (USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE); + c->epi->next_buf = get_sw1sw2; + } + + c->p += chunk_len; + c->len -= chunk_len; +#ifdef GNU_LINUX_EMULATION + usb_lld_tx_enable_buf (c->epi->ep_num, endp1_tx_buf, tx_size); +#else + usb_lld_tx_enable (c->epi->ep_num, tx_size); +#endif +#ifdef DEBUG_MORE + DEBUG_INFO ("DATA\r\n"); +#endif +} + + +static void +ccid_send_params (struct ccid *c) +{ + uint8_t p[CCID_MSG_HEADER_SIZE]; + const uint8_t params[] = { + 0x11, /* bmFindexDindex */ + 0x11, /* bmTCCKST1 */ + 0xFE, /* bGuardTimeT1 */ + 0x55, /* bmWaitingIntegersT1 */ + 0x03, /* bClockStop */ + 0xFE, /* bIFSC */ + 0 /* bNadValue */ + }; + + p[0] = CCID_PARAMS_RET; + p[1] = 0x07; /* Length = 0x00000007 */ + p[2] = 0; + p[3] = 0; + p[4] = 0; + p[5] = 0x00; /* Slot */ + p[CCID_MSG_SEQ_OFFSET] = c->ccid_header.seq; + p[CCID_MSG_STATUS_OFFSET] = 0; + p[CCID_MSG_ERROR_OFFSET] = 0; + p[CCID_MSG_CHAIN_OFFSET] = 0x01; /* ProtocolNum: T=1 */ + +#ifdef GNU_LINUX_EMULATION + memcpy (endp1_tx_buf, p, CCID_MSG_HEADER_SIZE); + memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE, params, sizeof params); +#else + usb_lld_txcpy (p, c->epi->ep_num, 0, CCID_MSG_HEADER_SIZE); + usb_lld_txcpy (params, c->epi->ep_num, CCID_MSG_HEADER_SIZE, sizeof params); +#endif + + /* This is a single packet Bulk-IN transaction */ + c->epi->buf = NULL; + c->epi->tx_done = 1; +#ifdef GNU_LINUX_EMULATION + usb_lld_tx_enable_buf (c->epi->ep_num, endp1_tx_buf, + CCID_MSG_HEADER_SIZE + sizeof params); +#else + usb_lld_tx_enable (c->epi->ep_num, CCID_MSG_HEADER_SIZE + sizeof params); +#endif +#ifdef DEBUG_MORE + DEBUG_INFO ("PARAMS\r\n"); +#endif +} + + +static enum ccid_state +ccid_handle_data (struct ccid *c) +{ + enum ccid_state next_state = c->ccid_state; + + if (c->err != 0) + { + ccid_reset (c); + ccid_error (c, CCID_OFFSET_DATA_LEN); + return next_state; + } + + switch (c->ccid_state) + { + case CCID_STATE_NOCARD: + if (c->ccid_header.msg_type == CCID_SLOT_STATUS) + ccid_send_status (c); + else + { + DEBUG_INFO ("ERR00\r\n"); + ccid_error (c, CCID_OFFSET_CMD_NOT_SUPPORTED); + } + break; + case CCID_STATE_START: + if (c->ccid_header.msg_type == CCID_POWER_ON) + { + ccid_reset (c); + next_state = ccid_power_on (c); + } + else if (c->ccid_header.msg_type == CCID_POWER_OFF) + { + ccid_reset (c); + next_state = ccid_power_off (c); + } + else if (c->ccid_header.msg_type == CCID_SLOT_STATUS) + ccid_send_status (c); + else + { + DEBUG_INFO ("ERR01\r\n"); + ccid_error (c, CCID_OFFSET_CMD_NOT_SUPPORTED); + } + break; + case CCID_STATE_WAIT: + if (c->ccid_header.msg_type == CCID_POWER_ON) + { + /* Not in the spec., but pcscd/libccid */ + ccid_reset (c); + next_state = ccid_power_on (c); + } + else if (c->ccid_header.msg_type == CCID_POWER_OFF) + { + ccid_reset (c); + next_state = ccid_power_off (c); + } + else if (c->ccid_header.msg_type == CCID_SLOT_STATUS) + ccid_send_status (c); + else if (c->ccid_header.msg_type == CCID_XFR_BLOCK) + { + if (c->ccid_header.param == 0) + { + if ((c->a->cmd_apdu_head[0] & 0x10) == 0) + { + if (c->state == APDU_STATE_COMMAND_CHAINING) + { /* command chaining finished */ + c->p += c->a->cmd_apdu_head[4]; + c->a->cmd_apdu_head[4] = 0; + DEBUG_INFO ("CMD chaning finished.\r\n"); + } + + if (c->a->cmd_apdu_head[1] == INS_GET_RESPONSE + && c->state == APDU_STATE_RESULT_GET_RESPONSE) + { + size_t len = c->a->expected_res_size; + + if (c->len <= c->a->expected_res_size) + len = c->len; + + ccid_send_data_block_gr (c, len); + if (c->len == 0) + c->state = APDU_STATE_RESULT; + c->ccid_state = CCID_STATE_WAIT; + DEBUG_INFO ("GET Response.\r\n"); + } + else + { /* Give this message to GPG thread */ + c->state = APDU_STATE_COMMAND_RECEIVED; + + c->a->sw = 0x9000; + c->a->res_apdu_data_len = 0; + c->a->res_apdu_data = &ccid_buffer[5]; + + eventflag_signal (&c->openpgp_comm, EV_CMD_AVAILABLE); + next_state = CCID_STATE_EXECUTE; + } + } + else + { + if (c->state == APDU_STATE_WAIT_COMMAND) + { /* command chaining is started */ + c->a->cmd_apdu_head[0] &= ~0x10; + memcpy (c->chained_cls_ins_p1_p2, c->a->cmd_apdu_head, 4); + c->state = APDU_STATE_COMMAND_CHAINING; + } + + c->p += c->a->cmd_apdu_head[4]; + c->len -= c->a->cmd_apdu_head[4]; + ccid_send_data_block_0x9000 (c); + DEBUG_INFO ("CMD chaning...\r\n"); + } + } + else + { /* ICC block chaining is not supported. */ + DEBUG_INFO ("ERR02\r\n"); + ccid_error (c, CCID_OFFSET_PARAM); + } + } + else if (c->ccid_header.msg_type == CCID_SET_PARAMS + || c->ccid_header.msg_type == CCID_GET_PARAMS + || c->ccid_header.msg_type == CCID_RESET_PARAMS) + ccid_send_params (c); + else if (c->ccid_header.msg_type == CCID_SECURE) + { + if (c->p != c->a->cmd_apdu_data) + { + /* SECURE received in the middle of command chaining */ + ccid_reset (c); + ccid_error (c, CCID_OFFSET_DATA_LEN); + return next_state; + } + + if (c->p[10-10] == 0x00) /* PIN verification */ + { + c->a->cmd_apdu_head[0] = c->p[25-10]; + c->a->cmd_apdu_head[1] = c->p[26-10]; + c->a->cmd_apdu_head[2] = c->p[27-10]; + c->a->cmd_apdu_head[3] = c->p[28-10]; + /**/ + c->a->cmd_apdu_data[0] = 0; /* bConfirmPIN */ + c->a->cmd_apdu_data[1] = c->p[17-10]; /* bEntryValidationCondition */ + c->a->cmd_apdu_data[2] = c->p[18-10]; /* bNumberMessage */ + c->a->cmd_apdu_data[3] = c->p[19-10]; /* wLangId L */ + c->a->cmd_apdu_data[4] = c->p[20-10]; /* wLangId H */ + c->a->cmd_apdu_data[5] = c->p[21-10]; /* bMsgIndex */ + + c->a->cmd_apdu_data_len = 6; + c->a->expected_res_size = 0; + + c->a->sw = 0x9000; + c->a->res_apdu_data_len = 0; + c->a->res_apdu_data = &c->p[5]; + + eventflag_signal (&c->openpgp_comm, EV_VERIFY_CMD_AVAILABLE); + next_state = CCID_STATE_EXECUTE; + } + else if (c->p[10-10] == 0x01) /* PIN Modification */ + { + uint8_t num_msgs = c->p[21-10]; + + if (num_msgs == 0x00) + num_msgs = 1; + else if (num_msgs == 0xff) + num_msgs = 3; + c->a->cmd_apdu_head[0] = c->p[27 + num_msgs-10]; + c->a->cmd_apdu_head[1] = c->p[28 + num_msgs-10]; + c->a->cmd_apdu_head[2] = c->p[29 + num_msgs-10]; + c->a->cmd_apdu_head[3] = c->p[30 + num_msgs-10]; + /**/ + c->a->cmd_apdu_data[0] = c->p[19-10]; /* bConfirmPIN */ + c->a->cmd_apdu_data[1] = c->p[20-10]; /* bEntryValidationCondition */ + c->a->cmd_apdu_data[2] = c->p[21-10]; /* bNumberMessage */ + c->a->cmd_apdu_data[3] = c->p[22-10]; /* wLangId L */ + c->a->cmd_apdu_data[4] = c->p[23-10]; /* wLangId H */ + c->a->cmd_apdu_data[5] = c->p[24-10]; /* bMsgIndex, bMsgIndex1 */ + if (num_msgs >= 2) + c->a->cmd_apdu_data[6] = c->p[25-10]; /* bMsgIndex2 */ + if (num_msgs == 3) + c->a->cmd_apdu_data[7] = c->p[26-10]; /* bMsgIndex3 */ + + c->a->cmd_apdu_data_len = 5 + num_msgs; + c->a->expected_res_size = 0; + + c->a->sw = 0x9000; + c->a->res_apdu_data_len = 0; + c->a->res_apdu_data = &ccid_buffer[5]; + + eventflag_signal (&c->openpgp_comm, EV_MODIFY_CMD_AVAILABLE); + next_state = CCID_STATE_EXECUTE; + } + else + ccid_error (c, CCID_MSG_DATA_OFFSET); + } + else + { + DEBUG_INFO ("ERR03\r\n"); + DEBUG_BYTE (c->ccid_header.msg_type); + ccid_error (c, CCID_OFFSET_CMD_NOT_SUPPORTED); + } + break; + case CCID_STATE_EXECUTE: + if (c->ccid_header.msg_type == CCID_POWER_OFF) + next_state = ccid_power_off (c); + else if (c->ccid_header.msg_type == CCID_SLOT_STATUS) + ccid_send_status (c); + else + { + DEBUG_INFO ("ERR04\r\n"); + DEBUG_BYTE (c->ccid_header.msg_type); + ccid_error (c, CCID_OFFSET_CMD_NOT_SUPPORTED); + } + break; + default: + next_state = CCID_STATE_START; + DEBUG_INFO ("ERR10\r\n"); + break; + } + + return next_state; +} + +static enum ccid_state +ccid_handle_timeout (struct ccid *c) +{ + enum ccid_state next_state = c->ccid_state; + + switch (c->ccid_state) + { + case CCID_STATE_EXECUTE: + ccid_send_data_block_time_extension (c); + break; + default: + break; + } + + led_blink (LED_ONESHOT); + return next_state; +} + +static struct ccid ccid; +enum ccid_state *const ccid_state_p = &ccid.ccid_state; + +void +ccid_card_change_signal (int how) +{ + struct ccid *c = &ccid; + + if (how == CARD_CHANGE_TOGGLE + || (c->ccid_state == CCID_STATE_NOCARD && how == CARD_CHANGE_INSERT) + || (c->ccid_state != CCID_STATE_NOCARD && how == CARD_CHANGE_REMOVE)) + eventflag_signal (&c->ccid_comm, EV_CARD_CHANGE); +} + +void +ccid_usb_reset (int full) +{ + struct ccid *c = &ccid; + + eventflag_signal (&c->ccid_comm, + full ? EV_USB_DEVICE_RESET : EV_USB_SET_INTERFACE); +} + +#ifdef GNU_LINUX_EMULATION +static uint8_t endp2_tx_buf[2]; +#endif + +#define NOTIFY_SLOT_CHANGE 0x50 +static void +ccid_notify_slot_change (struct ccid *c) +{ + uint8_t msg; + uint8_t notification[2]; + + if (c->ccid_state == CCID_STATE_NOCARD) + msg = 0x02; + else + msg = 0x03; + + notification[0] = NOTIFY_SLOT_CHANGE; + notification[1] = msg; +#ifdef GNU_LINUX_EMULATION + memcpy (endp2_tx_buf, notification, sizeof notification); + usb_lld_tx_enable_buf (ENDP2, endp2_tx_buf, sizeof notification); +#else + usb_lld_write (ENDP2, notification, sizeof notification); +#endif + led_blink (LED_TWOSHOTS); +} + + +#define USB_CCID_TIMEOUT (1950*1000) + +#define GPG_THREAD_TERMINATED 0xffff + + +#ifdef GNU_LINUX_EMULATION +#include +#define INTR_REQ_USB SIGUSR1 +#else +#define INTR_REQ_USB 20 +#endif + +extern uint32_t bDeviceState; +extern void usb_device_reset (struct usb_dev *dev); +extern int usb_setup (struct usb_dev *dev); +extern void usb_ctrl_write_finish (struct usb_dev *dev); +extern int usb_set_configuration (struct usb_dev *dev); +extern int usb_set_interface (struct usb_dev *dev); +extern int usb_get_interface (struct usb_dev *dev); +extern int usb_get_status_interface (struct usb_dev *dev); + +extern int usb_get_descriptor (struct usb_dev *dev); + +static void +usb_event_handle (struct usb_dev *dev) +{ + uint8_t ep_num; + int e; + + e = usb_lld_event_handler (dev); + ep_num = USB_EVENT_ENDP (e); + + if (ep_num != 0) + { + if (USB_EVENT_TXRX (e)) + usb_tx_done (ep_num, USB_EVENT_LEN (e)); + else + usb_rx_ready (ep_num, USB_EVENT_LEN (e)); + } + else + switch (USB_EVENT_ID (e)) + { + case USB_EVENT_DEVICE_RESET: + usb_device_reset (dev); + break; + + case USB_EVENT_DEVICE_ADDRESSED: + bDeviceState = ADDRESSED; + break; + + case USB_EVENT_GET_DESCRIPTOR: + if (usb_get_descriptor (dev) < 0) + usb_lld_ctrl_error (dev); + break; + + case USB_EVENT_SET_CONFIGURATION: + if (usb_set_configuration (dev) < 0) + usb_lld_ctrl_error (dev); + break; + + case USB_EVENT_SET_INTERFACE: + if (usb_set_interface (dev) < 0) + usb_lld_ctrl_error (dev); + break; + + case USB_EVENT_CTRL_REQUEST: + /* Device specific device request. */ + if (usb_setup (dev) < 0) + usb_lld_ctrl_error (dev); + break; + + case USB_EVENT_GET_STATUS_INTERFACE: + if (usb_get_status_interface (dev) < 0) + usb_lld_ctrl_error (dev); + break; + + case USB_EVENT_GET_INTERFACE: + if (usb_get_interface (dev) < 0) + usb_lld_ctrl_error (dev); + break; + + case USB_EVENT_SET_FEATURE_DEVICE: + case USB_EVENT_SET_FEATURE_ENDPOINT: + case USB_EVENT_CLEAR_FEATURE_DEVICE: + case USB_EVENT_CLEAR_FEATURE_ENDPOINT: + usb_lld_ctrl_ack (dev); + break; + + case USB_EVENT_CTRL_WRITE_FINISH: + /* Control WRITE transfer finished. */ + usb_ctrl_write_finish (dev); + break; + + case USB_EVENT_OK: + case USB_EVENT_DEVICE_SUSPEND: + default: + break; + } +} + +static void +poll_event_intr (uint32_t *timeout, struct eventflag *ev, chopstx_intr_t *intr) +{ + chopstx_poll_cond_t poll_desc; + struct chx_poll_head *pd_array[2] = { + (struct chx_poll_head *)intr, + (struct chx_poll_head *)&poll_desc + }; + + eventflag_prepare_poll (ev, &poll_desc); + chopstx_poll (timeout, 2, pd_array); +} + +void * +ccid_thread (void *arg) +{ + chopstx_intr_t interrupt; + uint32_t timeout; + struct usb_dev dev; + struct ccid *c = &ccid; + + (void)arg; + + eventflag_init (&ccid.ccid_comm); + eventflag_init (&ccid.openpgp_comm); + + usb_lld_init (&dev, USB_INITIAL_FEATURE); + chopstx_claim_irq (&interrupt, INTR_REQ_USB); + usb_event_handle (&dev); /* For old SYS < 3.0 */ + + device_reset: + { + struct ep_in *epi = &endpoint_in; + struct ep_out *epo = &endpoint_out; + struct apdu *a = &apdu; + + epi_init (epi, ENDP1, c); + epo_init (epo, ENDP1, c); + apdu_init (a); + ccid_init (c, epi, epo, a); + } + + while (bDeviceState != CONFIGURED) + { + poll_event_intr (NULL, &c->ccid_comm, &interrupt); + if (interrupt.ready) + usb_event_handle (&dev); + + eventflag_get (&c->ccid_comm); + /* Ignore event while not-configured. */ + } + + interface_reset: + timeout = USB_CCID_TIMEOUT; + ccid_prepare_receive (c); + ccid_notify_slot_change (c); + while (1) + { + eventmask_t m; + + poll_event_intr (&timeout, &c->ccid_comm, &interrupt); + if (interrupt.ready) + { + usb_event_handle (&dev); + continue; + } + + timeout = USB_CCID_TIMEOUT; + m = eventflag_get (&c->ccid_comm); + + if (m == EV_USB_DEVICE_RESET) + { + if (c->application) + { + chopstx_cancel (c->application); + chopstx_join (c->application, NULL); + c->application = 0; + } + goto device_reset; + } + else if (m == EV_USB_SET_INTERFACE) + /* Upon receival of SET_INTERFACE, the endpoint is reset to RX_NAK. + * Thus, we need to prepare receive again. + */ + goto interface_reset; + else if (m == EV_CARD_CHANGE) + { + if (c->ccid_state == CCID_STATE_NOCARD) + /* Inserted! */ + c->ccid_state = CCID_STATE_START; + else + { /* Removed! */ + if (c->application) + { + eventflag_signal (&c->openpgp_comm, EV_EXIT); + chopstx_join (c->application, NULL); + c->application = 0; + } + + c->ccid_state = CCID_STATE_NOCARD; + } + + ccid_notify_slot_change (c); + } + else if (m == EV_RX_DATA_READY) + c->ccid_state = ccid_handle_data (c); + else if (m == EV_EXEC_FINISHED) + if (c->ccid_state == CCID_STATE_EXECUTE) + { + if (c->a->sw == GPG_THREAD_TERMINATED) + { + c->sw1sw2[0] = 0x90; + c->sw1sw2[1] = 0x00; + c->state = APDU_STATE_RESULT; + ccid_send_data_block (c); + c->ccid_state = CCID_STATE_EXITED; + break; + } + + c->a->cmd_apdu_data_len = 0; + c->sw1sw2[0] = c->a->sw >> 8; + c->sw1sw2[1] = c->a->sw & 0xff; + + if (c->a->res_apdu_data_len <= c->a->expected_res_size) + { + c->state = APDU_STATE_RESULT; + ccid_send_data_block (c); + c->ccid_state = CCID_STATE_WAIT; + } + else + { + c->state = APDU_STATE_RESULT_GET_RESPONSE; + c->p = c->a->res_apdu_data; + c->len = c->a->res_apdu_data_len; + ccid_send_data_block_gr (c, c->a->expected_res_size); + c->ccid_state = CCID_STATE_WAIT; + } + } + else + { + DEBUG_INFO ("ERR07\r\n"); + } + else if (m == EV_TX_FINISHED) + { + if (c->state == APDU_STATE_RESULT) + { + c->state = APDU_STATE_WAIT_COMMAND; + c->p = c->a->cmd_apdu_data; + c->len = MAX_CMD_APDU_DATA_SIZE; + c->err = 0; + c->a->cmd_apdu_data_len = 0; + c->a->expected_res_size = 0; + } + + if (c->state == APDU_STATE_WAIT_COMMAND + || c->state == APDU_STATE_COMMAND_CHAINING + || c->state == APDU_STATE_RESULT_GET_RESPONSE) + ccid_prepare_receive (c); + } + else /* Timeout */ + c->ccid_state = ccid_handle_timeout (c); + } + + if (c->application) + { + chopstx_join (c->application, NULL); + c->application = 0; + } + + /* Loading reGNUal. */ + while (bDeviceState != UNCONNECTED) + { + chopstx_intr_wait (&interrupt); + usb_event_handle (&dev); + } + + return NULL; +} + + +#ifdef DEBUG +#include "usb-cdc.h" + +void +stdout_init (void) +{ + chopstx_mutex_init (&stdout.m); + chopstx_mutex_init (&stdout.m_dev); + chopstx_cond_init (&stdout.cond_dev); + stdout.connected = 0; +} + +void +_write (const char *s, int len) +{ + int packet_len; + + if (len == 0) + return; + + chopstx_mutex_lock (&stdout.m); + + chopstx_mutex_lock (&stdout.m_dev); + if (!stdout.connected) + chopstx_cond_wait (&stdout.cond_dev, &stdout.m_dev); + chopstx_mutex_unlock (&stdout.m_dev); + + do + { + packet_len = + (len < VIRTUAL_COM_PORT_DATA_SIZE) ? len : VIRTUAL_COM_PORT_DATA_SIZE; + + chopstx_mutex_lock (&stdout.m_dev); +#ifdef GNU_LINUX_EMULATION + usb_lld_tx_enable_buf (ENDP3, s, packet_len); +#else + usb_lld_write (ENDP3, s, packet_len); +#endif + chopstx_cond_wait (&stdout.cond_dev, &stdout.m_dev); + chopstx_mutex_unlock (&stdout.m_dev); + + s += packet_len; + len -= packet_len; + } + /* Send a Zero-Length-Packet if the last packet is full size. */ + while (len != 0 || packet_len == VIRTUAL_COM_PORT_DATA_SIZE); + + chopstx_mutex_unlock (&stdout.m); +} + +#else +void +_write (const char *s, int size) +{ + (void)s; + (void)size; +} +#endif diff --git a/gnuk/src/usb-cdc.h b/gnuk/src/usb-cdc.h new file mode 100644 index 0000000000..d004c79777 --- /dev/null +++ b/gnuk/src/usb-cdc.h @@ -0,0 +1,10 @@ +/* + * Class specific control requests for CDC + */ +#define USB_CDC_REQ_SET_LINE_CODING 0x20 +#define USB_CDC_REQ_GET_LINE_CODING 0x21 +#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22 +#define USB_CDC_REQ_SEND_BREAK 0x23 + +#define VIRTUAL_COM_PORT_DATA_SIZE 16 +#define VIRTUAL_COM_PORT_INT_SIZE 8 diff --git a/gnuk/src/usb-msc.c b/gnuk/src/usb-msc.c new file mode 100644 index 0000000000..7ded8e0c82 --- /dev/null +++ b/gnuk/src/usb-msc.c @@ -0,0 +1,574 @@ +/* + * usb-msc.c -- USB Mass Storage Class protocol handling + * + * Copyright (C) 2011, 2012, 2013, 2015 + * 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 +#include +#include + +#include "config.h" +#include "gnuk.h" +#include "usb_lld.h" +#include "usb-msc.h" + +#define STACK_PROCESS_5 +#include "stack-def.h" +#define STACK_ADDR_MSC ((uintptr_t)process5_base) +#define STACK_SIZE_MSC (sizeof process5_base) + +#define PRIO_MSC 3 + +static chopstx_mutex_t a_pinpad_mutex; +static chopstx_cond_t a_pinpad_cond; + +static chopstx_mutex_t a_msc_mutex; +static chopstx_cond_t a_msc_cond; + +#define RDY_OK 0 +#define RDY_RESET 1 +static uint8_t msg; + +chopstx_mutex_t *pinpad_mutex = &a_pinpad_mutex; +chopstx_cond_t *pinpad_cond = &a_pinpad_cond; + +static chopstx_mutex_t *msc_mutex = &a_msc_mutex; +static chopstx_cond_t *msc_cond = &a_msc_cond; + + +struct usb_endp_in { + const uint8_t *txbuf; /* Pointer to the transmission buffer. */ + size_t txsize; /* Transmit transfer size remained. */ + size_t txcnt; /* Transmitted bytes so far. */ +}; + +struct usb_endp_out { + uint8_t *rxbuf; /* Pointer to the receive buffer. */ + size_t rxsize; /* Requested receive transfer size. */ + size_t rxcnt; /* Received bytes so far. */ +}; + +static struct usb_endp_in ep6_in; +static struct usb_endp_out ep6_out; + +#define ENDP_MAX_SIZE 64 + +static uint8_t msc_state; + + +static void usb_start_transmit (const uint8_t *p, size_t n) +{ + size_t pkt_len = n > ENDP_MAX_SIZE ? ENDP_MAX_SIZE : n; + + ep6_in.txbuf = p; + ep6_in.txsize = n; + ep6_in.txcnt = 0; + + usb_lld_write (ENDP6, (uint8_t *)ep6_in.txbuf, pkt_len); +} + +/* "Data Transmitted" callback */ +void +EP6_IN_Callback (uint16_t len) +{ + size_t n = len; + + chopstx_mutex_lock (msc_mutex); + + ep6_in.txbuf += n; + ep6_in.txcnt += n; + ep6_in.txsize -= n; + + if (ep6_in.txsize > 0) /* More data to be sent */ + { + if (ep6_in.txsize > ENDP_MAX_SIZE) + n = ENDP_MAX_SIZE; + else + n = ep6_in.txsize; + usb_lld_write (ENDP6, (uint8_t *)ep6_in.txbuf, n); + } + else + /* Transmit has been completed, notify the waiting thread */ + switch (msc_state) + { + case MSC_SENDING_CSW: + case MSC_DATA_IN: + msg = RDY_OK; + chopstx_cond_signal (msc_cond); + break; + default: + break; + } + + chopstx_mutex_unlock (msc_mutex); +} + + +static void usb_start_receive (uint8_t *p, size_t n) +{ + ep6_out.rxbuf = p; + ep6_out.rxsize = n; + ep6_out.rxcnt = 0; + usb_lld_rx_enable (ENDP6); +} + +/* "Data Received" call back */ +void +EP6_OUT_Callback (uint16_t len) +{ + size_t n = len; + int err = 0; + + chopstx_mutex_lock (msc_mutex); + + if (n > ep6_out.rxsize) + { /* buffer overflow */ + err = 1; + n = ep6_out.rxsize; + } + + usb_lld_rxcpy (ep6_out.rxbuf, ENDP6, 0, n); + ep6_out.rxbuf += n; + ep6_out.rxcnt += n; + ep6_out.rxsize -= n; + + if (n == ENDP_MAX_SIZE && ep6_out.rxsize != 0) + /* More data to be received */ + usb_lld_rx_enable (ENDP6); + else + /* Receiving has been completed, notify the waiting thread */ + switch (msc_state) + { + case MSC_IDLE: + case MSC_DATA_OUT: + msg = err ? RDY_RESET : RDY_OK; + chopstx_cond_signal (msc_cond); + break; + default: + break; + } + + chopstx_mutex_unlock (msc_mutex); +} + +static const uint8_t scsi_inquiry_data_00[] = { 0, 0, 0, 0, 0 }; +static const uint8_t scsi_inquiry_data_83[] = { 0, 0x83, 0, 0 }; + + +static const uint8_t scsi_inquiry_data[] = { + 0x00, /* Direct Access Device. */ + 0x80, /* RMB = 1: Removable Medium. */ + 0x00, /* Version: not claim conformance. */ + 0x02, /* Response format: SPC-3. */ + 36 - 4, /* Additional Length. */ + 0x00, + 0x00, + 0x00, + /* Vendor Identification */ + 'F', 'S', 'I', 'J', ' ', ' ', ' ', ' ', + /* Product Identification */ + 'V', 'i', 'r', 't', 'u', 'a', 'l', ' ', + 'D', 'i', 's', 'k', ' ', ' ', ' ', ' ', + /* Product Revision Level */ + '1', '.', '0', ' ' +}; + +static uint8_t scsi_sense_data_desc[] = { + 0x72, /* Response Code: descriptor, current */ + 0x02, /* Sense Key */ + 0x3a, /* ASC (additional sense code) */ + 0x00, /* ASCQ (additional sense code qualifier) */ + 0x00, 0x00, 0x00, + 0x00, /* Additional Sense Length */ +}; + +static uint8_t scsi_sense_data_fixed[] = { + 0x70, /* Response Code: fixed, current */ + 0x00, + 0x02, /* Sense Key */ + 0x00, 0x00, 0x00, 0x00, + 0x0a, /* Additional Sense Length */ + 0x00, 0x00, 0x00, 0x00, + 0x3a, /* ASC (additional sense code) */ + 0x00, /* ASCQ (additional sense code qualifier) */ + 0x00, + 0x00, 0x00, 0x00, +}; + +static void set_scsi_sense_data(uint8_t sense_key, uint8_t asc) +{ + scsi_sense_data_desc[1] = scsi_sense_data_fixed[2] = sense_key; + scsi_sense_data_desc[2] = scsi_sense_data_fixed[12] = asc; +} + + +static uint8_t buf[512]; + +static uint8_t contingent_allegiance; +static uint8_t keep_contingent_allegiance; + +uint8_t media_available; + +void +msc_media_insert_change (int available) +{ + contingent_allegiance = 1; + media_available = available; + if (available) + { + set_scsi_sense_data (0x06, 0x28); /* UNIT_ATTENTION */ + keep_contingent_allegiance = 0; + } + else + { + set_scsi_sense_data (0x02, 0x3a); /* NOT_READY */ + keep_contingent_allegiance = 1; + } +} + + +static uint8_t scsi_read_format_capacities (uint32_t *nblocks, + uint32_t *secsize) +{ + *nblocks = 68; + *secsize = 512; + if (media_available) + return 2; /* Formatted Media.*/ + else + return 3; /* No Media.*/ +} + +static struct CBW CBW; + +static struct CSW CSW; + + +/* called with holding the lock. */ +static int msc_recv_data (void) +{ + msc_state = MSC_DATA_OUT; + usb_start_receive (buf, 512); + chopstx_cond_wait (msc_cond, msc_mutex); + return 0; +} + +/* called with holding the lock. */ +static void msc_send_data (const uint8_t *p, size_t n) +{ + msc_state = MSC_DATA_IN; + usb_start_transmit (p, n); + chopstx_cond_wait (msc_cond, msc_mutex); + CSW.dCSWDataResidue -= (uint32_t)n; +} + +/* called with holding the lock. */ +static void msc_send_result (const uint8_t *p, size_t n) +{ + if (p != NULL) + { + if (n > CBW.dCBWDataTransferLength) + n = CBW.dCBWDataTransferLength; + + CSW.dCSWDataResidue = CBW.dCBWDataTransferLength; + msc_send_data (p, n); + CSW.bCSWStatus = MSC_CSW_STATUS_PASSED; + } + + CSW.dCSWSignature = MSC_CSW_SIGNATURE; + + msc_state = MSC_SENDING_CSW; + usb_start_transmit ((uint8_t *)&CSW, sizeof CSW); + chopstx_cond_wait (msc_cond, msc_mutex); +} + + +void +msc_handle_command (void) +{ + size_t n; + uint32_t nblocks, secsize; + uint32_t lba; + int r; + + chopstx_mutex_lock (msc_mutex); + msc_state = MSC_IDLE; + usb_start_receive ((uint8_t *)&CBW, sizeof CBW); + chopstx_cond_wait (msc_cond, msc_mutex); + + if (msg != RDY_OK) + { + /* Error occured, ignore the request and go into error state */ + msc_state = MSC_ERROR; + usb_lld_stall_rx (ENDP6); + goto done; + } + + n = ep6_out.rxcnt; + + if ((n != sizeof (struct CBW)) || (CBW.dCBWSignature != MSC_CBW_SIGNATURE)) + { + msc_state = MSC_ERROR; + usb_lld_stall_rx (ENDP6); + goto done; + } + + CSW.dCSWTag = CBW.dCBWTag; + switch (CBW.CBWCB[0]) { + case SCSI_REPORT_LUN: + buf[0] = buf[1] = buf[2] = buf[3] = 0; + buf[4] = buf[5] = buf[6] = buf[7] = 0; + msc_send_result (buf, 8); + goto done; + case SCSI_REQUEST_SENSE: + if (CBW.CBWCB[1] & 0x01) /* DESC */ + msc_send_result ((uint8_t *)&scsi_sense_data_desc, + sizeof scsi_sense_data_desc); + else + msc_send_result ((uint8_t *)&scsi_sense_data_fixed, + sizeof scsi_sense_data_fixed); + /* After the error is reported, clear it, if it's . */ + if (!keep_contingent_allegiance) + { + contingent_allegiance = 0; + set_scsi_sense_data (0x00, 0x00); + } + goto done; + case SCSI_INQUIRY: + if (CBW.CBWCB[1] & 0x01) + /* EVPD */ + { + if (CBW.CBWCB[2] == 0x83) + /* Handle the case Page Code 0x83 */ + msc_send_result ((uint8_t *)&scsi_inquiry_data_83, + sizeof scsi_inquiry_data_83); + else + /* Otherwise, assume page 00 */ + msc_send_result ((uint8_t *)&scsi_inquiry_data_00, + sizeof scsi_inquiry_data_00); + } + else + msc_send_result ((uint8_t *)&scsi_inquiry_data, + sizeof scsi_inquiry_data); + goto done; + case SCSI_READ_FORMAT_CAPACITIES: + buf[8] = scsi_read_format_capacities (&nblocks, &secsize); + buf[0] = buf[1] = buf[2] = 0; + buf[3] = 8; + buf[4] = (uint8_t)(nblocks >> 24); + buf[5] = (uint8_t)(nblocks >> 16); + buf[6] = (uint8_t)(nblocks >> 8); + buf[7] = (uint8_t)(nblocks >> 0); + buf[9] = (uint8_t)(secsize >> 16); + buf[10] = (uint8_t)(secsize >> 8); + buf[11] = (uint8_t)(secsize >> 0); + msc_send_result (buf, 12); + goto done; + case SCSI_START_STOP_UNIT: + if (CBW.CBWCB[4] == 0x00 /* stop */ + || CBW.CBWCB[4] == 0x02 /* eject */ || CBW.CBWCB[4] == 0x03 /* close */) + { + msc_scsi_stop (CBW.CBWCB[4]); + set_scsi_sense_data (0x05, 0x24); /* ILLEGAL_REQUEST */ + contingent_allegiance = 1; + keep_contingent_allegiance = 1; + } + /* CBW.CBWCB[4] == 0x01 *//* start */ + goto success; + case SCSI_TEST_UNIT_READY: + if (contingent_allegiance) + { + CSW.bCSWStatus = MSC_CSW_STATUS_FAILED; + CSW.dCSWDataResidue = 0; + msc_send_result (NULL, 0); + goto done; + } + /* fall through */ + success: + case SCSI_SYNCHRONIZE_CACHE: + case SCSI_VERIFY10: + case SCSI_ALLOW_MEDIUM_REMOVAL: + CSW.bCSWStatus = MSC_CSW_STATUS_PASSED; + CSW.dCSWDataResidue = CBW.dCBWDataTransferLength; + msc_send_result (NULL, 0); + goto done; + case SCSI_MODE_SENSE6: + buf[0] = 0x03; + buf[1] = buf[2] = buf[3] = 0; + msc_send_result (buf, 4); + goto done; + case SCSI_READ_CAPACITY10: + scsi_read_format_capacities (&nblocks, &secsize); + buf[0] = (uint8_t)((nblocks - 1) >> 24); + buf[1] = (uint8_t)((nblocks - 1) >> 16); + buf[2] = (uint8_t)((nblocks - 1) >> 8); + buf[3] = (uint8_t)((nblocks - 1) >> 0); + buf[4] = (uint8_t)(secsize >> 24); + buf[5] = (uint8_t)(secsize >> 16); + buf[6] = (uint8_t)(secsize >> 8); + buf[7] = (uint8_t)(secsize >> 0); + msc_send_result (buf, 8); + goto done; + case SCSI_READ10: + case SCSI_WRITE10: + break; + default: + if (CBW.dCBWDataTransferLength == 0) + { + CSW.bCSWStatus = MSC_CSW_STATUS_FAILED; + CSW.dCSWDataResidue = 0; + msc_send_result (NULL, 0); + goto done; + } + else + { + msc_state = MSC_ERROR; + usb_lld_stall_tx (ENDP6); + usb_lld_stall_rx (ENDP6); + goto done; + } + } + + lba = (CBW.CBWCB[2] << 24) | (CBW.CBWCB[3] << 16) + | (CBW.CBWCB[4] << 8) | CBW.CBWCB[5]; + + /* Transfer direction.*/ + if (CBW.bmCBWFlags & 0x80) + { + /* IN, Device to Host.*/ + msc_state = MSC_DATA_IN; + if (CBW.CBWCB[0] == SCSI_READ10) + { + const uint8_t *p; + + CSW.dCSWDataResidue = 0; + while (1) + { + if (CBW.CBWCB[7] == 0 && CBW.CBWCB[8] == 0) + { + CSW.bCSWStatus = MSC_CSW_STATUS_PASSED; + break; + } + + if ((r = msc_scsi_read (lba, &p)) == 0) + { + msc_send_data (p, 512); + if (++CBW.CBWCB[5] == 0) + if (++CBW.CBWCB[4] == 0) + if (++CBW.CBWCB[3] == 0) + ++CBW.CBWCB[2]; + if (CBW.CBWCB[8]-- == 0) + CBW.CBWCB[7]--; + CSW.dCSWDataResidue += 512; + lba++; + } + else + { + CSW.bCSWStatus = MSC_CSW_STATUS_FAILED; + contingent_allegiance = 1; + if (r == SCSI_ERROR_NOT_READY) + set_scsi_sense_data (SCSI_ERROR_NOT_READY, 0x3a); + else + set_scsi_sense_data (r, 0x00); + break; + } + } + + msc_send_result (NULL, 0); + } + } + else + { + /* OUT, Host to Device.*/ + if (CBW.CBWCB[0] == SCSI_WRITE10) + { + CSW.dCSWDataResidue = CBW.dCBWDataTransferLength; + + while (1) + { + if (CBW.CBWCB[8] == 0 && CBW.CBWCB[7] == 0) + { + CSW.bCSWStatus = MSC_CSW_STATUS_PASSED; + break; + } + + msc_recv_data (); + if (msg != RDY_OK) + /* ignore erroneous packet, ang go next. */ + continue; + + if ((r = msc_scsi_write (lba, buf, 512)) == 0) + { + if (++CBW.CBWCB[5] == 0) + if (++CBW.CBWCB[4] == 0) + if (++CBW.CBWCB[3] == 0) + ++CBW.CBWCB[2]; + if (CBW.CBWCB[8]-- == 0) + CBW.CBWCB[7]--; + CSW.dCSWDataResidue -= 512; + lba++; + } + else + { + CSW.bCSWStatus = MSC_CSW_STATUS_FAILED; + contingent_allegiance = 1; + if (r == SCSI_ERROR_NOT_READY) + set_scsi_sense_data (SCSI_ERROR_NOT_READY, 0x3a); + else + set_scsi_sense_data (r, 0x00); + break; + } + } + + msc_send_result (NULL, 0); + } + } + + done: + chopstx_mutex_unlock (msc_mutex); +} + + +static void * +msc_main (void *arg) +{ + (void)arg; + + chopstx_mutex_init (msc_mutex); + chopstx_cond_init (msc_cond); + + chopstx_mutex_init (pinpad_mutex); + chopstx_cond_init (pinpad_cond); + + /* Initially, it starts with no media */ + msc_media_insert_change (0); + while (1) + msc_handle_command (); + + return NULL; +} + + +void +msc_init (void) +{ + chopstx_create (PRIO_MSC, STACK_ADDR_MSC, STACK_SIZE_MSC, msc_main, NULL); +} diff --git a/gnuk/src/usb-msc.h b/gnuk/src/usb-msc.h new file mode 100644 index 0000000000..2361a8f1a5 --- /dev/null +++ b/gnuk/src/usb-msc.h @@ -0,0 +1,54 @@ +#define MSC_CBW_SIGNATURE 0x43425355 +#define MSC_CSW_SIGNATURE 0x53425355 + +#define MSC_GET_MAX_LUN_COMMAND 0xFE +#define MSC_MASS_STORAGE_RESET_COMMAND 0xFF + +#define MSC_CSW_STATUS_PASSED 0 +#define MSC_CSW_STATUS_FAILED 1 + +#define SCSI_INQUIRY 0x12 +#define SCSI_MODE_SENSE6 0x1A +#define SCSI_ALLOW_MEDIUM_REMOVAL 0x1E +#define SCSI_READ10 0x28 +#define SCSI_READ_CAPACITY10 0x25 +#define SCSI_REQUEST_SENSE 0x03 +#define SCSI_START_STOP_UNIT 0x1B +#define SCSI_TEST_UNIT_READY 0x00 +#define SCSI_WRITE10 0x2A +#define SCSI_VERIFY10 0x2F +#define SCSI_READ_FORMAT_CAPACITIES 0x23 +#define SCSI_SYNCHRONIZE_CACHE 0x35 +#define SCSI_REPORT_LUN 0xA0 + +#define MSC_IDLE 0 +#define MSC_DATA_OUT 1 +#define MSC_DATA_IN 2 +#define MSC_SENDING_CSW 3 +#define MSC_ERROR 4 + +struct CBW { + uint32_t dCBWSignature; + uint32_t dCBWTag; + uint32_t dCBWDataTransferLength; + uint8_t bmCBWFlags; + uint8_t bCBWLUN; + uint8_t bCBWCBLength; + uint8_t CBWCB[16]; +} __attribute__((packed)); + +struct CSW { + uint32_t dCSWSignature; + uint32_t dCSWTag; + uint32_t dCSWDataResidue; + uint8_t bCSWStatus; +} __attribute__((packed)); + +#define SCSI_ERROR_NOT_READY 2 +#define SCSI_ERROR_ILLEAGAL_REQUEST 5 +#define SCSI_ERROR_UNIT_ATTENTION 6 +#define SCSI_ERROR_DATA_PROTECT 7 + +extern uint8_t media_available; +extern chopstx_mutex_t *pinpad_mutex; +extern chopstx_cond_t *pinpad_cond; diff --git a/gnuk/src/usb-strings.c.inc b/gnuk/src/usb-strings.c.inc new file mode 100644 index 0000000000..d2d2df6e55 --- /dev/null +++ b/gnuk/src/usb-strings.c.inc @@ -0,0 +1,55 @@ +static const uint8_t gnuk_string_vendor[] = { + 33*2+2, /* bLength */ + STRING_DESCRIPTOR, /* bDescriptorType */ + /* Manufacturer: "Free Software Initiative of Japan" */ + 'F', 0, 'r', 0, 'e', 0, 'e', 0, ' ', 0, 'S', 0, 'o', 0, 'f', 0, + 't', 0, 'w', 0, 'a', 0, 'r', 0, 'e', 0, ' ', 0, 'I', 0, 'n', 0, + 'i', 0, 't', 0, 'i', 0, 'a', 0, 't', 0, 'i', 0, 'v', 0, 'e', 0, + ' ', 0, 'o', 0, 'f', 0, ' ', 0, 'J', 0, 'a', 0, 'p', 0, 'a', 0, + 'n', 0, +}; + +static const uint8_t gnuk_string_product[] = { + 10*2+2, /* bLength */ + STRING_DESCRIPTOR, /* bDescriptorType */ + /* Product name: "Gnuk Token" */ + 'G', 0, 'n', 0, 'u', 0, 'k', 0, ' ', 0, 'T', 0, 'o', 0, 'k', 0, + 'e', 0, 'n', 0, +}; + +const uint8_t gnuk_string_serial[] = { + 11*2+2+16, /* bLength */ + STRING_DESCRIPTOR, /* bDescriptorType */ + /* Serial number: "FSIJ-1.2.6-" */ + 'F', 0, 'S', 0, 'I', 0, 'J', 0, '-', 0, '1', 0, '.', 0, '2', 0, + '.', 0, '6', 0, '-', 0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +#ifdef USB_STRINGS_FOR_GNUK +static const uint8_t gnuk_revision_detail[] = { + 35*2+2, /* bLength */ + STRING_DESCRIPTOR, /* bDescriptorType */ + /* revision detail: "release/1.1.7-199-gf4083b8-modified" */ + 'r', 0, 'e', 0, 'l', 0, 'e', 0, 'a', 0, 's', 0, 'e', 0, '/', 0, + '1', 0, '.', 0, '1', 0, '.', 0, '7', 0, '-', 0, '1', 0, '9', 0, + '9', 0, '-', 0, 'g', 0, 'f', 0, '4', 0, '0', 0, '8', 0, '3', 0, + 'b', 0, '8', 0, '-', 0, 'm', 0, 'o', 0, 'd', 0, 'i', 0, 'f', 0, + 'i', 0, 'e', 0, 'd', 0, +}; + +static const uint8_t gnuk_config_options[] = { + 59*2+2, /* bLength */ + STRING_DESCRIPTOR, /* bDescriptorType */ + /* configure options: "FST_01:dfu=no:debug=no:pinpad=no:certdo=no:factory_reset=no" */ + 'F', 0, 'S', 0, 'T', 0, '_', 0, '0', 0, '1', 0, ':', 0, 'd', 0, + 'f', 0, 'u', 0, '=', 0, 'n', 0, 'o', 0, ':', 0, 'd', 0, 'e', 0, + 'b', 0, 'u', 0, 'g', 0, '=', 0, 'n', 0, 'o', 0, ':', 0, 'p', 0, + 'i', 0, 'n', 0, 'p', 0, 'a', 0, 'd', 0, '=', 0, 'n', 0, 'o', 0, + ':', 0, 'c', 0, 'e', 0, 'r', 0, 't', 0, 'd', 0, 'o', 0, '=', 0, + 'n', 0, 'o', 0, ':', 0, 'f', 0, 'a', 0, 'c', 0, 't', 0, 'o', 0, + 'r', 0, 'y', 0, '_', 0, 'r', 0, 'e', 0, 's', 0, 'e', 0, 't', 0, + '=', 0, 'n', 0, 'o', 0, +}; +#endif diff --git a/gnuk/src/usb-vid-pid-ver.c.inc b/gnuk/src/usb-vid-pid-ver.c.inc new file mode 100644 index 0000000000..1913c63138 --- /dev/null +++ b/gnuk/src/usb-vid-pid-ver.c.inc @@ -0,0 +1,3 @@ + 0x4b, 0x23, /* idVendor */ + 0x00, 0x00, /* idProduct */ + 0x00, 0x02, /* bcdDevice */ diff --git a/gnuk/src/usb_conf.h b/gnuk/src/usb_conf.h new file mode 100644 index 0000000000..6ee01ce94a --- /dev/null +++ b/gnuk/src/usb_conf.h @@ -0,0 +1,67 @@ +/* USB buffer memory definition and number of string descriptors */ + +#ifndef __USB_CONF_H +#define __USB_CONF_H + +#define CCID_NUM_INTERFACES 1 +#define CCID_INTERFACE 0 +#ifdef HID_CARD_CHANGE_SUPPORT +#define HID_NUM_INTERFACES 1 +#define HID_INTERFACE 1 +#else +#define HID_NUM_INTERFACES 0 +#endif +#ifdef ENABLE_VIRTUAL_COM_PORT +#define VCOM_NUM_INTERFACES 2 +#define VCOM_INTERFACE_0 (CCID_NUM_INTERFACES + HID_NUM_INTERFACES) +#define VCOM_INTERFACE_1 (CCID_NUM_INTERFACES + HID_NUM_INTERFACES + 1) +#else +#define VCOM_NUM_INTERFACES 0 +#endif +#ifdef PINPAD_DND_SUPPORT +#define MSC_NUM_INTERFACES 1 +#define MSC_INTERFACE (CCID_NUM_INTERFACES + HID_NUM_INTERFACES + VCOM_NUM_INTERFACES) +#else +#define MSC_NUM_INTERFACES 0 +#endif +#define NUM_INTERFACES (CCID_NUM_INTERFACES + HID_NUM_INTERFACES \ + + VCOM_NUM_INTERFACES + MSC_NUM_INTERFACES) + +#if defined(USB_SELF_POWERED) +#define USB_INITIAL_FEATURE 0xC0 /* bmAttributes: self powered */ +#else +#define USB_INITIAL_FEATURE 0x80 /* bmAttributes: bus powered */ +#endif + +/* Control pipe */ +/* EP0: 64-byte, 64-byte */ +#define ENDP0_RXADDR (0x40) +#define ENDP0_TXADDR (0x80) + +/* CCID/ICCD BULK_IN, BULK_OUT */ +/* EP1: 64-byte, 64-byte */ +#define ENDP1_TXADDR (0xc0) +#define ENDP1_RXADDR (0x100) +/* EP2: INTR_IN: 4-byte */ +#define ENDP2_TXADDR (0x140) + +/* CDC BULK_IN, INTR_IN, BULK_OUT */ +/* EP3: 16-byte */ +#define ENDP3_TXADDR (0x144) +/* EP4: 8-byte */ +#define ENDP4_TXADDR (0x154) +/* EP5: 16-byte */ +#define ENDP5_RXADDR (0x15c) + +/* 0x16c - 0x17e : 18-byte */ + +/* HID INTR_IN */ +/* EP7: 2-byte */ +#define ENDP7_TXADDR (0x17e) + +/* MSC BULK_IN, BULK_OUT */ +/* EP6: 64-byte, 64-byte */ +#define ENDP6_TXADDR (0x180) +#define ENDP6_RXADDR (0x1c0) + +#endif /* __USB_CONF_H */ diff --git a/gnuk/src/usb_ctrl.c b/gnuk/src/usb_ctrl.c new file mode 100644 index 0000000000..e9335b735a --- /dev/null +++ b/gnuk/src/usb_ctrl.c @@ -0,0 +1,540 @@ +/* + * usb_ctrl.c - USB control pipe device specific code for Gnuk + * + * Copyright (C) 2010, 2011, 2012, 2013, 2015, 2016, 2017 + * 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 . + * + */ + +/* Packet size of USB Bulk transfer for full speed */ +#define GNUK_MAX_PACKET_SIZE 64 + +#include +#include +#include + +#include "config.h" + +#ifdef DEBUG +#include "debug.h" +#endif + +#include "usb_lld.h" +#include "usb_conf.h" +#include "gnuk.h" +#include "neug.h" + +#ifdef ENABLE_VIRTUAL_COM_PORT +#include "usb-cdc.h" + +struct line_coding +{ + uint32_t bitrate; + uint8_t format; + uint8_t paritytype; + uint8_t datatype; +}; + +static struct line_coding line_coding = { + 115200, /* baud rate: 115200 */ + 0x00, /* stop bits: 1 */ + 0x00, /* parity: none */ + 0x08 /* bits: 8 */ +}; + +#define CDC_CTRL_DTR 0x0001 + +static int +vcom_port_data_setup (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + + if (USB_SETUP_GET (arg->type)) + { + if (arg->request == USB_CDC_REQ_GET_LINE_CODING) + return usb_lld_ctrl_send (dev, &line_coding, sizeof (line_coding)); + } + else /* USB_SETUP_SET (req) */ + { + if (arg->request == USB_CDC_REQ_SET_LINE_CODING + && arg->len == sizeof (line_coding)) + return usb_lld_ctrl_recv (dev, &line_coding, sizeof (line_coding)); + else if (arg->request == USB_CDC_REQ_SET_CONTROL_LINE_STATE) + return usb_lld_ctrl_ack (dev); + } + + return -1; +} +#endif + +#ifdef PINPAD_DND_SUPPORT +#include "usb-msc.h" +#endif + +uint32_t bDeviceState = UNCONNECTED; /* USB device status */ + +#define USB_HID_REQ_GET_REPORT 1 +#define USB_HID_REQ_GET_IDLE 2 +#define USB_HID_REQ_GET_PROTOCOL 3 +#define USB_HID_REQ_SET_REPORT 9 +#define USB_HID_REQ_SET_IDLE 10 +#define USB_HID_REQ_SET_PROTOCOL 11 + +#ifndef HID_LED_STATUS_CARDCHANGE +/* NumLock=1, CapsLock=2, ScrollLock=4 */ +#define HID_LED_STATUS_CARDCHANGE 0x04 +#endif + +#ifdef HID_CARD_CHANGE_SUPPORT +static uint8_t hid_idle_rate; /* in 4ms */ +static uint8_t hid_report_saved; +static uint16_t hid_report; +#endif + +static void +gnuk_setup_endpoints_for_interface (struct usb_dev *dev, + uint16_t interface, int stop) +{ +#if !defined(GNU_LINUX_EMULATION) + (void)dev; +#endif + + if (interface == CCID_INTERFACE) + { + if (!stop) + { +#ifdef GNU_LINUX_EMULATION + usb_lld_setup_endp (dev, ENDP1, 1, 1); + usb_lld_setup_endp (dev, ENDP2, 0, 1); +#else + usb_lld_setup_endpoint (ENDP1, EP_BULK, 0, ENDP1_RXADDR, + ENDP1_TXADDR, GNUK_MAX_PACKET_SIZE); + usb_lld_setup_endpoint (ENDP2, EP_INTERRUPT, 0, 0, ENDP2_TXADDR, 0); +#endif + } + else + { + usb_lld_stall_rx (ENDP1); + usb_lld_stall_tx (ENDP1); + usb_lld_stall_tx (ENDP2); + } + } +#ifdef HID_CARD_CHANGE_SUPPORT + else if (interface == HID_INTERFACE) + { + if (!stop) +#ifdef GNU_LINUX_EMULATION + usb_lld_setup_endp (dev, ENDP7, 0, 1); +#else + usb_lld_setup_endpoint (ENDP7, EP_INTERRUPT, 0, 0, ENDP7_TXADDR, 0); +#endif + else + usb_lld_stall_tx (ENDP7); + } +#endif +#ifdef ENABLE_VIRTUAL_COM_PORT + else if (interface == VCOM_INTERFACE_0) + { + if (!stop) +#ifdef GNU_LINUX_EMULATION + usb_lld_setup_endp (dev, ENDP4, 0, 1); +#else + usb_lld_setup_endpoint (ENDP4, EP_INTERRUPT, 0, 0, ENDP4_TXADDR, 0); +#endif + else + usb_lld_stall_tx (ENDP4); + } + else if (interface == VCOM_INTERFACE_1) + { + if (!stop) + { +#ifdef GNU_LINUX_EMULATION + usb_lld_setup_endp (dev, ENDP3, 0, 1); + usb_lld_setup_endp (dev, ENDP5, 1, 0); +#else + usb_lld_setup_endpoint (ENDP3, EP_BULK, 0, 0, ENDP3_TXADDR, 0); + usb_lld_setup_endpoint (ENDP5, EP_BULK, 0, ENDP5_RXADDR, 0, + VIRTUAL_COM_PORT_DATA_SIZE); +#endif + } + else + { + usb_lld_stall_tx (ENDP3); + usb_lld_stall_rx (ENDP5); + } + } +#endif +#ifdef PINPAD_DND_SUPPORT + else if (interface == MSC_INTERFACE) + { + if (!stop) +#ifdef GNU_LINUX_EMULATION + usb_lld_setup_endp (dev, ENDP6, 1, 1); +#else + usb_lld_setup_endpoint (ENDP6, EP_BULK, 0, + ENDP6_RXADDR, ENDP6_TXADDR, 64); +#endif + else + { + usb_lld_stall_tx (ENDP6); + usb_lld_stall_rx (ENDP6); + } + } +#endif +} + +void +usb_device_reset (struct usb_dev *dev) +{ + int i; + + usb_lld_reset (dev, USB_INITIAL_FEATURE); + + /* Initialize Endpoint 0 */ +#ifdef GNU_LINUX_EMULATION + usb_lld_setup_endp (dev, ENDP0, 1, 1); +#else + usb_lld_setup_endpoint (ENDP0, EP_CONTROL, 0, ENDP0_RXADDR, ENDP0_TXADDR, + 64); +#endif + + /* Stop the interface */ + for (i = 0; i < NUM_INTERFACES; i++) + gnuk_setup_endpoints_for_interface (dev, i, 1); + + bDeviceState = ATTACHED; + ccid_usb_reset (1); +} + +#define USB_CCID_REQ_ABORT 0x01 +#define USB_CCID_REQ_GET_CLOCK_FREQUENCIES 0x02 +#define USB_CCID_REQ_GET_DATA_RATES 0x03 + +static const uint8_t freq_table[] = { 0xa0, 0x0f, 0, 0, }; /* dwDefaultClock */ +static const uint8_t data_rate_table[] = { 0x80, 0x25, 0, 0, }; /* dwDataRate */ + +#if defined(PINPAD_DND_SUPPORT) +static const uint8_t lun_table[] = { 0, 0, 0, 0, }; +#endif + +#ifdef FLASH_UPGRADE_SUPPORT +static const uint8_t *const mem_info[] = { &_regnual_start, __heap_end__, }; +#endif + +#define USB_FSIJ_GNUK_MEMINFO 0 +#define USB_FSIJ_GNUK_DOWNLOAD 1 +#define USB_FSIJ_GNUK_EXEC 2 +#define USB_FSIJ_GNUK_CARD_CHANGE 3 + +#ifdef FLASH_UPGRADE_SUPPORT +/* After calling this function, CRC module remain enabled. */ +static int +download_check_crc32 (struct usb_dev *dev, const uint32_t *end_p) +{ + uint32_t crc32 = *end_p; + const uint32_t *p; + + crc32_rv_reset (); + + for (p = (const uint32_t *)&_regnual_start; p < end_p; p++) + crc32_rv_step (rbit (*p)); + + if ((rbit (crc32_rv_get ()) ^ crc32) == 0xffffffff) + return usb_lld_ctrl_ack (dev); + + return -1; +} +#endif + +int +usb_setup (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT); + + if (type_rcp == (VENDOR_REQUEST | DEVICE_RECIPIENT)) + { + if (USB_SETUP_GET (arg->type)) + { +#ifdef FLASH_UPGRADE_SUPPORT + if (arg->request == USB_FSIJ_GNUK_MEMINFO) + return usb_lld_ctrl_send (dev, mem_info, sizeof (mem_info)); +#else + return -1; +#endif + } + else /* SETUP_SET */ + { +#ifdef FLASH_UPGRADE_SUPPORT + uint8_t *addr = sram_address ((arg->value * 0x100) + arg->index); +#endif + + if (arg->request == USB_FSIJ_GNUK_DOWNLOAD) + { +#ifdef FLASH_UPGRADE_SUPPORT + if (*ccid_state_p != CCID_STATE_EXITED) + return -1; + + if (addr < &_regnual_start || addr + arg->len > __heap_end__) + return -1; + + if (arg->index + arg->len < 256) + memset (addr + arg->index + arg->len, 0, + 256 - (arg->index + arg->len)); + + return usb_lld_ctrl_recv (dev, addr, arg->len); +#else + return -1; +#endif + } + else if (arg->request == USB_FSIJ_GNUK_EXEC && arg->len == 0) + { +#ifdef FLASH_UPGRADE_SUPPORT + if (*ccid_state_p != CCID_STATE_EXITED) + return -1; + + if (((uintptr_t)addr & 0x03)) + return -1; + + return download_check_crc32 (dev, (uint32_t *)addr); +#else + return -1; +#endif + } + else if (arg->request == USB_FSIJ_GNUK_CARD_CHANGE && arg->len == 0) + { + if (arg->value != 0 && arg->value != 1 && arg->value != 2) + return -1; + + ccid_card_change_signal (arg->value); + return usb_lld_ctrl_ack (dev); + } + } + } + else if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT)) + { + if (arg->index == CCID_INTERFACE) + { + if (USB_SETUP_GET (arg->type)) + { + if (arg->request == USB_CCID_REQ_GET_CLOCK_FREQUENCIES) + return usb_lld_ctrl_send (dev, freq_table, sizeof (freq_table)); + else if (arg->request == USB_CCID_REQ_GET_DATA_RATES) + return usb_lld_ctrl_send (dev, data_rate_table, + sizeof (data_rate_table)); + } + else + { + if (arg->request == USB_CCID_REQ_ABORT) + /* wValue: bSeq, bSlot */ + /* Abortion is not supported in Gnuk */ + return -1; + } + } +#ifdef HID_CARD_CHANGE_SUPPORT + else if (arg->index == HID_INTERFACE) + { + switch (arg->request) + { + case USB_HID_REQ_GET_IDLE: + return usb_lld_ctrl_send (dev, &hid_idle_rate, 1); + case USB_HID_REQ_SET_IDLE: + return usb_lld_ctrl_recv (dev, &hid_idle_rate, 1); + + case USB_HID_REQ_GET_REPORT: + /* Request of LED status and key press */ + return usb_lld_ctrl_send (dev, &hid_report, 2); + + case USB_HID_REQ_SET_REPORT: + /* Received LED set request */ + if (arg->len == 1) + return usb_lld_ctrl_recv (dev, &hid_report, arg->len); + else + return usb_lld_ctrl_ack (dev); + + case USB_HID_REQ_GET_PROTOCOL: + case USB_HID_REQ_SET_PROTOCOL: + /* This driver doesn't support boot protocol. */ + return -1; + + default: + return -1; + } + } +#endif +#ifdef ENABLE_VIRTUAL_COM_PORT + else if (arg->index == VCOM_INTERFACE_0) + return vcom_port_data_setup (dev); +#endif +#ifdef PINPAD_DND_SUPPORT + else if (arg->index == MSC_INTERFACE) + { + if (USB_SETUP_GET (arg->type)) + { + if (arg->request == MSC_GET_MAX_LUN_COMMAND) + return usb_lld_ctrl_send (dev, lun_table, sizeof (lun_table)); + } + else + if (arg->request == MSC_MASS_STORAGE_RESET_COMMAND) + return usb_lld_ctrl_ack (dev); + } +#endif + } + + return -1; +} + + +void +usb_ctrl_write_finish (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT); + + if (type_rcp == (VENDOR_REQUEST | DEVICE_RECIPIENT)) + { + if (USB_SETUP_SET (arg->type) && arg->request == USB_FSIJ_GNUK_EXEC) + { + if (*ccid_state_p != CCID_STATE_EXITED) + return; + + bDeviceState = UNCONNECTED; + usb_lld_prepare_shutdown (); /* No further USB communication */ + led_blink (LED_GNUK_EXEC); /* Notify the main. */ + } + } +#if defined(HID_CARD_CHANGE_SUPPORT) || defined (ENABLE_VIRTUAL_COM_PORT) + else if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT)) + { +# if defined(ENABLE_VIRTUAL_COM_PORT) + if (arg->index == VCOM_INTERFACE_0 && USB_SETUP_SET (arg->type) + && arg->request == USB_CDC_REQ_SET_CONTROL_LINE_STATE) + { + uint8_t connected_saved = stdout.connected; + + if ((arg->value & CDC_CTRL_DTR) != 0) + { + if (stdout.connected == 0) + /* It's Open call */ + stdout.connected++; + } + else + { + if (stdout.connected) + /* Close call */ + stdout.connected = 0; + } + + chopstx_mutex_lock (&stdout.m_dev); + if (stdout.connected != connected_saved) + chopstx_cond_signal (&stdout.cond_dev); + chopstx_mutex_unlock (&stdout.m_dev); + } +# endif +# if defined(HID_CARD_CHANGE_SUPPORT) + if (arg->index == HID_INTERFACE && arg->request == USB_HID_REQ_SET_REPORT) + { + if ((hid_report ^ hid_report_saved) & HID_LED_STATUS_CARDCHANGE) + ccid_card_change_signal (CARD_CHANGE_TOGGLE); + + hid_report_saved = hid_report; + } +# endif + } +#endif +} + + +int +usb_set_configuration (struct usb_dev *dev) +{ + int i; + uint8_t current_conf; + + current_conf = usb_lld_current_configuration (dev); + if (current_conf == 0) + { + if (dev->dev_req.value != 1) + return -1; + + usb_lld_set_configuration (dev, 1); + for (i = 0; i < NUM_INTERFACES; i++) + gnuk_setup_endpoints_for_interface (dev, i, 0); + bDeviceState = CONFIGURED; + } + else if (current_conf != dev->dev_req.value) + { + if (dev->dev_req.value != 0) + return -1; + + usb_lld_set_configuration (dev, 0); + for (i = 0; i < NUM_INTERFACES; i++) + gnuk_setup_endpoints_for_interface (dev, i, 1); + bDeviceState = ADDRESSED; + ccid_usb_reset (1); + } + + /* Do nothing when current_conf == value */ + return usb_lld_ctrl_ack (dev); +} + + +int +usb_set_interface (struct usb_dev *dev) +{ + uint16_t interface = dev->dev_req.index; + uint16_t alt = dev->dev_req.value; + + if (interface >= NUM_INTERFACES) + return -1; + + if (alt != 0) + return -1; + else + { + gnuk_setup_endpoints_for_interface (dev, interface, 0); + ccid_usb_reset (0); + return usb_lld_ctrl_ack (dev); + } +} + + +int +usb_get_interface (struct usb_dev *dev) +{ + const uint8_t zero = 0; + uint16_t interface = dev->dev_req.index; + + if (interface >= NUM_INTERFACES) + return -1; + + return usb_lld_ctrl_send (dev, &zero, 1); +} + +int +usb_get_status_interface (struct usb_dev *dev) +{ + const uint16_t status_info = 0; + uint16_t interface = dev->dev_req.index; + + if (interface >= NUM_INTERFACES) + return -1; + + return usb_lld_ctrl_send (dev, &status_info, 2); +} diff --git a/gnuk/src/usb_desc.c b/gnuk/src/usb_desc.c new file mode 100644 index 0000000000..8031088300 --- /dev/null +++ b/gnuk/src/usb_desc.c @@ -0,0 +1,406 @@ +/* + * usb_desc.c - USB Descriptor + */ + +#include +#include + +#include "config.h" + +#include "sys.h" +#include "usb_lld.h" +#include "usb_conf.h" +#include "usb-cdc.h" + + +#ifdef HID_CARD_CHANGE_SUPPORT +/* HID report descriptor. */ +#define HID_REPORT_DESC_SIZE (sizeof (hid_report_desc)) + +static const uint8_t hid_report_desc[] = { + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x07, /* USAGE (Keypad) */ + 0xa1, 0x01, /* COLLECTION (Application) */ + 0x05, 0x07, /* USAGE_PAGE (Key Codes) */ + 0x19, 0xe0, /* USAGE_MINIMUM (Keyboard LeftControl) */ + 0x29, 0xe7, /* USAGE_MAXIMUM (Keyboard Right GUI) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x95, 0x08, /* REPORT_COUNT (8) */ + 0x81, 0x02, /* INPUT (Data,Var,Abs); Modifier byte */ + + /* + * NumLock, CapsLock, ScrollLock, Compose, Kana, Power, Shift, + * Do Not Disturb + */ + 0x05, 0x08, /* USAGE_PAGE (LEDs) */ + 0x19, 0x01, /* USAGE_MINIMUM (1) */ + 0x29, 0x08, /* USAGE_MAXIMUM (8) */ + 0x95, 0x08, /* REPORT_COUNT (8) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x91, 0x02, /* OUTPUT (Data,Var,Abs); LED report */ + + 0x05, 0x07, /* USAGE_PAGE (Key Codes) */ + 0x19, 0x00, /* USAGE_MINIMUM (Reserved (no event indicated)) */ + 0x29, 0x65, /* USAGE_MAXIMUM (Keyboard Application) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x65, /* LOGICAL_MAXIMUM (101) */ + 0x95, 0x06, /* REPORT_COUNT (1) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x81, 0x00, /* INPUT (Data,Ary,Abs); Key arrays (1 bytes) */ + 0xc0 /* END_COLLECTION */ +}; +#endif + +#define USB_CCID_INTERFACE_CLASS 0x0B +#define USB_CCID_INTERFACE_SUBCLASS 0x00 +#define USB_CCID_INTERFACE_BULK_PROTOCOL 0x00 +#define USB_CCID_DATA_SIZE 64 + +/* USB Standard Device Descriptor */ +#if !defined(GNU_LINUX_EMULATION) +static const +#endif +uint8_t device_desc[] = { + 18, /* bLength */ + DEVICE_DESCRIPTOR, /* bDescriptorType */ + 0x10, 0x01, /* bcdUSB = 1.1 */ + 0x00, /* bDeviceClass: 0 means deferred to interface */ + 0x00, /* bDeviceSubClass */ + 0x00, /* bDeviceProtocol */ + 0x40, /* bMaxPacketSize0 */ +#include "usb-vid-pid-ver.c.inc" + 1, /* Index of string descriptor describing manufacturer */ + 2, /* Index of string descriptor describing product */ + 3, /* Index of string descriptor describing the device's serial number */ + 0x01 /* bNumConfigurations */ +}; + +#define CCID_TOTAL_LENGTH (9+9+54+7+7+7) + +#ifdef HID_CARD_CHANGE_SUPPORT +#define HID_TOTAL_LENGTH (9+9+7) +#else +#define HID_TOTAL_LENGTH 0 +#endif + +#ifdef ENABLE_VIRTUAL_COM_PORT +#define VCOM_TOTAL_LENGTH (9+5+5+4+5+7+9+7+7) +#else +#define VCOM_TOTAL_LENGTH 0 +#endif + +#ifdef PINPAD_DND_SUPPORT +#define MSC_TOTAL_LENGTH (9+7+7) +#else +#define MSC_TOTAL_LENGTH 0 +#endif + +#define W_TOTAL_LENGTH (CCID_TOTAL_LENGTH + HID_TOTAL_LENGTH \ + + VCOM_TOTAL_LENGTH + MSC_TOTAL_LENGTH) + + +/* Configuation Descriptor */ +static const uint8_t config_desc[] = { + 9, /* bLength: Configuation Descriptor size */ + CONFIG_DESCRIPTOR, /* bDescriptorType: Configuration */ + W_TOTAL_LENGTH, 0x00, /* wTotalLength:no of returned bytes */ + NUM_INTERFACES, /* bNumInterfaces: */ + 0x01, /* bConfigurationValue: Configuration value */ + 0x00, /* iConfiguration: Index of string descriptor describing the configuration */ + USB_INITIAL_FEATURE, /* bmAttributes*/ + 50, /* MaxPower 100 mA */ + + /* Interface Descriptor */ + 9, /* bLength: Interface Descriptor size */ + INTERFACE_DESCRIPTOR, /* bDescriptorType: Interface */ + CCID_INTERFACE, /* bInterfaceNumber: Index of this interface */ + 0, /* Alternate setting for this interface */ + 3, /* bNumEndpoints: Bulk-IN, Bulk-OUT, Intr-IN */ + USB_CCID_INTERFACE_CLASS, + USB_CCID_INTERFACE_SUBCLASS, + USB_CCID_INTERFACE_BULK_PROTOCOL, + 0, /* string index for interface */ + + /* ICC Descriptor */ + 54, /* bLength: */ + 0x21, /* bDescriptorType: USBDESCR_ICC */ + 0x10, 0x01, /* bcdCCID: revision 1.1 (of CCID) */ + 0, /* bMaxSlotIndex: */ + 1, /* bVoltageSupport: 5V-only */ + 0x02, 0, 0, 0, /* dwProtocols: T=1 */ + 0xa0, 0x0f, 0, 0, /* dwDefaultClock: 4000 */ + 0xa0, 0x0f, 0, 0, /* dwMaximumClock: 4000 */ + 0, /* bNumClockSupported: 0x00 */ + 0x80, 0x25, 0, 0, /* dwDataRate: 9600 */ + 0x80, 0x25, 0, 0, /* dwMaxDataRate: 9600 */ + 0, /* bNumDataRateSupported: 0x00 */ + 0xfe, 0, 0, 0, /* dwMaxIFSD: 254 */ + 0, 0, 0, 0, /* dwSynchProtocols: 0 */ + 0, 0, 0, 0, /* dwMechanical: 0 */ + 0x7a, 0x04, 0x02, 0x00, /* dwFeatures: + * Short and extended APDU level: 0x40000 ---- + * Short APDU level : 0x20000 * + * (ICCD?) : 0x00800 ---- + * Automatic IFSD : 0x00400 * + * NAD value other than 0x00 : 0x00200 + * Can set ICC in clock stop : 0x00100 + * Automatic PPS CUR : 0x00080 + * Automatic PPS PROP : 0x00040 * + * Auto baud rate change : 0x00020 * + * Auto clock change : 0x00010 * + * Auto voltage selection : 0x00008 * + * Auto activaction of ICC : 0x00004 + * Automatic conf. based on ATR : 0x00002 * + */ + 0x0f, 0x01, 0, 0, /* dwMaxCCIDMessageLength: 271 */ + 0xff, /* bClassGetResponse: 0xff */ + 0x00, /* bClassEnvelope: 0 */ + 0, 0, /* wLCDLayout: 0 */ +#if defined(PINPAD_SUPPORT) +#if defined(PINPAD_CIR_SUPPORT) || defined(PINPAD_DND_SUPPORT) + 1, /* bPinSupport: with PIN pad (verify) */ +#elif defined(PINPAD_DIAL_SUPPORT) + 3, /* bPinSupport: with PIN pad (verify, modify) */ +#endif +#else + 0, /* bPinSupport: No PIN pad */ +#endif + 1, /* bMaxCCIDBusySlots: 1 */ + /*Endpoint IN1 Descriptor*/ + 7, /* bLength: Endpoint Descriptor size */ + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + 0x81, /* bEndpointAddress: (IN1) */ + 0x02, /* bmAttributes: Bulk */ + USB_CCID_DATA_SIZE, 0x00, /* wMaxPacketSize: */ + 0x00, /* bInterval */ + /*Endpoint OUT1 Descriptor*/ + 7, /* bLength: Endpoint Descriptor size */ + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + 0x01, /* bEndpointAddress: (OUT1) */ + 0x02, /* bmAttributes: Bulk */ + USB_CCID_DATA_SIZE, 0x00, /* wMaxPacketSize: */ + 0x00, /* bInterval */ + /*Endpoint IN2 Descriptor*/ + 7, /* bLength: Endpoint Descriptor size */ + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + 0x82, /* bEndpointAddress: (IN2) */ + 0x03, /* bmAttributes: Interrupt */ + 0x04, 0x00, /* wMaxPacketSize: 4 */ + 0xFF, /* bInterval (255ms) */ + +#ifdef HID_CARD_CHANGE_SUPPORT + /* Interface Descriptor */ + 9, /* bLength: Interface Descriptor size */ + INTERFACE_DESCRIPTOR, /* bDescriptorType: Interface */ + HID_INTERFACE, /* bInterfaceNumber: Number of Interface */ + 0x00, /* bAlternateSetting: Alternate setting */ + 0x01, /* bNumEndpoints: One endpoint used */ + 0x03, /* bInterfaceClass: HID */ + 0x00, /* bInterfaceSubClass: no boot */ + 0x00, /* bInterfaceProtocol: 0=none */ + 0x00, /* iInterface: no string for this interface */ + + 9, /* bLength: HID Descriptor size */ + 0x21, /* bDescriptorType: HID */ + 0x10, 0x01, /* bcdHID: HID Class Spec release number */ + 0x00, /* bCountryCode: Hardware target country */ + 0x01, /* bNumDescriptors: Number of HID class descriptors to follow */ + 0x22, /* bDescriptorType */ + HID_REPORT_DESC_SIZE, 0, /* wItemLength: Total length of Report descriptor */ + + /*Endpoint IN7 Descriptor*/ + 7, /* bLength: Endpoint Descriptor size */ + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + 0x87, /* bEndpointAddress: (IN7) */ + 0x03, /* bmAttributes: Interrupt */ + 0x02, 0x00, /* wMaxPacketSize: 2 */ + 0x20, /* bInterval (32ms) */ +#endif + +#ifdef ENABLE_VIRTUAL_COM_PORT + /* Interface Descriptor */ + 9, /* bLength: Interface Descriptor size */ + INTERFACE_DESCRIPTOR, /* bDescriptorType: Interface */ + VCOM_INTERFACE_0, /* bInterfaceNumber: Index of Interface */ + 0x00, /* bAlternateSetting: Alternate setting */ + 0x01, /* bNumEndpoints: One endpoints used */ + 0x02, /* bInterfaceClass: Communication Interface Class */ + 0x02, /* bInterfaceSubClass: Abstract Control Model */ + 0x01, /* bInterfaceProtocol: Common AT commands */ + 0x00, /* iInterface: */ + /*Header Functional Descriptor*/ + 5, /* bLength: Endpoint Descriptor size */ + 0x24, /* bDescriptorType: CS_INTERFACE */ + 0x00, /* bDescriptorSubtype: Header Func Desc */ + 0x10, 0x01, /* bcdCDC: spec release number */ + /*Call Managment Functional Descriptor*/ + 5, /* bFunctionLength */ + 0x24, /* bDescriptorType: CS_INTERFACE */ + 0x01, /* bDescriptorSubtype: Call Management Func Desc */ + 0x03, /* bmCapabilities: D0+D1 */ + VCOM_INTERFACE_1, /* bDataInterface */ + /*ACM Functional Descriptor*/ + 4, /* bFunctionLength */ + 0x24, /* bDescriptorType: CS_INTERFACE */ + 0x02, /* bDescriptorSubtype: Abstract Control Management desc */ + 0x02, /* bmCapabilities */ + /*Union Functional Descriptor*/ + 5, /* bFunctionLength */ + 0x24, /* bDescriptorType: CS_INTERFACE */ + 0x06, /* bDescriptorSubtype: Union func desc */ + VCOM_INTERFACE_0, /* bMasterInterface: Communication class interface */ + VCOM_INTERFACE_1, /* bSlaveInterface0: Data Class Interface */ + /*Endpoint 4 Descriptor*/ + 7, /* bLength: Endpoint Descriptor size */ + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + 0x84, /* bEndpointAddress: (IN4) */ + 0x03, /* bmAttributes: Interrupt */ + VIRTUAL_COM_PORT_INT_SIZE, 0x00, /* wMaxPacketSize: */ + 0xFF, /* bInterval: */ + + /*Data class interface descriptor*/ + 9, /* bLength: Endpoint Descriptor size */ + INTERFACE_DESCRIPTOR, /* bDescriptorType: */ + VCOM_INTERFACE_1, /* bInterfaceNumber: Index of Interface */ + 0x00, /* bAlternateSetting: Alternate setting */ + 0x02, /* bNumEndpoints: Two endpoints used */ + 0x0A, /* bInterfaceClass: CDC */ + 0x00, /* bInterfaceSubClass: */ + 0x00, /* bInterfaceProtocol: */ + 0x00, /* iInterface: */ + /*Endpoint 5 Descriptor*/ + 7, /* bLength: Endpoint Descriptor size */ + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + 0x05, /* bEndpointAddress: (OUT5) */ + 0x02, /* bmAttributes: Bulk */ + VIRTUAL_COM_PORT_DATA_SIZE, 0x00, /* wMaxPacketSize: */ + 0x00, /* bInterval: ignore for Bulk transfer */ + /*Endpoint 3 Descriptor*/ + 7, /* bLength: Endpoint Descriptor size */ + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + 0x83, /* bEndpointAddress: (IN3) */ + 0x02, /* bmAttributes: Bulk */ + VIRTUAL_COM_PORT_DATA_SIZE, 0x00, /* wMaxPacketSize: */ + 0x00, /* bInterval */ +#endif +#ifdef PINPAD_DND_SUPPORT + /* Interface Descriptor.*/ + 9, /* bLength: Interface Descriptor size */ + INTERFACE_DESCRIPTOR, /* bDescriptorType: Interface */ + MSC_INTERFACE, /* bInterfaceNumber. */ + 0x00, /* bAlternateSetting. */ + 0x02, /* bNumEndpoints. */ + 0x08, /* bInterfaceClass (Mass Stprage). */ + 0x06, /* bInterfaceSubClass (SCSI + transparent command set, MSCO + chapter 2). */ + 0x50, /* bInterfaceProtocol (Bulk-Only + Mass Storage, MSCO chapter 3). */ + 0x00, /* iInterface. */ + /* Endpoint Descriptor.*/ + 7, /* bLength: Endpoint Descriptor size */ + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + 0x86, /* bEndpointAddress: (IN6) */ + 0x02, /* bmAttributes (Bulk). */ + 0x40, 0x00, /* wMaxPacketSize. */ + 0x00, /* bInterval (ignored for bulk). */ + /* Endpoint Descriptor.*/ + 7, /* bLength: Endpoint Descriptor size */ + ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */ + 0x06, /* bEndpointAddress: (OUT6) */ + 0x02, /* bmAttributes (Bulk). */ + 0x40, 0x00, /* wMaxPacketSize. */ + 0x00, /* bInterval (ignored for bulk). */ +#endif +}; + + +/* USB String Descriptors */ +static const uint8_t gnuk_string_lang_id[] = { + 4, /* bLength */ + STRING_DESCRIPTOR, + 0x09, 0x04 /* LangID = 0x0409: US-English */ +}; + +#define USB_STRINGS_FOR_GNUK 1 +#include "usb-strings.c.inc" + +struct desc +{ + const uint8_t *desc; + uint16_t size; +}; + +static const struct desc string_descriptors[] = { + {gnuk_string_lang_id, sizeof (gnuk_string_lang_id)}, + {gnuk_string_vendor, sizeof (gnuk_string_vendor)}, + {gnuk_string_product, sizeof (gnuk_string_product)}, + {gnuk_string_serial, sizeof (gnuk_string_serial)}, + {gnuk_revision_detail, sizeof (gnuk_revision_detail)}, + {gnuk_config_options, sizeof (gnuk_config_options)}, + {sys_version, sizeof (sys_version)}, +}; +#define NUM_STRING_DESC (sizeof (string_descriptors) / sizeof (struct desc)) + +#define USB_DT_HID 0x21 +#define USB_DT_REPORT 0x22 + +int +usb_get_descriptor (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + uint8_t desc_type = (arg->value >> 8); + uint8_t desc_index = (arg->value & 0xff); + + if (rcp == DEVICE_RECIPIENT) + { + if (desc_type == DEVICE_DESCRIPTOR) + return usb_lld_ctrl_send (dev, device_desc, sizeof (device_desc)); + else if (desc_type == CONFIG_DESCRIPTOR) + return usb_lld_ctrl_send (dev, config_desc, sizeof (config_desc)); + else if (desc_type == STRING_DESCRIPTOR) + { + if (desc_index < NUM_STRING_DESC) + return usb_lld_ctrl_send (dev, string_descriptors[desc_index].desc, + string_descriptors[desc_index].size); +#ifdef USE_SYS3 + else if (desc_index == NUM_STRING_DESC) + { + uint8_t usbbuf[64]; + int i; + size_t len; + + for (i = 0; i < (int)sizeof (usbbuf)/2 - 2; i++) + { + if (sys_board_name[i] == 0) + break; + + usbbuf[i*2+2] = sys_board_name[i]; + usbbuf[i*2+3] = 0; + } + usbbuf[0] = len = i*2 + 2; + usbbuf[1] = STRING_DESCRIPTOR; + return usb_lld_ctrl_send (dev, usbbuf, len); + } +#endif + } + } +#ifdef HID_CARD_CHANGE_SUPPORT + else if (rcp == INTERFACE_RECIPIENT) + { + if (arg->index == HID_INTERFACE) + { + if (desc_type == USB_DT_HID) + return usb_lld_ctrl_send (dev, config_desc+CCID_TOTAL_LENGTH+9, 9); + else if (desc_type == USB_DT_REPORT) + return usb_lld_ctrl_send (dev, hid_report_desc, + HID_REPORT_DESC_SIZE); + } + } +#endif + + return -1; +} diff --git a/gnuk/test/README b/gnuk/test/README new file mode 100644 index 0000000000..ddcde9c5de --- /dev/null +++ b/gnuk/test/README @@ -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. diff --git a/gnuk/test/ecc_nistp256_keys.py b/gnuk/test/ecc_nistp256_keys.py new file mode 100644 index 0000000000..cc3bb7d652 --- /dev/null +++ b/gnuk/test/ecc_nistp256_keys.py @@ -0,0 +1,31 @@ +# Data taken from: +# A. Jivsov, Sample Keys and Messages: +# https://sites.google.com/site/brainhub/pgpecckeys + +# uid ec_dsa_dh_256 +# sign key: +# nistp256/BAA59D9C 2010-09-17 +# keygrip: 8E06A180EFFE4C65B812150CAF19BF30C0689A4C +# +# q=(x, y) and d +key[0] = (0x0bc7a7baebd5f08c74c77b71ee44e7bb0b5a18317b996da5393e33acc52932c6, + 0xd2f60f4d1efe35a0b9fb8d3787ed4bee97ca012d07b8f5835be7093545d532e6, + 0xd8f28c530c99821faa5ee2ff4dd8d1df01995d4e98fb45f8768cb65abd4adaa9) + +# decryption key: +# sub nistp256/4089AB73 2010-09-17 nistp256 +# keygrip: E4403F3FD7A443FAC29FEF288FA0D20AC212851E +# +# q=(x, y) and d +key[1] = (0x7f70c0a8184cdcaea5db20ba8fed17e47bdefb744d575ec449130af37edade65, + 0x8ae7ee35d20e8897911c9f564be33d9a94bc1e5c927b1aa07ff750d2d11c2971, + 0xa05cd14749bea3f3d14c92dc438e45e351efe860360c431705b7d42410581843) + +# auth key from: uid ec_dsa_dh_256_no_pass +# +# q=(x, y) and d +key[2] = (0x81fbbc20eea9e8d1c3ceabb0a8185925b113d1ac42cd5c78403bd83da19235c6, + 0x5ed6db13d91db34507d0129bf88981878d29adbf8fcd1720afdb767bb3fcaaff, + 0xa355916f8665eb99c1af48d9560b5c6889e5287bc75aa693aaae9bdb15e8b3fd) + +# This file is here to extend the test suite for ECC. diff --git a/gnuk/test/features/000_empty_check.feature b/gnuk/test/features/000_empty_check.feature new file mode 100644 index 0000000000..4f4d35e3f7 --- /dev/null +++ b/gnuk/test/features/000_empty_check.feature @@ -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 diff --git a/gnuk/test/features/001_empty_check_passphrase.feature b/gnuk/test/features/001_empty_check_passphrase.feature new file mode 100644 index 0000000000..f026b08845 --- /dev/null +++ b/gnuk/test/features/001_empty_check_passphrase.feature @@ -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 diff --git a/gnuk/test/features/002_get_data_static.feature b/gnuk/test/features/002_get_data_static.feature new file mode 100644 index 0000000000..6c1f7d44a4 --- /dev/null +++ b/gnuk/test/features/002_get_data_static.feature @@ -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 data should match: \x00\x31\x84\x73\x80\x01\x80[\x00\x05]\x90\x00 + + Scenario: data object extended capabilities + When requesting extended capabilities: c0 + Then data should match: [\x70\x74]\x00\x00\x20[\x00\x08]\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-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff]\x00\x00 diff --git a/gnuk/test/features/003_keyattr_change.feature b/gnuk/test/features/003_keyattr_change.feature new file mode 100644 index 0000000000..d93c7d494b --- /dev/null +++ b/gnuk/test/features/003_keyattr_change.feature @@ -0,0 +1,28 @@ +@keyattr +Feature: key attribute change + In order to use a token with multiple different kind of key algorighm + A token should support key attribute change + + Scenario: key attribute data object write: algorithm for signature (RSA-4K) + Given cmd_put_data with c1 and "\x01\x10\x00\x00\x20\x00" + Then it should get success + + Scenario: key attribute data object write: algorithm for signature (RSA-2K) + Given cmd_put_data with c1 and "\x01\x08\x00\x00\x20\x00" + Then it should get success + + Scenario: key attribute data object write: algorithm for decryption (RSA-4K) + Given cmd_put_data with c2 and "\x01\x10\x00\x00\x20\x00" + Then it should get success + + Scenario: key attribute data object write: algorithm for decryption (RSA-2K) + Given cmd_put_data with c2 and "\x01\x08\x00\x00\x20\x00" + Then it should get success + + Scenario: key attribute data object write: algorithm for authentication (RSA-4K) + Given cmd_put_data with c3 and "\x01\x10\x00\x00\x20\x00" + Then it should get success + + Scenario: key attribute data object write: algorithm for authentication (RSA-2K) + Given cmd_put_data with c3 and "\x01\x08\x00\x00\x20\x00" + Then it should get success diff --git a/gnuk/test/features/010_setup_passphrase.feature b/gnuk/test/features/010_setup_passphrase.feature new file mode 100644 index 0000000000..ae9b8403d8 --- /dev/null +++ b/gnuk/test/features/010_setup_passphrase.feature @@ -0,0 +1,11 @@ +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 PW3 (admin-full mode) + Given cmd_change_reference_data with 3 and "12345678admin 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 diff --git a/gnuk/test/features/020_personalization_write.feature b/gnuk/test/features/020_personalization_write.feature new file mode 100644 index 0000000000..2fdd3874aa --- /dev/null +++ b/gnuk/test/features/020_personalization_write.feature @@ -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 diff --git a/gnuk/test/features/021_personalization_read.feature b/gnuk/test/features/021_personalization_read.feature new file mode 100644 index 0000000000..5f0f972698 --- /dev/null +++ b/gnuk/test/features/021_personalization_read.feature @@ -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 diff --git a/gnuk/test/features/030_key_registration.feature b/gnuk/test/features/030_key_registration.feature new file mode 100644 index 0000000000..305a5c14ab --- /dev/null +++ b/gnuk/test/features/030_key_registration.feature @@ -0,0 +1,60 @@ +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: setup PW1 (admin-full mode) + Given cmd_change_reference_data with 1 and "123456another 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 diff --git a/gnuk/test/features/040_passphrase_change.feature b/gnuk/test/features/040_passphrase_change.feature new file mode 100644 index 0000000000..c0c183518c --- /dev/null +++ b/gnuk/test/features/040_passphrase_change.feature @@ -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 (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 diff --git a/gnuk/test/features/100_compute_signature.feature b/gnuk/test/features/100_compute_signature.feature new file mode 100644 index 0000000000..67fcbccf74 --- /dev/null +++ b/gnuk/test/features/100_compute_signature.feature @@ -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 diff --git a/gnuk/test/features/101_decryption.feature b/gnuk/test/features/101_decryption.feature new file mode 100644 index 0000000000..1985dee51c --- /dev/null +++ b/gnuk/test/features/101_decryption.feature @@ -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 + diff --git a/gnuk/test/features/200_key_removal.feature b/gnuk/test/features/200_key_removal.feature new file mode 100644 index 0000000000..2582c253b2 --- /dev/null +++ b/gnuk/test/features/200_key_removal.feature @@ -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 diff --git a/gnuk/test/features/201_keygen.feature b/gnuk/test/features/201_keygen.feature new file mode 100644 index 0000000000..472ccde42a --- /dev/null +++ b/gnuk/test/features/201_keygen.feature @@ -0,0 +1,22 @@ +@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 diff --git a/gnuk/test/features/202_setup_passphrase.feature b/gnuk/test/features/202_setup_passphrase.feature new file mode 100644 index 0000000000..dd223cb32c --- /dev/null +++ b/gnuk/test/features/202_setup_passphrase.feature @@ -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 diff --git a/gnuk/test/features/203_passphrase_change.feature b/gnuk/test/features/203_passphrase_change.feature new file mode 100644 index 0000000000..e392f0b0ac --- /dev/null +++ b/gnuk/test/features/203_passphrase_change.feature @@ -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 diff --git a/gnuk/test/features/210_compute_signature.feature b/gnuk/test/features/210_compute_signature.feature new file mode 100644 index 0000000000..7d9398bd45 --- /dev/null +++ b/gnuk/test/features/210_compute_signature.feature @@ -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 data should match: \x00\x00(\x02|\x03) diff --git a/gnuk/test/features/211_decryption.feature b/gnuk/test/features/211_decryption.feature new file mode 100644 index 0000000000..e1cc98ca3b --- /dev/null +++ b/gnuk/test/features/211_decryption.feature @@ -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 + diff --git a/gnuk/test/features/370_key_removal.feature b/gnuk/test/features/370_key_removal.feature new file mode 100644 index 0000000000..5f89bc1097 --- /dev/null +++ b/gnuk/test/features/370_key_removal.feature @@ -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 diff --git a/gnuk/test/features/380_personalization_reset.feature b/gnuk/test/features/380_personalization_reset.feature new file mode 100644 index 0000000000..51a3430231 --- /dev/null +++ b/gnuk/test/features/380_personalization_reset.feature @@ -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 diff --git a/gnuk/test/features/390_reset_passphrase.feature b/gnuk/test/features/390_reset_passphrase.feature new file mode 100644 index 0000000000..853c515a73 --- /dev/null +++ b/gnuk/test/features/390_reset_passphrase.feature @@ -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 diff --git a/gnuk/test/features/400_empty_check.feature b/gnuk/test/features/400_empty_check.feature new file mode 100644 index 0000000000..4f4d35e3f7 --- /dev/null +++ b/gnuk/test/features/400_empty_check.feature @@ -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 diff --git a/gnuk/test/features/401_empty_check_passphrase.feature b/gnuk/test/features/401_empty_check_passphrase.feature new file mode 100644 index 0000000000..f026b08845 --- /dev/null +++ b/gnuk/test/features/401_empty_check_passphrase.feature @@ -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 diff --git a/gnuk/test/features/402_get_data_static.feature b/gnuk/test/features/402_get_data_static.feature new file mode 100644 index 0000000000..6c1f7d44a4 --- /dev/null +++ b/gnuk/test/features/402_get_data_static.feature @@ -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 data should match: \x00\x31\x84\x73\x80\x01\x80[\x00\x05]\x90\x00 + + Scenario: data object extended capabilities + When requesting extended capabilities: c0 + Then data should match: [\x70\x74]\x00\x00\x20[\x00\x08]\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-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff]\x00\x00 diff --git a/gnuk/test/features/410_setup_passphrase.feature b/gnuk/test/features/410_setup_passphrase.feature new file mode 100644 index 0000000000..2e97ab4bb8 --- /dev/null +++ b/gnuk/test/features/410_setup_passphrase.feature @@ -0,0 +1,7 @@ +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: verify PW3 (admin-less mode) + Given cmd_verify with 3 and "12345678" + Then it should get success diff --git a/gnuk/test/features/420_personalization_write.feature b/gnuk/test/features/420_personalization_write.feature new file mode 100644 index 0000000000..2fdd3874aa --- /dev/null +++ b/gnuk/test/features/420_personalization_write.feature @@ -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 diff --git a/gnuk/test/features/421_personalization_read.feature b/gnuk/test/features/421_personalization_read.feature new file mode 100644 index 0000000000..5f0f972698 --- /dev/null +++ b/gnuk/test/features/421_personalization_read.feature @@ -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 diff --git a/gnuk/test/features/430_key_registration.feature b/gnuk/test/features/430_key_registration.feature new file mode 100644 index 0000000000..95bab9748e --- /dev/null +++ b/gnuk/test/features/430_key_registration.feature @@ -0,0 +1,60 @@ +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: setup PW1 (admin-less mode) + Given cmd_change_reference_data with 1 and "123456another 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 diff --git a/gnuk/test/features/440_passphrase_change.feature b/gnuk/test/features/440_passphrase_change.feature new file mode 100644 index 0000000000..9f567e638f --- /dev/null +++ b/gnuk/test/features/440_passphrase_change.feature @@ -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 diff --git a/gnuk/test/features/500_compute_signature.feature b/gnuk/test/features/500_compute_signature.feature new file mode 100644 index 0000000000..67fcbccf74 --- /dev/null +++ b/gnuk/test/features/500_compute_signature.feature @@ -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 diff --git a/gnuk/test/features/501_decryption.feature b/gnuk/test/features/501_decryption.feature new file mode 100644 index 0000000000..1985dee51c --- /dev/null +++ b/gnuk/test/features/501_decryption.feature @@ -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 + diff --git a/gnuk/test/features/600_key_removal.feature b/gnuk/test/features/600_key_removal.feature new file mode 100644 index 0000000000..2bcf353f29 --- /dev/null +++ b/gnuk/test/features/600_key_removal.feature @@ -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 diff --git a/gnuk/test/features/601_keygen.feature b/gnuk/test/features/601_keygen.feature new file mode 100644 index 0000000000..472ccde42a --- /dev/null +++ b/gnuk/test/features/601_keygen.feature @@ -0,0 +1,22 @@ +@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 diff --git a/gnuk/test/features/602_setup_passphrase.feature b/gnuk/test/features/602_setup_passphrase.feature new file mode 100644 index 0000000000..476c2b047f --- /dev/null +++ b/gnuk/test/features/602_setup_passphrase.feature @@ -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 diff --git a/gnuk/test/features/603_passphrase_change.feature b/gnuk/test/features/603_passphrase_change.feature new file mode 100644 index 0000000000..accb4b83a8 --- /dev/null +++ b/gnuk/test/features/603_passphrase_change.feature @@ -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 diff --git a/gnuk/test/features/610_compute_signature.feature b/gnuk/test/features/610_compute_signature.feature new file mode 100644 index 0000000000..7d9398bd45 --- /dev/null +++ b/gnuk/test/features/610_compute_signature.feature @@ -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 data should match: \x00\x00(\x02|\x03) diff --git a/gnuk/test/features/611_decryption.feature b/gnuk/test/features/611_decryption.feature new file mode 100644 index 0000000000..e1cc98ca3b --- /dev/null +++ b/gnuk/test/features/611_decryption.feature @@ -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 + diff --git a/gnuk/test/features/770_key_removal.feature b/gnuk/test/features/770_key_removal.feature new file mode 100644 index 0000000000..a8c50b1e2e --- /dev/null +++ b/gnuk/test/features/770_key_removal.feature @@ -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 diff --git a/gnuk/test/features/780_personalization_reset.feature b/gnuk/test/features/780_personalization_reset.feature new file mode 100644 index 0000000000..51a3430231 --- /dev/null +++ b/gnuk/test/features/780_personalization_reset.feature @@ -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 diff --git a/gnuk/test/features/790_reset_passphrase.feature b/gnuk/test/features/790_reset_passphrase.feature new file mode 100644 index 0000000000..78edf9af9c --- /dev/null +++ b/gnuk/test/features/790_reset_passphrase.feature @@ -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 diff --git a/gnuk/test/features/800_empty_check.feature b/gnuk/test/features/800_empty_check.feature new file mode 100644 index 0000000000..4f4d35e3f7 --- /dev/null +++ b/gnuk/test/features/800_empty_check.feature @@ -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 diff --git a/gnuk/test/features/801_empty_check_passphrase.feature b/gnuk/test/features/801_empty_check_passphrase.feature new file mode 100644 index 0000000000..f026b08845 --- /dev/null +++ b/gnuk/test/features/801_empty_check_passphrase.feature @@ -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 diff --git a/gnuk/test/features/802_get_data_static.feature b/gnuk/test/features/802_get_data_static.feature new file mode 100644 index 0000000000..6c1f7d44a4 --- /dev/null +++ b/gnuk/test/features/802_get_data_static.feature @@ -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 data should match: \x00\x31\x84\x73\x80\x01\x80[\x00\x05]\x90\x00 + + Scenario: data object extended capabilities + When requesting extended capabilities: c0 + Then data should match: [\x70\x74]\x00\x00\x20[\x00\x08]\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-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff][\x00-\xff]\x00\x00 diff --git a/gnuk/test/features/810_setup_passphrase.feature b/gnuk/test/features/810_setup_passphrase.feature new file mode 100644 index 0000000000..d3b086de75 --- /dev/null +++ b/gnuk/test/features/810_setup_passphrase.feature @@ -0,0 +1,15 @@ +Feature: check pass phrase + In order to conform OpenPGP card 2.0 specification + A token should support pass phrase: PW1, PW3 and reset code + + Scenario: verify PW1 (1) + Given cmd_verify with 1 and "123456" + Then it should get success + + Scenario: verify PW1 (2) + Given cmd_verify with 2 and "123456" + Then it should get success + + Scenario: verify PW3 + Given cmd_verify with 3 and "12345678" + Then it should get success diff --git a/gnuk/test/features/820_personalization_write.feature b/gnuk/test/features/820_personalization_write.feature new file mode 100644 index 0000000000..2fdd3874aa --- /dev/null +++ b/gnuk/test/features/820_personalization_write.feature @@ -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 diff --git a/gnuk/test/features/821_personalization_read.feature b/gnuk/test/features/821_personalization_read.feature new file mode 100644 index 0000000000..5f0f972698 --- /dev/null +++ b/gnuk/test/features/821_personalization_read.feature @@ -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 diff --git a/gnuk/test/features/830_key_registration.feature b/gnuk/test/features/830_key_registration.feature new file mode 100644 index 0000000000..24246afd5b --- /dev/null +++ b/gnuk/test/features/830_key_registration.feature @@ -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 "123456" + Then it should get success + + Scenario: verify PW1 (2) again + Given cmd_verify with 2 and "123456" + Then it should get success diff --git a/gnuk/test/features/850_compute_signature.feature b/gnuk/test/features/850_compute_signature.feature new file mode 100644 index 0000000000..67fcbccf74 --- /dev/null +++ b/gnuk/test/features/850_compute_signature.feature @@ -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 diff --git a/gnuk/test/features/851_decryption.feature b/gnuk/test/features/851_decryption.feature new file mode 100644 index 0000000000..1985dee51c --- /dev/null +++ b/gnuk/test/features/851_decryption.feature @@ -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 + diff --git a/gnuk/test/features/860_key_removal.feature b/gnuk/test/features/860_key_removal.feature new file mode 100644 index 0000000000..ad4db98b52 --- /dev/null +++ b/gnuk/test/features/860_key_removal.feature @@ -0,0 +1,48 @@ +@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 + + Scenario: verify PW1 + Given cmd_verify with 1 and "123456" + Then it should get success + + Scenario: verify PW2 + Given cmd_verify with 2 and "123456" + Then it should get success diff --git a/gnuk/test/features/862_keygen.feature b/gnuk/test/features/862_keygen.feature new file mode 100644 index 0000000000..9cbde5bdb2 --- /dev/null +++ b/gnuk/test/features/862_keygen.feature @@ -0,0 +1,37 @@ +@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: compute digital signature by OPENPGP.1 key + Given a message "GnuPG assumes that PW1 keeps valid after keygen." + 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: verify PW1 (1) after keygen + Given cmd_verify with 1 and "123456" + Then it should get success + + Scenario: verify PW1 (2) after keygen + Given cmd_verify with 2 and "123456" + Then it should get success diff --git a/gnuk/test/features/870_compute_signature.feature b/gnuk/test/features/870_compute_signature.feature new file mode 100644 index 0000000000..7d9398bd45 --- /dev/null +++ b/gnuk/test/features/870_compute_signature.feature @@ -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 data should match: \x00\x00(\x02|\x03) diff --git a/gnuk/test/features/871_decryption.feature b/gnuk/test/features/871_decryption.feature new file mode 100644 index 0000000000..e1cc98ca3b --- /dev/null +++ b/gnuk/test/features/871_decryption.feature @@ -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 + diff --git a/gnuk/test/features/970_key_removal.feature b/gnuk/test/features/970_key_removal.feature new file mode 100644 index 0000000000..a8c50b1e2e --- /dev/null +++ b/gnuk/test/features/970_key_removal.feature @@ -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 diff --git a/gnuk/test/features/980_personalization_reset.feature b/gnuk/test/features/980_personalization_reset.feature new file mode 100644 index 0000000000..51a3430231 --- /dev/null +++ b/gnuk/test/features/980_personalization_reset.feature @@ -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 diff --git a/gnuk/test/features/990_reset_passphrase.feature b/gnuk/test/features/990_reset_passphrase.feature new file mode 100644 index 0000000000..78edf9af9c --- /dev/null +++ b/gnuk/test/features/990_reset_passphrase.feature @@ -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 diff --git a/gnuk/test/features/991_version_string.feature b/gnuk/test/features/991_version_string.feature new file mode 100644 index 0000000000..9a10d4c3d5 --- /dev/null +++ b/gnuk/test/features/991_version_string.feature @@ -0,0 +1,8 @@ +@usb +Feature: examine USB version string + In order to work as Gnuk Token + A token should support version string + + Scenario: USB version string + Given USB version string of the token + Then data should match: ([a-zA-Z0-9]*)-([.0-9]+)-[0-9A-F]+ diff --git a/gnuk/test/features/steps.py b/gnuk/test/features/steps.py new file mode 100644 index 0000000000..2e00ff4738 --- /dev/null +++ b/gnuk/test/features/steps.py @@ -0,0 +1,173 @@ +from freshen import * +from freshen.checks import * +from nose.tools import assert_regexp_matches +from binascii import hexlify + +import ast + +import gnuk_token as gnuk +import rsa_keys +from array import array + +@Before +def ini(sc): + if not glc.token: + glc.token = gnuk.get_gnuk_device() + glc.token.cmd_select_openpgp() + +@Given("cmd_verify with (.*) and \"(.*)\"") +def cmd_verify(who_str,pass_str): + who = int(who_str) + scc.result = glc.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 = glc.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("b" + content_str_repr + "") + tag = int(tag_str, 16) + tagh = tag >> 8 + tagl = tag & 0xff + scc.result = glc.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 = glc.token.cmd_reset_retry_counter(how, 0x81, 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 = glc.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 = glc.token.cmd_put_data(tagh, tagl, scc.result) + +@Given("a message (\".*\")") +def set_msg(content_str_repr): + msg = ast.literal_eval(content_str_repr).encode('UTF-8') + 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 = glc.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(glc.token.cmd_pso(0x9e, 0x9a, scc.digestinfo)),16) + +@Given("let a token authenticate") +def internal_authenticate(): + scc.sig = int(hexlify(glc.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("b" + 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 = glc.token.cmd_pso_longdata(0x80, 0x86, scc.ciphertext).tostring() + +@Given("USB version string of the token") +def usb_version_string(): + scc.result = glc.token.get_string(3) + +@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 = glc.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 = glc.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 = glc.token.cmd_genkey(openpgp_keyno) + scc.data = rsa_keys.calc_fpr(pubkey_info[0].tostring(), pubkey_info[1].tostring()) + +@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 = glc.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 = glc.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("b'" + v + "'") + assert_equal(scc.result, array('B', 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, array('B')) + +@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) diff --git a/gnuk/test/generate_keys.py b/gnuk/test/generate_keys.py new file mode 100644 index 0000000000..80b1167c40 --- /dev/null +++ b/gnuk/test/generate_keys.py @@ -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) diff --git a/gnuk/test/gnuk_token.py b/gnuk/test/gnuk_token.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gnuk/test/rsa-aut.key b/gnuk/test/rsa-aut.key new file mode 100644 index 0000000000..cdf2d5ecbd --- /dev/null +++ b/gnuk/test/rsa-aut.key @@ -0,0 +1,4 @@ +9cf7192b51a574d1ad3ccb08ba09b87f228573893eee355529ff243e90fd4b86f79a82097cc7922c0485bed1616b1656a9b0b19ef78ea8ec34c384019adc5d5bf4db2d2a0a2d9cf14277bdcb7056f48b81214e3f7f7742231e29673966f9b1106862112cc798dba8d4a138bb5abfc6d4c12d53a5d39b2f783da916da20852ee139bbafda61d429caf2a4f30847ce7e7ae32ab4061e27dd9e4d00d60910249db8d8559dd85f7ca59659ef400c8f6318700f4e97f0c6f4165de80641490433c88da8682befe68eb311f54af2b07d97ac74edb5399cf054764211694fbb8d1d333f3269f235abe025067f811ff83a2224826219b309ea3e6c968f42b3e52f245dc9 +010001 +b5ab7b159220b18e363258f61ebde08bae83d6ce2dbfe4adc143628c527887acde9de09bf9b49f438019004d71855f30c2d69b6c29bb9882ab641b3387409fe9199464a7faa4b5230c56d9e17cd9ed074bc00180ebed62bae3af28e6ff2ac2654ad968834c5d5c88f8d9d3cc5e167b10453b049d4e454a5761fb0ac717185907 +dd2fffa9814296156a6926cd17b65564187e424dcadce9b032246ad7e46448bb0f9e0ff3c64f987424b1a40bc694e2e9ac4fb1930d163582d7acf20653a1c44b97846c1c5fd8a7b19bb225fb39c30e25410483deaf8c2538d222b748c4d8103b11cec04f666a5c0dbcbf5d5f625f158f65746c3fafe6418145f7cffa5fadeeaf diff --git a/gnuk/test/rsa-dec.key b/gnuk/test/rsa-dec.key new file mode 100644 index 0000000000..8c2aa47229 --- /dev/null +++ b/gnuk/test/rsa-dec.key @@ -0,0 +1,4 @@ +d392714c29738aac6372f2c8654a08c25a1299fed7004bd512cd2452b503ebad6301130816ac525ba528dc155be6347a5c70407fb4fbdaed751dfc0a7cd5e3910272ff236c4ed1ce5de6620b191a172e5b247347b8cab73a43d79221708755c959a2f83f486439da30917384554331532aabc8326db48866f8c91198834a86ab94679f6175db737bdf399e3f0b737dcb1f4208279d3e1cc694e78686785e4f363a377dec912b7c2f757b1422d866fb9fa85c96b83adfd6a223989a9a02988bdee81ad17eff6385e7b38cec8611fdf367ba4ac8e90d5f48ac7715c5f47aea06a4a37cdaa3029ce59d29bc66853bf6758ef4a7da5a5953f5e557a5a22f67c368c3 +010001 +dae085952c5beee38f25f09bc37a4ca2434c31f78055469d0d5f0bf3337e3a70ba6c91734f195b742e211a5fe283befdf66820008e6ef2c8ca54a91922838fce07d9e33a331ce20dac36803e777d5ee2195ed28d6a4045e28623a6a60b0661e45f7c4f84ae2b1dfad0cf1ec30605158323382a819e730c09a33fad704dd67501 +f774be43ea198aa2f089274e4fffd7d0092ee7b35a1d2f854cdb166f698caab72fdeb099e690e78438b2e043e452d4d2f19d7f44ba6b286642f0ce5204966ff98ecd9e3b448877324631365dc860797429b9414a21a7e166d504cace156588b9a145657eeb1afb43b8ff65d8d6d93cea2ba4ef8aab047885c4de64ffef0b49c3 diff --git a/gnuk/test/rsa-sig.key b/gnuk/test/rsa-sig.key new file mode 100644 index 0000000000..c83179a939 --- /dev/null +++ b/gnuk/test/rsa-sig.key @@ -0,0 +1,4 @@ +c6c877dfd3b441f8fb1b8dc504093a51c2efe4883fe0a6379205acc6e673709905e4d767ddf46143c535cc6d7f10b616f520d8346320ef69ff4a2c4f4a148edc65f7ad24ed7d4fe23bb862a0ae71f4f7904abac0397abf3213df91326b1a25554b3b18cf54584d8bf220169fc92b2aa511e8313983e72b4c9110b3a1aea087aebef95873865608e8faea9ef10e7f7f3a66ca8def2d499c3149c127491e0e4339fd6abe10bfc6c13e43d522004f1485767328eabe35d6ffa8df4c15f0fbcd4eb1c07cc6d85e275139ac69e2962273ae987236926dd6c1144fce3e7ae567fa58ea60620dfafc52f95299fea601739fce27ee71eea978d0074f21e7086f60ba8331 +010001 +cc365b5702714bf203e8c49b0b8afa8dad586e929cf5edca38ad07fa45efd5c2d89022d29f40283a57e50ca24c5f28c8e911a74faaf796f112e7e48195956f9a4df7668a5342523b27179cec958f363211ee11d0ec0e0e1b92ca007a61e8c9ac14e00229b9a7624850199e6667afa1a44db8f3c5de0a8eef0e6de050ac0ac633 +f931a3c12f0e3a5276f712b7706590ba02e14a97ff9b8ce3152af0fc4d9cdc690ea9bc4c82cb16c7d23136cbdab58fbec69880a88bca85c4214df01045082cbe9f4192e3e39c79896533c37dad9eb9e73c2643b9c0a704a4f93d81573537963d6b6e5140a24c702d9f26e06a2095de906daa8824172a6b39f563b7153907050b diff --git a/gnuk/test/rsa_keys.py b/gnuk/test/rsa_keys.py new file mode 100644 index 0000000000..0c02cf5a40 --- /dev/null +++ b/gnuk/test/rsa_keys.py @@ -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 = b'\x99' + pack('>H', m_len) + b'\x04' + timestamp_data + b'\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_bytes = key[keyno][0] + e_bytes = b'\x00' + key[keyno][1] + p_bytes = key[keyno][2] + q_bytes = key[keyno][3] + + if openpgp_keyno == 1: + keyspec = b'\xb6' + elif openpgp_keyno == 2: + keyspec = b'\xb8' + else: + keyspec = b'\xa4' + + key_template = b'\x91\x04'+ b'\x92\x81\x80' + b'\x93\x81\x80' + + exthdr = keyspec + b'\x00' + b'\x7f\x48' + b'\x08' + key_template + + suffix = b'\x5f\x48' + b'\x82\x01\x04' + + t = b'\x4d' + b'\x82\x01\x16' + exthdr + suffix + e_bytes + p_bytes + q_bytes + return t + +def build_privkey_template_for_remove(openpgp_keyno): + if openpgp_keyno == 1: + keyspec = b'\xb6' + elif openpgp_keyno == 2: + keyspec = b'\xb8' + else: + keyspec = b'\xa4' + return b'\x4d\02' + keyspec + b'\0x00' + +def compute_digestinfo(msg): + digest = sha256(msg).digest() + prefix = b'\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 = b'\x00' + b'\x01' + bytes.ljust(b'', 256 - 19 - 32 - 3, b'\xff') \ + + b'\x00' + digestinfo + return int(hexlify(byte_repr), 16) + +def pkcs1_pad_for_crypt(msg): + padlen = 256 - 3 - len(msg) + byte_repr = b'\x00' + b'\x02' \ + + bytes.replace(urandom(padlen), b'\x00', b'\x01') + b'\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_256(i): + s = hex(i)[2:] + s = s.rstrip('L') + if len(s) & 1: + s = '0' + s + return string.rjust(unhexlify(s), 256, '\x00') + +def encrypt(keyno, plaintext): + e = key[keyno][4] + n = key[keyno][7] + m = pkcs1_pad_for_crypt(plaintext) + return b'\x00' + integer_to_bytes_256(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 b'\x00' + integer_to_bytes_256(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 diff --git a/gnuk/tests/README b/gnuk/tests/README new file mode 100644 index 0000000000..8aefe706a9 --- /dev/null +++ b/gnuk/tests/README @@ -0,0 +1,13 @@ +Here is a test suite for OpenPGP card. + +For now, only TPDU card reader is supported for OpenPGP card. +Gnuk Token is supported as well. + + +You need to install: + + $ sudo apt-get install python3-pytest python3-usb + +Please run test by typing: + + $ py.test-3 -x diff --git a/gnuk/tests/card_reader.py b/gnuk/tests/card_reader.py new file mode 100644 index 0000000000..91a0ae6855 --- /dev/null +++ b/gnuk/tests/card_reader.py @@ -0,0 +1,329 @@ +""" +card_reader.py - a library for smartcard reader + +Copyright (C) 2016 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 . +""" + +import usb.core +from struct import pack +from usb.util import find_descriptor, claim_interface, get_string, \ + endpoint_type, endpoint_direction, \ + ENDPOINT_TYPE_BULK, ENDPOINT_OUT, ENDPOINT_IN + +# USB class, subclass, protocol +CCID_CLASS = 0x0B +CCID_SUBCLASS = 0x00 +CCID_PROTOCOL_0 = 0x00 + +def ccid_compose(msg_type, seq, slot=0, rsv=0, param=0, data=b""): + return pack(' None + Initialize the DEV of CCID. + device: usb.core.Device object. + """ + + cfg = dev.get_active_configuration() + intf = find_descriptor(cfg, bInterfaceClass=CCID_CLASS, + bInterfaceSubClass=CCID_SUBCLASS, + bInterfaceProtocol=CCID_PROTOCOL_0) + if intf is None: + raise ValueError("Not a CCID device") + + claim_interface(dev, intf) + + for ep in intf: + if endpoint_type(ep.bmAttributes) == ENDPOINT_TYPE_BULK and \ + endpoint_direction(ep.bEndpointAddress) == ENDPOINT_OUT: + self.__bulkout = ep.bEndpointAddress + if endpoint_type(ep.bmAttributes) == ENDPOINT_TYPE_BULK and \ + endpoint_direction(ep.bEndpointAddress) == ENDPOINT_IN: + self.__bulkin = ep.bEndpointAddress + + assert len(intf.extra_descriptors) == 54 + assert intf.extra_descriptors[1] == 33 + + if (intf.extra_descriptors[42] & 0x02): + # Short APDU level exchange + self.__use_APDU = True + elif (intf.extra_descriptors[42] & 0x04): + # Short and extended APDU level exchange + self.__use_APDU = True + elif (intf.extra_descriptors[42] & 0x01): + # TPDU level exchange + self.__use_APDU = False + else: + raise ValueError("Unknown exchange level") + + # Check other bits??? + # intf.extra_descriptors[40] + # intf.extra_descriptors[41] + + self.__dev = dev + self.__timeout = 10000 + self.__seq = 0 + + def get_string(self, num): + return get_string(self.__dev, num) + + def increment_seq(self): + self.__seq = (self.__seq + 1) & 0xff + + def reset_device(self): + try: + self.__dev.reset() + except: + pass + + def is_tpdu_reader(self): + return not self.__use_APDU + + def ccid_get_result(self): + msg = self.__dev.read(self.__bulkin, 1024, self.__timeout) + if len(msg) < 10: + print(msg) + raise ValueError("ccid_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.tobytes()) + + def ccid_get_status(self): + msg = ccid_compose(0x65, self.__seq) + self.__dev.write(self.__bulkout, msg, self.__timeout) + self.increment_seq() + status, chain, data = self.ccid_get_result() + # XXX: check chain, data + return status + + def ccid_power_on(self): + msg = ccid_compose(0x62, self.__seq, rsv=1) # Vcc=5V + self.__dev.write(self.__bulkout, msg, self.__timeout) + self.increment_seq() + status, chain, data = self.ccid_get_result() + # XXX: check status, chain + self.atr = data + # + if self.__use_APDU == False: + # TPDU reader configuration + self.ns = 0 + self.nr = 0 + # Set PPS + pps = b"\xFF\x11\x18\xF6" + status, chain, ret_pps = self.ccid_send_data_block(pps) + # Set parameters + param = b"\x18\x10\xFF\x75\x00\xFE\x00" + # ^--- This shoud be adapted by ATR string, see update_param_by_atr + msg = ccid_compose(0x61, self.__seq, rsv=0x1, data=param) + self.__dev.write(self.__bulkout, msg, self.__timeout) + self.increment_seq() + status, chain, ret_param = self.ccid_get_result() + # Send an S-block of changing IFSD=254 + sblk = b"\x00\xC1\x01\xFE\x3E" + status, chain, ret_sblk = self.ccid_send_data_block(sblk) + return self.atr + + def ccid_power_off(self): + msg = ccid_compose(0x63, self.__seq) + self.__dev.write(self.__bulkout, msg, self.__timeout) + self.increment_seq() + status, chain, data = self.ccid_get_result() + # XXX: check chain, data + return status + + def ccid_send_data_block(self, data): + msg = ccid_compose(0x6f, self.__seq, data=data) + self.__dev.write(self.__bulkout, msg, self.__timeout) + self.increment_seq() + return self.ccid_get_result() + + def ccid_send_cmd(self, data): + status, chain, data_rcv = self.ccid_send_data_block(data) + if chain == 0: + while status == 0x80: + status, chain, data_rcv = self.ccid_get_result() + return data_rcv + elif chain == 1: + d = data_rcv + while True: + msg = ccid_compose(0x6f, self.__seq, param=0x10) + self.__dev.write(self.__bulkout, msg, self.__timeout) + self.increment_seq() + status, chain, data_rcv = self.ccid_get_result() + # XXX: check status + d += data_rcv + if chain == 2: + break + elif chain == 3: + continue + else: + raise ValueError("ccid_send_cmd chain") + return d + else: + raise ValueError("ccid_send_cmd") + + def send_tpdu(self, info=None, more=0, response_time_ext=0, + edc_error=0, no_error=0): + if info: + data = compose_i_block(self.ns, info, more) + elif response_time_ext: + # compose S-block + data = b"\x00\xE3\x00\xE3" + elif edc_error: + data = compose_r_block(self.nr, edc_error=1) + elif no_error: + data = compose_r_block(self.nr) + msg = ccid_compose(0x6f, self.__seq, data=data) + self.__dev.write(self.__bulkout, msg, self.__timeout) + self.increment_seq() + + def recv_tpdu(self): + status, chain, data = self.ccid_get_result() + return data + + def send_cmd(self, cmd): + # Simple APDU case + if self.__use_APDU: + return self.ccid_send_cmd(cmd) + # TPDU case + while len(cmd) > 254: + blk = cmd[0:254] + cmd = cmd[254:] + while True: + self.send_tpdu(info=blk,more=1) + rblk = self.recv_tpdu() + if is_r_block_no_error_or_other(rblk): + break + self.ns = self.ns ^ 1 + while True: + self.send_tpdu(info=cmd) + blk = self.recv_tpdu() + if is_r_block_no_error_or_other(blk): + break + self.ns = self.ns ^ 1 + res = b"" + while True: + if is_s_block_time_ext(blk): + self.send_tpdu(response_time_ext=1) + elif is_i_block_last(blk): + self.nr = self.nr ^ 1 + if is_edc_error(blk): + self.send_tpdu(edc_error=1) + else: + res += i_block_content(blk) + break + elif is_i_block_more(blk): + self.nr = self.nr ^ 1 + if is_edc_error(blk): + self.send_tpdu(edc_error=1) + else: + res += i_block_content(blk) + self.send_tpdu(no_error=1) + blk = self.recv_tpdu() + return res + + +class find_class(object): + def __init__(self, usb_class): + self.__class = usb_class + def __call__(self, device): + if device.bDeviceClass == self.__class: + return True + for cfg in device: + intf = find_descriptor(cfg, bInterfaceClass=self.__class) + if intf is not None: + return True + return False + +def get_ccid_device(): + ccid = None + dev_list = usb.core.find(find_all=True, custom_match=find_class(CCID_CLASS)) + for dev in dev_list: + try: + ccid = CardReader(dev) + print("CCID device: Bus %03d Device %03d" % (dev.bus, dev.address)) + break + except: + pass + if not ccid: + raise ValueError("No CCID device present") + status = ccid.ccid_get_status() + if status == 0: + # It's ON already + atr = ccid.ccid_power_on() + elif status == 1: + atr = ccid.ccid_power_on() + else: + raise ValueError("Unknown CCID status", status) + return ccid diff --git a/gnuk/tests/conftest.py b/gnuk/tests/conftest.py new file mode 100644 index 0000000000..ddb92213d3 --- /dev/null +++ b/gnuk/tests/conftest.py @@ -0,0 +1,18 @@ +import pytest +from card_reader import get_ccid_device +from openpgp_card import OpenPGP_Card + +def pytest_addoption(parser): + parser.addoption("--reader", dest="reader", type=str, action="store", + default="gnuk", help="specify reader: gnuk or gemalto") + +@pytest.fixture(scope="session") +def card(): + print() + print("Test start!") + reader = get_ccid_device() + print("Reader:", reader.get_string(1), reader.get_string(2)) + card = OpenPGP_Card(reader) + card.cmd_select_openpgp() + yield card + del card diff --git a/gnuk/tests/openpgp_card.py b/gnuk/tests/openpgp_card.py new file mode 100644 index 0000000000..1b63b2989e --- /dev/null +++ b/gnuk/tests/openpgp_card.py @@ -0,0 +1,338 @@ +""" +openpgp_card.py - a library for OpenPGP card + +Copyright (C) 2011, 2012, 2013, 2015, 2016 + 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 . +""" + +from struct import pack + +def iso7816_compose(ins, p1, p2, data, cls=0x00, le=None): + data_len = len(data) + if data_len == 0: + if not le: + return pack('>BBBB', cls, ins, p1, p2) + else: + return pack('>BBBBB', cls, ins, p1, p2, le) + else: + if not le: + if data_len <= 255: + return pack('>BBBBB', cls, ins, p1, p2, data_len) + data + else: + return pack('>BBBBBH', cls, ins, p1, p2, 0, data_len) \ + + data + else: + if data_len <= 255 and le < 256: + return pack('>BBBBB', cls, ins, p1, p2, data_len) \ + + data + pack('>B', le) + else: + return pack('>BBBBBH', cls, ins, p1, p2, 0, data_len) \ + + data + pack('>H', le) + +class OpenPGP_Card(object): + def __init__(self, reader): + """ + __init__(reader) -> None + Initialize a OpenPGP card with a CardReader. + reader: CardReader object. + """ + + self.__reader = reader + + def cmd_get_response(self, expected_len): + result = b"" + while True: + cmd_data = iso7816_compose(0xc0, 0x00, 0x00, b'') + pack('>B', expected_len) + response = self.__reader.send_cmd(cmd_data) + result += response[:-2] + sw = response[-2:] + if sw[0] == 0x90 and sw[1] == 0x00: + return 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.__reader.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, b'') + sw = self.__reader.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.__reader.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.__reader.send_cmd(cmd_data1) + if len(sw) != 2: + raise ValueError("cmd_write_binary 1", sw) + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError("cmd_write_binary 1", "%02x%02x" % (sw[0], sw[1])) + count += 1 + + def cmd_select_openpgp(self): + cmd_data = iso7816_compose(0xa4, 0x04, 0x00, b"\xD2\x76\x00\x01\x24\x01") + r = self.__reader.send_cmd(cmd_data) + if len(r) < 2: + raise ValueError(r) + sw = r[-2:] + r = r[0:-2] + if sw[0] == 0x61: + self.cmd_get_response(sw[1]) + return True + 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, b"", le=254) + sw = self.__reader.send_cmd(cmd_data) + if len(sw) < 2: + raise ValueError(sw) + if sw[0] == 0x61: + return self.cmd_get_response(sw[1]) + elif sw[-2] == 0x90 and sw[-1] == 0x00: + return sw[0:-2] + if sw[0] == 0x6a and sw[1] == 0x88: + return None + else: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + + def cmd_change_reference_data(self, who, data): + cmd_data = iso7816_compose(0x24, 0x00, 0x80+who, data) + sw = self.__reader.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.__reader.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): + if self.__reader.is_tpdu_reader(): + cmd_data = iso7816_compose(0xdb, tagh, tagl, content) + sw = self.__reader.send_cmd(cmd_data) + else: + cmd_data0 = iso7816_compose(0xdb, tagh, tagl, content[:128], 0x10) + cmd_data1 = iso7816_compose(0xdb, tagh, tagl, content[128:]) + sw = self.__reader.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.__reader.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, who, data): + cmd_data = iso7816_compose(0x2c, how, who, data) + sw = self.__reader.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): + if self.__reader.is_tpdu_reader(): + cmd_data = iso7816_compose(0x2a, p1, p2, data, le=256) + r = self.__reader.send_cmd(cmd_data) + if len(r) < 2: + raise ValueError(r) + sw = r[-2:] + r = r[0:-2] + if sw[0] == 0x61: + return self.cmd_get_response(sw[1]) + elif sw[0] == 0x90 and sw[1] == 0x00: + return r + else: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + else: + if len(data) > 128: + cmd_data0 = iso7816_compose(0x2a, p1, p2, data[:128], 0x10) + cmd_data1 = iso7816_compose(0x2a, p1, p2, data[128:]) + sw = self.__reader.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.__reader.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]) + else: + cmd_data = iso7816_compose(0x2a, p1, p2, data) + sw = self.__reader.send_cmd(cmd_data) + if len(sw) != 2: + raise ValueError(sw) + if sw[0] == 0x90 and sw[1] == 0x00: + return b"" + 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): + if self.__reader.is_tpdu_reader(): + cmd_data = iso7816_compose(0x88, 0, 0, data, le=256) + else: + cmd_data = iso7816_compose(0x88, 0, 0, data) + r = self.__reader.send_cmd(cmd_data) + if len(r) < 2: + raise ValueError(r) + sw = r[-2:] + r = r[0:-2] + if sw[0] == 0x61: + return self.cmd_get_response(sw[1]) + elif sw[0] == 0x90 and sw[1] == 0x00: + return r + else: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + + def cmd_genkey(self, keyno): + if keyno == 1: + data = b'\xb6\x00' + elif keyno == 2: + data = b'\xb8\x00' + else: + data = b'\xa4\x00' + cmd_data = iso7816_compose(0x47, 0x80, 0, data) + sw = self.__reader.send_cmd(cmd_data) + if len(sw) != 2: + raise ValueError(sw) + if sw[0] == 0x90 and sw[1] == 0x00: + return b"" + 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 = b'\xb6\x00' + elif keyno == 2: + data = b'\xb8\x00' + else: + data = b'\xa4\x00' + if self.__reader.is_tpdu_reader(): + cmd_data = iso7816_compose(0x47, 0x81, 0, data, le=512) + r = self.__reader.send_cmd(cmd_data) + else: + cmd_data = iso7816_compose(0x47, 0x81, 0, data) + r = self.__reader.send_cmd(cmd_data) + if len(r) < 2: + raise ValueError(r) + sw = r[-2:] + r = r[0:-2] + if sw[0] == 0x61: + pk = self.cmd_get_response(sw[1]) + elif sw[0] == 0x90 and sw[1] == 0x00: + pk = r + else: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + return pk + + def cmd_put_data_remove(self, tagh, tagl): + cmd_data = iso7816_compose(0xda, tagh, tagl, b"") + sw = self.__reader.send_cmd(cmd_data) + if sw[0] != 0x90 and sw[1] != 0x00: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + + def cmd_put_data_key_import_remove(self, keyno): + if keyno == 1: + keyspec = b"\xb6\x00" # SIG + elif keyno == 2: + keyspec = b"\xb8\x00" # DEC + else: + keyspec = b"\xa4\x00" # AUT + cmd_data = iso7816_compose(0xdb, 0x3f, 0xff, b"\x4d\x02" + keyspec) + sw = self.__reader.send_cmd(cmd_data) + if sw[0] != 0x90 and sw[1] != 0x00: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + + def cmd_get_challenge(self): + cmd_data = iso7816_compose(0x84, 0x00, 0x00, '') + sw = self.__reader.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_external_authenticate(self, keyno, signed): + cmd_data = iso7816_compose(0x82, 0x00, keyno, signed[0:128], cls=0x10) + sw = self.__reader.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])) + cmd_data = iso7816_compose(0x82, 0x00, keyno, signed[128:]) + sw = self.__reader.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])) diff --git a/gnuk/tests/rsa-aut.key b/gnuk/tests/rsa-aut.key new file mode 100644 index 0000000000..cdf2d5ecbd --- /dev/null +++ b/gnuk/tests/rsa-aut.key @@ -0,0 +1,4 @@ +9cf7192b51a574d1ad3ccb08ba09b87f228573893eee355529ff243e90fd4b86f79a82097cc7922c0485bed1616b1656a9b0b19ef78ea8ec34c384019adc5d5bf4db2d2a0a2d9cf14277bdcb7056f48b81214e3f7f7742231e29673966f9b1106862112cc798dba8d4a138bb5abfc6d4c12d53a5d39b2f783da916da20852ee139bbafda61d429caf2a4f30847ce7e7ae32ab4061e27dd9e4d00d60910249db8d8559dd85f7ca59659ef400c8f6318700f4e97f0c6f4165de80641490433c88da8682befe68eb311f54af2b07d97ac74edb5399cf054764211694fbb8d1d333f3269f235abe025067f811ff83a2224826219b309ea3e6c968f42b3e52f245dc9 +010001 +b5ab7b159220b18e363258f61ebde08bae83d6ce2dbfe4adc143628c527887acde9de09bf9b49f438019004d71855f30c2d69b6c29bb9882ab641b3387409fe9199464a7faa4b5230c56d9e17cd9ed074bc00180ebed62bae3af28e6ff2ac2654ad968834c5d5c88f8d9d3cc5e167b10453b049d4e454a5761fb0ac717185907 +dd2fffa9814296156a6926cd17b65564187e424dcadce9b032246ad7e46448bb0f9e0ff3c64f987424b1a40bc694e2e9ac4fb1930d163582d7acf20653a1c44b97846c1c5fd8a7b19bb225fb39c30e25410483deaf8c2538d222b748c4d8103b11cec04f666a5c0dbcbf5d5f625f158f65746c3fafe6418145f7cffa5fadeeaf diff --git a/gnuk/tests/rsa-dec.key b/gnuk/tests/rsa-dec.key new file mode 100644 index 0000000000..8c2aa47229 --- /dev/null +++ b/gnuk/tests/rsa-dec.key @@ -0,0 +1,4 @@ +d392714c29738aac6372f2c8654a08c25a1299fed7004bd512cd2452b503ebad6301130816ac525ba528dc155be6347a5c70407fb4fbdaed751dfc0a7cd5e3910272ff236c4ed1ce5de6620b191a172e5b247347b8cab73a43d79221708755c959a2f83f486439da30917384554331532aabc8326db48866f8c91198834a86ab94679f6175db737bdf399e3f0b737dcb1f4208279d3e1cc694e78686785e4f363a377dec912b7c2f757b1422d866fb9fa85c96b83adfd6a223989a9a02988bdee81ad17eff6385e7b38cec8611fdf367ba4ac8e90d5f48ac7715c5f47aea06a4a37cdaa3029ce59d29bc66853bf6758ef4a7da5a5953f5e557a5a22f67c368c3 +010001 +dae085952c5beee38f25f09bc37a4ca2434c31f78055469d0d5f0bf3337e3a70ba6c91734f195b742e211a5fe283befdf66820008e6ef2c8ca54a91922838fce07d9e33a331ce20dac36803e777d5ee2195ed28d6a4045e28623a6a60b0661e45f7c4f84ae2b1dfad0cf1ec30605158323382a819e730c09a33fad704dd67501 +f774be43ea198aa2f089274e4fffd7d0092ee7b35a1d2f854cdb166f698caab72fdeb099e690e78438b2e043e452d4d2f19d7f44ba6b286642f0ce5204966ff98ecd9e3b448877324631365dc860797429b9414a21a7e166d504cace156588b9a145657eeb1afb43b8ff65d8d6d93cea2ba4ef8aab047885c4de64ffef0b49c3 diff --git a/gnuk/tests/rsa-sig.key b/gnuk/tests/rsa-sig.key new file mode 100644 index 0000000000..c83179a939 --- /dev/null +++ b/gnuk/tests/rsa-sig.key @@ -0,0 +1,4 @@ +c6c877dfd3b441f8fb1b8dc504093a51c2efe4883fe0a6379205acc6e673709905e4d767ddf46143c535cc6d7f10b616f520d8346320ef69ff4a2c4f4a148edc65f7ad24ed7d4fe23bb862a0ae71f4f7904abac0397abf3213df91326b1a25554b3b18cf54584d8bf220169fc92b2aa511e8313983e72b4c9110b3a1aea087aebef95873865608e8faea9ef10e7f7f3a66ca8def2d499c3149c127491e0e4339fd6abe10bfc6c13e43d522004f1485767328eabe35d6ffa8df4c15f0fbcd4eb1c07cc6d85e275139ac69e2962273ae987236926dd6c1144fce3e7ae567fa58ea60620dfafc52f95299fea601739fce27ee71eea978d0074f21e7086f60ba8331 +010001 +cc365b5702714bf203e8c49b0b8afa8dad586e929cf5edca38ad07fa45efd5c2d89022d29f40283a57e50ca24c5f28c8e911a74faaf796f112e7e48195956f9a4df7668a5342523b27179cec958f363211ee11d0ec0e0e1b92ca007a61e8c9ac14e00229b9a7624850199e6667afa1a44db8f3c5de0a8eef0e6de050ac0ac633 +f931a3c12f0e3a5276f712b7706590ba02e14a97ff9b8ce3152af0fc4d9cdc690ea9bc4c82cb16c7d23136cbdab58fbec69880a88bca85c4214df01045082cbe9f4192e3e39c79896533c37dad9eb9e73c2643b9c0a704a4f93d81573537963d6b6e5140a24c702d9f26e06a2095de906daa8824172a6b39f563b7153907050b diff --git a/gnuk/tests/rsa_keys.py b/gnuk/tests/rsa_keys.py new file mode 100644 index 0000000000..3da4ade331 --- /dev/null +++ b/gnuk/tests/rsa_keys.py @@ -0,0 +1,148 @@ +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 = b'\x99' + pack('>H', m_len) + b'\x04' + timestamp_data + b'\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_bytes = key[keyno][0] + e_bytes = b'\x00' + key[keyno][1] + p_bytes = key[keyno][2] + q_bytes = key[keyno][3] + + if openpgp_keyno == 1: + keyspec = b'\xb6' + elif openpgp_keyno == 2: + keyspec = b'\xb8' + else: + keyspec = b'\xa4' + + key_template = b'\x91\x04'+ b'\x92\x81\x80' + b'\x93\x81\x80' + + exthdr = keyspec + b'\x00' + b'\x7f\x48' + b'\x08' + key_template + + suffix = b'\x5f\x48' + b'\x82\x01\x04' + + t = b'\x4d' + b'\x82\x01\x16' + exthdr + suffix + e_bytes + p_bytes + q_bytes + return t + +def build_privkey_template_for_remove(openpgp_keyno): + if openpgp_keyno == 1: + keyspec = b'\xb6' + elif openpgp_keyno == 2: + keyspec = b'\xb8' + else: + keyspec = b'\xa4' + return b'\x4d\02' + keyspec + b'\0x00' + +def compute_digestinfo(msg): + digest = sha256(msg).digest() + prefix = b'\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 = b'\x00' + b'\x01' + bytes.ljust(b'', 256 - 19 - 32 - 3, b'\xff') \ + + b'\x00' + digestinfo + return int(hexlify(byte_repr), 16) + +def pkcs1_pad_for_crypt(msg): + padlen = 256 - 3 - len(msg) + byte_repr = b'\x00' + b'\x02' \ + + bytes.replace(urandom(padlen), b'\x00', b'\x01') + b'\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_256(i): + return i.to_bytes(256, byteorder='big') + +def encrypt(keyno, plaintext): + e = key[keyno][4] + n = key[keyno][7] + m = pkcs1_pad_for_crypt(plaintext) + return b'\x00' + integer_to_bytes_256(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 b'\x00' + integer_to_bytes_256(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 diff --git a/gnuk/tests/test_empty_card.py b/gnuk/tests/test_empty_card.py new file mode 100644 index 0000000000..e0c7c3a22c --- /dev/null +++ b/gnuk/tests/test_empty_card.py @@ -0,0 +1,184 @@ +""" +test_empty_card.py - test empty card + +Copyright (C) 2016 g10 Code GmbH +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 . +""" + +from binascii import hexlify +from re import match, DOTALL +from struct import pack +from util import * +import pytest + +EMPTY_60=bytes(60) + +FACTORY_PASSPHRASE_PW1=b"123456" +FACTORY_PASSPHRASE_PW3=b"12345678" + +def test_login(card): + login = get_data_object(card, 0x5e) + assert check_null(login) + +""" +def test_name(card): + name = get_data_object(card, 0x5b) + assert check_null(name) + +def test_lang(card): + lang = get_data_object(card, 0x5f2d) + assert check_null(lang) + +def test_sex(card): + sex = get_data_object(card, 0x5f35) + assert check_null(sex) +""" + +def test_name_lang_sex(card): + name = b"" + lang = b"" + sex = b"9" + expected = b'\x5b' + pack('B', len(name)) + name \ + + b'\x5f\x2d' + pack('B', len(lang)) + lang \ + + b'\x5f\x35' + pack('B', len(sex)) + sex + name_lang_sex = get_data_object(card, 0x65) + assert name_lang_sex == b'' or name_lang_sex == expected + +def test_app_data(card): + app_data = get_data_object(card, 0x6e) + hist_len = app_data[20] + # FIXME: parse and check DO of C0, C1, C2, C3, C4, and C6 + assert app_data[0:8] == b"\x4f\x10\xd2\x76\x00\x01\x24\x01" and \ + app_data[18:18+2] == b"\x5f\x52" + +def test_url(card): + url = get_data_object(card, 0x5f50) + assert check_null(url) + +def test_ds_counter(card): + c = get_data_object(card, 0x7a) + assert c == b'\x93\x03\x00\x00\x00' + +def test_pw1_status(card): + s = get_data_object(card, 0xc4) + assert match(b'\x00...\x03[\x00\x03]\x03', s, DOTALL) + +def test_fingerprint_0(card): + fprlist = get_data_object(card, 0xC5) + assert fprlist == None or fprlist == EMPTY_60 + +def test_fingerprint_1(card): + fpr = get_data_object(card, 0xC7) + assert check_null(fpr) + +def test_fingerprint_2(card): + fpr = get_data_object(card, 0xC8) + assert check_null(fpr) + +def test_fingerprint_3(card): + fpr = get_data_object(card, 0xC9) + assert check_null(fpr) + +def test_ca_fingerprint_0(card): + cafprlist = get_data_object(card, 0xC6) + assert cafprlist == None or cafprlist == EMPTY_60 + +def test_ca_fingerprint_1(card): + cafp = get_data_object(card, 0xCA) + assert check_null(cafp) + +def test_ca_fingerprint_2(card): + cafp = get_data_object(card, 0xCB) + assert check_null(cafp) + +def test_ca_fingerprint_3(card): + cafp = get_data_object(card, 0xCC) + assert check_null(cafp) + +def test_timestamp_0(card): + t = get_data_object(card, 0xCD) + assert t == None or t == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + +def test_timestamp_1(card): + t = get_data_object(card, 0xCE) + assert check_null(t) + +def test_timestamp_2(card): + t = get_data_object(card, 0xCF) + assert check_null(t) + +def test_timestamp_3(card): + t = get_data_object(card, 0xD0) + assert check_null(t) + +def test_verify_pw1_1(card): + v = card.cmd_verify(1, FACTORY_PASSPHRASE_PW1) + assert v + +def test_verify_pw1_2(card): + v = card.cmd_verify(2, FACTORY_PASSPHRASE_PW1) + assert v + +def test_verify_pw3(card): + v = card.cmd_verify(3, FACTORY_PASSPHRASE_PW3) + assert v + +def test_historical_bytes(card): + h = get_data_object(card, 0x5f52) + assert h == b'\x001\xc5s\xc0\x01@\x05\x90\x00' or \ + h == b'\x00\x31\x84\x73\x80\x01\x80\x00\x90\x00' or \ + h == b'\x00\x31\x84\x73\x80\x01\x80\x05\x90\x00' + +def test_extended_capabilities(card): + a = get_data_object(card, 0xc0) + assert a == None or match(b'[\x70\x74]\x00\x00\x20[\x00\x08]\x00\x00\xff\x01\x00', a) + +def test_algorithm_attributes_1(card): + a = get_data_object(card, 0xc1) + assert a == None or a == b'\x01\x08\x00\x00\x20\x00' + +def test_algorithm_attributes_2(card): + a = get_data_object(card, 0xc2) + assert a == None or a == b'\x01\x08\x00\x00\x20\x00' + +def test_algorithm_attributes_3(card): + a = get_data_object(card, 0xc3) + assert a == None or a == b'\x01\x08\x00\x00\x20\x00' + +def test_public_key_1(card): + with pytest.raises(Exception) as excinfo: + pk = card.cmd_get_public_key(1) + assert excinfo.value.args[0] == "6a88" + +def test_public_key_2(card): + with pytest.raises(Exception) as excinfo: + pk = card.cmd_get_public_key(2) + assert excinfo.value.args[0] == "6a88" + +def test_public_key_3(card): + with pytest.raises(Exception) as excinfo: + pk = card.cmd_get_public_key(3) + assert excinfo.value.args[0] == "6a88" + +def test_AID(card): + a = get_data_object(card, 0x4f) + print() + print("OpenPGP card version: %d.%d" % (a[6], a[7])) + print("Card Manufacturer: ", hexlify(a[8:10]).decode("UTF-8")) + print("Card serial: ", hexlify(a[10:14]).decode("UTF-8")) + assert match(b'\xd2\x76\x00\x01\\$\x01........\x00\x00', a, DOTALL) diff --git a/gnuk/tests/test_personalize_card.py b/gnuk/tests/test_personalize_card.py new file mode 100644 index 0000000000..c22af93dfb --- /dev/null +++ b/gnuk/tests/test_personalize_card.py @@ -0,0 +1,290 @@ +""" +test_personalize_card.py - test personalizing card + +Copyright (C) 2016 g10 Code GmbH +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 . +""" + +from struct import pack +from re import match, DOTALL +from util import * +import rsa_keys + +FACTORY_PASSPHRASE_PW1=b"123456" +FACTORY_PASSPHRASE_PW3=b"12345678" +PW1_TEST0=b"another user pass phrase" +PW1_TEST1=b"PASSPHRASE SHOULD BE LONG" +PW1_TEST2=b"new user pass phrase" +PW1_TEST3=b"next user pass phrase" +PW1_TEST4=b"another user pass phrase" +PW3_TEST0=b"admin pass phrase" +PW3_TEST1=b"another admin pass phrase" + +RESETCODE_TEST=b"example reset code 000" + +def test_setup_pw3_0(card): + r = card.cmd_change_reference_data(3, FACTORY_PASSPHRASE_PW3 + PW3_TEST0) + assert r + +def test_verify_pw3_0(card): + v = card.cmd_verify(3, PW3_TEST0) + assert v + +def test_login_put(card): + r = card.cmd_put_data(0x00, 0x5e, b"gpg_user") + assert r + +def test_name_put(card): + r = card.cmd_put_data(0x00, 0x5b, b"GnuPG User") + assert r + +def test_lang_put(card): + r = card.cmd_put_data(0x5f, 0x2d, b"ja") + assert r + +def test_sex_put(card): + r = card.cmd_put_data(0x5f, 0x35, b"1") + assert r + +def test_url_put(card): + r = card.cmd_put_data(0x5f, 0x50, b"https://www.fsij.org/gnuk/") + assert r + +def test_pw1_status_put(card): + r = card.cmd_put_data(0x00, 0xc4, b"\x01") + assert r + +def test_login(card): + login = get_data_object(card, 0x5e) + assert login == b"gpg_user" + +def test_name_lang_sex(card): + name = b"GnuPG User" + lang = b"ja" + sex = b"1" + expected = b'\x5b' + pack('B', len(name)) + name \ + + b'\x5f\x2d' + pack('B', len(lang)) + lang \ + + b'\x5f\x35' + pack('B', len(sex)) + sex + name_lang_sex = get_data_object(card, 0x65) + assert name_lang_sex == expected + +def test_url(card): + url = get_data_object(card, 0x5f50) + assert url == b"https://www.fsij.org/gnuk/" + +def test_pw1_status(card): + s = get_data_object(card, 0xc4) + assert match(b'\x01...\x03[\x00\x03]\x03', s, DOTALL) + +def test_rsa_import_key_1(card): + t = rsa_keys.build_privkey_template(1, 0) + r = card.cmd_put_data_odd(0x3f, 0xff, t) + assert r + +def test_rsa_import_key_2(card): + t = rsa_keys.build_privkey_template(2, 1) + r = card.cmd_put_data_odd(0x3f, 0xff, t) + assert r + +def test_rsa_import_key_3(card): + t = rsa_keys.build_privkey_template(3, 2) + r = card.cmd_put_data_odd(0x3f, 0xff, t) + assert r + +def test_fingerprint_1_put(card): + fpr1 = rsa_keys.fpr[0] + r = card.cmd_put_data(0x00, 0xc7, fpr1) + assert r + +def test_fingerprint_2_put(card): + fpr2 = rsa_keys.fpr[1] + r = card.cmd_put_data(0x00, 0xc8, fpr2) + assert r + +def test_fingerprint_3_put(card): + fpr3 = rsa_keys.fpr[2] + r = card.cmd_put_data(0x00, 0xc9, fpr3) + assert r + +def test_timestamp_1_put(card): + timestamp1 = rsa_keys.timestamp[0] + r = card.cmd_put_data(0x00, 0xce, timestamp1) + assert r + +def test_timestamp_2_put(card): + timestamp2 = rsa_keys.timestamp[1] + r = card.cmd_put_data(0x00, 0xcf, timestamp2) + assert r + +def test_timestamp_3_put(card): + timestamp3 = rsa_keys.timestamp[2] + r = card.cmd_put_data(0x00, 0xd0, timestamp3) + assert r + +def test_ds_counter_0(card): + c = get_data_object(card, 0x7a) + assert c == b'\x93\x03\x00\x00\x00' + +def test_pw1_status(card): + s = get_data_object(card, 0xc4) + assert match(b'\x01...\x03[\x00\x03]\x03', s, DOTALL) + +def test_app_data(card): + app_data = get_data_object(card, 0x6e) + hist_len = app_data[20] + # FIXME: parse and check DO of C0, C1, C2, C3, C4, and C6 + assert app_data[0:8] == b"\x4f\x10\xd2\x76\x00\x01\x24\x01" and \ + app_data[18:18+2] == b"\x5f\x52" + +def test_public_key_1(card): + pk = card.cmd_get_public_key(1) + assert rsa_keys.key[0][0] == pk[9:9+256] + +def test_public_key_2(card): + pk = card.cmd_get_public_key(2) + assert rsa_keys.key[1][0] == pk[9:9+256] + +def test_public_key_3(card): + pk = card.cmd_get_public_key(3) + assert rsa_keys.key[2][0] == pk[9:9+256] + +def test_setup_pw1_0(card): + r = card.cmd_change_reference_data(1, FACTORY_PASSPHRASE_PW1 + PW1_TEST0) + assert r + +def test_verify_pw1_0(card): + v = card.cmd_verify(1, PW1_TEST0) + assert v + +def test_verify_pw1_0_2(card): + v = card.cmd_verify(2, PW1_TEST0) + assert v + +def test_setup_pw1_1(card): + r = card.cmd_change_reference_data(1, PW1_TEST0 + PW1_TEST1) + assert r + +def test_verify_pw1_1(card): + v = card.cmd_verify(1, PW1_TEST1) + assert v + +def test_verify_pw1_1_2(card): + v = card.cmd_verify(2, PW1_TEST1) + assert v + +def test_setup_reset_code(card): + r = card.cmd_put_data(0x00, 0xd3, RESETCODE_TEST) + assert r + +def test_reset_code(card): + r = card.cmd_reset_retry_counter(0, 0x81, RESETCODE_TEST + PW1_TEST2) + assert r + +def test_verify_pw1_2(card): + v = card.cmd_verify(1, PW1_TEST2) + assert v + +def test_verify_pw1_2_2(card): + v = card.cmd_verify(2, PW1_TEST2) + assert v + +def test_setup_pw3_1(card): + r = card.cmd_change_reference_data(3, PW3_TEST0 + PW3_TEST1) + assert r + +def test_verify_pw3_1(card): + v = card.cmd_verify(3, PW3_TEST1) + assert v + +def test_reset_userpass_admin(card): + r = card.cmd_reset_retry_counter(2, 0x81, PW1_TEST3) + assert r + +def test_verify_pw1_3(card): + v = card.cmd_verify(1, PW1_TEST3) + assert v + +def test_verify_pw1_3_2(card): + v = card.cmd_verify(2, PW1_TEST3) + assert v + +def test_setup_pw1_4(card): + r = card.cmd_change_reference_data(1, PW1_TEST3 + PW1_TEST4) + assert r + +def test_verify_pw1_4(card): + v = card.cmd_verify(1, PW1_TEST4) + assert v + +def test_verify_pw1_4_2(card): + v = card.cmd_verify(2, PW1_TEST4) + assert v + +def test_setup_pw3_2(card): + r = card.cmd_change_reference_data(3, PW3_TEST1 + PW3_TEST0) + assert r + +def test_verify_pw3_2(card): + v = card.cmd_verify(3, PW3_TEST0) + assert v + +PLAIN_TEXT0=b"This is a test message." +PLAIN_TEXT1=b"RSA decryption is as easy as pie." +PLAIN_TEXT2=b"This is another test message.\nMultiple lines.\n" + +def test_sign_0(card): + digestinfo = rsa_keys.compute_digestinfo(PLAIN_TEXT0) + r = card.cmd_pso(0x9e, 0x9a, digestinfo) + sig = rsa_keys.compute_signature(0, digestinfo) + sig_bytes = sig.to_bytes(int((sig.bit_length()+7)/8), byteorder='big') + assert r == sig_bytes + +def test_sign_1(card): + digestinfo = rsa_keys.compute_digestinfo(PLAIN_TEXT1) + r = card.cmd_pso(0x9e, 0x9a, digestinfo) + sig = rsa_keys.compute_signature(0, digestinfo) + sig_bytes = sig.to_bytes(int((sig.bit_length()+7)/8), byteorder='big') + assert r == sig_bytes + +def test_ds_counter_1(card): + c = get_data_object(card, 0x7a) + assert c == b'\x93\x03\x00\x00\x02' + +def test_sign_auth_0(card): + digestinfo = rsa_keys.compute_digestinfo(PLAIN_TEXT0) + r = card.cmd_internal_authenticate(digestinfo) + sig = rsa_keys.compute_signature(2, digestinfo) + sig_bytes = sig.to_bytes(int((sig.bit_length()+7)/8), byteorder='big') + assert r == sig_bytes + +def test_sign_auth_1(card): + digestinfo = rsa_keys.compute_digestinfo(PLAIN_TEXT1) + r = card.cmd_internal_authenticate(digestinfo) + sig = rsa_keys.compute_signature(2, digestinfo) + sig_bytes = sig.to_bytes(int((sig.bit_length()+7)/8), byteorder='big') + assert r == sig_bytes + +def test_decrypt_0(card): + ciphertext = rsa_keys.encrypt(1, PLAIN_TEXT0) + r = card.cmd_pso(0x80, 0x86, ciphertext) + assert r == PLAIN_TEXT0 + +def test_decrypt_1(card): + ciphertext = rsa_keys.encrypt(1, PLAIN_TEXT1) + r = card.cmd_pso(0x80, 0x86, ciphertext) + assert r == PLAIN_TEXT1 diff --git a/gnuk/tests/test_personalize_reset_card.py b/gnuk/tests/test_personalize_reset_card.py new file mode 100644 index 0000000000..ab6828a176 --- /dev/null +++ b/gnuk/tests/test_personalize_reset_card.py @@ -0,0 +1,91 @@ +""" +test_personalize_reset_card.py - test resetting personalization of card + +Copyright (C) 2016 g10 Code GmbH +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 . +""" + +from struct import pack +from re import match, DOTALL +from util import * +import rsa_keys + +FACTORY_PASSPHRASE_PW1=b"123456" +FACTORY_PASSPHRASE_PW3=b"12345678" +PW1_TEST0=b"another user pass phrase" +PW1_TEST1=b"PASSPHRASE SHOULD BE LONG" +PW1_TEST2=b"new user pass phrase" +PW1_TEST3=b"next user pass phrase" +PW1_TEST4=b"another user pass phrase" +PW3_TEST0=b"admin pass phrase" +PW3_TEST1=b"another admin pass phrase" + +RESETCODE_TEST=b"example reset code 000" + +def test_login_put(card): + r = card.cmd_put_data(0x00, 0x5e, b"") + assert r + +def test_name_put(card): + r = card.cmd_put_data(0x00, 0x5b, b"") + assert r + +def test_lang_put(card): + r = card.cmd_put_data(0x5f, 0x2d, b"") + assert r + +def test_sex_put(card): + try: + # Gnuk + r = card.cmd_put_data(0x5f, 0x35, b"") + except ValueError: + # OpenPGP card which doesn't allow b"" + r = card.cmd_put_data(0x5f, 0x35, b"9") + assert r + +def test_url_put(card): + r = card.cmd_put_data(0x5f, 0x50, b"") + assert r + +def test_pw1_status_put(card): + r = card.cmd_put_data(0x00, 0xc4, b"\x00") + assert r + +def test_setup_pw3_0(card): + r = card.cmd_change_reference_data(3, PW3_TEST0 + FACTORY_PASSPHRASE_PW3) + assert r + +def test_verify_pw3_0(card): + v = card.cmd_verify(3, FACTORY_PASSPHRASE_PW3) + assert v + +def test_setup_pw1_0(card): + r = card.cmd_change_reference_data(1, PW1_TEST4 + FACTORY_PASSPHRASE_PW1) + assert r + +def test_verify_pw1_0(card): + v = card.cmd_verify(1, FACTORY_PASSPHRASE_PW1) + assert v + +def test_verify_pw1_0_2(card): + v = card.cmd_verify(2, FACTORY_PASSPHRASE_PW1) + assert v + +def test_setup_reset_code(card): + r = card.cmd_put_data(0x00, 0xd3, b"") + assert r diff --git a/gnuk/tests/test_remove_keys_card.py b/gnuk/tests/test_remove_keys_card.py new file mode 100644 index 0000000000..c185a266be --- /dev/null +++ b/gnuk/tests/test_remove_keys_card.py @@ -0,0 +1,44 @@ +""" +test_remove_keys_card.py - test removing keys on card + +Copyright (C) 2016 g10 Code GmbH +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 . +""" + +# Remove a key material on card by changing algorithm attributes of the key + +KEY_ATTRIBUTES_RSA4K=b"\x01\x10\x00\x00\x20\x00" +KEY_ATTRIBUTES_RSA2K=b"\x01\x08\x00\x00\x20\x00" + +def test_rsa_import_key_1(card): + r = card.cmd_put_data(0x00, 0xc1, KEY_ATTRIBUTES_RSA4K) + if r: + r = card.cmd_put_data(0x00, 0xc1, KEY_ATTRIBUTES_RSA2K) + assert r + +def test_rsa_import_key_2(card): + r = card.cmd_put_data(0x00, 0xc2, KEY_ATTRIBUTES_RSA4K) + if r: + r = card.cmd_put_data(0x00, 0xc2, KEY_ATTRIBUTES_RSA2K) + assert r + +def test_rsa_import_key_3(card): + r = card.cmd_put_data(0x00, 0xc3, KEY_ATTRIBUTES_RSA4K) + if r: + r = card.cmd_put_data(0x00, 0xc3, KEY_ATTRIBUTES_RSA2K) + assert r diff --git a/gnuk/tests/util.py b/gnuk/tests/util.py new file mode 100644 index 0000000000..9e2726ac4c --- /dev/null +++ b/gnuk/tests/util.py @@ -0,0 +1,7 @@ +def get_data_object(card, tag): + tagh = tag >> 8 + tagl = tag & 0xff + return card.cmd_get_data(tagh, tagl) + +def check_null(data_object): + return data_object == None or len(data_object) == 0 diff --git a/gnuk/tool/add_openpgp_authkey_from_gpgssh.py b/gnuk/tool/add_openpgp_authkey_from_gpgssh.py new file mode 100644 index 0000000000..1eb80de9c8 --- /dev/null +++ b/gnuk/tool/add_openpgp_authkey_from_gpgssh.py @@ -0,0 +1,189 @@ +""" +add_openpgp_authkey_from_gpgssh.py + +Copyright (C) 2014 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 . +""" + +from gpg_agent import gpg_agent +from binascii import hexlify, unhexlify +from sexp import sexp +from time import time +from struct import pack, unpack +from hashlib import sha1, sha256 +import re + +ALGO_RSA=1 +DIGEST_SHA256=8 +OPENPGP_VERSION=4 + +def count_bits(mpi_bytes): + return ord(mpi_bytes[0]).bit_length()+(len(mpi_bytes)-1)*8 + +class rsa_key(object): + def __init__(self, timestamp, n, e): + self.__timestamp = timestamp + self.__n = n + self.__e = e + + def hash_pubkey_key(self, md): + hl = 6 + len(self.__n) + 2 + len(self.__e) + 2 + md.update(pack('>BHBLB', 0x99, hl, 4, self.__timestamp, ALGO_RSA)) + md.update(pack('>H', count_bits(self.__n)) + self.__n) + md.update(pack('>H', count_bits(self.__e)) + self.__e) + + def compose_public_subkey_packet(self): + psp = pack('>BLB', OPENPGP_VERSION, self.__timestamp, ALGO_RSA) + psp += pack('>H', count_bits(self.__n)) + self.__n + psp += pack('>H', count_bits(self.__e)) + self.__e + return '\xB9' + pack('>H', len(psp)) + psp + + def compute_keygrip(self): + md = sha1("\x00" + self.__n) + return md.digest() + + def compute_fpr(self): + md = sha1() + self.hash_pubkey_key(md) + return md.digest() + +def compose_binding_signature_packet(g, primary_key, subkey, sig_timestamp): + # Binding signature packet consists of: subpackets of hashed and unhashed + # (1) hashed subpacket: this subpacket is the target to calculate digest + sig_subp_hashed = pack('>B', 5) + '\x02' + pack('>L', sig_timestamp) + sig_subp_hashed += pack('>B', 2) + '\x1b' + '\x20' # Usage AUTH + # (2) unhashed subpacket: this subpacket is _not_ the target for digest + sig_subp_unhashed = pack('>B', 9) + '\x10' + primary_key.compute_fpr()[-8:] + # + md = sha256() + primary_key.hash_pubkey_key(md) + subkey.hash_pubkey_key(md) + # Start building binding signature packet, starting OPENPGP_VERSION... + sigp = pack('>BBBB', OPENPGP_VERSION, 0x18, ALGO_RSA, DIGEST_SHA256) + sigp += pack('>H', len(sig_subp_hashed)) + sig_subp_hashed + # And feed it to digest calculator + md.update(sigp) + md.update(pack('>BBL', OPENPGP_VERSION, 0xff, len(sig_subp_hashed)+6)) + digest = md.digest() + # Then, add unhashed subpacket and first two bytes of digest + sigp += pack('>H', len(sig_subp_unhashed)) + sig_subp_unhashed + sigp += digest[0:2] + print("Digest 2-byte: %s" % hexlify(digest[0:2])) + # Ask signing to this digest by the corresponding secret key to PRIMARY_KEY + signature = do_sign(g, primary_key, DIGEST_SHA256, digest) + # Then, add the signature to the binding signature packet + sigp += pack('>H', count_bits(signature)) + signature + # Prepending header, it's the binding signature packet + return '\x89' + pack('>H', len(sigp)) + sigp + +def build_rsakey_from_ssh_key_under_gpg_agent(g, timestamp=None): + # (1) Get the list of available key specifying '--with-ssh' + g.send_command("KEYINFO --list --with-ssh --data\n") + kl_str = g.get_response() + kl_str = kl_str[0:-1] + kl = kl_str.split('\n') + # (2) Select SSH key(s) + kl_ssh = [kg for kg in kl if re.search("S$", kg)] # Select SSH key + # (3) Use the first entry of the list (in future, use all???) + print("KEYINFO: %s" % kl_ssh[0]) + # KG: The keygrip of key in question + kg = kl_ssh[0].split(' ')[0] + # By READKEY command, get the public key information of KG + g.send_command("READKEY %s\n" % kg) + pubkey_info_str = g.get_response() + # The information is in SEXP format, extract N and E + s = sexp(pubkey_info_str) + if s[0] != 'public-key': + print s + exit(1) + rsa = s[1] + if rsa[0] != 'rsa': + print rsa + exit(1) + n_x = rsa[1] + if n_x[0] != 'n': + print n_x + exit(1) + n_byte_str = n_x[1] + while n_byte_str[0] == '\x00': + n_byte_str = n_byte_str[1:] + n = n_byte_str + e_x = rsa[2] + if e_x[0] != 'e': + print e_x + exit(1) + e = e_x[1] + if not timestamp: + timestamp = int(time()) + # Compose our RSA_KEY by TIMESTAMP, N, and E + return rsa_key(timestamp,n,e) + +BUFSIZE=1024 +def build_rsakey_from_openpgp_file(filename): + f = open(filename, "rb") + openpgp_bytes = f.read(BUFSIZE) + f.close() + header_tag, packet_len, version, timestamp, algo, n_bitlen = \ + unpack('>BHBLBH', openpgp_bytes[:11]) + if header_tag != 0x99: + print ("openpgp: 0x99 expected (0x%02x)" % header_tag) + exit(1) + n_len = (n_bitlen + 7) / 8 + n = openpgp_bytes[11:11+n_len] + e_bitlen = unpack('>H', openpgp_bytes[11+n_len:11+n_len+2])[0] + e_len = (e_bitlen + 7) / 8 + e = openpgp_bytes[11+n_len+2:11+n_len+2+e_len] + return rsa_key(timestamp,n,e) + +def do_sign(g, pubkey, digest_algo, digest): + g.send_command('SIGKEY %s\n' % hexlify(pubkey.compute_keygrip())) + if digest_algo == DIGEST_SHA256: + g.send_command('SETHASH --hash=sha256 %s\n' % hexlify(digest)) + else: + raise('Unknown digest algorithm', digest_algo) + g.send_command('PKSIGN\n') + sig_result_str = g.get_response() + sig_sexp = sexp(sig_result_str) # [ "sig-val" [ "rsa" [ "s" "xxx" ] ] ] + return sig_sexp[1][1][1] + +import sys + +if __name__ == '__main__': + # + filename = sys.argv[1] + # Connect to GPG-agent: + g = gpg_agent() + print("GPG-agent says: %s" % g.read_line()) + # + primary_key = build_rsakey_from_openpgp_file(filename) + print("Primary key fingerprint: %s" % hexlify(primary_key.compute_fpr())) + print("Primary keygrip: %s" % hexlify(primary_key.compute_keygrip())) + # + subkey = build_rsakey_from_ssh_key_under_gpg_agent(g) + print("Subkey fingerprint: %s" % hexlify(subkey.compute_fpr())) + print("Subkey keygrip: %s" % hexlify(subkey.compute_keygrip())) + # + openpgp_subkey_packet = subkey.compose_public_subkey_packet() + openpgp_sig_packet = compose_binding_signature_packet(g, primary_key, subkey, int(time())) + # Query to GPG-agent finished + g.close() + # Append OpenPGP packets to file + f = open(filename, "ab") + f.write(openpgp_subkey_packet) + f.write(openpgp_sig_packet) + f.close() diff --git a/gnuk/tool/asm-thumb/README b/gnuk/tool/asm-thumb/README new file mode 100644 index 0000000000..e24b92a5a2 --- /dev/null +++ b/gnuk/tool/asm-thumb/README @@ -0,0 +1,2 @@ +These assembler program are source code of program fragments in +stlinkv2.py. diff --git a/gnuk/tool/asm-thumb/blank_check.S b/gnuk/tool/asm-thumb/blank_check.S new file mode 100644 index 0000000000..53befbf187 --- /dev/null +++ b/gnuk/tool/asm-thumb/blank_check.S @@ -0,0 +1,19 @@ +/* ARM Thumb Assembler code */ +// arm-none-eabi-gcc -Wa,-amhls=blank_check.lst -c blank_check.S + + .cpu cortex-m0 + .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 diff --git a/gnuk/tool/asm-thumb/flash_write.S b/gnuk/tool/asm-thumb/flash_write.S new file mode 100644 index 0000000000..66a577d93b --- /dev/null +++ b/gnuk/tool/asm-thumb/flash_write.S @@ -0,0 +1,38 @@ +/* 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 + + .cpu cortex-m0 + .thumb + ldr r2, .SIZE + 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 0x2000003C +.TARGET_ADDR: .word 0x08000000 +.SIZE: .word 0x00000000 diff --git a/gnuk/tool/asm-thumb/opt_bytes_write.S b/gnuk/tool/asm-thumb/opt_bytes_write.S new file mode 100644 index 0000000000..aafcbf91e1 --- /dev/null +++ b/gnuk/tool/asm-thumb/opt_bytes_write.S @@ -0,0 +1,34 @@ +/* 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 + + .cpu cortex-m0 + .thumb + ldr r0, .OPTION_BYTES + 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 + add r1, #2 + mov r0, #255 + strh r0, [r1] +2: ldr r0, [r2, #FLASH_SR_OFFSET] + tst r0, r4 + bne 2b + mov r0, #0 + str r0, [r2, #FLASH_CR_OFFSET] + bkpt #0x00 + .align 2 +.FLASH_BASE_ADDR: .word 0x40022000 +.TARGET_ADDR: .word 0x1FFFF800 +.OPTION_BYTES: .word 0x00000000 diff --git a/gnuk/tool/calc_precompute_table_ecc.py b/gnuk/tool/calc_precompute_table_ecc.py new file mode 100644 index 0000000000..2b59789329 --- /dev/null +++ b/gnuk/tool/calc_precompute_table_ecc.py @@ -0,0 +1,28 @@ +from ecdsa import curves, ecdsa +G = ecdsa.generator_secp256k1 +# G = ecdsa.generator_256 + +def print_nG(n): + nG = n*G + nGx_str = "%064x" % nG.x() + nGy_str = "%064x" % nG.y() + print256(nGx_str) + print256(nGy_str) + print + +def print256(s): + print("0x%s, 0x%s, 0x%s, 0x%s," % (s[56:64], s[48:56], s[40:48], s[32:40])) + print("0x%s, 0x%s, 0x%s, 0x%s" % (s[24:32], s[16:24], s[8:16], s[0:8])) + print + + +for i in range(1,16): + n = (i & 1) + (i & 2) * 0x8000000000000000L + (i & 4) * 0x40000000000000000000000000000000L + (i & 8) * 0x200000000000000000000000000000000000000000000000L + print "%064x" % n + print_nG(n) + +for i in range(1,16): + n = (i & 1) + (i & 2) * 0x8000000000000000L + (i & 4) * 0x40000000000000000000000000000000L + (i & 8) * 0x200000000000000000000000000000000000000000000000L + n = n * 0x100000000L + print "%064x" % n + print_nG(n) diff --git a/gnuk/tool/dfuse.py b/gnuk/tool/dfuse.py new file mode 100644 index 0000000000..cd0f172848 --- /dev/null +++ b/gnuk/tool/dfuse.py @@ -0,0 +1,395 @@ +#! /usr/bin/python + +""" +dfuse.py - DFU (Device Firmware Upgrade) tool for STM32 Processor. +"SE" in DfuSe stands for "STmicroelectronics Extention". + +Copyright (C) 2010, 2011 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 . +""" + +from intel_hex import * +import sys, time, struct + +# INPUT: intel hex file + +# As of October 2010 (DfuSe V3.0.1 - 06/18/2010), it seems that +# following features are not supported by DfuSe implementation on +# target: +# +# unprotect +# leave_dfu_mode +# write to option bytes +# erase for mass erase + + +# See: AN3156 by STMicroelectronics + +import usb + +# check string descriptor in interrface descriptor in config descriptor: iInterface + +# USB DFU class, subclass, protocol +DFU_CLASS = 0xFE +DFU_SUBCLASS = 0x01 +DFU_STM32PROTOCOL_0 = 0 +DFU_STM32PROTOCOL_2 = 2 + +# DFU request +DFU_DETACH = 0x00 +DFU_DNLOAD = 0x01 +DFU_UPLOAD = 0x02 +DFU_GETSTATUS = 0x03 +DFU_CLRSTATUS = 0x04 +DFU_GETSTATE = 0x05 +DFU_ABORT = 0x06 + +# DFU status +DFU_STATUS_OK = 0x00 +DFU_STATUS_ERROR_TARGET = 0x01 +DFU_STATUS_ERROR_FILE = 0x02 +DFU_STATUS_ERROR_WRITE = 0x03 +DFU_STATUS_ERROR_ERASE = 0x04 +DFU_STATUS_ERROR_CHECK_ERASED = 0x05 +DFU_STATUS_ERROR_PROG = 0x06 +DFU_STATUS_ERROR_VERIFY = 0x07 +DFU_STATUS_ERROR_ADDRESS = 0x08 +DFU_STATUS_ERROR_NOTDONE = 0x09 +DFU_STATUS_ERROR_FIRMWARE = 0x0a +DFU_STATUS_ERROR_VENDOR = 0x0b +DFU_STATUS_ERROR_USBR = 0x0c +DFU_STATUS_ERROR_POR = 0x0d +DFU_STATUS_ERROR_UNKNOWN = 0x0e +DFU_STATUS_ERROR_STALLEDPKT = 0x0f + +# DFU state +STATE_APP_IDLE = 0x00 +STATE_APP_DETACH = 0x01 +STATE_DFU_IDLE = 0x02 +STATE_DFU_DOWNLOAD_SYNC = 0x03 +STATE_DFU_DOWNLOAD_BUSY = 0x04 +STATE_DFU_DOWNLOAD_IDLE = 0x05 +STATE_DFU_MANIFEST_SYNC = 0x06 +STATE_DFU_MANIFEST = 0x07 +STATE_DFU_MANIFEST_WAIT_RESET = 0x08 +STATE_DFU_UPLOAD_IDLE = 0x09 +STATE_DFU_ERROR = 0x0a + +# Return tuple of 4-bytes from integer +def get_four_bytes (v): + return [ v % 256, (v >> 8)%256, (v >> 16)%256, (v >> 24) ] + +class DFU_STM32(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 != DFU_CLASS: + raise ValueError, "Wrong interface class" + if interface.interfaceSubClass != DFU_SUBCLASS: + raise ValueError, "Wrong interface sub class" + self.__protocol = interface.interfaceProtocol + self.__devhandle = device.open() + self.__devhandle.setConfiguration(configuration) + self.__devhandle.claimInterface(interface) + self.__devhandle.setAltInterface(0) + + self.__intf = interface.interfaceNumber + self.__alt = interface.alternateSetting + self.__conf = configuration + # Initialize members + self.__blocknum = 0 + + def ll_getdev(self): + return self.__devhandle + + def ll_get_string(self, index): + # specify buffer length for 80 + return self.__devhandle.getString(index, 80) + + def ll_get_status(self): + # Status, PollTimeout[3], State, String + return self.__devhandle.controlMsg(requestType = 0xa1, + request = DFU_GETSTATUS, + value = 0, + index = self.__intf, + buffer = 6, + timeout = 3000000) + + def ll_clear_status(self): + return self.__devhandle.controlMsg(requestType = 0x21, + request = DFU_CLRSTATUS, + value = 0, + index = self.__intf, + buffer = None) + + # Upload: TARGET -> HOST + def ll_upload_block(self, block_num): + return self.__devhandle.controlMsg(requestType = 0xa1, + request = DFU_UPLOAD, + value = block_num, + index = self.__intf, + buffer = 1024, + timeout = 3000000) + + # Download: HOST -> TARGET + def ll_download_block(self, block_num, block): + return self.__devhandle.controlMsg(requestType = 0x21, + request = DFU_DNLOAD, + value = block_num, + index = self.__intf, + buffer = block) + + def dfuse_read_memory(self): + blocknum = self.__blocknum + self.__blocknum = self.__blocknum + 1 + try: + block = self.ll_upload_block(blocknum) + return block + except: + s = self.ll_get_status() + while s[4] == STATE_DFU_DOWNLOAD_BUSY: + time.sleep(0.1) + s = self.ll_get_status() + raise ValueError, "Read memory failed (%d)" % s[0] + + def dfuse_set_address_pointer(self, address): + bytes = get_four_bytes (address) + self.__blocknum = 2 + self.ll_download_block(0, [0x21] + bytes) + s = self.ll_get_status() + while s[4] == STATE_DFU_DOWNLOAD_BUSY: + time.sleep(0.1) + s = self.ll_get_status() + if s[4] != STATE_DFU_DOWNLOAD_IDLE: + raise ValueError, "Set Address Pointer failed" + + def dfuse_erase(self, address): + bytes = get_four_bytes (address) + self.ll_download_block(0, [0x41] + bytes) + s = self.ll_get_status() + while s[4] == STATE_DFU_DOWNLOAD_BUSY: + time.sleep(0.1) + s = self.ll_get_status() + if s[4] != STATE_DFU_DOWNLOAD_IDLE: + raise ValueError, "Erase failed" + + def dfuse_write_memory(self, block): + blocknum = self.__blocknum + self.__blocknum = self.__blocknum + 1 + self.ll_download_block(blocknum, block) + s = self.ll_get_status() + while s[4] == STATE_DFU_DOWNLOAD_BUSY: + time.sleep(0.1) + s = self.ll_get_status() + if s[4] != STATE_DFU_DOWNLOAD_IDLE: + raise ValueError, "Write memory failed" + + def download(self, ih): + # First, erase pages + sys.stdout.write("Erasing: ") + sys.stdout.flush() + last_addr = 0 + for start_addr in sorted(ih.memory.keys()): + data = ih.memory[start_addr] + end_addr = start_addr + len(data) + addr = start_addr & 0xfffffc00 + if not last_addr == 0: + i = 0 + if last_addr > addr: + addr = last_addr + else: + while last_addr < addr: + self.dfuse_erase(last_addr) + if i & 0x03 == 0x03: + sys.stdout.write(".") + sys.stdout.flush() + last_addr += 1024 + i += 1 + i = 0 + while addr < end_addr: + self.dfuse_erase(addr) + if i & 0x03 == 0x03: + sys.stdout.write("#") + sys.stdout.flush() + addr += 1024 + i += 1 + last_addr = addr + sys.stdout.write("\n") + sys.stdout.flush() + # Then, write pages + sys.stdout.write("Writing: ") + sys.stdout.flush() + last_addr = 0 + for start_addr in sorted(ih.memory.keys()): + data = ih.memory[start_addr] + end_addr = start_addr + len(data) + addr = start_addr & 0xfffffc00 + if not last_addr == 0: + i = 0 + while last_addr < addr: + if i & 0x03 == 0x03: + sys.stdout.write(".") + sys.stdout.flush() + last_addr += 1024 + i += 1 + i = 0 + if addr != start_addr: + self.dfuse_set_address_pointer(start_addr) + self.dfuse_write_memory(data[0:(addr + 1024 - start_addr)]) + data = data[(addr + 1024 - start_addr):] + addr += 1024 + self.dfuse_set_address_pointer(addr) + while addr < end_addr: + self.dfuse_write_memory(data[i*1024:(i+1)*1024]) + if i & 0x03 == 0x03: + sys.stdout.write("#") + sys.stdout.flush() + addr += 1024 + i += 1 + last_addr = addr + if self.__protocol == DFU_STM32PROTOCOL_0: + # 0-length write at the end + self.ll_download_block(self.__blocknum, None) + s = self.ll_get_status() + if s[4] == STATE_DFU_MANIFEST: + time.sleep(1) + try: + s = self.ll_get_status() + except: + self.__devhandle.reset() + elif s[4] == STATE_DFU_MANIFEST_WAIT_RESET: + self.__devhandle.reset() + elif s[4] != STATE_DFU_IDLE: + raise ValueError, "write failed (%d)." % s[4] + else: + self.ll_clear_status() + self.ll_clear_status() + sys.stdout.write("\n") + sys.stdout.flush() + + def verify(self, ih): + s = self.ll_get_status() + if s[4] != STATE_DFU_IDLE: + self.ll_clear_status() + # Read pages + sys.stdout.write("Reading: ") + sys.stdout.flush() + last_addr = 0 + for start_addr in sorted(ih.memory.keys()): + data = ih.memory[start_addr] + end_addr = start_addr + len(data) + addr = start_addr & 0xfffffc00 + if not last_addr == 0: + i = 0 + while last_addr < addr: + if i & 0x03 == 0x03: + sys.stdout.write(".") + sys.stdout.flush() + last_addr += 1024 + i += 1 + if addr != start_addr: + self.dfuse_set_address_pointer(addr) + self.ll_clear_status() + self.ll_clear_status() + block = self.dfuse_read_memory() + j = 0 + for c in data[0:(addr + 1024 - start_addr)]: + if (ord(c)&0xff) != block[j + start_addr - addr]: + raise ValueError, "verify failed at %08x" % (addr + i*1024+j) + j += 1 + data = data[(addr + 1024 - start_addr):] + addr += 1024 + self.ll_clear_status() + self.ll_clear_status() + self.dfuse_set_address_pointer(addr) + self.ll_clear_status() + self.ll_clear_status() + i = 0 + while addr < end_addr: + block = self.dfuse_read_memory() + j = 0 + for c in data[i*1024:(i+1)*1024]: + if (ord(c)&0xff) != block[j]: + raise ValueError, "verify failed at %08x" % (addr + i*1024+j) + j += 1 + if i & 0x03 == 0x03: + sys.stdout.write("#") + sys.stdout.flush() + addr += 1024 + i += 1 + last_addr = addr + self.ll_clear_status() + self.ll_clear_status() + self.ll_clear_status() + sys.stdout.write("\n") + sys.stdout.flush() + +busses = usb.busses() + +# 0483: SGS Thomson Microelectronics +# df11: DfuSe +USB_VENDOR_STMICRO=0x0483 +USB_PRODUCT_DFUSE=0xdf11 + +def get_device(): + for bus in busses: + devices = bus.devices + for dev in devices: + if dev.idVendor != USB_VENDOR_STMICRO: + continue + if dev.idProduct != USB_PRODUCT_DFUSE: + continue + for config in dev.configurations: + for intf in config.interfaces: + for alt in intf: + if alt.interfaceClass == DFU_CLASS and \ + alt.interfaceSubClass == DFU_SUBCLASS and \ + (alt.interfaceProtocol == DFU_STM32PROTOCOL_0 or \ + alt.interfaceProtocol == DFU_STM32PROTOCOL_2): + return dev, config, alt + raise ValueError, "Device not found" + +def main(filename): + dev, config, intf = get_device() + print "Device:", dev.filename + print "Configuration", config.value + print "Interface", intf.interfaceNumber + dfu = DFU_STM32(dev, config, intf) + print dfu.ll_get_string(intf.iInterface) + s = dfu.ll_get_status() + if s[4] == STATE_DFU_ERROR: + dfu.ll_clear_status() + s = dfu.ll_get_status() + print s + if s[4] == STATE_DFU_IDLE: + exit + transfer_size = 1024 + if s[0] != DFU_STATUS_OK: + print s + exit + ih = intel_hex(filename) + dfu.download(ih) + dfu.verify(ih) + +if __name__ == '__main__': + main(sys.argv[1]) diff --git a/gnuk/tool/dump_mem.py b/gnuk/tool/dump_mem.py new file mode 100644 index 0000000000..806bafe5b1 --- /dev/null +++ b/gnuk/tool/dump_mem.py @@ -0,0 +1,50 @@ +#! /usr/bin/python + +""" +dump_mem.py - dump memory with DfuSe for STM32 Processor. + +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 . +""" + +import sys +from dfuse import * + +dev, config, intf = get_device() +dfu = DFU_STM32(dev, config, intf) +print dfu.ll_get_string(intf.iInterface) +s = dfu.ll_get_status() +dfu.ll_clear_status() +s = dfu.ll_get_status() +print s +dfu.dfuse_set_address_pointer(int(sys.argv[1], 16)) +s = dfu.ll_get_status() +dfu.ll_clear_status() +s = dfu.ll_get_status() +dfu.ll_clear_status() +s = dfu.ll_get_status() +print s +block = dfu.dfuse_read_memory() +count = 0 +for d in block: + print "%02x" % d, + if count & 0x0f == 0x0f: + print + count += 1 +dfu.ll_clear_status() +s = dfu.ll_get_status() diff --git a/gnuk/tool/get_raw_public_key.py b/gnuk/tool/get_raw_public_key.py new file mode 100644 index 0000000000..fb1272f091 --- /dev/null +++ b/gnuk/tool/get_raw_public_key.py @@ -0,0 +1,32 @@ +#! /usr/bin/python + +import sys, binascii +from subprocess import check_output + +def get_gpg_public_key(keygrip): + result = check_output(["gpg-connect-agent", "READKEY %s" % keygrip, "/bye"]) + key = "" + while True: + i = result.find('%') + if i < 0: + key += result + break + hex_str = result[i+1:i+3] + key += result[0:i] + key += chr(int(hex_str,16)) + result = result[i+3:] + + pos = key.index("D (10:public-key(3:rsa(1:n257:") + 31 # skip NUL too + pos_last = key.index(")(1:e3:") + key = key[pos:pos_last] + if len(key) != 256: + raise ValueError, binascii.hexlify(key) + return key + +if __name__ == '__main__': + keygrip = sys.argv[1] + k = get_gpg_public_key(keygrip) + shorthand = keygrip[0:8] + ".bin" + f = open(shorthand,"w") + f.write(k) + f.close() diff --git a/gnuk/tool/gnuk-emulation-setup b/gnuk/tool/gnuk-emulation-setup new file mode 100644 index 0000000000..19f0bc8235 --- /dev/null +++ b/gnuk/tool/gnuk-emulation-setup @@ -0,0 +1,48 @@ +#! /bin/sh +# +# gnuk-emulation-setup - Generate flash image for Gnuk +# +# Copyright (C) 2017 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 . + +if test "$1" = "--help"; then + echo "Usage:" + echo " $0 [output-file]" + echo " Generate Gnuk flash image" + echo " $0 --help" + echo " Show this message" + exit 0 +fi + +OUTPUT_FILE=${1:-$HOME/.gnuk-flash-image} + +# Generate 8192-byte flash data into OUTPUT_FILE + +exec > $OUTPUT_FILE + +for i in $(seq 512); do + /bin/echo -n -e '\xff\xff\xff\xff\xff\xff\xff\xff' +done + +/bin/echo -n -e '\x00\x00\xff\xff\xff\xff\xff\xff' + +for i in $(seq 511); do + /bin/echo -n -e '\xff\xff\xff\xff\xff\xff\xff\xff' +done + +chmod og-rw $OUTPUT_FILE diff --git a/gnuk/tool/gnuk_put_binary_libusb.py b/gnuk/tool/gnuk_put_binary_libusb.py new file mode 100644 index 0000000000..5363615793 --- /dev/null +++ b/gnuk/tool/gnuk_put_binary_libusb.py @@ -0,0 +1,111 @@ +#! /usr/bin/python + +""" +gnuk_put_binary.py - a tool to put binary to Gnuk Token +This tool is for importing certificate, writing serial number, etc. + +Copyright (C) 2011, 2012 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 . +""" + +from struct import * +import sys, time, os, binascii +from gnuk_token import * + +# INPUT: binary file + +# Assume only single CCID device is attached to computer, and it's Gnuk Token + +DEFAULT_PW3 = "12345678" +BY_ADMIN = 3 + +def main(fileid, is_update, data, passwd): + gnuk = None + for (dev, config, intf) in gnuk_devices(): + try: + gnuk = gnuk_token(dev, config, intf) + print("Device: %s" % dev.filename) + print("Configuration: %d" % config.value) + print("Interface: %d" % intf.interfaceNumber) + break + except: + pass + if gnuk.icc_get_status() == 2: + raise ValueError("No ICC present") + elif gnuk.icc_get_status() == 1: + gnuk.icc_power_on() + gnuk.cmd_select_openpgp() + gnuk.cmd_verify(BY_ADMIN, passwd.encode('UTF-8')) + gnuk.cmd_write_binary(fileid, data, is_update) + gnuk.cmd_select_openpgp() + if fileid == 0: + data_in_device = gnuk.cmd_get_data(0x00, 0x4f) + print(' '.join([ "%02x" % d for d in data_in_device ])) + compare(data + b'\x00\x00', data_in_device[8:].tostring()) + elif fileid >= 1 and fileid <= 4: + data_in_device = gnuk.cmd_read_binary(fileid) + compare(data, data_in_device) + else: + data_in_device = gnuk.cmd_get_data(0x7f, 0x21) + compare(data, data_in_device) + gnuk.icc_power_off() + return 0 + +if __name__ == '__main__': + passwd = DEFAULT_PW3 + if sys.argv[1] == '-p': + from getpass import getpass + passwd = getpass("Admin password: ") + sys.argv.pop(1) + if sys.argv[1] == '-u': + is_update = True + sys.argv.pop(1) + else: + is_update = False + if sys.argv[1] == '-s': + fileid = 0 # serial number + filename = sys.argv[2] + f = open(filename) + email = os.environ['EMAIL'] + serial_data_hex = None + for line in f.readlines(): + field = str.split(line) + if field[0] == email: + serial_data_hex = field[1].replace(':','') + f.close() + if not serial_data_hex: + print("No serial number") + exit(1) + print("Writing serial number") + data = binascii.unhexlify(serial_data_hex) + elif sys.argv[1] == '-k': # firmware update key + keyno = sys.argv[2] + fileid = 1 + int(keyno) + filename = sys.argv[3] + f = open(filename, "rb") + data = f.read() + f.close() + else: + fileid = 5 # Card holder certificate + filename = sys.argv[1] + f = open(filename, "rb") + data = f.read() + f.close() + print("%s: %d" % (filename, len(data))) + print("Updating card holder certificate") + main(fileid, is_update, data, passwd) diff --git a/gnuk/tool/gnuk_remove_keys_libusb.py b/gnuk/tool/gnuk_remove_keys_libusb.py new file mode 100644 index 0000000000..d9e147be82 --- /dev/null +++ b/gnuk/tool/gnuk_remove_keys_libusb.py @@ -0,0 +1,71 @@ +#! /usr/bin/python + +""" +gnuk_remove_keys_libusb.py - a tool to remove keys in Gnuk Token + +Copyright (C) 2012 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 . +""" + +import sys, os + +from gnuk_token import * + +# Assume only single CCID device is attached to computer and it's Gnuk Token + +DEFAULT_PW3 = "12345678" +BY_ADMIN = 3 + +def main(passwd): + gnuk = None + for (dev, config, intf) in gnuk_devices(): + try: + gnuk = gnuk_token(dev, config, intf) + print("Device: %s" % dev.filename) + print("Configuration: %d" % config.value) + print("Interface: %d" % intf.interfaceNumber) + break + except: + pass + if gnuk.icc_get_status() == 2: + raise ValueError("No ICC present") + elif gnuk.icc_get_status() == 1: + gnuk.icc_power_on() + gnuk.cmd_select_openpgp() + gnuk.cmd_verify(BY_ADMIN, passwd.encode('UTF-8')) + gnuk.cmd_select_openpgp() + gnuk.cmd_put_data_remove(0x00, 0xc7) # FP_SIG + gnuk.cmd_put_data_remove(0x00, 0xce) # KGTIME_SIG + gnuk.cmd_put_data_key_import_remove(1) + gnuk.cmd_put_data_remove(0x00, 0xc8) # FP_DEC + gnuk.cmd_put_data_remove(0x00, 0xcf) # KGTIME_DEC + gnuk.cmd_put_data_key_import_remove(2) + gnuk.cmd_put_data_remove(0x00, 0xc9) # FP_AUT + gnuk.cmd_put_data_remove(0x00, 0xd0) # KGTIME_AUT + gnuk.cmd_put_data_key_import_remove(3) + gnuk.icc_power_off() + return 0 + + +if __name__ == '__main__': + passwd = DEFAULT_PW3 + if len(sys.argv) > 1 and sys.argv[1] == '-p': + from getpass import getpass + passwd = getpass("Admin password: ") + sys.argv.pop(1) + main(passwd) diff --git a/gnuk/tool/gnuk_token.py b/gnuk/tool/gnuk_token.py new file mode 100644 index 0000000000..6d46b9ca4f --- /dev/null +++ b/gnuk/tool/gnuk_token.py @@ -0,0 +1,637 @@ +""" +gnuk_token.py - a library for Gnuk Token + +Copyright (C) 2011, 2012, 2013, 2015, 2017 + 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 . +""" + +from struct import * +import binascii +import usb, time +from array import array + +# Possible Gnuk Token products +USB_PRODUCT_LIST=[ + { 'vendor' : 0x234b, 'product' : 0x0000 }, # FSIJ Gnuk Token + { 'vendor' : 0x20a0, 'product' : 0x4211 }, # Nitrokey Start +] + +# USB class, subclass, protocol +CCID_CLASS = 0x0B +CCID_SUBCLASS = 0x00 +CCID_PROTOCOL_0 = 0x00 + +HID_CLASS = 0x03 +HID_SUBCLASS_NO_BOOT = 0x00 +HID_PROTOCOL_0 = 0x00 + +def icc_compose(msg_type, data_len, slot, seq, param, data): + return pack('BBBB', cls, ins, p1, p2) + else: + return pack('>BBBBB', cls, ins, p1, p2, le) + else: + if not le: + return pack('>BBBBB', cls, ins, p1, p2, data_len) + data + else: + return pack('>BBBBB', cls, ins, p1, p2, data_len) \ + + data + pack('>B', le) + +# This class only supports Gnuk (for now) +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() + self.__devhandle.claimInterface(interface) + + self.__intf = interface.interfaceNumber + self.__alt = interface.alternateSetting + self.__conf = configuration + + self.__hid_intf = None + for intf in configuration.interfaces: + for alt in intf: + if alt.interfaceClass == HID_CLASS and \ + alt.interfaceSubClass == HID_SUBCLASS_NO_BOOT and \ + alt.interfaceProtocol == HID_PROTOCOL_0: + self.__hid_intf = alt.interfaceNumber + + self.__bulkout = 1 + self.__bulkin = 0x81 + + self.__timeout = 10000 + self.__seq = 0 + + def get_string(self, num): + return self.__devhandle.getString(num, 512) + + 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 stop_gnuk(self): + self.__devhandle.releaseInterface() + if self.__hid_intf: + self.__devhandle.detachKernelDriver(self.__hid_intf) + self.__devhandle.setConfiguration(0) + return + + def mem_info(self): + mem = self.__devhandle.controlMsg(requestType = 0xc0, request = 0, + buffer = 8, value = 0, index = 0, + timeout = 10) + start = ((mem[3]*256 + mem[2])*256 + mem[1])*256 + mem[0] + end = ((mem[7]*256 + mem[6])*256 + mem[5])*256 + mem[4] + return (start, end) + + def download(self, start, data, verbose=False): + addr = start + addr_end = (start + len(data)) & 0xffffff00 + i = int((addr - 0x20000000) / 0x100) + j = 0 + print("start %08x" % addr) + print("end %08x" % addr_end) + while addr < addr_end: + if verbose: + print("# %08x: %d : %d" % (addr, i, 256)) + self.__devhandle.controlMsg(requestType = 0x40, request = 1, + buffer = data[j*256:j*256+256], + value = i, index = 0, timeout = 10) + i = i+1 + j = j+1 + addr = addr + 256 + residue = len(data) % 256 + if residue != 0: + if verbose: + print("# %08x: %d : %d" % (addr, i, residue)) + self.__devhandle.controlMsg(requestType = 0x40, request = 1, + buffer = data[j*256:], + value = i, index = 0, timeout = 10) + + def execute(self, last_addr): + i = int((last_addr - 0x20000000) / 0x100) + o = (last_addr - 0x20000000) % 0x100 + self.__devhandle.controlMsg(requestType = 0x40, request = 2, + buffer = None, value = i, index = o, + timeout = 10) + + def icc_get_result(self): + usbmsg = self.__devhandle.bulkRead(self.__bulkin, 1024, self.__timeout) + if len(usbmsg) < 10: + print(usbmsg) + raise ValueError("icc_get_result") + msg = array('B', usbmsg) + 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, b"") + 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, b"") + self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout) + self.increment_seq() + status, chain, data = self.icc_get_result() + # XXX: check status, chain + self.atr = data + return self.atr + + def icc_power_off(self): + msg = icc_compose(0x63, 0, 0, self.__seq, 0, b"") + 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, b"") + 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 = array('B') + while True: + cmd_data = iso7816_compose(0xc0, 0x00, 0x00, b'') + 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 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, b'') + 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 1", sw) + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError("cmd_write_binary 1", "%02x%02x" % (sw[0], sw[1])) + count += 1 + + def cmd_select_openpgp(self): + cmd_data = iso7816_compose(0xa4, 0x04, 0x00, b"\xD2\x76\x00\x01\x24\x01") + r = self.icc_send_cmd(cmd_data) + if len(r) < 2: + raise ValueError(r) + sw = r[-2:] + r = r[0:-2] + if sw[0] == 0x61: + self.cmd_get_response(sw[1]) + return True + elif sw[0] == 0x90 and sw[1] == 0x00: + return True + else: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + + def cmd_get_data(self, tagh, tagl): + cmd_data = iso7816_compose(0xca, tagh, tagl, b"") + sw = self.icc_send_cmd(cmd_data) + if len(sw) != 2: + raise ValueError(sw) + if sw[0] == 0x90 and sw[1] == 0x00: + return array('B') + 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, who, data): + cmd_data = iso7816_compose(0x2c, how, 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_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 array('B') + 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 array('B') + 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 = b'\xb6\x00' + elif keyno == 2: + data = b'\xb8\x00' + else: + data = b'\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 array('B') + 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 = b'\xb6\x00' + elif keyno == 2: + data = b'\xb8\x00' + else: + data = b'\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 cmd_put_data_remove(self, tagh, tagl): + cmd_data = iso7816_compose(0xda, tagh, tagl, b"") + sw = self.icc_send_cmd(cmd_data) + if sw[0] != 0x90 and sw[1] != 0x00: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + + def cmd_put_data_key_import_remove(self, keyno): + if keyno == 1: + keyspec = b"\xb6\x00" # SIG + elif keyno == 2: + keyspec = b"\xb8\x00" # DEC + else: + keyspec = b"\xa4\x00" # AUT + cmd_data = iso7816_compose(0xdb, 0x3f, 0xff, b"\x4d\x02" + keyspec) + sw = self.icc_send_cmd(cmd_data) + if sw[0] != 0x90 and sw[1] != 0x00: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + + def cmd_get_challenge(self): + cmd_data = iso7816_compose(0x84, 0x00, 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_external_authenticate(self, keyno, signed): + cmd_data = iso7816_compose(0x82, 0x00, keyno, signed[0:128], cls=0x10) + 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])) + cmd_data = iso7816_compose(0x82, 0x00, keyno, signed[128:]) + 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])) + + +class regnual(object): + def __init__(self, dev): + conf = dev.configurations[0] + intf_alt = conf.interfaces[0] + intf = intf_alt[0] + if intf.interfaceClass != 0xff: + raise ValueError("Wrong interface class") + self.__devhandle = dev.open() + self.__devhandle.claimInterface(intf) + + def mem_info(self): + mem = self.__devhandle.controlMsg(requestType = 0xc0, request = 0, + buffer = 8, value = 0, index = 0, + timeout = 10000) + start = ((mem[3]*256 + mem[2])*256 + mem[1])*256 + mem[0] + end = ((mem[7]*256 + mem[6])*256 + mem[5])*256 + mem[4] + return (start, end) + + def download(self, start, data, verbose=False): + addr = start + addr_end = (start + len(data)) & 0xffffff00 + i = int((addr - 0x08000000) / 0x100) + j = 0 + print("start %08x" % addr) + print("end %08x" % addr_end) + while addr < addr_end: + if verbose: + print("# %08x: %d: %d : %d" % (addr, i, j, 256)) + self.__devhandle.controlMsg(requestType = 0x40, request = 1, + buffer = data[j*256:j*256+256], + value = 0, index = 0, timeout = 10000) + crc32code = crc32(data[j*256:j*256+256]) + res = self.__devhandle.controlMsg(requestType = 0xc0, request = 2, + buffer = 4, value = 0, index = 0, + timeout = 10000) + r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0] + if (crc32code ^ r_value) != 0xffffffff: + print("failure") + self.__devhandle.controlMsg(requestType = 0x40, request = 3, + buffer = None, + value = i, index = 0, timeout = 10000) + time.sleep(0.010) + res = self.__devhandle.controlMsg(requestType = 0xc0, request = 2, + buffer = 4, value = 0, index = 0, + timeout = 10000) + r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0] + if r_value == 0: + print("failure") + i = i+1 + j = j+1 + addr = addr + 256 + residue = len(data) % 256 + if residue != 0: + if verbose: + print("# %08x: %d : %d" % (addr, i, residue)) + self.__devhandle.controlMsg(requestType = 0x40, request = 1, + buffer = data[j*256:], + value = 0, index = 0, timeout = 10000) + crc32code = crc32(data[j*256:].ljust(256,b'\xff')) + res = self.__devhandle.controlMsg(requestType = 0xc0, request = 2, + buffer = 4, value = 0, index = 0, + timeout = 10000) + r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0] + if (crc32code ^ r_value) != 0xffffffff: + print("failure") + self.__devhandle.controlMsg(requestType = 0x40, request = 3, + buffer = None, + value = i, index = 0, timeout = 10000) + time.sleep(0.010) + res = self.__devhandle.controlMsg(requestType = 0xc0, request = 2, + buffer = 4, value = 0, index = 0, + timeout = 10000) + r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0] + if r_value == 0: + print("failure") + + def protect(self): + self.__devhandle.controlMsg(requestType = 0x40, request = 4, + buffer = None, value = 0, index = 0, + timeout = 10000) + time.sleep(0.100) + res = self.__devhandle.controlMsg(requestType = 0xc0, request = 2, + buffer = 4, value = 0, index = 0, + timeout = 10000) + r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0] + if r_value == 0: + print("protection failure") + + def finish(self): + self.__devhandle.controlMsg(requestType = 0x40, request = 5, + buffer = None, value = 0, index = 0, + timeout = 10000) + + def reset_device(self): + try: + self.__devhandle.reset() + except: + pass + +def compare(data_original, data_in_device): + if data_original == data_in_device: + return True + raise ValueError("verify failed") + +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 gnuk_devices_by_vidpid(): + busses = usb.busses() + for bus in busses: + devices = bus.devices + for dev in devices: + for cand in USB_PRODUCT_LIST: + if dev.idVendor != cand['vendor']: + continue + if dev.idProduct != cand['product']: + continue + yield dev + break + +def get_gnuk_device(): + icc = None + for (dev, config, intf) in gnuk_devices(): + try: + icc = gnuk_token(dev, config, intf) + print("Device: %s" % dev.filename) + print("Configuration: %d" % config.value) + print("Interface: %d" % 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 + +SHA256_OID_PREFIX="3031300d060960864801650304020105000420" + +def UNSIGNED(n): + return n & 0xffffffff + +def crc32(bytestr): + crc = binascii.crc32(bytestr) + return UNSIGNED(crc) diff --git a/gnuk/tool/gnuk_token.pyc b/gnuk/tool/gnuk_token.pyc new file mode 100644 index 0000000000..b13a8c020e Binary files /dev/null and b/gnuk/tool/gnuk_token.pyc differ diff --git a/gnuk/tool/gnuk_upgrade.py b/gnuk/tool/gnuk_upgrade.py new file mode 100644 index 0000000000..09d9e236a3 --- /dev/null +++ b/gnuk/tool/gnuk_upgrade.py @@ -0,0 +1,148 @@ +#! /usr/bin/python + +""" +gnuk_upgrade.py - a tool to upgrade firmware of Gnuk Token + +Copyright (C) 2012, 2015 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 . +""" + +from struct import * +import sys, time, os, binascii + +# INPUT: binary files (regnual_image, upgrade_firmware_image) + +# Assume only single CCID device is attached to computer, and it's Gnuk Token + +import usb + +from gnuk_token import * + +from subprocess import check_output + +SHA256_OID_PREFIX="3031300d060960864801650304020105000420" + +# When user specify KEYGRIP, use it. Or else, connect to SCD directly. +def gpg_sign(keygrip, hash): + if keygrip: + result = check_output(["gpg-connect-agent", + "SIGKEY %s" % keygrip, + "SETHASH --hash=sha256 %s" % hash, + "PKSIGN --hash=sha256", "/bye"]) + else: + result = check_output(["gpg-connect-agent", + "SCD SETDATA " + SHA256_OID_PREFIX + hash, + "SCD PKAUTH OPENPGP.3", + "/bye"]) + signed = "" + while True: + i = result.find('%') + if i < 0: + signed += result + break + hex_str = result[i+1:i+3] + signed += result[0:i] + signed += chr(int(hex_str,16)) + result = result[i+3:] + + if keygrip: + pos = signed.index("D (7:sig-val(3:rsa(1:s256:") + 26 + signed = signed[pos:-7] + else: + pos = signed.index("D ") + 2 + signed = signed[pos:-4] # \nOK\n + if len(signed) != 256: + raise ValueError(binascii.hexlify(signed)) + return signed + +def main(keyno,keygrip, data_regnual, data_upgrade): + l = len(data_regnual) + if (l & 0x03) != 0: + data_regnual = data_regnual.ljust(l + 4 - (l & 0x03), b'\x00') + crc32code = crc32(data_regnual) + print("CRC32: %04x\n" % crc32code) + data_regnual += pack(' + +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 . +""" + +import platform, os, socket +IS_WINDOWS=(platform.system() == 'Windows') + +BUFLEN=1024 + +class gpg_agent(object): + def __init__(self): + if IS_WINDOWS: + home = os.getenv("HOME") + if not home: + home = os.getenv("APPDATA") + comm_port = os.path.join(home, "gnupg", "S.gpg-agent") + # + f = open(comm_port, "rb", 0) + infostr = f.read().decode('UTF-8') + f.close() + # + info = infostr.split('\n', 1) + port = int(info[0]) + nonce = info[1] + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect(("localhost", port)) + s.send(nonce) + else: + infostr = os.getenv("GPG_AGENT_INFO") + info = infostr.split(':', 2) + path = info[0] + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + s.connect(path) + self.sock = s + self.buf_remained = b"" + self.response = None + + def read_line(self): + line = b"" + if self.buf_remained != b"": + chunk = self.buf_remained + else: + chunk = self.sock.recv(BUFLEN) + while True: + pos = chunk.find(b'\n') + if pos >= 0: + self.buf_remained = chunk[pos+1:] + line = line + chunk[0:pos] + return line + else: + line = line + chunk + chunk = self.sock.recv(BUFLEN) + + def get_response(self): + r = self.response + result = b"" + while True: + i = r.find(b'%') + if i < 0: + result += r + break + hex_str = r[i+1:i+3].decode('UTF-8') + result += r[0:i] + if bytes == str: + result += chr(int(hex_str,16)) + else: + result += bytes.fromhex(hex_str) + r = r[i+3:] + return result + + def send_command(self, cmd): + self.sock.send(cmd.encode('UTF-8')) + self.response = b"" + while True: + while True: + l = self.read_line() + if l[0] != b'#'[0]: + break + if l[0] == b'D'[0]: + self.response += l[2:] + elif l[0:2] == b'OK': + return True + elif l[0:3] == b'ERR': + return False + else: # XXX: S, INQUIRE, END + return False + + def close(self): + self.sock.send(b'BYE\n') + bye = self.read_line() + self.sock.close() + return bye # "OK closing connection" + +# Test +if __name__ == '__main__': + g = gpg_agent() + print(g.read_line().decode('UTF-8')) + print(g.send_command("KEYINFO --list --data\n")) + kl_str = g.get_response().decode('UTF-8') + kl_str = kl_str[0:-1] + kl = kl_str.split('\n') + import re + kl_o3 = [kg for kg in kl if re.search("OPENPGP\\.3", kg)] + print(kl_o3) + kg = kl_o3[0].split(' ')[0] + print(g.send_command("READKEY %s\n" % kg)) + r = g.get_response() + import binascii + print(binascii.hexlify(r).decode('UTF-8')) + print(g.close().decode('UTF-8')) diff --git a/gnuk/tool/hub_ctrl.py b/gnuk/tool/hub_ctrl.py new file mode 100644 index 0000000000..5ace89789b --- /dev/null +++ b/gnuk/tool/hub_ctrl.py @@ -0,0 +1,257 @@ +#! /usr/bin/python + +""" +hub_ctrl.py - a tool to control port power/led of USB hub + +Copyright (C) 2006, 2011, 2016 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 . +""" + +from __future__ import print_function +import usb + +USB_RT_HUB = (usb.TYPE_CLASS | usb.RECIP_DEVICE) +USB_RT_PORT = (usb.TYPE_CLASS | usb.RECIP_OTHER) +USB_PORT_FEAT_RESET = 4 +USB_PORT_FEAT_POWER = 8 +USB_PORT_FEAT_INDICATOR = 22 +USB_DIR_IN = 0x80 # device to host + +COMMAND_SET_NONE = 0 +COMMAND_SET_LED = 1 +COMMAND_SET_POWER = 2 + +HUB_LED_GREEN = 2 + +def find_hubs(listing, verbose, busnum=None, devnum=None, hub=None): + number_of_hubs_with_feature = 0 + hubs = [] + busses = usb.busses() + if not busses: + raise ValueError("can't access USB") + + for bus in busses: + devices = bus.devices + for dev in devices: + if dev.deviceClass != usb.CLASS_HUB: + continue + + printout_enable = 0 + if (listing + or (verbose + and ((bus.dirname == busnum and dev.devnum == devnumd) + or hub == number_of_hubs_with_feature))): + printout_enable = 1 + + uh = dev.open() + + desc = None + try: + # Get USB Hub descriptor + desc = uh.controlMsg(requestType = USB_DIR_IN | USB_RT_HUB, + request = usb.REQ_GET_DESCRIPTOR, + value = usb.DT_HUB << 8, + index = 0, buffer = 1024, timeout = 1000) + finally: + del uh + + if not desc: + continue + + # desc[3] is lower byte of wHubCharacteristics + if (desc[3] & 0x80) == 0 and (desc[3] & 0x03) >= 2: + # Hub doesn't have features of controling port power/indicator + continue + + if printout_enable: + print("Hub #%d at %s:%03d" % (number_of_hubs_with_feature, + bus.dirname, dev.devnum)) + if (desc[3] & 0x03) == 0: + print(" INFO: ganged power switching.") + elif (desc[3] & 0x03) == 1: + print(" INFO: individual power switching.") + elif (desc[3] & 0x03) == 2 or (desc[3] & 0x03) == 3: + print(" WARN: no power switching.") + + if (desc[3] & 0x80) == 0: + print(" WARN: Port indicators are NOT supported.") + + hubs.append({ 'busnum' : bus.dirname, 'devnum' : dev.devnum, + 'indicator_support' : (desc[3] & 0x80) == 0x80, + 'dev' : dev, 'num_ports' : desc[2] }) + number_of_hubs_with_feature += 1 + + return hubs + +def hub_port_status(handle, num_ports): + print(" Hub Port Status:") + for i in range(num_ports): + port = i + 1 + status = handle.controlMsg(requestType = USB_RT_PORT | usb.ENDPOINT_IN, + request = usb.REQ_GET_STATUS, + value = 0, + index = port, buffer = 4, + timeout = 1000) + + print(" Port %d: %02x%02x.%02x%02x" % (port, status[3], status[2], + status[1], status[0]),) + if status[1] & 0x10: + print(" indicator", end='') + if status[1] & 0x08: + print(" test" , end='') + if status[1] & 0x04: + print(" highspeed", end='') + if status[1] & 0x02: + print(" lowspeed", end='') + if status[1] & 0x01: + print(" power", end='') + + if status[0] & 0x10: + print(" RESET", end='') + if status[0] & 0x08: + print(" oc", end='') + if status[0] & 0x04: + print(" suspend", end='') + if status[0] & 0x02: + print(" enable", end='') + if status[0] & 0x01: + print(" connect", end='') + + print() + +import sys + +COMMAND_SET_NONE = 0 +COMMAND_SET_LED = 1 +COMMAND_SET_POWER = 2 +HUB_LED_GREEN = 2 + +def usage(progname): + print("""Usage: %s [{-h HUBNUM | -b BUSNUM -d DEVNUM}] + [-P PORT] [{-p [VALUE]|-l [VALUE]}] +""" % progname, file=sys.stderr) + +def exit_with_usage(progname): + usage(progname) + exit(1) + +if __name__ == '__main__': + busnum = None + devnum = None + listing = False + verbose = False + hub = None + port = 1 + cmd = COMMAND_SET_NONE + + if len(sys.argv) == 1: + listing = True + else: + try: + while len(sys.argv) >= 2: + option = sys.argv[1] + sys.argv.pop(1) + if option == '-h': + if busnum != None or devnum != None: + exit_with_usage(sys.argv[0]) + hub = int(sys.argv[1]) + sys.argv.pop(1) + elif option == '-b': + busnum = int(sys.argv[1]) + sys.argv.pop(1) + elif option == '-d': + devnum = int(sys.argv[1]) + sys.argv.pop(1) + elif option == '-P': + port = int(sys.argv[1]) + sys.argv.pop(1) + elif option == '-l': + if cmd != COMMAND_SET_NONE: + exit_with_usage(sys.argv[0]) + if len(sys.argv) > 1: + value = int(sys.argv[1]) + sys.argv.pop(1) + else: + value = HUB_LED_GREEN + cmd = COMMAND_SET_LED + elif option == '-p': + if cmd != COMMAND_SET_NONE: + exit_with_usage(sys.argv[0]) + if len(sys.argv) > 1: + value = int(sys.argv[1]) + sys.argv.pop(1) + else: + value = 0 + cmd = COMMAND_SET_POWER + elif option == '-v': + verbose = True + if len(sys.argv) == 1: + listing = True + else: + exit_with_usage(sys.argv[0]) + except: + exit_with_usage(sys.argv[0]) + + if ((busnum != None and devnum == None) + or (busnum == None and devnum != None)): + exit_with_usage(sys.argv[0]) + + if hub == None and busnum == None: + hub = 0 # Default hub = 0 + + if cmd == COMMAND_SET_NONE: + cmd = COMMAND_SET_POWER + + hubs = find_hubs(listing, verbose, busnum, devnum, hub) + if len(hubs) == 0: + print("No hubs found.", file=sys.stderr) + exit(1) + if listing: + exit(0) + + if hub == None: + for h in hubs: + if h['busnum'] == busnum and h['devnum'] == devnum: + dev_hub = h['dev'] + nports = h['num_ports'] + else: + dev_hub = hubs[hub]['dev'] + nports = hubs[hub]['num_ports'] + + uh = dev_hub.open() + if cmd == COMMAND_SET_POWER: + feature = USB_PORT_FEAT_POWER + index = port + if value: + request = usb.REQ_SET_FEATURE + else: + request = usb.REQ_CLEAR_FEATURE + else: + request = usb.REQ_SET_FEATURE + feature = USB_PORT_FEAT_INDICATOR + index = (value << 8) | port + if verbose: + print("Send control message (REQUEST=%d, FEATURE=%d, INDEX=%d) " % (request, feature, index)) + + uh.controlMsg(requestType = USB_RT_PORT, request = request, value = feature, + index = index, buffer = None, timeout = 1000) + if verbose: + hub_port_status(uh,nports) + + del uh diff --git a/gnuk/tool/intel_hex.py b/gnuk/tool/intel_hex.py new file mode 100644 index 0000000000..e9c7c2eb5d --- /dev/null +++ b/gnuk/tool/intel_hex.py @@ -0,0 +1,94 @@ +""" +intel_hex.py - Intel Hex file reader. + +Copyright (C) 2010 Free Software Initiative of Japan +Author: NIIBE Yutaka + +You can use/distribute/modify/etc. this for any purpose. +""" + +import binascii + +class intel_hex(object): + def __init__(self, filename): + self.start_address = 0 + self.address = 0 + self.memory = {} + self.lineno = 0 + file = open(filename, 'r') + for line in file: + self.lineno += 1 + if self.parse_line(line): + break + file.close() + self.pack() + + def pack(self): + memory = {} + prev_addr = 0 + prev_data_len = 0 + for addr in sorted(self.memory.keys()): + data = self.memory[addr] + if addr == prev_addr + prev_data_len: + memory[prev_addr] += data + prev_data_len += len(data) + else: + memory[addr] = data + prev_addr = addr + prev_data_len = len(data) + self.memory = memory + + def calc_checksum(self, byte_count, offset, type_code, data): + s = byte_count + s += (offset >> 8) + s += offset & 0xff + s += type_code + for d in data: + s += (ord(d) & 0xff) + s &= 0xff + if s != 0: + s = 256 - s + return s + + def add_data(self, count, offset, data): + address = self.address + offset + try: + self.memory[address] + except: + pass + else: + raise ValueError("data overwritten (%d)" % self.lineno) + self.memory[address] = data + + def parse_line(self, line): + if line[0] != ':': + raise ValueError("invalid line (%d)" % self.lineno) + count = int(line[1:3], 16) + offset = int(line[3:7], 16) + type_code = int(line[7:9], 16) + data = binascii.unhexlify(line[9:(9+count*2)]) + check_sum = int(line[(9+count*2):], 16) + if check_sum != self.calc_checksum(count, offset, type_code, data): + raise ValueError("invalid checksum (%d)" % self.lineno) + if type_code == 0x00: + self.add_data(count, offset, data) + return 0 + elif type_code == 0x01: + return 1 + elif type_code == 0x04: + if count != 2: + raise ValueError("invalid count (%d): (%d) Expected 2" \ + % (self.lineno, count)) + self.address = ((ord(data[0])&0xff)<<24) + ((ord(data[1])&0xff)<<16) + return 0 + elif type_code == 0x05: + if count != 4: + raise ValueError("invalid count (%d): (%d) Expected 4" \ + % (self.lineno, count)) + self.start_address \ + = ((ord(data[0])&0xff)<<24) + ((ord(data[1])&0xff)<<16) \ + + ((ord(data[2])&0xff)<<8) + ((ord(data[3])&0xff)) + return 0 + else: + raise ValueError("invalid type code (%d): (%d)" \ + % (self.lineno, type_code)) diff --git a/gnuk/tool/openocd-script/lock.tcl b/gnuk/tool/openocd-script/lock.tcl new file mode 100644 index 0000000000..9142fd08a5 --- /dev/null +++ b/gnuk/tool/openocd-script/lock.tcl @@ -0,0 +1,6 @@ +init +reset +halt +stm32x lock 0 +reset +shutdown diff --git a/gnuk/tool/openocd-script/options_read.tcl b/gnuk/tool/openocd-script/options_read.tcl new file mode 100644 index 0000000000..e56b3050d0 --- /dev/null +++ b/gnuk/tool/openocd-script/options_read.tcl @@ -0,0 +1,5 @@ +init +reset +halt +stm32f1x options_read 0 +shutdown diff --git a/gnuk/tool/openocd-script/unlock.tcl b/gnuk/tool/openocd-script/unlock.tcl new file mode 100644 index 0000000000..5a706aee2d --- /dev/null +++ b/gnuk/tool/openocd-script/unlock.tcl @@ -0,0 +1,6 @@ +init +reset +halt +stm32f1x unlock 0 +reset +shutdown diff --git a/gnuk/tool/openocd-script/write.tcl b/gnuk/tool/openocd-script/write.tcl new file mode 100644 index 0000000000..1ddf8cd24d --- /dev/null +++ b/gnuk/tool/openocd-script/write.tcl @@ -0,0 +1,5 @@ +init +reset +halt +flash write_image erase gnuk.elf +shutdown diff --git a/gnuk/tool/pageant_proxy_to_gpg.py b/gnuk/tool/pageant_proxy_to_gpg.py new file mode 100644 index 0000000000..a755efcd28 --- /dev/null +++ b/gnuk/tool/pageant_proxy_to_gpg.py @@ -0,0 +1,205 @@ +""" +pagent_proxy_to_gpg.py - Connect gpg-agent as Pagent + +Copyright (C) 2013 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 . +""" + +import os, sys, re, hashlib, binascii +from struct import * +from gpg_agent import gpg_agent +from sexp import sexp + +# Assume it's only OPENPGP.3 key and it's 2048-bit + +def debug(string): + print "DEBUG: %s" % string + sys.stdout.flush() + +def get_keygrip_list(keyinfo_result): + kl_str = keyinfo_result[0:-1] # Chop last newline + kl = kl_str.split('\n') + # filter by "OPENPGP.3", and only keygrip + return [kg.split(' ')[0] for kg in kl if re.search("OPENPGP\\.3", kg)] + +# Connect GPG-Agent, and get list of KEYGRIPs. +g = gpg_agent() +g.read_line() # Greeting message + +g.send_command('KEYINFO --list --data\n') +keyinfo_result = g.get_response() +keygrip_list = get_keygrip_list(keyinfo_result) + +debug(keygrip_list) + +keylist = [] +# For each KEYGRIP, get its PUBLIC-KEY. +for kg in keygrip_list: + g.send_command('READKEY %s\n' % kg) + key = sexp(g.get_response()) + # [ "public-key" [ "rsa" [ "n" MODULUS ] [ "e" EXPONENT] ] ] + n = key[1][1][1] + e = key[1][2][1] + debug(binascii.hexlify(n)) + debug(binascii.hexlify(e)) + keylist.append([n, e, kg]) + +# FIXME: should handle all keys, not only a single key +# FIXME: should support different key size +n = keylist[0][0] +e = keylist[0][1] +keygrip = keylist[0][2] + +ssh_rsa_public_blob = "\x00\x00\x00\x07ssh-rsa" + \ + "\x00\x00\x00\x03" + e + "\x00\x00\x01\x01" + n + +ssh_key_comment = "key_on_gpg" # XXX: get login from card for comment? + +import win32con, win32api, win32gui, ctypes, ctypes.wintypes + + +# For WM_COPYDATA structure +class COPYDATA(ctypes.Structure): + _fields_ = [ ('dwData', ctypes.wintypes.LPARAM), + ('cbData', ctypes.wintypes.DWORD), + ('lpData', ctypes.c_void_p) ] + +P_COPYDATA = ctypes.POINTER(COPYDATA) + +class SSH_MSG_HEAD(ctypes.BigEndianStructure): + _pack_ = 1 + _fields_ = [ ('msg_len', ctypes.c_uint32), + ('msg_type', ctypes.c_byte) ] + +P_SSH_MSG_HEAD = ctypes.POINTER(SSH_MSG_HEAD) + +class SSH_MSG_ID_ANSWER_HEAD(ctypes.BigEndianStructure): + _pack_ = 1 + _fields_ = [ ('msg_len', ctypes.c_uint32), + ('msg_type', ctypes.c_byte), + ('keys', ctypes.c_uint32)] + +P_SSH_MSG_ID_ANSWER = ctypes.POINTER(SSH_MSG_ID_ANSWER_HEAD) + +class SSH_MSG_SIGN_RESPONSE_HEAD(ctypes.BigEndianStructure): + _pack_ = 1 + _fields_ = [ ('msg_len', ctypes.c_uint32), + ('msg_type', ctypes.c_byte), + ('sig_len', ctypes.c_uint32)] + +P_SSH_MSG_SIGN_RESPONSE = ctypes.POINTER(SSH_MSG_SIGN_RESPONSE_HEAD) + + +FILE_MAP_ALL_ACCESS=0x000F001F + +class windows_ipc_listener(object): + def __init__(self): + message_map = { win32con.WM_COPYDATA: self.OnCopyData } + wc = win32gui.WNDCLASS() + wc.lpfnWndProc = message_map + wc.lpszClassName = 'Pageant' + hinst = wc.hInstance = win32api.GetModuleHandle(None) + classAtom = win32gui.RegisterClass(wc) + self.hwnd = win32gui.CreateWindow ( + classAtom, + "Pageant", + 0, + 0, + 0, + win32con.CW_USEDEFAULT, + win32con.CW_USEDEFAULT, + 0, + 0, + hinst, + None + ) + debug("created: window=%08x" % self.hwnd) + + def OnCopyData(self, hwnd, msg, wparam, lparam): + debug("WM_COPYDATA message") + debug(" window=%08x" % hwnd) + debug(" msg =%08x" % msg) + debug(" wparam=%08x" % wparam) + pCDS = ctypes.cast(lparam, P_COPYDATA) + debug(" dwData=%08x" % (pCDS.contents.dwData & 0xffffffff)) + debug(" len=%d" % pCDS.contents.cbData) + mapname = ctypes.string_at(pCDS.contents.lpData) + debug(" mapname='%s'" % ctypes.string_at(pCDS.contents.lpData)) + hMapObject = ctypes.windll.kernel32.OpenFileMappingA(FILE_MAP_ALL_ACCESS, 0, mapname) + if hMapObject == 0: + debug("error on OpenFileMapping") + return 0 + pBuf = ctypes.windll.kernel32.MapViewOfFile(hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, 0) + if pBuf == 0: + ctypes.windll.kernel32.CloseHandle(hMapObject) + debug("error on MapViewOfFile") + return 0 + pSshMsg = ctypes.cast(pBuf, P_SSH_MSG_HEAD) + debug(" ssh_msg_len: %d" % pSshMsg.contents.msg_len) + debug(" ssh_msg_type: %d" % pSshMsg.contents.msg_type) + if pSshMsg.contents.msg_type == 11: # SSH2_AGENT_REQUEST_IDENTITIES + blob_len = len(ssh_rsa_public_blob) + cmnt_len = len(ssh_key_comment) + pAns = ctypes.cast(pBuf, P_SSH_MSG_ID_ANSWER) + pAns.contents.msg_len = 1+4+4+blob_len+4+cmnt_len + pAns.contents.msg_type = 12 # SSH2_AGENT_IDENTITIES_ANSWER + pAns.contents.keys = 1 + ctypes.memmove(pBuf+4+1+4, pack('>I', blob_len), 4) + ctypes.memmove(pBuf+4+1+4+4, ssh_rsa_public_blob, blob_len) + ctypes.memmove(pBuf+4+1+4+4+blob_len, pack('>I', cmnt_len), 4) + ctypes.memmove(pBuf+4+1+4+4+blob_len+4, ssh_key_comment, cmnt_len) + + debug("answer is:") + debug(" ssh_msg_len: %d" % pSshMsg.contents.msg_len) + debug(" ssh_msg_type: %d" % pSshMsg.contents.msg_type) + elif pSshMsg.contents.msg_type == 13: # SSH2_AGENT_SIGN_REQUEST + req_blob_len = unpack(">I", ctypes.string_at(pBuf+5, 4))[0] + req_blob = ctypes.string_at(pBuf+5+4, req_blob_len) + req_data_len = unpack(">I", ctypes.string_at(pBuf+5+4+req_blob_len,4))[0] + req_data = ctypes.string_at(pBuf+5+4+req_blob_len+4,req_data_len) + debug(" blob_len=%d" % req_blob_len) + debug(" data_len=%d" % req_data_len) + hash = hashlib.sha1(req_data).hexdigest() + debug(" hash=%s" % hash) + g.send_command('SIGKEY %s\n' % keygrip) + g.send_command('SETHASH --hash=sha1 %s\n' % hash) + g.send_command('PKSIGN\n') + sig = sexp(g.get_response()) + # [ "sig-val" [ "rsa" [ "s" "xxx" ] ] ] + sig = sig[1][1][1] + sig = "\x00\x00\x00\x07" + "ssh-rsa" + "\x00\x00\x01\x00" + sig # FIXME: should support different key size + siglen = len(sig) + debug("sig_len=%d" % siglen) + debug("sig=%s" % binascii.hexlify(sig)) + pRes = ctypes.cast(pBuf, P_SSH_MSG_SIGN_RESPONSE) + pRes.contents.msg_len = 1+4+siglen + pRes.contents.msg_type = 14 # SSH2_AGENT_SIGN_RESPONSE + pRes.contents.sig_len = siglen + ctypes.memmove(pBuf+4+1+4, sig, siglen) + debug("answer is:") + debug(" ssh_msg_len: %d" % pSshMsg.contents.msg_len) + debug(" ssh_msg_type: %d" % pSshMsg.contents.msg_type) + else: + exit(0) + ctypes.windll.kernel32.UnmapViewOfFile(pBuf) + ctypes.windll.kernel32.CloseHandle(hMapObject) + debug(" ssh_msg: done") + return 1 + +l = windows_ipc_listener() +win32gui.PumpMessages() diff --git a/gnuk/tool/pinpadtest.py b/gnuk/tool/pinpadtest.py new file mode 100644 index 0000000000..99b1326360 --- /dev/null +++ b/gnuk/tool/pinpadtest.py @@ -0,0 +1,440 @@ +#! /usr/bin/python + +""" +pinpadtest.py - a tool to test variable length pin entry with pinpad + +Copyright (C) 2011, 2012, 2013 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 . +""" + +import sys + +# Assume only single CCID device is attached to computer with card + +from smartcard.CardType import AnyCardType +from smartcard.CardRequest import CardRequest +from smartcard.util import toHexString + +from getpass import getpass + +CM_IOCTL_GET_FEATURE_REQUEST = (0x42000000 + 3400) +CM_IOCTL_VENDOR_IFD_EXCHANGE = (0x42000000 + 1) +FEATURE_VERIFY_PIN_DIRECT = 0x06 +FEATURE_MODIFY_PIN_DIRECT = 0x07 + +BY_ADMIN = 3 +BY_USER = 1 +PIN_MIN_DEFAULT = 6 # min of OpenPGP card +PIN_MAX_DEFAULT = 15 # max of VASCO DIGIPASS 920 + +def s2l(s): + return [ ord(c) for c in s ] + +def confirm_pin_setting(single_step): + if single_step: + return 0x01 # bConfirmPIN: new PIN twice + else: + return 0x03 # bConfirmPIN: old PIN and new PIN twice + +class Card(object): + def __init__(self, add_a_byte, pinmin, pinmax, fixed): + cardtype = AnyCardType() + cardrequest = CardRequest(timeout=10, cardType=cardtype) + cardservice = cardrequest.waitforcard() + self.connection = cardservice.connection + self.verify_ioctl = -1 + self.modify_ioctl = -1 + self.another_byte = add_a_byte + self.pinmin = pinmin + self.pinmax = pinmax + self.fixed = fixed + + def get_features(self): + p = self.connection.control(CM_IOCTL_GET_FEATURE_REQUEST, []) + i = 0 + while i < len(p): + code = p[i] + l = p[i+1] + i = i + 2 + if l == 4: + ioctl = (p[i] << 24) | (p[i+1] << 16) | (p[i+2] << 8) | p[i+3] + i = i + l + else: + i = i + l + continue + if code == FEATURE_VERIFY_PIN_DIRECT: + self.verify_ioctl = ioctl + elif code == FEATURE_MODIFY_PIN_DIRECT: + self.modify_ioctl = ioctl + if self.verify_ioctl == -1: + raise ValueError, "Not supported" + + def cmd_select_openpgp(self): + apdu = [0x00, 0xa4, 0x04, 0x00, 6, 0xd2, 0x76, 0x00, 0x01, 0x24, 0x01 ] + response, sw1, sw2 = self.connection.transmit(apdu) + if sw1 == 0x61: # More data + response, sw1, sw2 = self.connection.transmit([0x00, 0xc0, 0, 0, sw2]) + elif not (sw1 == 0x90 and sw2 == 0x00): + raise ValueError, ("cmd_select_openpgp %02x %02x" % (sw1, sw2)) + + def possibly_add_dummy_byte(self): + if self.another_byte: + return [ 0 ] + else: + return [] + + def cmd_vega_alpha_disable_empty_verify(self): + apdu = [ 0xB5, # -| + 0x01, # | Pre-command parameters + 0x00, # -| + 0x03, # retry counter value (fixed value) + 0x00 # enable 3s timeout + ] + data = self.connection.control(CM_IOCTL_VENDOR_IFD_EXCHANGE, apdu) + + def cmd_verify_pinpad(self, who): + apdu = [0x00, 0x20, 0x00, 0x80+who ] + pin_verify = [ 0x00, # bTimeOut + 0x00, # bTimeOut2 + 0x82, # bmFormatString: Byte, pos=0, left, ASCII. + self.fixed, # bmPINBlockString + 0x00, # bmPINLengthFormat + self.pinmax, # wPINMaxExtraDigit Low (PINmax) + self.pinmin, # wPINMaxExtraDigit High (PINmin) + 0x02, # bEntryValidationCondition + 0x01, # bNumberMessage + 0x00, # wLangId Low + 0x00, # wLangId High + 0x00, # bMsgIndex + 0x00, # bTeoPrologue[0] + 0x00, # bTeoPrologue[1] + ] + if self.fixed > 0: + apdu += [ self.fixed ] + apdu += [ 255 ] * self.fixed + else: + apdu += self.possibly_add_dummy_byte() + pin_verify += [ len(apdu) ] # bTeoPrologue[2] + pin_verify += [ len(apdu), 0, 0, 0 ] + apdu + data = self.connection.control(self.verify_ioctl,pin_verify) + sw1 = data[0] + sw2 = data[1] + if not (sw1 == 0x90 and sw2 == 0x00): + raise ValueError, ("cmd_verify_pinpad %02x %02x" % (sw1, sw2)) + + def send_modify_pinpad(self, apdu, single_step, command): + if self.modify_ioctl == -1: + raise ValueError, "Not supported" + pin_modify = [ 0x00, # bTimerOut + 0x00, # bTimerOut2 + 0x82, # bmFormatString: Byte, pos=0, left, ASCII. + self.fixed, # bmPINBlockString + 0x00, # bmPINLengthFormat + 0x00, # bInsertionOffsetOld + self.fixed, # bInsertionOffsetNew + self.pinmax, # wPINMaxExtraDigit Low (PINmax) + self.pinmin, # wPINMaxExtraDigit High (PINmin) + confirm_pin_setting(single_step), + 0x02, # bEntryValidationCondition + 0x03, # bNumberMessage + 0x00, # wLangId Low + 0x00, # wLangId High + 0x00, # bMsgIndex1 + 0x01, # bMsgIndex2 + 0x02, # bMsgIndex3 + 0x00, # bTeoPrologue[0] + 0x00, # bTeoPrologue[1] + ] + if self.fixed > 0: + apdu += [ 2*self.fixed ] + apdu += [ 255 ] * (2*self.fixed) + else: + apdu += self.possibly_add_dummy_byte() + pin_modify += [ len(apdu) ] # bTeoPrologue[2] + pin_modify += [ len(apdu), 0, 0, 0 ] + apdu + data = self.connection.control(self.modify_ioctl,pin_modify) + sw1 = data[0] + sw2 = data[1] + if not (sw1 == 0x90 and sw2 == 0x00): + raise ValueError, ("%s %02x %02x" % (command, sw1, sw2)) + + def cmd_reset_retry_counter(self, who, data): + if who == BY_ADMIN: + apdu = [0x00, 0x2c, 0x02, 0x81, len(data) ] + data # BY_ADMIN + else: + apdu = [0x00, 0x2c, 0x00, 0x81, len(data) ] + data # BY_USER with resetcode + response, sw1, sw2 = self.connection.transmit(apdu) + if not (sw1 == 0x90 and sw2 == 0x00): + raise ValueError, ("cmd_reset_retry_counter %02x %02x" % (sw1, sw2)) + + # Note: CCID specification doesn't permit this (only 0x20 and 0x24) + def cmd_reset_retry_counter_pinpad(self, who): + if who == BY_ADMIN: + apdu = [0x00, 0x2c, 0x02, 0x81] # BY_ADMIN + self.send_modify_pinpad(apdu, True, "cmd_reset_retry_counter_pinpad") + else: + apdu = [0x00, 0x2c, 0x00, 0x81] # BY_USER with resetcode + self.send_modify_pinpad(apdu, False, "cmd_reset_retry_counter_pinpad") + + def cmd_put_resetcode(self, data): + apdu = [0x00, 0xda, 0x00, 0xd3, len(data) ] + data # BY_ADMIN + response, sw1, sw2 = self.connection.transmit(apdu) + if not (sw1 == 0x90 and sw2 == 0x00): + raise ValueError, ("cmd_put_resetcode %02x %02x" % (sw1, sw2)) + + # Note: CCID specification doesn't permit this (only 0x20 and 0x24) + def cmd_put_resetcode_pinpad(self): + apdu = [0x00, 0xda, 0x00, 0xd3] + self.send_modify_pinpad(apdu, True, "cmd_put_resetcode_pinpad") + + def cmd_change_reference_data_pinpad(self, who, is_exchange): + if is_exchange: + apdu = [0x00, 0x24, 1, 0x80+who] + else: + apdu = [0x00, 0x24, 0x00, 0x80+who] + self.send_modify_pinpad(apdu, is_exchange, + "cmd_change_reference_data_pinpad") + +COVADIS_VEGA_ALPHA="COVADIS VEGA-ALPHA (000000F5) 00 00" +# We need to set ifdDriverOptions in /etc/libccid_Info.plist: +# +# ifdDriverOptions +# 0x0001 +# +# 1: DRIVER_OPTION_CCID_EXCHANGE_AUTHORIZED +# the CCID Exchange command is allowed. You can use it through +# SCardControl(hCard, IOCTL_SMARTCARD_VENDOR_IFD_EXCHANGE, ...) + +def main(who, method, add_a_byte, pinmin, pinmax, change_by_two_steps, fixed): + card = Card(add_a_byte, pinmin, pinmax, fixed) + card.connection.connect() + + ident = card.connection.getReader() + print "Reader/Token:", ident + print "ATR:", toHexString( card.connection.getATR() ) + + if ident == COVADIS_VEGA_ALPHA: + card.cmd_vega_alpha_disable_empty_verify() + + card.get_features() + + card.cmd_select_openpgp() + if method == "verify": + if who == BY_USER: + print "Please input User's PIN" + else: + print "Please input Admin's PIN" + card.cmd_verify_pinpad(who) + elif method == "change": + if change_by_two_steps: + if who == BY_USER: + print "Please input User's PIN" + else: + print "Please input Admin's PIN" + card.cmd_verify_pinpad(who) + if who == BY_USER: + print "Please input New User's PIN twice" + else: + print "Please input New Admin's PIN twice" + card.cmd_change_reference_data_pinpad(who, True) + else: + if who == BY_USER: + print "Please input User's PIN" + print "and New User's PIN twice" + else: + print "Please input Admin's PIN" + print "and New Admin's PIN twice" + card.cmd_change_reference_data_pinpad(who, False) + elif method == "unblock": + if change_by_two_steps: + # It means using keyboard for new PIN + if who == BY_USER: + resetcode=s2l(getpass("Please input reset code from keyboard: ")) + newpin=s2l(getpass("Please input New User's PIN from keyboard: ")) + card.cmd_reset_retry_counter(who,resetcode+newpin) + else: + print "Please input Admin's PIN" + card.cmd_verify_pinpad(BY_ADMIN) + newpin=s2l(getpass("Please input New User's PIN from keyboard: ")) + card.cmd_reset_retry_counter(who,newpin) + else: + if who == BY_USER: + print "Please input reset code" + print "and New User's PIN twice" + else: + print "Please input Admin's PIN" + card.cmd_verify_pinpad(BY_ADMIN) + print "Please input New User's PIN twice" + card.cmd_reset_retry_counter_pinpad(who) + elif method == "put": + if change_by_two_steps: + # It means using keyboard for new PIN + print "Please input Admin's PIN" + card.cmd_verify_pinpad(BY_ADMIN) + resetcode=s2l(getpass("Please input New Reset Code from keyboard: ")) + card.cmd_put_resetcode(resetcode) + else: + print "Please input Admin's PIN" + card.cmd_verify_pinpad(BY_ADMIN) + print "Please input New Reset Code twice" + card.cmd_put_resetcode_pinpad() + else: + raise ValueError, method + card.connection.disconnect() + + print "OK." + return 0 + +def print_usage(): + print "pinpad-test: testing pinentry of PC/SC card reader" + print " help:" + print "\t--help:\t\tthis message" + print " method:\t\t\t\t\t\t\t[verify]" + print "\t--verify:\tverify PIN" + print "\t--change:\tchange PIN (old PIN, new PIN twice)" + print "\t--change2:\tchange PIN by two steps (old PIN, new PIN twice)" + print "\t--unblock:\tunblock PIN (admin PIN/resetcode, new PIN twice)" + print "\t--unblock2:\tunblock PIN (admin PIN:pinpad, new PIN:kbd)" + print "\t--put:\t\tsetup resetcode (admin PIN, new PIN twice)" + print "\t--put2::\t\tsetup resetcode (admin PIN:pinpad, new PIN:kbd)" + print " options:" + print "\t--fixed N:\tUse fixed length input" + print "\t--admin:\tby administrator\t\t\t[False]" + print "\t--add:\t\tadd a dummy byte at the end of APDU\t[False]" + print "\t--pinmin:\tspecify minimum length of PIN\t\t[6]" + print "\t--pinmax:\tspecify maximum length of PIN\t\t[15]" + print "EXAMPLES:" + print " $ pinpad-test # verify user's PIN " + print " $ pinpad-test --admin # verify admin's PIN " + print " $ pinpad-test --change # change user's PIN " + print " $ pinpad-test --change --admin # change admin's PIN " + print " $ pinpad-test --change2 # change user's PIN by two steps" + print " $ pinpad-test --change2 --admin # change admin's PIN by two steps" + print " $ pinpad-test --unblock # change user's PIN by reset code" + print " $ pinpad-test --unblock --admin # change user's PIN by admin's PIN" + print " $ pinpad-test --put # setup resetcode " + +if __name__ == '__main__': + who = BY_USER + method = "verify" + add_a_byte = False + pinmin = PIN_MIN_DEFAULT + pinmax = PIN_MAX_DEFAULT + change_by_two_steps = False + fixed=0 + while len(sys.argv) >= 2: + option = sys.argv[1] + sys.argv.pop(1) + if option == '--admin': + who = BY_ADMIN + elif option == '--change': + method = "change" + elif option == '--change2': + method = "change" + change_by_two_steps = True + elif option == '--unblock': + method = "unblock" + elif option == '--unblock2': + method = "unblock" + change_by_two_steps = True + elif option == '--add': + add_a_byte = True + elif option == '--fixed': + fixed = int(sys.argv[1]) + sys.argv.pop(1) + elif option == '--pinmin': + pinmin = int(sys.argv[1]) + sys.argv.pop(1) + elif option == '--pinmax': + pinmax = int(sys.argv[1]) + sys.argv.pop(1) + elif option == '--put': + method = "put" + elif option == '--put2': + method = "put" + change_by_two_steps = True + elif option == "verify": + method = "verify" + elif option == '--help': + print_usage() + exit(0) + else: + raise ValueError, option + main(who, method, add_a_byte, pinmin, pinmax, change_by_two_steps, fixed) + +# Failure +# 67 00: Wrong length; no further indication +# 69 82: Security status not satisfied: pin doesn't match +# 69 85: Conditions of use not satisfied +# 6b 00: Wrong parameters P1-P2 +# 6b 80 +# 64 02: PIN different + +# General +# OpenPGP card v2 doesn't support CHANGE REFERENCE DATA in exchanging +# mode (with P1 == 01, replacing PIN). +# FAIL: --change2 fails with 6b 00 (after input of PIN) +# FAIL: --change2 --admin fails with 6b 00 (after input of PIN) + +# "FSIJ Gnuk (0.16-34006F06) 00 00" +# Works well except --change2 +# It could support --put and --unblock, but currently it's disabled. + +# "Vasco DIGIPASS 920 [CCID] 00 00" +# OK: --verify +# OK: --verify --admin +# OK: --change +# OK: --change --admin +# OK: --unblock +# FAIL: --unblock --admin fails with 69 85 (after input of PIN) +# FAIL: --put fails with 6b 80 (before input of resetcode) +# OK: --put2 +# FAIL: --unblock2 fails with 69 85 +# FAIL: --unblock2 --admin fails with 69 85 (after input of PIN) + +# 0c4b:0500 Reiner SCT Kartensysteme GmbH +# "REINER SCT cyberJack RFID standard (7592671050) 00 00" +# OK: --verify +# OK: --verify --admin +# OK: --change +# OK: --change --admin +# OK: --unblock +# OK: --unblock --admin +# FAIL: --put fails with 69 85 + +# Gemalto GemPC Pinpad 00 00 +# It asks users PIN with --add but it results 67 00 +# It seems that it doesn't support variable length PIN +# Firmware version: GemTwRC2-V2.10-GL04 + +# 072f:90d2 Advanced Card Systems, Ltd +# ACS ACR83U 01 00 +# --verify failed with 6b 80 + +# 08e6:34c2 Gemplus +# Gemalto Ezio Shield PinPad 01 00 +# works well +# FAIL: --unblock2 fails with 6d 00 + +# 076b:3821 OmniKey AG CardMan 3821 +# OmniKey CardMan 3821 01 00 +# Works well with --pinmax 31 --pinmin 1 + +# 046a:003e Cherry GmbH SmartTerminal ST-2xxx +# Cherry ST-2000 +# Needs --add to function properly diff --git a/gnuk/tool/rsa.py b/gnuk/tool/rsa.py new file mode 100644 index 0000000000..418f6f8edc --- /dev/null +++ b/gnuk/tool/rsa.py @@ -0,0 +1,70 @@ +from binascii import hexlify, unhexlify +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) + +# 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 = b'\x00' + b'\x01' \ + + bytes.ljust(b'', 256 - 19 - 32 - 3, b'\xff') \ + + b'\x00' + digestinfo + return int(hexlify(byte_repr), 16) + +def compute_signature(key, digestinfo): + e = key[4] + p = key[5] + q = key[6] + n = key[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_256(i): + s = hex(i)[2:] + s = s.rstrip('L') + if len(s) & 1: + s = '0' + s + return bytes.rjust(unhexlify(s), 256, b'\x00') + +def get_raw_pubkey(key): + return key[0] diff --git a/gnuk/tool/rsa_example.key b/gnuk/tool/rsa_example.key new file mode 100644 index 0000000000..cdf2d5ecbd --- /dev/null +++ b/gnuk/tool/rsa_example.key @@ -0,0 +1,4 @@ +9cf7192b51a574d1ad3ccb08ba09b87f228573893eee355529ff243e90fd4b86f79a82097cc7922c0485bed1616b1656a9b0b19ef78ea8ec34c384019adc5d5bf4db2d2a0a2d9cf14277bdcb7056f48b81214e3f7f7742231e29673966f9b1106862112cc798dba8d4a138bb5abfc6d4c12d53a5d39b2f783da916da20852ee139bbafda61d429caf2a4f30847ce7e7ae32ab4061e27dd9e4d00d60910249db8d8559dd85f7ca59659ef400c8f6318700f4e97f0c6f4165de80641490433c88da8682befe68eb311f54af2b07d97ac74edb5399cf054764211694fbb8d1d333f3269f235abe025067f811ff83a2224826219b309ea3e6c968f42b3e52f245dc9 +010001 +b5ab7b159220b18e363258f61ebde08bae83d6ce2dbfe4adc143628c527887acde9de09bf9b49f438019004d71855f30c2d69b6c29bb9882ab641b3387409fe9199464a7faa4b5230c56d9e17cd9ed074bc00180ebed62bae3af28e6ff2ac2654ad968834c5d5c88f8d9d3cc5e167b10453b049d4e454a5761fb0ac717185907 +dd2fffa9814296156a6926cd17b65564187e424dcadce9b032246ad7e46448bb0f9e0ff3c64f987424b1a40bc694e2e9ac4fb1930d163582d7acf20653a1c44b97846c1c5fd8a7b19bb225fb39c30e25410483deaf8c2538d222b748c4d8103b11cec04f666a5c0dbcbf5d5f625f158f65746c3fafe6418145f7cffa5fadeeaf diff --git a/gnuk/tool/sexp.py b/gnuk/tool/sexp.py new file mode 100644 index 0000000000..a35e105eae --- /dev/null +++ b/gnuk/tool/sexp.py @@ -0,0 +1,86 @@ +# SEXP (S-expressions) Basic Transport Support +# +# See: http://people.csail.mit.edu/rivest/sexp.html +# +""" +sexp.py - a library for SEXP + +Copyright (C) 2013 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 . +""" + +import re + +WHITESPACE='[ \n\t\v\r\f]+' +re_ws = re.compile(WHITESPACE) +DIGITS='[0-9]+' +re_digit = re.compile(DIGITS) + +def skip_whitespace(string, pos): + m = re_ws.match(string, pos) + if m: + return m.start() + else: + return pos + +def sexp_match(string, ch, pos): + pos = skip_whitespace(string,pos) + if string[pos] == ch: + return pos+1 + else: + raise ValueError("expect '%s'" % ch) + +def sexp_parse_simple_string(string, pos): + pos = skip_whitespace(string,pos) + m = re_digit.match(string, pos) + if m: + length = int(string[m.start():m.end()],10) + pos = sexp_match(string, ':', m.end()) + return (string[pos:pos+length], pos+length) + else: + raise ValueError('expect digit') + +def sexp_parse_list(string,pos): + l = [] + while True: + pos = skip_whitespace(string,pos) + if string[pos] == ')': + return (l, pos) + else: + (sexp, pos) = sexp_parse(string,pos) + l.append(sexp) + +def sexp_parse(string, pos=0): + pos = skip_whitespace(string,pos) + if string[pos] == '(': + (l, pos) = sexp_parse_list(string,pos+1) + pos = sexp_match(string, ')', pos) + return (l, pos) + elif string[pos] == '[': + pos = skip_whitespace(string,pos) + (dsp, pos) = sexp_parse_simple_string(string,pos+1) + pos = sexp_match(string, ']', pos) + pos = skip_whitespace(string,pos) + (ss, pos) = sexp_parse_simple_string(string, pos) + return ((dsp, ss), pos) + else: + return sexp_parse_simple_string(string, pos) + +def sexp(string): + (sexp, pos) = sexp_parse(string) + return sexp diff --git a/gnuk/tool/stlinkv2.py b/gnuk/tool/stlinkv2.py new file mode 100644 index 0000000000..a169ff5169 --- /dev/null +++ b/gnuk/tool/stlinkv2.py @@ -0,0 +1,773 @@ +#! /usr/bin/python + +""" +stlinkv2.py - a tool to control ST-Link/V2 + +Copyright (C) 2012, 2013, 2015 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 . +""" + +from struct import * +import sys, time +import usb +from colorama import init as colorama_init, Fore, Back, Style +from array import array + +# INPUT: binary file + +# Assumes only single ST-Link/V2 device is attached to computer. + +CORE_ID_CORTEX_M3=0x1ba01477 +CORE_ID_CORTEX_M0=0x0bb11477 + +CHIP_ID_STM32F103xB=0x20036410 +CHIP_ID_STM32F103xE=0x10016414 +# CHIP_ID_STM32F0 0x20006440 +# CHIP_ID_STM32F030?? 0x10006444; FSM-55 + +GPIOA=0x40010800 +GPIOB=0x40010C00 +OPTION_BYTES_ADDR=0x1ffff800 +RDP_KEY_F1=0x00a5 # Unlock readprotection +RDP_KEY_F0=0x00aa # 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) + +prog_flash_write_body = b"\x0D\x4A\x0B\x48\x0B\x49\x09\x4C\x01\x25\x14\x26\x00\x27\x25\x61" + \ + b"\xC3\x5B\xCB\x53\xE3\x68\x2B\x42\xFC\xD1\x33\x42\x02\xD1\x02\x37\x97\x42\xF5\xD1" + \ + b"\x00\x27\x27\x61\x00\xBE\xC0\x46\x00\x20\x02\x40\x3C\x00\x00\x20" +# .SRC_ADDR: 0x2000003C +## HERE comes: target_addr in 4-byte +# .TARGET_ADDR +## HERE comes: size in 4-byte +# .SIZE + +def gen_prog_flash_write(addr,size): + return prog_flash_write_body + pack("> 12) & 0x0f, (val >> 6) & 0x3f, val & 0x3f) + + # For FST-01-00 and FST-01: LED on, USB connect + 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, 0xffffffff) # 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, 0x88888888) # CRH + self.write_memory_u32(GPIOB+0x00, 0x88888883) # CRL + + # For FST-01-00 and FST-01: LED on, USB disconnect + def usb_disconnect(self): + self.write_memory_u32(GPIOA+0x0c, 0xfffffbff) # ODR + + # For FST-01-00 and FST-01: LED off, USB connect + def finish_gpio(self): + self.write_memory_u32(GPIOA+0x0c, 0xfffffeff) # ODR + self.write_memory_u32(GPIOB+0x0c, 0xfffffffe) # ODR + apb2enr = self.read_memory_u32(0x40021018) + apb2enr = apb2enr & ~(4 | 8 | 0x1000) + self.write_memory_u32(0x40021018, apb2enr) # RCC->APB2ENR + + 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(self.flash_size) + 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 OperationFailure("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:]) > self.flash_block_size: + size = self.flash_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 OperationFailure("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 OperationFailure("flash page erase") + + def start(self): + mode = self.stl_mode() + if mode == 2: + self.exit_from_debug_swd() + elif mode == 5: + self.exit_from_debug_swim() + elif mode != 1 and mode != 4: + self.exit_from_dfu() + new_mode = self.stl_mode() + print("Change ST-Link/V2 mode %04x -> %04x" % (mode, new_mode)) + self.control_nrst(2) + self.enter_swd() + s = self.get_status() + if s != 0x0080: + print("Status is %04x" % s) + self.run() + s = self.get_status() + if s != 0x0080: + # DCB_DHCSR DBGKEY + self.write_debug_reg(0xE000EDF0, 0xA05F0000) + 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) + + self.core_id = self.get_core_id() + if self.core_id == CORE_ID_CORTEX_M3: + self.chip_stm32 = True + elif self.core_id == CORE_ID_CORTEX_M0: + self.chip_stm32 = False + else: + raise ValueError("Unknown core ID", self.core_id) + if self.chip_stm32: + self.rdp_key = RDP_KEY_F1 + self.flash_block_size = FLASH_BLOCK_SIZE_F1 + self.require_nrst = False + self.external_spi_flash = True + self.protection_feature = True + else: + self.rdp_key = RDP_KEY_F0 + self.flash_block_size = FLASH_BLOCK_SIZE_F0 + self.require_nrst = True + self.external_spi_flash = False + self.protection_feature = False + return self.core_id + + def get_chip_id(self): + if self.chip_stm32: + self.chip_id = self.read_memory_u32(0xE0042000) + self.flash_size = (self.read_memory_u32(0x1ffff7e0) & 0xffff)*1024 + if self.chip_id == CHIP_ID_STM32F103xB: + pass + elif self.chip_id == CHIP_ID_STM32F103xE: + pass + else: + raise ValueError("Unknown chip ID", self.chip_id) + else: + self.chip_id = self.read_memory_u32(0x40015800) + self.flash_size = (self.read_memory_u32(0x1ffff7cc) & 0xffff)*1024 + print("Flash size: %dKiB" % (self.flash_size/1024)) + return self.chip_id + + def get_rdp_key(self): + return self.rdp_key + + def has_spi_flash(self): + return self.external_spi_flash + + def has_protection(self): + return self.protection_feature + + +USB_VENDOR_ST=0x0483 # 0x0483 SGS Thomson Microelectronics +USB_VENDOR_STLINKV2=0x3748 # 0x3748 ST-LINK/V2 +USB_VENDOR_STLINKV2_1=0x374b # 0x374b ST-LINK/V2_1 + +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 and dev.idProduct != USB_VENDOR_STLINKV2_1: + continue + yield dev + +def compare(data_original, data_in_device): + i = 0 + for d in data_original: + if 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) + + print("ST-Link/V2 version info: %d %d %d" % stl.version()) + core_id = stl.start() + + stl.control_nrst(2) + stl.enter_debug() + status = stl.get_status() + if status != 0x0081: + print("Core does not halt, try API V2 halt.") + # DCB_DHCSR DBGKEY|C_HALT|C_DEBUGEN + stl.write_debug_reg(0xE000EDF0, 0xA05F0003) + status = stl.get_status() + stl.write_debug_reg(0xE000EDF0, 0xA05F0003) + status = stl.get_status() + if status != 0x0081: + raise ValueError("Status of core is not halt.", status) + + chip_id = stl.get_chip_id() + + # FST-01 chip id: 0x20036410 + print("CORE: %08x, CHIP_ID: %08x" % (core_id, chip_id)) + protection = stl.protection() + print("Flash ROM read protection: " + ("ON" if protection else "off")) + option_bytes = stl.option_bytes_read() + print("Option bytes: %08x" % option_bytes) + rdp_key = stl.get_rdp_key() + if (option_bytes & 0xff) == rdp_key: + ob_protection_enable = False + else: + ob_protection_enable = True + + 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: + stl.reset_sys() + 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: + if option_bytes != 0xff: + stl.reset_sys() + stl.option_bytes_erase() + stl.reset_sys() + stl.option_bytes_write(OPTION_BYTES_ADDR,rdp_key) + stl.usb_disconnect() + time.sleep(0.100) + stl.finish_gpio() + print("Flash ROM read protection disabled. Reset the board, now.") + return 0 + + if spi_flash_check and stl.has_spi_flash(): + 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.usb_disconnect() + time.sleep(0.100) + stl.finish_gpio() + return 0 + + time.sleep(0.100) + + print("WRITE") + stl.flash_write(0x08000000, data) + + print("VERIFY") + data_received = array('B') + size = len(data) + off = 0 + while size > 0: + if size > 1024: + blk_size = 1024 + else: + blk_size = size + data_received = data_received + array('B', stl.read_memory(0x08000000+off, blk_size)) + size = size - blk_size + off = off + blk_size + compare(array('B', data), data_received) + + if not no_protect and stl.has_protection(): + print("PROTECT") + stl.option_bytes_erase() + print("Flash ROM read protection enabled. Reset the board to enable protection.") + + if reset_after_successful_write: + stl.control_nrst(2) + stl.usb_disconnect() + 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() + if len(data) % 1: + raise ValueError("The size of file should be even") + 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) diff --git a/gnuk/tool/upgrade_by_passwd.py b/gnuk/tool/upgrade_by_passwd.py new file mode 100644 index 0000000000..f76f9e2aef --- /dev/null +++ b/gnuk/tool/upgrade_by_passwd.py @@ -0,0 +1,139 @@ +#! /usr/bin/python + +""" +upgrade_by_passwd.py - a tool to install another firmware for Gnuk Token + which is just shipped from factory + +Copyright (C) 2012, 2013, 2015 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 . +""" + +from gnuk_token import * +import sys, binascii, time, os +import rsa + +DEFAULT_PW3 = "12345678" +BY_ADMIN = 3 + +KEYNO_FOR_AUTH=2 + +def main(wait_e, keyno, passwd, data_regnual, data_upgrade): + l = len(data_regnual) + if (l & 0x03) != 0: + data_regnual = data_regnual.ljust(l + 4 - (l & 0x03), chr(0)) + crc32code = crc32(data_regnual) + print("CRC32: %04x\n" % crc32code) + data_regnual += pack(' 3: + option = sys.argv[1] + sys.argv.pop(1) + if option == '-f': # F for Factory setting + passwd = DEFAULT_PW3 + elif option == '-e': # E for Enumeration + wait_e = int(sys.argv[1]) + sys.argv.pop(1) + elif option == '-k': # K for Key number + keyno = int(sys.argv[1]) + sys.argv.pop(1) + else: + raise ValueError("unknown option", option) + if not passwd: + passwd = getpass("Admin password: ") + filename_regnual = sys.argv[1] + filename_upgrade = sys.argv[2] + if not filename_regnual.endswith('bin') or not filename_upgrade.endswith('bin'): + print("Both input files must be in binary format (*.bin)!") + exit(1) + f = open(filename_regnual,"rb") + data_regnual = f.read() + f.close() + print("%s: %d" % (filename_regnual, len(data_regnual))) + f = open(filename_upgrade,"rb") + data_upgrade = f.read() + f.close() + print("%s: %d" % (filename_upgrade, len(data_upgrade))) + # First 4096-byte in data_upgrade is SYS, so, skip it. + main(wait_e, keyno, passwd, data_regnual, data_upgrade[4096:]) diff --git a/gnuk/tool/usb_strings.py b/gnuk/tool/usb_strings.py new file mode 100644 index 0000000000..a8c3748b05 --- /dev/null +++ b/gnuk/tool/usb_strings.py @@ -0,0 +1,46 @@ +#! /usr/bin/python + +""" +usb_strings.py - a tool to dump USB string + +Copyright (C) 2012, 2015, 2017 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 . +""" + +from gnuk_token import * +import usb, sys + +field = ['', 'Vendor', 'Product', 'Serial', 'Revision', 'Config', 'Sys', 'Board'] + +def main(n): + for dev in gnuk_devices_by_vidpid(): + handle = dev.open() + try: + for i in range(1,n): + s = handle.getString(i, 512) + print("%10s: %s" % (field[i], s.decode('UTF-8'))) + except: + pass + del dev + +if __name__ == '__main__': + if len(sys.argv) > 1: + n = int(sys.argv[1]) + else: + n = 8 # Gnuk has eight strings + main(n)