Added support to Nitrokey's nitropy tool.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
Pol Henarejos 2023-05-15 19:11:02 +02:00
parent 07729f807b
commit 24224b78dd
No known key found for this signature in database
GPG Key ID: C0095B7870A4CCD3
5 changed files with 145 additions and 4 deletions

@ -1 +1 @@
Subproject commit 46e7d3a181fab92fb8e394ed9b579b2b2f6a54a0
Subproject commit 2700e3ae7a6bd6a43b8bdf39f243df8e8c52e7bf

View File

@ -464,12 +464,12 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2,
self_attestation == false ? 3 : 2));
self_attestation == false || is_nitrokey ? 3 : 2));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg"));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, self_attestation ? -alg : -FIDO2_ALG_ES256));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, self_attestation || is_nitrokey ? -alg : -FIDO2_ALG_ES256));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "sig"));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, sig, olen));
if (self_attestation == false) {
if (self_attestation == false || is_nitrokey) {
CborEncoder arrEncoder;
file_t *ef_cert = NULL;
if (enterpriseAttestation == 2) {

View File

@ -46,6 +46,8 @@ file_t file_entries[] = {
{ .fid = EF_LARGEBLOB, .parent = 0, .name = NULL,
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
.ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Large Blob
{ .fid = EF_OTP_PIN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH,
.data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } },
{ .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL,
.ef_structure = 0, .acl = { 0 } } //end
};

View File

@ -36,6 +36,7 @@
#define EF_OATH_CODE 0xBAFF
#define EF_OTP_SLOT1 0xBB00
#define EF_OTP_SLOT2 0xBB01
#define EF_OTP_PIN 0x10A0 // Nitrokey OTP PIN
extern file_t *ef_keydev;
extern file_t *ef_certdev;

View File

@ -22,9 +22,11 @@
#include "random.h"
#include "version.h"
#include "asn1.h"
#include "crypto_utils.h"
#define MAX_OATH_CRED 255
#define CHALLENGE_LEN 8
#define MAX_OTP_COUNTER 3
#define TAG_NAME 0x71
#define TAG_NAME_LIST 0x72
@ -38,6 +40,9 @@
#define TAG_IMF 0x7a
#define TAG_ALGO 0x7b
#define TAG_TOUCH_RESPONSE 0x7c
#define TAG_PASSWORD 0x80
#define TAG_NEW_PASSWORD 0x81
#define TAG_PIN_COUNTER 0x82
#define ALG_HMAC_SHA1 0x01
#define ALG_HMAC_SHA256 0x02
@ -88,6 +93,16 @@ app_t *oath_select(app_t *a, const uint8_t *aid, uint8_t aid_len) {
memcpy(res_APDU + res_APDU_size, challenge, sizeof(challenge));
res_APDU_size += sizeof(challenge);
}
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
if (file_has_data(ef_otp_pin)) {
const uint8_t *pin_data = file_get_data(ef_otp_pin);
res_APDU[res_APDU_size++] = TAG_PIN_COUNTER;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = *pin_data;
}
res_APDU[res_APDU_size++] = TAG_ALGO;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = ALG_HMAC_SHA1;
apdu.ne = res_APDU_size;
return a;
}
@ -252,6 +267,8 @@ int cmd_reset() {
}
}
delete_file(search_dynamic_file(EF_OATH_CODE));
flash_clear_file(search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF));
low_flash_available();
validated = true;
return SW_OK();
}
@ -479,6 +496,119 @@ int cmd_send_remaining() {
return SW_OK();
}
int cmd_set_otp_pin() {
size_t pw_len = 0;
uint8_t *pw = NULL, hsh[33] = { 0 };
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
if (file_has_data(ef_otp_pin)) {
return SW_CONDITIONS_NOT_SATISFIED();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_PASSWORD, &pw_len, &pw) == false) {
return SW_INCORRECT_PARAMS();
}
hsh[0] = MAX_OTP_COUNTER;
double_hash_pin(pw, pw_len, hsh + 1);
flash_write_data_to_file(ef_otp_pin, hsh, sizeof(hsh));
low_flash_available();
return SW_OK();
}
int cmd_change_otp_pin() {
size_t pw_len = 0, new_pw_len = 0;
uint8_t *pw = NULL, *new_pw = NULL, hsh[33] = { 0 };
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
if (!file_has_data(ef_otp_pin)) {
return SW_CONDITIONS_NOT_SATISFIED();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_PASSWORD, &pw_len, &pw) == false) {
return SW_INCORRECT_PARAMS();
}
double_hash_pin(pw, pw_len, hsh + 1);
if (memcmp(file_get_data(ef_otp_pin) + 1, hsh + 1, 32) != 0) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_NEW_PASSWORD, &new_pw_len, &new_pw) == false) {
return SW_INCORRECT_PARAMS();
}
hsh[0] = MAX_OTP_COUNTER;
double_hash_pin(new_pw, new_pw_len, hsh + 1);
flash_write_data_to_file(ef_otp_pin, hsh, sizeof(hsh));
low_flash_available();
return SW_OK();
}
int cmd_verify_otp_pin() {
size_t pw_len = 0;
uint8_t *pw = NULL, hsh[33] = { 0 }, data_hsh[33];
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
if (!file_has_data(ef_otp_pin)) {
return SW_CONDITIONS_NOT_SATISFIED();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_PASSWORD, &pw_len, &pw) == false) {
return SW_INCORRECT_PARAMS();
}
double_hash_pin(pw, pw_len, hsh + 1);
memcpy(data_hsh, file_get_data(ef_otp_pin), sizeof(data_hsh));
if (data_hsh[0] == 0 || memcmp(data_hsh + 1, hsh + 1, 32) != 0) {
if (data_hsh[0] > 0) {
data_hsh[0] -= 1;
}
flash_write_data_to_file(ef_otp_pin, data_hsh, sizeof(data_hsh));
low_flash_available();
validated = false;
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
data_hsh[0] = MAX_OTP_COUNTER;
flash_write_data_to_file(ef_otp_pin, data_hsh, sizeof(data_hsh));
low_flash_available();
validated = true;
return SW_OK();
}
int cmd_verify_hotp() {
size_t key_len = 0, chal_len = 0, name_len = 0, code_len = 0;
uint8_t *key = NULL, *chal = NULL, *name = NULL, *code = NULL;
uint32_t code_int = 0;
if (asn1_find_tag(apdu.data, apdu.nc, TAG_NAME, &name_len, &name) == false) {
return SW_INCORRECT_PARAMS();
}
file_t *ef = find_oath_cred(name, name_len);
if (file_has_data(ef) == false) {
return SW_DATA_INVALID();
}
if (asn1_find_tag(file_get_data(ef), file_get_size(ef), TAG_KEY, &key_len, &key) == false) {
return SW_INCORRECT_PARAMS();
}
if ((key[0] & OATH_TYPE_MASK) != OATH_TYPE_HOTP) {
return SW_DATA_INVALID();
}
if (asn1_find_tag(file_get_data(ef), file_get_size(ef), TAG_IMF, &chal_len,
&chal) == false) {
return SW_INCORRECT_PARAMS();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_RESPONSE, &code_len, &code) == true) {
code_int = (code[0] << 24) | (code[1] << 16) | (code[2] << 8) | code[3];
}
int ret = calculate_oath(0x01, key, key_len, chal, chal_len);
if (ret != CCID_OK) {
return SW_EXEC_ERROR();
}
uint32_t res_int = (res_APDU[2] << 24) | (res_APDU[3] << 16) | (res_APDU[4] << 8) | res_APDU[5];
if (res_APDU[1] == 6) {
res_int %= (uint32_t)1e6;
}
else {
res_int %= (uint32_t)1e8;
}
if (res_int != code_int) {
return SW_WRONG_DATA();
}
res_APDU_size = apdu.ne = 0;
return SW_OK();
}
#define INS_PUT 0x01
#define INS_DELETE 0x02
#define INS_SET_CODE 0x03
@ -488,6 +618,10 @@ int cmd_send_remaining() {
#define INS_VALIDATE 0xa3
#define INS_CALC_ALL 0xa4
#define INS_SEND_REMAINING 0xa5
#define INS_VERIFY_CODE 0xb1
#define INS_VERIFY_PIN 0xb2
#define INS_CHANGE_PIN 0xb3
#define INS_SET_PIN 0xb4
static const cmd_t cmds[] = {
{ INS_PUT, cmd_put },
@ -499,6 +633,10 @@ static const cmd_t cmds[] = {
{ INS_CALCULATE, cmd_calculate },
{ INS_CALC_ALL, cmd_calculate_all },
{ INS_SEND_REMAINING, cmd_send_remaining },
{ INS_SET_PIN, cmd_set_otp_pin },
{ INS_CHANGE_PIN, cmd_change_otp_pin },
{ INS_VERIFY_PIN, cmd_verify_otp_pin },
{ INS_VERIFY_CODE, cmd_verify_hotp },
{ 0x00, 0x0 }
};