Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/static password #77

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 16 additions & 13 deletions applets/admin/admin.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <ndef.h>
#include <oath.h>
#include <openpgp.h>
#include <pass.h>
#include <pin.h>
#include <piv.h>

Expand All @@ -16,7 +17,7 @@

static pin_t pin = {.min_length = 6, .max_length = PIN_MAX_LENGTH, .is_validated = 0, .path = "admin-pin"};

static const admin_device_config_t default_cfg = {.led_normally_on = 1, .ndef_en = 1, .webusb_landing_en = 1, .kbd_with_return_en = 1};
static const admin_device_config_t default_cfg = {.led_normally_on = 1, .ndef_en = 1, .webusb_landing_en = 1};

static admin_device_config_t current_config;

Expand Down Expand Up @@ -46,14 +47,10 @@ __attribute__((weak)) int admin_vendor_hw_sn(const CAPDU *capdu, RAPDU *rapdu) {

uint8_t cfg_is_led_normally_on(void) { return current_config.led_normally_on; }

uint8_t cfg_is_kbd_interface_enable(void) { return current_config.kbd_interface_en; }

uint8_t cfg_is_ndef_enable(void) { return current_config.ndef_en; }

uint8_t cfg_is_webusb_landing_enable(void) { return current_config.webusb_landing_en; }

uint8_t cfg_is_kbd_with_return_enable(void) { return current_config.kbd_with_return_en; }

void admin_poweroff(void) { pin.is_validated = 0; }

int admin_install(const uint8_t reset) {
Expand Down Expand Up @@ -117,18 +114,12 @@ static int admin_config(const CAPDU *capdu, RAPDU *rapdu) {
case ADMIN_P1_CFG_LED_ON:
current_config.led_normally_on = P2 & 1;
break;
case ADMIN_P1_CFG_KBDIFACE:
current_config.kbd_interface_en = P2 & 1;
break;
case ADMIN_P1_CFG_NDEF:
current_config.ndef_en = P2 & 1;
break;
case ADMIN_P1_CFG_WEBUSB_LANDING:
current_config.webusb_landing_en = P2 & 1;
break;
case ADMIN_P1_CFG_KBD_WITH_RETURN:
current_config.kbd_with_return_en = P2 & 1;
break;
default:
EXCEPT(SW_WRONG_P1P2);
}
Expand All @@ -142,11 +133,11 @@ static int admin_read_config(const CAPDU *capdu, RAPDU *rapdu) {
if (LE < 5) EXCEPT(SW_WRONG_LENGTH);

RDATA[0] = current_config.led_normally_on;
RDATA[1] = current_config.kbd_interface_en;
RDATA[1] = 0; // reserved
RDATA[2] = ndef_get_read_only();
RDATA[3] = current_config.ndef_en;
RDATA[4] = current_config.webusb_landing_en;
RDATA[5] = current_config.kbd_with_return_en;
RDATA[5] = 0; // reserved
LL = 6;
dangfan marked this conversation as resolved.
Show resolved Hide resolved

return 0;
Expand Down Expand Up @@ -187,8 +178,11 @@ static int admin_factory_reset(const CAPDU *capdu, RAPDU *rapdu) {
if (ret < 0) return ret;
ret = ndef_install(1);
if (ret < 0) return ret;
ret = pass_install(1);
if (ret < 0) return ret;
ret = admin_install(1);
if (ret < 0) return ret;

return 0;
}

Expand Down Expand Up @@ -261,6 +255,9 @@ int admin_process_apdu(const CAPDU *capdu, RAPDU *rapdu) {
case ADMIN_INS_TOGGLE_NDEF_READ_ONLY:
ret = ndef_toggle_read_only(capdu, rapdu);
break;
case ADMIN_INS_RESET_PASS:
ret = pass_install(1);
break;
case ADMIN_INS_RESET_CTAP:
ret = ctap_install(1);
break;
Expand All @@ -285,6 +282,12 @@ int admin_process_apdu(const CAPDU *capdu, RAPDU *rapdu) {
case ADMIN_INS_READ_CONFIG:
ret = admin_read_config(capdu, rapdu);
break;
case ADMIN_INS_READ_PASS_CONFIG:
ret = pass_read_config(capdu, rapdu);
break;
case ADMIN_INS_WRITE_PASS_CONFIG:
ret = pass_write_config(capdu, rapdu);
break;
case ADMIN_INS_VENDOR_SPECIFIC:
ret = admin_vendor_specific(capdu, rapdu);
break;
Expand Down
41 changes: 13 additions & 28 deletions applets/oath/oath.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <hmac.h>
#include <memzero.h>
#include <oath.h>
#include <pass.h>
#include <rand.h>
#include <string.h>

Expand All @@ -30,8 +31,6 @@ int oath_install(const uint8_t reset) {
oath_poweroff();
if (!reset && get_file_size(OATH_FILE) >= 0) return 0;
if (write_file(OATH_FILE, NULL, 0, 0, 1) < 0) return -1;
const uint32_t default_item = 0xffffffff;
if (write_attr(OATH_FILE, ATTR_DEFAULT_RECORD, &default_item, sizeof(default_item)) < 0) return -1;
if (write_attr(OATH_FILE, ATTR_KEY, NULL, 0) < 0) return -1;
uint8_t handle[HANDLE_LEN];
random_buffer(handle, sizeof(handle));
Expand Down Expand Up @@ -166,12 +165,13 @@ static int oath_delete(const CAPDU *capdu, RAPDU *rapdu) {
for (size_t i = 0; i != n_records; ++i) {
if (read_file(OATH_FILE, &record, i * sizeof(OATH_RECORD), sizeof(OATH_RECORD)) < 0) return -1;
if (record.name_len == name_len && memcmp(record.name, name_ptr, name_len) == 0) {
uint32_t default_item;
if (read_attr(OATH_FILE, ATTR_DEFAULT_RECORD, &default_item, sizeof(default_item)) < 0) return -1;
if (default_item == i) { // clear the default set if it is to be deleted
default_item = 0xffffffff;
if (write_attr(OATH_FILE, ATTR_DEFAULT_RECORD, &default_item, sizeof(default_item)) < 0) return -1;
}
// uint32_t default_item;
// if (read_attr(OATH_FILE, ATTR_DEFAULT_RECORD, &default_item, sizeof(default_item)) < 0) return -1;
// if (default_item == i) { // clear the default set if it is to be deleted
// default_item = 0xffffffff;
// if (write_attr(OATH_FILE, ATTR_DEFAULT_RECORD, &default_item, sizeof(default_item)) < 0) return -1;
// }
// TODO: delete pass config

record.name_len = 0;
return write_file(OATH_FILE, &record, i * sizeof(OATH_RECORD), sizeof(OATH_RECORD), 0);
Expand Down Expand Up @@ -384,7 +384,7 @@ static uint8_t *oath_digest(const OATH_RECORD *record, uint8_t buffer[SHA512_DIG
return buffer + offset;
}

static int oath_calculate_by_offset(const size_t file_offset, uint8_t result[4]) {
int oath_calculate_by_offset(size_t file_offset, uint8_t result[4]) {
if (file_offset % sizeof(OATH_RECORD) != 0) return -2;
const int size = get_file_size(OATH_FILE);
if (size < 0 || file_offset >= (size_t)size) return -2;
Expand Down Expand Up @@ -417,7 +417,8 @@ static int oath_calculate_by_offset(const size_t file_offset, uint8_t result[4])
}

static int oath_set_default(const CAPDU *capdu, RAPDU *rapdu) {
if (P1 != 0x00 || P2 != 0x00) EXCEPT(SW_WRONG_P1P2);
if (P1 != 0x01 && P1 != 0x02) EXCEPT(SW_WRONG_P1P2);
if (P2 != 0x00) EXCEPT(SW_WRONG_P1P2);

uint16_t offset = 0;
if (offset + 1 >= LC) EXCEPT(SW_WRONG_LENGTH);
Expand All @@ -433,7 +434,7 @@ static int oath_set_default(const CAPDU *capdu, RAPDU *rapdu) {
if (size < 0) return -1;
const uint32_t n_records = size / sizeof(OATH_RECORD);
uint32_t i;
uint32_t file_offset;
uint32_t file_offset = 0;
OATH_RECORD record;
for (i = 0; i != n_records; ++i) {
file_offset = i * sizeof(OATH_RECORD);
Expand All @@ -443,8 +444,7 @@ static int oath_set_default(const CAPDU *capdu, RAPDU *rapdu) {
if (i == n_records) EXCEPT(SW_DATA_INVALID);
if ((record.key[0] & OATH_TYPE_MASK) == OATH_TYPE_TOTP) EXCEPT(SW_CONDITIONS_NOT_SATISFIED);

if (write_attr(OATH_FILE, ATTR_DEFAULT_RECORD, &file_offset, sizeof(file_offset)) < 0) return -1;
return 0;
return pass_update_oath(P1 -1, file_offset, record.name_len, record.name);
}

static int oath_calculate(const CAPDU *capdu, RAPDU *rapdu) {
Expand Down Expand Up @@ -618,21 +618,6 @@ static int oath_send_remaining(const CAPDU *capdu, RAPDU *rapdu) {
EXCEPT(SW_CONDITIONS_NOT_SATISFIED);
}

int oath_process_one_touch(char *output, const size_t maxlen) {
uint32_t offset = 0xffffffff, otp_code;
if (read_attr(OATH_FILE, ATTR_DEFAULT_RECORD, &offset, sizeof(offset)) < 0) return -2;
int ret = oath_calculate_by_offset(offset, (uint8_t *)&otp_code);
if (ret < 0) return ret;
if ((size_t)(ret + 1) > maxlen) return -1;
output[ret] = '\0';
otp_code = htobe32(otp_code);
while (ret--) {
output[ret] = otp_code % 10 + '0';
otp_code /= 10;
}
return 0;
}

// ReSharper disable once CppDFAConstantFunctionResult
int oath_process_apdu(const CAPDU *capdu, RAPDU *rapdu) {
LL = 0;
Expand Down
179 changes: 179 additions & 0 deletions applets/pass/pass.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// SPDX-License-Identifier: Apache-2.0
#include <common.h>
#include <device.h>
#include <memzero.h>
#include <oath.h>
#include <pass.h>

#define PASS_FILE "pass"
#define SLOT_SHORT 0
#define SLOT_LONG 1

typedef enum {
PASS_SLOT_OFF,
PASS_SLOT_OATH,
PASS_SLOT_STATIC,
} slot_type_t;

typedef struct {
slot_type_t type;
union {
struct {
uint8_t password_len;
uint8_t password[PASS_MAX_PASSWORD_LENGTH];
} __packed;
struct {
uint32_t oath_offset;
uint8_t name_len;
uint8_t name[MAX_NAME_LEN];
} __packed;
};
uint8_t with_enter;
} __packed pass_slot_t;

static pass_slot_t slots[2];

int pass_install(const uint8_t reset) {
if (reset || get_file_size(PASS_FILE) != sizeof(slots)) {
memzero(slots, sizeof(slots));
if (write_file(PASS_FILE, slots, 0, sizeof(slots), 1) < 0) return -1;
} else {
if (read_file(PASS_FILE, slots, 0, sizeof(slots)) < 0) return -1;
}

return 0;
}

// Dump slots to buffer, return the length of the buffer
// For each slot, the first byte is the type.
// For PASS_SLOT_OFF, there is no more data
// For PASS_SLOT_STATIC, the second byte is with_enter
// For PASS_SLOT_OATH, the next byte is the length of the name, followed by the name, and the next byte is with_enter
static int dump_slot(const pass_slot_t *slot, uint8_t *buffer) {
int length = 0;

// First byte is always the type
buffer[0] = (uint8_t)slot->type;
length++;

switch (slot->type) {
case PASS_SLOT_OFF:
break;

case PASS_SLOT_STATIC:
// For STATIC, the second byte is with_enter
buffer[length++] = slot->with_enter;
break;

case PASS_SLOT_OATH:
// For OATH, the second byte is the length of the name
buffer[length++] = slot->name_len;
// The next bytes are the name
memcpy(buffer + length, slot->name, slot->name_len);
length += slot->name_len;
// The next byte is with_enter
buffer[length++] = slot->with_enter;
break;
}

return length;
}

int pass_read_config(const CAPDU *capdu, RAPDU *rapdu) {
UNUSED(capdu);

int length = dump_slot(&slots[SLOT_SHORT], RDATA);
length += dump_slot(&slots[SLOT_LONG], RDATA + length);
LL = length;

return 0;
}

// P1 for the slot index, 1 for short slot, 2 for long slot
// DATA contains the slot data:
// The first byte is the slot type
// For OFF, there is no more data
// For STATIC, the second byte is the length of the password, followed by the password, and the next byte is with_enter
// OATH is not allowed to be written here
int pass_write_config(const CAPDU *capdu, RAPDU *rapdu) {
if (P1 != 1 && P1 != 2) EXCEPT(SW_WRONG_P1P2);
if (LC < 1) EXCEPT(SW_WRONG_LENGTH);

pass_slot_t *slot = &slots[P1 - 1];
slot->type = (slot_type_t)DATA[0];

switch (slot->type) {
case PASS_SLOT_OFF:
if (LC != 1) EXCEPT(SW_WRONG_LENGTH);
break;

case PASS_SLOT_STATIC:
if (LC < 3) EXCEPT(SW_WRONG_LENGTH);
slot->password_len = DATA[1];
if (slot->password_len > PASS_MAX_PASSWORD_LENGTH) EXCEPT(SW_WRONG_LENGTH);
memcpy(slot->password, DATA + 2, slot->password_len);
slot->with_enter = DATA[2 + slot->password_len];
if (LC != 3 + slot->password_len) EXCEPT(SW_WRONG_LENGTH);
break;

default:
EXCEPT(SW_WRONG_DATA);
}

return write_file(PASS_FILE, slots, 0, sizeof(slots), 1);
}

int pass_update_oath(uint8_t slot_index, uint32_t offset, uint8_t name_len, const uint8_t *name) {
pass_slot_t *slot = &slots[slot_index];
slot->type = PASS_SLOT_OATH;
slot->oath_offset = offset;
slot->name_len = name_len;
memcpy(slot->name, name, name_len);

return write_file(PASS_FILE, slots, 0, sizeof(slots), 1);
}

static int oath_process_offset(uint32_t offset, char *output) {
uint32_t otp_code;
int ret = oath_calculate_by_offset(offset, (uint8_t *)&otp_code);
if (ret < 0) return ret;
const int len = ret;

otp_code = htobe32(otp_code);
while (ret--) {
output[ret] = otp_code % 10 + '0';
otp_code /= 10;
}
output[len] = '\0';

return len;
}

int pass_handle_touch(uint8_t touch_type, char *output) {
pass_slot_t *slot;
if (touch_type == TOUCH_SHORT)
slot = &slots[SLOT_SHORT];
else if (touch_type == TOUCH_LONG)
slot = &slots[SLOT_LONG];
else
return -1;

int length;
switch (slot->type) {
case PASS_SLOT_OFF:
return 0;
case PASS_SLOT_OATH:
length = oath_process_offset(slot->oath_offset, output);
break;
case PASS_SLOT_STATIC:
memcpy(output, slot->password + 1, slot->password[0]);
length = slot->password[0];
break;
default:
return -1;
}

if (slot->with_enter) output[length++] = '\r';

return length;
}
Loading
Loading