/* Copyright 2017 Cedric Mesnil , Ledger SAS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "os.h" #include "cx.h" #include "gpg_types.h" #include "gpg_api.h" #include "gpg_vars.h" #include "gpg_ux_nanos.h" gpg_pin_t *gpg_pin_get_pin(int id) { switch (id) { case PIN_ID_PW1 : return &N_gpg_pstate->PW1; case PIN_ID_PW2 : return &N_gpg_pstate->PW2; case PIN_ID_PW3: return &N_gpg_pstate->PW3; case PIN_ID_RC: return &N_gpg_pstate->RC; } return NULL; } static int gpg_pin_get_index(unsigned int id) { switch (id) { case PIN_ID_PW1 : return 1; case PIN_ID_PW2 : return 2; case PIN_ID_PW3 : return 3; case PIN_ID_RC : return 4; } return -1; } void gpg_pin_sync12() { gpg_pin_t *pin1, *pin2; pin1 = gpg_pin_get_pin(PIN_ID_PW1); pin2 = gpg_pin_get_pin(PIN_ID_PW2); if (os_memcmp(pin1, pin2, sizeof(gpg_pin_t))) { pin1 = gpg_pin_get_pin(PIN_ID_PW1); pin2 = gpg_pin_get_pin(PIN_ID_PW2); gpg_nvm_write(pin2, pin1, sizeof(gpg_pin_t)); } } static int gpg_pin_check_internal(gpg_pin_t *pin, unsigned char *pin_val, int pin_len) { cx_sha256_t sha256; unsigned int counter; gpg_pin_t *brother; brother = NULL; if (pin->ref == PIN_ID_PW1) { brother = gpg_pin_get_pin(PIN_ID_PW2); } else if (pin->ref == PIN_ID_PW2) { brother = gpg_pin_get_pin(PIN_ID_PW1); } if (pin->counter == 0) { return SW_PIN_BLOCKED; } counter = pin->counter-1; gpg_nvm_write(&(pin->counter), &counter, sizeof(int)); if (brother) { gpg_nvm_write(&(brother->counter), &counter, sizeof(int)); } cx_sha256_init(&sha256); cx_hash((cx_hash_t*)&sha256, CX_LAST, pin_val, pin_len, NULL); if (os_memcmp(sha256.acc, pin->value, 32)) { return SW_SECURITY_STATUS_NOT_SATISFIED; } counter = 3; gpg_nvm_write(&(pin->counter), &counter, sizeof(int)); if (brother) { gpg_nvm_write(&(brother->counter), &counter, sizeof(int)); } return SW_OK; } static void gpg_pin_check_throw(gpg_pin_t *pin, unsigned char *pin_val, int pin_len) { int sw; gpg_pin_set_verified(pin,0); sw = gpg_pin_check_internal(pin,pin_val,pin_len); if (sw == SW_OK) { gpg_pin_set_verified(pin,1); return; } THROW(sw); } int gpg_pin_check(gpg_pin_t *pin, unsigned char *pin_val, unsigned int pin_len) { int sw; sw = gpg_pin_check_internal(pin,pin_val,pin_len); gpg_pin_set_verified(pin,0); if (sw == SW_OK) { gpg_pin_set_verified(pin,1); } return sw; } void gpg_pin_set(gpg_pin_t *pin, unsigned char *pin_val, unsigned int pin_len) { cx_sha256_t sha256; gpg_pin_t newpin; cx_sha256_init(&sha256); cx_hash((cx_hash_t*)&sha256, CX_LAST, pin_val, pin_len, newpin.value); newpin.length = pin_len; newpin.counter = 3; gpg_nvm_write(pin, &newpin, sizeof(gpg_pin_t)); gpg_pin_sync12(); } int gpg_pin_set_verified(gpg_pin_t *pin, int verified) { int idx; idx = gpg_pin_get_index(pin->ref); if (idx >= 0) { G_gpg_vstate.verified_pin[idx]=verified; return verified; } return 0; } int gpg_pin_is_verified(gpg_pin_t *pin) { int idx; idx = gpg_pin_get_index(pin->ref); if (idx >= 0) { return G_gpg_vstate.verified_pin[idx]; } return 0; } int gpg_pin_is_blocked(gpg_pin_t *pin) { return pin->counter == 0; } int gpg_apdu_verify() { gpg_pin_t *pin; pin = gpg_pin_get_pin(G_gpg_vstate.io_p2); if (pin == NULL) { THROW(SW_WRONG_DATA); return SW_WRONG_DATA; } gpg_pin_set_verified(pin,0); if (gpg_pin_is_blocked(pin)) { THROW(SW_PIN_BLOCKED); return SW_PIN_BLOCKED; } if (G_gpg_vstate.io_length == 0) { if (G_gpg_vstate.pinmode == PIN_MODE_SCREEN) { //Delegate pin check to ui gpg_io_discard(1); ui_menu_pinentry_display(0); return 0; } if (G_gpg_vstate.pinmode == PIN_MODE_CONFIRM) { //Delegate pin check to ui gpg_io_discard(1); ui_menu_pinconfirm_display(0); return 0; } if (G_gpg_vstate.pinmode == PIN_MODE_TRUST) { gpg_pin_set_verified(pin,1); gpg_io_discard(1); return SW_OK; } } gpg_pin_check_throw(pin, G_gpg_vstate.work.io_buffer+ G_gpg_vstate.io_offset, G_gpg_vstate.io_length); gpg_io_discard(1); return SW_OK; } int gpg_apdu_change_ref_data() { gpg_pin_t *pin; int len, newlen; pin = gpg_pin_get_pin(G_gpg_vstate.io_p2); if (pin == NULL) { THROW(SW_WRONG_DATA); return SW_WRONG_DATA; } gpg_pin_set_verified(pin,0); // --- RC pin --- if (pin->ref == PIN_ID_RC) { newlen = G_gpg_vstate.io_length; if (newlen == 0) { gpg_nvm_write(pin, NULL, sizeof(gpg_pin_t)); } else if ((newlen > GPG_MAX_PW_LENGTH) || (newlen < 8)) { THROW(SW_WRONG_DATA); return SW_WRONG_DATA; } else { gpg_pin_set(pin, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, newlen); } gpg_io_discard(1); return SW_OK; } // --- PW1/PW3 pin --- if (gpg_pin_is_blocked(pin)) { THROW(SW_PIN_BLOCKED); return SW_PIN_BLOCKED; } //avoid any-overflow whitout giving info if (G_gpg_vstate.io_length == 0) { if (G_gpg_vstate.pinmode != PIN_MODE_HOST) { //Delegate pin change to ui gpg_io_discard(1); ui_menu_pinentry_display(0); return 0; } } if (pin->length > G_gpg_vstate.io_length) { len = G_gpg_vstate.io_length; } else { len = pin->length; } gpg_pin_check_throw(pin, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, len); newlen = G_gpg_vstate.io_length-len; if ( (newlen > GPG_MAX_PW_LENGTH) || ((pin->ref == PIN_ID_PW1) && (newlen < 6)) || ((pin->ref == PIN_ID_PW3) && (newlen < 8)) ) { THROW(SW_WRONG_DATA); return SW_WRONG_DATA; } gpg_pin_set(pin, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset+len, newlen); gpg_io_discard(1); return SW_OK; } int gpg_apdu_reset_retry_counter() { gpg_pin_t *pin_pw1; gpg_pin_t *pin_pw3; gpg_pin_t *pin_rc; int rc_len, pw1_len; pin_pw1 = gpg_pin_get_pin(PIN_ID_PW1); pin_pw3 = gpg_pin_get_pin(PIN_ID_PW3); pin_rc = gpg_pin_get_pin(PIN_ID_RC); if (G_gpg_vstate.io_p1 == 2) { if (!gpg_pin_is_verified(pin_pw3)) { THROW(SW_SECURITY_STATUS_NOT_SATISFIED); return SW_SECURITY_STATUS_NOT_SATISFIED; } rc_len = 0; pw1_len = G_gpg_vstate.io_length; } else { //avoid any-overflow whitout giving info if (pin_rc->length > G_gpg_vstate.io_length) { rc_len = G_gpg_vstate.io_length; } else { rc_len = pin_rc->length; } pw1_len = G_gpg_vstate.io_length-rc_len; gpg_pin_check_throw(pin_rc, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, rc_len); } if ((pw1_len > GPG_MAX_PW_LENGTH) ||(pw1_len < 6)) { THROW(SW_WRONG_DATA); return SW_WRONG_DATA; } gpg_pin_set(pin_pw1, G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset+rc_len, pw1_len); gpg_io_discard(1); return SW_OK; }