You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
430 lines
8.2 KiB
C
430 lines
8.2 KiB
C
/* Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, 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"
|
|
|
|
void gpg_check_access_ins() {
|
|
unsigned int ref;
|
|
|
|
ref = (G_gpg_vstate.io_p1 << 8) | G_gpg_vstate.io_p2;
|
|
|
|
switch (G_gpg_vstate.io_ins) {
|
|
case INS_EXIT:
|
|
if (gpg_pin_is_verified(PIN_ID_PW2)) {
|
|
return;
|
|
}
|
|
break;
|
|
#ifdef GPG_LOG
|
|
#warning GPG_LOG activated
|
|
case INS_GET_LOG:
|
|
return;
|
|
#endif
|
|
|
|
case INS_SELECT:
|
|
return;
|
|
case INS_GET_DATA:
|
|
case INS_GET_NEXT_DATA:
|
|
return;
|
|
|
|
case INS_VERIFY:
|
|
return;
|
|
|
|
case INS_CHANGE_REFERENCE_DATA:
|
|
return;
|
|
|
|
case INS_RESET_RETRY_COUNTER:
|
|
if (gpg_pin_is_verified(PIN_ID_PW3) || gpg_pin_is_verified(PIN_ID_RC)) {
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case INS_PUT_DATA:
|
|
case INS_PUT_DATA_ODD:
|
|
return;
|
|
|
|
case INS_GEN_ASYM_KEYPAIR:
|
|
if (G_gpg_vstate.io_p1 == 0x81) {
|
|
return;
|
|
}
|
|
if (gpg_pin_is_verified(PIN_ID_PW3)) {
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case INS_MSE:
|
|
return;
|
|
|
|
case INS_PSO:
|
|
if ((ref == 0x9e9a) && gpg_pin_is_verified(PIN_ID_PW1)) {
|
|
// pso:sign
|
|
if (N_gpg_pstate->PW_status[0] == 0) {
|
|
gpg_pin_set_verified(PIN_ID_PW1, 0);
|
|
}
|
|
return;
|
|
}
|
|
if (((ref == 0x8086) || (ref == 0x8680)) && gpg_pin_is_verified(PIN_ID_PW2)) {
|
|
// pso:dec/enc
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case INS_INTERNAL_AUTHENTICATE:
|
|
if (gpg_pin_is_verified(PIN_ID_PW2)) {
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case INS_GET_CHALLENGE:
|
|
return;
|
|
|
|
case INS_TERMINATE_DF:
|
|
if (gpg_pin_is_verified(PIN_ID_PW3)) {
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case INS_ACTIVATE_FILE:
|
|
return;
|
|
|
|
default:
|
|
THROW(SW_INS_NOT_SUPPORTED);
|
|
break;
|
|
}
|
|
THROW(SW_CONDITIONS_NOT_SATISFIED);
|
|
}
|
|
|
|
void gpg_check_access_read_DO() {
|
|
unsigned int ref;
|
|
|
|
ref = (G_gpg_vstate.io_p1 << 8) | G_gpg_vstate.io_p2;
|
|
|
|
switch (ref) {
|
|
// ALWAYS
|
|
case 0x0101:
|
|
case 0x0102:
|
|
case 0x01F0:
|
|
case 0x01F1:
|
|
case 0x01F2:
|
|
case 0x01F8:
|
|
case 0x006E:
|
|
case 0x0065:
|
|
case 0x0073:
|
|
case 0x007A:
|
|
case 0x004F:
|
|
case 0x005E:
|
|
case 0x005B:
|
|
case 0x5F2D:
|
|
case 0x5F35:
|
|
case 0x5F50:
|
|
case 0x5F52:
|
|
case 0x7F21:
|
|
case 0x0093:
|
|
case 0x00C0:
|
|
case 0x00C1:
|
|
case 0x00C2:
|
|
case 0x00C3:
|
|
case 0x00C4:
|
|
case 0x00C5:
|
|
case 0x00C7:
|
|
case 0x00C8:
|
|
case 0x00C9:
|
|
case 0x00C6:
|
|
case 0x00CA:
|
|
case 0x00CD:
|
|
case 0x00CC:
|
|
case 0x00CE:
|
|
case 0x00CF:
|
|
case 0x00D0:
|
|
case 0x7F74:
|
|
case 0x7F66:
|
|
case 0x00D6:
|
|
case 0x00D7:
|
|
case 0x00D8:
|
|
return;
|
|
|
|
// PW2
|
|
case 0x0103:
|
|
if (gpg_pin_is_verified(PIN_ID_PW2)) {
|
|
return;
|
|
}
|
|
break;
|
|
|
|
// PW3
|
|
case 0x00B6:
|
|
case 0x00A4:
|
|
case 0x00B8:
|
|
case 0x0104:
|
|
if (gpg_pin_is_verified(PIN_ID_PW3)) {
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
|
|
THROW(SW_CONDITIONS_NOT_SATISFIED);
|
|
}
|
|
|
|
char debugbuff[5];
|
|
|
|
void gpg_check_access_write_DO() {
|
|
unsigned int ref;
|
|
gpg_pin_t * pin_pw2, *pin_pw3;
|
|
|
|
pin_pw2 = gpg_pin_get_pin(PIN_ID_PW2);
|
|
pin_pw3 = gpg_pin_get_pin(PIN_ID_PW3);
|
|
ref = (G_gpg_vstate.io_p1 << 8) | G_gpg_vstate.io_p2;
|
|
|
|
switch (ref) {
|
|
// PW2
|
|
case 0x0101:
|
|
case 0x0103:
|
|
case 0x01F2:
|
|
if (gpg_pin_is_verified(PIN_ID_PW2)) {
|
|
return;
|
|
}
|
|
break;
|
|
|
|
// PW3
|
|
case 0x3FFF: // only used for putkey under PW3 control
|
|
case 0x4f:
|
|
case 0x0102:
|
|
case 0x0104:
|
|
case 0x01F1:
|
|
case 0x01F8:
|
|
case 0x005E:
|
|
case 0x005B:
|
|
case 0x5F2D:
|
|
case 0x5F35:
|
|
case 0x5F50:
|
|
case 0x5F48:
|
|
case 0x7F21:
|
|
case 0x00B6:
|
|
case 0x00A4:
|
|
case 0x00B8:
|
|
case 0x00C1:
|
|
case 0x00C2:
|
|
case 0x00C3:
|
|
case 0x00C4:
|
|
case 0x00C5:
|
|
case 0x00C7:
|
|
case 0x00C8:
|
|
case 0x00C9:
|
|
case 0x00C6:
|
|
case 0x00CA:
|
|
case 0x00CB:
|
|
case 0x00CC:
|
|
case 0x00CD:
|
|
case 0x00CE:
|
|
case 0x00CF:
|
|
case 0x00D0:
|
|
case 0x00D1:
|
|
case 0x00D2:
|
|
case 0x00D3:
|
|
case 0x00D5:
|
|
case 0x00F4:
|
|
case 0x00D6:
|
|
case 0x00D7:
|
|
case 0x00D8:
|
|
if (gpg_pin_is_verified(PIN_ID_PW3)) {
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
THROW(SW_CONDITIONS_NOT_SATISFIED);
|
|
}
|
|
|
|
/* assume command is fully received */
|
|
int gpg_dispatch() {
|
|
unsigned int tag, t, l;
|
|
int sw;
|
|
|
|
if ((G_gpg_vstate.io_cla != 0x00) && (G_gpg_vstate.io_cla != 0x10) && (G_gpg_vstate.io_cla != 0xEF)) {
|
|
THROW(SW_CLA_NOT_SUPPORTED);
|
|
return SW_CLA_NOT_SUPPORTED;
|
|
}
|
|
|
|
tag = (G_gpg_vstate.io_p1 << 8) | G_gpg_vstate.io_p2;
|
|
|
|
switch (G_gpg_vstate.io_ins) {
|
|
#ifdef GPG_LOG
|
|
case INS_GET_LOG:
|
|
gpg_io_discard(1);
|
|
gpg_io_insert(G_gpg_vstate.log_buffer, 32);
|
|
return SW_OK;
|
|
#endif
|
|
|
|
/* --- SELECT --- */
|
|
case INS_SELECT:
|
|
sw = gpg_apdu_select();
|
|
return sw;
|
|
break;
|
|
|
|
/* --- ACTIVATE/TERMINATE FILE --- */
|
|
case INS_ACTIVATE_FILE:
|
|
gpg_io_discard(0);
|
|
if (N_gpg_pstate->histo[7] == STATE_TERMINATE) {
|
|
gpg_install(STATE_ACTIVATE);
|
|
}
|
|
return (SW_OK);
|
|
break;
|
|
|
|
case INS_TERMINATE_DF:
|
|
gpg_io_discard(0);
|
|
if (gpg_pin_is_verified(PIN_ID_PW3) || (N_gpg_pstate->PW3.counter == 0)) {
|
|
gpg_install(STATE_TERMINATE);
|
|
return (SW_OK);
|
|
break;
|
|
}
|
|
THROW(SW_CONDITIONS_NOT_SATISFIED);
|
|
break;
|
|
}
|
|
|
|
/* Other commands allowed if not terminated */
|
|
if (N_gpg_pstate->histo[7] != 0x07) {
|
|
THROW(SW_STATE_TERMINATED);
|
|
}
|
|
|
|
/* Process */
|
|
gpg_check_access_ins();
|
|
|
|
switch (G_gpg_vstate.io_ins) {
|
|
#ifdef GPG_DEBUG_APDU
|
|
case 0x42:
|
|
sw = debug_apdu();
|
|
break;
|
|
#endif
|
|
|
|
case INS_EXIT:
|
|
os_sched_exit(0);
|
|
sw = SW_OK;
|
|
break;
|
|
|
|
/* --- CHALLENGE --- */
|
|
case INS_GET_CHALLENGE:
|
|
sw = gpg_apdu_get_challenge();
|
|
break;
|
|
|
|
/* --- DATA --- */
|
|
|
|
case INS_SELECT_DATA:
|
|
if ((G_gpg_vstate.io_p1 > 2) || (G_gpg_vstate.io_p2 != 0x04)) {
|
|
THROW(SW_WRONG_P1P2);
|
|
}
|
|
gpg_io_fetch_tl(&t, &l);
|
|
if (t != 0x60) {
|
|
// TODO add l check
|
|
THROW(SW_DATA_INVALID);
|
|
}
|
|
gpg_io_fetch_tl(&t, &l);
|
|
if (t != 0x5C) {
|
|
// TODO add l check
|
|
THROW(SW_WRONG_DATA);
|
|
}
|
|
if (l == 1) {
|
|
tag = gpg_io_fetch_u8();
|
|
} else if (l == 2) {
|
|
tag = gpg_io_fetch_u16();
|
|
} else {
|
|
THROW(SW_WRONG_DATA);
|
|
}
|
|
sw = gpg_apdu_select_data(tag, G_gpg_vstate.io_p1);
|
|
break;
|
|
|
|
case INS_GET_DATA:
|
|
gpg_check_access_read_DO();
|
|
switch (tag) {
|
|
case 0x00B6:
|
|
case 0x00A4:
|
|
case 0x00B8:
|
|
sw = gpg_apdu_get_key_data(tag);
|
|
break;
|
|
default:
|
|
sw = gpg_apdu_get_data(tag);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case INS_GET_NEXT_DATA:
|
|
gpg_check_access_read_DO();
|
|
sw = gpg_apdu_get_next_data(tag);
|
|
break;
|
|
|
|
case INS_PUT_DATA_ODD:
|
|
case INS_PUT_DATA:
|
|
gpg_check_access_write_DO();
|
|
switch (tag) {
|
|
case 0x00B6:
|
|
case 0x00A4:
|
|
case 0x00B8:
|
|
sw = gpg_apdu_put_key_data(tag);
|
|
break;
|
|
default:
|
|
sw = gpg_apdu_put_data(tag);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
/* --- PIN -- */
|
|
case INS_VERIFY:
|
|
if ((G_gpg_vstate.io_p2 == 0x81) || (G_gpg_vstate.io_p2 == 0x82) || (G_gpg_vstate.io_p2 == 0x83)) {
|
|
sw = gpg_apdu_verify();
|
|
break;
|
|
}
|
|
THROW(SW_INCORRECT_P1P2);
|
|
|
|
case INS_CHANGE_REFERENCE_DATA:
|
|
if ((G_gpg_vstate.io_p2 == 0x81) || (G_gpg_vstate.io_p2 == 0x83)) {
|
|
sw = gpg_apdu_change_ref_data();
|
|
break;
|
|
}
|
|
THROW(SW_INCORRECT_P1P2);
|
|
|
|
case INS_RESET_RETRY_COUNTER:
|
|
if ((G_gpg_vstate.io_p2 == 0x81) && ((G_gpg_vstate.io_p1 == 0) || (G_gpg_vstate.io_p1 == 2))) {
|
|
sw = gpg_apdu_reset_retry_counter();
|
|
break;
|
|
}
|
|
THROW(SW_INCORRECT_P1P2);
|
|
|
|
/* --- Key Management --- */
|
|
case INS_GEN_ASYM_KEYPAIR:
|
|
sw = gpg_apdu_gen();
|
|
break;
|
|
|
|
/* --- MSE --- */
|
|
case INS_MSE:
|
|
sw = gpg_apdu_mse(tag);
|
|
break;
|
|
|
|
/* --- PSO --- */
|
|
case INS_PSO:
|
|
sw = gpg_apdu_pso();
|
|
break;
|
|
|
|
case INS_INTERNAL_AUTHENTICATE:
|
|
sw = gpg_apdu_internal_authenticate();
|
|
break;
|
|
|
|
default:
|
|
THROW(SW_INS_NOT_SUPPORTED);
|
|
break;
|
|
}
|
|
|
|
return sw;
|
|
}
|