Skip to content

Commit

Permalink
wip: add pass
Browse files Browse the repository at this point in the history
  • Loading branch information
dangfan committed Jan 1, 2024
1 parent 4db715b commit 8af54da
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 84 deletions.
20 changes: 4 additions & 16 deletions applets/admin/admin.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,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 +46,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; }

uint8_t cfg_is_piv_algo_extension_enable(void) { return current_config.piv_algo_ext_en; }

void admin_poweroff(void) { pin.is_validated = 0; }
Expand Down Expand Up @@ -119,18 +115,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;
case ADMIN_P1_CFG_PIV_ALGO_EXT:
current_config.piv_algo_ext_en = P2 & 1;
break;
Expand All @@ -147,11 +137,9 @@ 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[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[1] = ndef_get_read_only();
RDATA[2] = current_config.ndef_en;
RDATA[3] = current_config.webusb_landing_en;
LL = 6;

return 0;
Expand Down
17 changes: 1 addition & 16 deletions applets/oath/oath.c
Original file line number Diff line number Diff line change
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 @@ -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
157 changes: 157 additions & 0 deletions applets/pass/pass.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// 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

static pass_slot_t slots[2];

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

memzero(slots, sizeof(slots));
if (write_file(PASS_FILE, slots, 0, 0, 1) < 0) return -1;

return 0;
}

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:
case PASS_SLOT_STATIC:
// For OFF and STATIC, the second byte is with_enter
buffer[1] = slot->with_enter;
length++;
break;

case PASS_SLOT_OATH:
// For OATH, the next 4 bytes are oath_offset
memcpy(&buffer[1], &slot->oath_offset, sizeof(slot->oath_offset));
buffer[5] = slot->with_enter;
length += 5;
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;
}

int pass_write_config(const CAPDU *capdu, RAPDU *rapdu) {
size_t index = 0;

for (int i = 0; i < 2; i++) {
if (index >= LC) {
// Data is not enough to parse a slot
EXCEPT(SW_WRONG_LENGTH);
}

const slot_type_t type = DATA[index++];
switch (type) {
case PASS_SLOT_OFF:
slots[i].type = type;
break;

case PASS_SLOT_STATIC:
if (index + sizeof(slots[0].password) + sizeof(slots[0].with_enter) > LC ||
slots[i].password[0] > PASS_MAX_PASSWORD_LENGTH) {
// Not enough data for PASS_SLOT_STATIC or password is too long
EXCEPT(SW_WRONG_DATA);
}
slots[i].type = type;
memcpy(slots[i].password, &DATA[index], sizeof(slots[0].password));
index += sizeof(slots[0].password);
slots[i].with_enter = DATA[index++];
break;

case PASS_SLOT_OATH:
if (index + sizeof(slots[0].oath_offset) + sizeof(slots[0].with_enter) > LC) {
// Not enough data for PASS_SLOT_OATH
EXCEPT(SW_WRONG_DATA);
}
slots[i].type = type;
memcpy(&slots[i].oath_offset, &DATA[index], sizeof(slots[0].oath_offset));
index += sizeof(slots[0].oath_offset);
slots[i].with_enter = DATA[index++];
break;

default:
// Invalid slot type
EXCEPT(SW_WRONG_DATA);
}
}

if (index != LC) {
// Extra data present that doesn't fit in the slot structure
EXCEPT(SW_WRONG_LENGTH);
}

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;
}
8 changes: 0 additions & 8 deletions include/admin.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,14 @@
#define ADMIN_INS_VENDOR_SPECIFIC 0xFF

#define ADMIN_P1_CFG_LED_ON 0x01
#define ADMIN_P1_CFG_KBDIFACE 0x03
#define ADMIN_P1_CFG_NDEF 0x04
#define ADMIN_P1_CFG_WEBUSB_LANDING 0x05
#define ADMIN_P1_CFG_KBD_WITH_RETURN 0x06
#define ADMIN_P1_CFG_PIV_ALGO_EXT 0x07

typedef struct {
uint32_t reserved;
uint32_t led_normally_on : 1;
uint32_t unused : 1;
uint32_t kbd_interface_en : 1;
uint32_t ndef_en : 1;
uint32_t webusb_landing_en : 1;
uint32_t kbd_with_return_en : 1;
uint32_t piv_algo_ext_en : 1;
} __packed admin_device_config_t;

Expand All @@ -50,10 +44,8 @@ int admin_vendor_hw_variant(const CAPDU *capdu, RAPDU *rapdu);
int admin_vendor_hw_sn(const CAPDU *capdu, RAPDU *rapdu);

uint8_t cfg_is_led_normally_on(void);
uint8_t cfg_is_kbd_interface_enable(void);
uint8_t cfg_is_ndef_enable(void);
uint8_t cfg_is_webusb_landing_enable(void);
uint8_t cfg_is_kbd_with_return_enable(void);
uint8_t cfg_is_piv_algo_extension_enable(void);

#endif // CANOKEY_CORE_ADMIN_ADMIN_H_
2 changes: 1 addition & 1 deletion include/oath.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,6 @@ typedef struct {
void oath_poweroff(void);
int oath_install(uint8_t reset);
int oath_process_apdu(const CAPDU *capdu, RAPDU *rapdu);
int oath_process_one_touch(char *output, size_t maxlen);
int oath_calculate_by_offset(size_t file_offset, uint8_t result[4]);

#endif // CANOKEY_CORE_OATH_OATH_H_
29 changes: 29 additions & 0 deletions include/pass.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: Apache-2.0 */
#ifndef CANOKEY_CORE_INCLUDE_PASS_H
#define CANOKEY_CORE_INCLUDE_PASS_H

#include <apdu.h>

#define PASS_MAX_PASSWORD_LENGTH 32

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

typedef struct {
slot_type_t type;
union {
uint8_t password[33]; // 1-byte length + at most 32-byte content
uint32_t oath_offset;
};
uint8_t with_enter;
} __packed pass_slot_t;

int pass_install(uint8_t reset);
int pass_read_config(const CAPDU *capdu, RAPDU *rapdu);
int pass_write_config(const CAPDU *capdu, RAPDU *rapdu);
int pass_handle_touch(uint8_t touch_type, char *output);

#endif // CANOKEY_CORE_INCLUDE_PASS_H
Loading

0 comments on commit 8af54da

Please sign in to comment.