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

Add support for nitrokey 3 distinction between the secrets app firmware and the device firmware versions #43

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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
39 changes: 38 additions & 1 deletion src/ccid.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ IccResult parse_icc_result(uint8_t *buf, size_t buf_len) {
// .buffer_len = buf_len
};
// Make sure the response do not contain overread attempts
rassert(i.data_len < buf_len - 10);
rassert(i.data_len <= buf_len - 10);
return i;
}

Expand Down Expand Up @@ -308,6 +308,43 @@ int send_select_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_siz
}


int send_select_nk3_admin_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult) {
unsigned char cmd_select[] = {
0x6f,
0x0E,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0xa4,
0x04,
0x00,
0x09,
0xa0,
0x00,
0x00,
0x08,
0x47,
0x00,
0x00,
0x00,
0x01,
};

check_ret(
ccid_process_single(handle, buf, buf_size, cmd_select, sizeof cmd_select, iccResult),
RET_COMM_ERROR);


return RET_NO_ERROR;
}


int ccid_init(libusb_device_handle *handle) {

unsigned char cmd_select[] = {
Expand Down
1 change: 1 addition & 0 deletions src/ccid.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ uint32_t icc_pack_tlvs_for_sending(uint8_t *buf, size_t buflen, TLV tlvs[], int
libusb_device_handle *get_device(libusb_context *ctx, const struct VidPid pPid[], int devices_count);
int ccid_init(libusb_device_handle *handle);
int send_select_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult);
int send_select_nk3_admin_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult);


enum {
Expand Down
20 changes: 5 additions & 15 deletions src/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -265,17 +265,7 @@ int device_get_status(struct Device *dev, struct ResponseStatus *out_status) {
memset(out_status, 0, sizeof(struct ResponseStatus));

if (dev->connection_type == CONNECTION_CCID) {
int counter = 0;
uint32_t serial = 0;
uint16_t version = 0;
int res = status_ccid(dev->mp_devhandle_ccid,
&counter,
&version,
&serial);
out_status->retry_admin = counter;
out_status->retry_user = counter;
out_status->card_serial_u32 = serial;
out_status->firmware_version = version;
int res = status_ccid(dev->mp_devhandle_ccid, out_status);
return res;
}

Expand All @@ -292,15 +282,15 @@ int device_get_status(struct Device *dev, struct ResponseStatus *out_status) {
device_receive_buf(dev);
*out_status = *(struct ResponseStatus *) dev->packet_response.response_st.payload;

if (out_status->firmware_version_st.minor == 1) {
if (out_status->firmware_version_legacy.firmware_version_st.minor == 1) {
for (int i = 0; i < 100; ++i) {
device_send_buf(dev, GET_DEVICE_STATUS);
device_receive_buf(dev);

struct StatusResponsePayloadStorage *status = (struct StatusResponsePayloadStorage *) (dev->packet_response.response_st.payload + 22);
out_status->card_serial_u32 = status->ActiveSmartCardID_u32;
out_status->firmware_version_st.major = status->versionInfo.major;
out_status->firmware_version_st.minor = status->versionInfo.minor;
out_status->firmware_version_legacy.firmware_version_st.major = status->versionInfo.major;
out_status->firmware_version_legacy.firmware_version_st.minor = status->versionInfo.minor;
if (out_status->card_serial_u32 != 0) {
break;
}
Expand Down Expand Up @@ -343,4 +333,4 @@ const char *command_status_to_string(uint8_t status_code) {
void clean_buffers(struct Device *dev) {
memset(dev->ccid_buffer_in, 0, sizeof dev->ccid_buffer_in);
memset(dev->ccid_buffer_out, 0, sizeof dev->ccid_buffer_out);
}
}
16 changes: 13 additions & 3 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,19 @@ int parse_cmd_and_run(int argc, char *const *argv) {
printf("Connected device status:\n");
printf("\tCard serial: ");
print_card_serial(&status);
printf("\tFirmware: v%d.%d\n",
status.firmware_version_st.major,
status.firmware_version_st.minor);
if (status.is_nk3) {
printf("\tFirmware Nitrokey 3: v%d.%d.%d\n",
(status.firmware_version_nk3.firmware_version >> 22) & 0b1111111111,
(status.firmware_version_nk3.firmware_version >> 6) & 0xFFFF,
status.firmware_version_nk3.firmware_version & 0b111111);
printf("\tFirmware Secrets App: v%d.%d\n",
status.firmware_version_nk3.secrets_app_firmware_version_st.major,
status.firmware_version_nk3.secrets_app_firmware_version_st.minor);
} else {
printf("\tFirmware: v%d.%d\n",
status.firmware_version_legacy.firmware_version_st.major,
status.firmware_version_legacy.firmware_version_st.minor);
}
if (res != RET_NO_PIN_ATTEMPTS) {
printf("\tCard counters: Admin %d, User %d\n",
status.retry_admin, status.retry_user);
Expand Down
1 change: 1 addition & 0 deletions src/operations.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ static const int HOTP_MIN_INT = 0;
static const int NK_STORAGE_BUSY = 2;
#include "device.h"
#include "return_codes.h"
#include "structs.h"

int set_secret_on_device(struct Device *dev, const char *OTP_secret_base32, const char *admin_PIN, const uint64_t hotp_counter);
int check_code_on_device(struct Device *dev, const char *HOTP_code_to_verify);
Expand Down
106 changes: 81 additions & 25 deletions src/operations_ccid.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "tlv.h"
#include "utils.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

Expand Down Expand Up @@ -115,15 +116,14 @@ int authenticate_or_set_ccid(struct Device *dev, const char *admin_PIN) {
}


int delete_secret_on_device_ccid(struct Device *dev) {
int delete_secret_on_device_ccid(struct Device *dev) {
TLV tlvs[] = {
{
.tag = Tag_CredentialId,
.length = SLOT_NAME_LEN,
.type = 'S',
.v_str = SLOT_NAME,
}
};
{
.tag = Tag_CredentialId,
.length = SLOT_NAME_LEN,
.type = 'S',
.v_str = SLOT_NAME,
}};

clean_buffers(dev);
// encode
Expand Down Expand Up @@ -167,11 +167,11 @@ int set_secret_on_device_ccid(struct Device *dev, const char *admin_PIN, const c
}

#ifdef CCID_SECRETS_AUTHENTICATE_OR_CREATE_PIN
if (strnlen(admin_PIN, 30) > 0) {
if (authenticate_or_set_ccid(dev, admin_PIN) != RET_NO_ERROR) {
return RET_SECURITY_STATUS_NOT_SATISFIED;
}
if (strnlen(admin_PIN, 30) > 0) {
if (authenticate_or_set_ccid(dev, admin_PIN) != RET_NO_ERROR) {
return RET_SECURITY_STATUS_NOT_SATISFIED;
}
}
#endif
TLV tlvs[] = {
{
Expand Down Expand Up @@ -209,7 +209,7 @@ int set_secret_on_device_ccid(struct Device *dev, const char *admin_PIN, const c
// send
IccResult iccResult;
r = ccid_process_single(dev->mp_devhandle_ccid, dev->ccid_buffer_in, sizeof dev->ccid_buffer_in,
dev->ccid_buffer_out, icc_actual_length, &iccResult);
dev->ccid_buffer_out, icc_actual_length, &iccResult);


if (r != 0) {
Expand Down Expand Up @@ -273,14 +273,61 @@ int verify_code_ccid(struct Device *dev, const uint32_t code_to_verify) {
return RET_VALIDATION_PASSED;
}

int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *firmware_version, uint32_t *serial_number) {
int status_ccid(libusb_device_handle *handle, struct ResponseStatus *response) {
rassert(handle != NULL);
rassert(attempt_counter != NULL);
rassert(firmware_version != NULL);
rassert(serial_number != NULL);
uint8_t buf[1024] = {};
IccResult iccResult = {};
int r = send_select_ccid(handle, buf, sizeof buf, &iccResult);
bool pin_counter_is_error = false;
int r;
libusb_device *usb_dev;
struct libusb_device_descriptor usb_desc;
usb_dev = libusb_get_device(handle);

r = libusb_get_device_descriptor(usb_dev, &usb_desc);

if (r < 0) {
return r;
}


if (usb_desc.idVendor == 0x20a0 && usb_desc.idProduct == 0x42b2) {
r = send_select_nk3_admin_ccid(handle, buf, sizeof buf, &iccResult);
if (r != RET_NO_ERROR) {
return r;
}

response->is_nk3 = true;

uint8_t data_iso[MAX_CCID_BUFFER_SIZE] = {};
uint32_t iso_actual_length = iso7816_compose(
data_iso, sizeof data_iso,
0x61, 0, 0, 0, 4, NULL, 0);

// encode ccid wrapper
uint32_t icc_actual_length = icc_compose(buf, sizeof buf,
0x6F, iso_actual_length,
0, 0, 0, data_iso);
int transferred;
r = ccid_send(handle, &transferred, buf, icc_actual_length);
if (r != 0) {
return r;
}

r = ccid_receive(handle, &transferred, buf, sizeof buf);
if (r != 0) {
return r;
}

IccResult iccResult = parse_icc_result(buf, transferred);
rassert(iccResult.data_status_code == 0x9000);
rassert(iccResult.data_len == 6);
response->firmware_version_nk3.firmware_version = be32toh(*(uint32_t *) iccResult.data);
} else {
response->is_nk3 = false;
printf("Not an NK3\n");
}

r = send_select_ccid(handle, buf, sizeof buf, &iccResult);
if (r != RET_NO_ERROR) {
return r;
}
Expand All @@ -292,29 +339,38 @@ int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *fi
r = get_tlv(iccResult.data, iccResult.data_len, Tag_PINCounter, &counter_tlv);
if (!(r == RET_NO_ERROR && counter_tlv.tag == Tag_PINCounter)) {
// PIN counter not found - comm error (ignore) or PIN not set
*attempt_counter = -1;
pin_counter_is_error = true;
} else {
*attempt_counter = counter_tlv.v_data[0];
response->retry_admin = counter_tlv.v_data[0];
response->retry_user = counter_tlv.v_data[0];
}

TLV serial_tlv = {};
r = get_tlv(iccResult.data, iccResult.data_len, Tag_SerialNumber, &serial_tlv);
if (r == RET_NO_ERROR && serial_tlv.tag == Tag_SerialNumber) {
*serial_number = be32toh(*(uint32_t *) serial_tlv.v_data);
response->card_serial_u32 = be32toh(*(uint32_t *) serial_tlv.v_data);
} else {
// ignore errors - unsupported or hidden serial_tlv number
*serial_number = 0;
response->card_serial_u32 = 0;
}

TLV version_tlv = {};
r = get_tlv(iccResult.data, iccResult.data_len, Tag_Version, &version_tlv);
if (!(r == RET_NO_ERROR && version_tlv.tag == Tag_Version)) {
*firmware_version = 0;
if (response->is_nk3) {
response->firmware_version_nk3.secrets_app_firmware_version = 0;
} else {
response->firmware_version_legacy.firmware_version = 0;
}
return RET_COMM_ERROR;
}
*firmware_version = be16toh(*(uint16_t *) version_tlv.v_data);
if (response->is_nk3) {
response->firmware_version_nk3.secrets_app_firmware_version = be16toh(*(uint16_t *) version_tlv.v_data);
} else {
response->firmware_version_legacy.firmware_version = be16toh(*(uint16_t *) version_tlv.v_data);
}

if (*attempt_counter == -1) {
if (pin_counter_is_error == true) {
return RET_NO_PIN_ATTEMPTS;
}
return RET_NO_ERROR;
Expand Down
2 changes: 1 addition & 1 deletion src/operations_ccid.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ int authenticate_ccid(struct Device *dev, const char *admin_PIN);
int authenticate_or_set_ccid(struct Device *dev, const char *admin_PIN);
int set_secret_on_device_ccid(struct Device *dev, const char *admin_PIN, const char *OTP_secret_base32, const uint64_t hotp_counter);
int verify_code_ccid(struct Device *dev, const uint32_t code_to_verify);
int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *firmware_version, uint32_t *serial_number);
int status_ccid(libusb_device_handle *handle, struct ResponseStatus *response);


#endif//NITROKEY_HOTP_VERIFICATION_OPERATIONS_CCID_H
2 changes: 1 addition & 1 deletion src/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
// #define FEATURE_CCID_ASK_FOR_PIN_ON_ERROR

// Use the provided PIN for authentication over CCID
// #define CCID_AUTHENTICATE
// #define CCID_AUTHENTICATE

// Attempt to authenticate before setting the PIN, if no pin is present, create the PIN
#define CCID_SECRETS_AUTHENTICATE_OR_CREATE_PIN
Expand Down
28 changes: 23 additions & 5 deletions src/structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,31 @@ struct DeviceResponse {
//------------------------------------

struct ResponseStatus {
bool is_nk3;
union {
uint16_t firmware_version;
// Valid if and only if is_nk3 = false
union {
/** Firmware version of the secrets app, different from that of the NK3 firmware. If zero, device is not an NK3 */
uint16_t firmware_version;
struct {
uint8_t minor;
uint8_t major;
} firmware_version_st;
} __packed firmware_version_legacy;
// Valid if and only if is_nk3 = true
struct {
uint8_t minor;
uint8_t major;
} firmware_version_st;
} __packed;
// Firmware version of the firmware of the device
uint32_t firmware_version;
union {
/** Firmware version of the secrets app, different from that of the NK3 firmware. If zero, device is not an NK3 */
uint16_t secrets_app_firmware_version;
struct {
uint8_t minor;
uint8_t major;
} secrets_app_firmware_version_st;
} __packed;
} firmware_version_nk3;
};
union {
uint8_t card_serial[4];
uint32_t card_serial_u32;
Expand Down
22 changes: 16 additions & 6 deletions test_ccid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,28 @@ TEST_CASE("test ccid status", "[main]") {
int res = device_connect(&dev);
REQUIRE(res == RET_NO_ERROR);
int counter;
struct ResponseStatus out_status;
uint16_t firmware_version;
uint16_t secrets_app_version;
uint32_t serial;
int status_res = status_ccid(dev.mp_devhandle_ccid, &counter, &firmware_version, &serial);
int status_res = status_ccid(dev.mp_devhandle_ccid, &out_status);
if (status_res == RET_NO_ERROR) {
REQUIRE((0 <= counter && counter <= 8));
REQUIRE((0 <= out_status->retry_admin && out_status->retry_admin <= 8));
} else if (status_res == RET_NO_PIN_ATTEMPTS) {
REQUIRE(counter == -1);
REQUIRE(out_status->retry_admin == -1);
}
REQUIRE((firmware_version != 0 && firmware_version != 0xFFFF));
INFO("Current serial number " << serial);
if (out_status->is_nk3) {
REQUIRE((
out_status->firmware_version_nk3.firmware_version != 0 &&
out_status->firmware_version_nk3.firmware_version != 0xFFFFFFFF));
} else {
REQUIRE((
out_status->firmware_version_legacy.firmware_version != 0 &&
out_status->firmware_version_legacy.firmware_version != 0xFFFF));
}
INFO("Current serial number " << out_status->card_serial_u32);
INFO("SN is supported by the Secrets App since 0.11");
REQUIRE((serial != 0 && serial != 0xFFFFFFFF));
REQUIRE((out_status->card_serial_u32 != 0 && out_status->card_serial_u32 != 0xFFFFFFFF));
}

TEST_CASE("test tlv", "[Helper]") {
Expand Down
Loading