diff --git a/src/openpgp/piv.c b/src/openpgp/piv.c index 3a37be2..91e6377 100644 --- a/src/openpgp/piv.c +++ b/src/openpgp/piv.c @@ -100,6 +100,24 @@ static uint8_t piv_default_pin_policy(uint8_t key_ref) { return PINPOLICY_ONCE; } +static bool piv_resolve_policies(uint8_t key_ref, bool has_pin, const tlv_ctx_t *pin, bool has_touch, const tlv_ctx_t *touch, uint8_t *pin_policy, uint8_t *touch_policy) { + *pin_policy = piv_default_pin_policy(key_ref); + *touch_policy = PIV_DEFAULT_TOUCH_POLICY; + if (has_pin) { + if (tlv_len(pin) != 1 || (pin->data[0] != PINPOLICY_NEVER && pin->data[0] != PINPOLICY_ONCE && pin->data[0] != PINPOLICY_ALWAYS)) { + return false; + } + *pin_policy = pin->data[0]; + } + if (has_touch) { + if (tlv_len(touch) != 1 || (touch->data[0] != TOUCHPOLICY_NEVER && touch->data[0] != TOUCHPOLICY_ALWAYS && touch->data[0] != TOUCHPOLICY_CACHED)) { + return false; + } + *touch_policy = touch->data[0]; + } + return true; +} + static bool piv_reference_pair(const uint8_t **old_ref, const uint8_t **new_ref) { if (apdu.nc != 2u * PIV_PIN_WIRE_SIZE) { return false; @@ -1001,11 +1019,15 @@ static int cmd_asym_keygen(void) { } tlv_ctx_t a80 = {0}, aaa = {0}, aab = {0}; tlv_find_tag(&aac, 0x80, &a80); - tlv_find_tag(&aac, 0xAA, &aaa); - tlv_find_tag(&aac, 0xAB, &aab); + bool has_aaa = tlv_find_tag(&aac, 0xAA, &aaa); + bool has_aab = tlv_find_tag(&aac, 0xAB, &aab); if (tlv_len(&a80) == 0) { return SW_WRONG_DATA(); } + uint8_t pin_policy, touch_policy; + if (!piv_resolve_policies(key_ref, has_aaa, &aaa, has_aab, &aab, &pin_policy, &touch_policy)) { + return SW_INCORRECT_PARAMS(); + } uint16_t key_cert = 0; if (key_ref == EF_PIV_KEY_AUTHENTICATION) { key_cert = EF_PIV_AUTHENTICATION; @@ -1074,8 +1096,7 @@ static int cmd_asym_keygen(void) { else { return SW_DATA_INVALID(); } - uint8_t def_pinpol = piv_default_pin_policy(key_ref); - uint8_t meta[] = {a80.data[0], tlv_len(&aaa) ? aaa.data[0] : def_pinpol, tlv_len(&aab) ? aab.data[0] : PIV_DEFAULT_TOUCH_POLICY, ORIGIN_GENERATED}; + uint8_t meta[] = {a80.data[0], pin_policy, touch_policy, ORIGIN_GENERATED}; if (meta_add(key_ref, CONST_BYTE_ARRAY(meta, sizeof(meta))) != PICOKEYS_OK || !flash_commit_sync(PIV_FLASH_COMMIT_TIMEOUT_MS)) { return SW_MEMORY_FAILURE(); } @@ -1483,8 +1504,12 @@ static int cmd_import_asym(void) { } tlv_ctx_t ctxi, aaa = {0}, aab = {0}; tlv_ctx_init(BYTE_ARRAY(apdu.data, apdu.nc), &ctxi); - tlv_find_tag(&ctxi, 0xAA, &aaa); - tlv_find_tag(&ctxi, 0xAB, &aab); + bool has_aaa = tlv_find_tag(&ctxi, 0xAA, &aaa); + bool has_aab = tlv_find_tag(&ctxi, 0xAB, &aab); + uint8_t pin_policy, touch_policy; + if (!piv_resolve_policies(key_ref, has_aaa, &aaa, has_aab, &aab, &pin_policy, &touch_policy)) { + return SW_INCORRECT_PARAMS(); + } if (algo == PIV_ALGO_RSA1024 || algo == PIV_ALGO_RSA2048 || algo == PIV_ALGO_RSA3072 || algo == PIV_ALGO_RSA4096) { tlv_ctx_t a1 = { 0 }, a2 = { 0 }; tlv_find_tag(&ctxi, 0x01, &a1); @@ -1561,8 +1586,7 @@ static int cmd_import_asym(void) { else { return SW_WRONG_DATA(); } - uint8_t def_pinpol = piv_default_pin_policy(key_ref); - uint8_t meta[] = { algo, tlv_len(&aaa) ? aaa.data[0] : def_pinpol, tlv_len(&aab) ? aab.data[0] : PIV_DEFAULT_TOUCH_POLICY, ORIGIN_IMPORTED }; + uint8_t meta[] = { algo, pin_policy, touch_policy, ORIGIN_IMPORTED }; if (meta_add(key_ref, CONST_BYTE_ARRAY(meta, sizeof(meta))) != PICOKEYS_OK || !flash_commit_sync(PIV_FLASH_COMMIT_TIMEOUT_MS)) { return SW_MEMORY_FAILURE(); } diff --git a/tests/piv/test_060_regressions.py b/tests/piv/test_060_regressions.py index c240011..cfdf47a 100644 --- a/tests/piv/test_060_regressions.py +++ b/tests/piv/test_060_regressions.py @@ -154,3 +154,15 @@ def test_piv_generated_key_uses_no_touch_by_default(managed_piv): assert managed_piv.get_slot_metadata(slot).touch_policy == TOUCH_POLICY.NEVER finally: delete_key(managed_piv, slot) + + +@pytest.mark.parametrize("policy", (Tlv(0xAA, b"\x00"), Tlv(0xAB, b"\x00"), Tlv(0xAA, b"\xFF"))) +def test_piv_explicit_zero_or_undefined_policy_is_rejected(managed_piv, policy): + slot = SLOT.RETIRED1 + delete_key(managed_piv, slot) + try: + request = Tlv(0xAC, Tlv(0x80, bytes([KEY_TYPE.ECCP256])) + policy) + assert_apdu_error(lambda: managed_piv.protocol.send_apdu(0, 0x47, 0, slot, request), SW.INCORRECT_PARAMETERS) + assert_apdu_error(lambda: managed_piv.get_slot_metadata(slot), SW.REFERENCE_DATA_NOT_FOUND) + finally: + delete_key(managed_piv, slot)