lib/android_key_attestation/authorization_list.rb
# frozen_string_literal: true module AndroidKeyAttestation class AuthorizationList # https://source.android.com/security/keystore/attestation#attestation-extension PURPOSE_TAG = 1 ALGORITHM_TAG = 2 KEY_SIZE_TAG = 3 DIGEST_TAG = 5 PADDING_TAG = 6 EC_CURVE_TAG = 10 RSA_PUBLIC_EXPONENT_TAG = 200 ROLLBACK_RESISTANCE_TAG = 303 ACTIVE_DATE_TIME_TAG = 400 ORIGINATION_EXPIRE_DATE_TIME_TAG = 401 USAGE_EXPIRE_DATE_TIME_TAG = 402 NO_AUTH_REQUIRED_TAG = 503 USER_AUTH_TYPE_TAG = 504 AUTH_TIMEOUT_TAG = 505 ALLOW_WHILE_ON_BODY_TAG = 506 TRUSTED_USER_PRESENCE_REQUIRED_TAG = 507 TRUSTED_CONFIRMATION_REQUIRED_TAG = 508 UNLOCK_DEVICE_REQUIRED_TAG = 509 ALL_APPLICATIONS_TAG = 600 APPLICATION_ID_TAG = 601 CREATION_DATE_TIME_TAG = 701 ORIGIN_TAG = 702 ROOT_OF_TRUST_TAG = 704 OS_VERSION_TAG = 705 OS_PATCH_LEVEL_TAG = 706 ATTESTATION_APPLICATION_ID_TAG = 709 ATTESTATION_ID_BRAND_TAG = 710 ATTESTATION_ID_DEVICE_TAG = 711 ATTESTATION_ID_PRODUCT_TAG = 712 ATTESTATION_ID_SERIAL_TAG = 713 ATTESTATION_ID_IMEI_TAG = 714 ATTESTATION_ID_MEID_TAG = 715 ATTESTATION_ID_MANUFACTURER_TAG = 716 ATTESTATION_ID_MODEL_TAG = 717 VENDOR_PATCH_LEVEL_TAG = 718 BOOT_PATCH_LEVEL_TAG = 719 # https://source.android.com/security/keystore/tags PURPOSE_ENUM = { 0 => :encrypt, 1 => :decrypt, 2 => :sign, 3 => :verify, 4 => :derive_key, 5 => :wrap_key, }.freeze ORIGIN_ENUM = { 0 => :generated, 1 => :derived, 2 => :imported, 3 => :unknown, }.freeze def initialize(sequence) @sequence = sequence end def purpose integers = find_optional_integer_set(PURPOSE_TAG) integers&.map { |i| PURPOSE_ENUM.fetch(i) } end def all_applications find_boolean(ALL_APPLICATIONS_TAG) end def creation_date find_time_milliseconds(CREATION_DATE_TIME_TAG) end def origin integer = find_optional_integer(ORIGIN_TAG) ORIGIN_ENUM.fetch(integer) if integer end def find_by_tag(tag) sequence.detect { |data| data.tag == tag } end private attr_reader :sequence def find_optional_integer_set(tag) value = find_by_tag(tag)&.value&.at(0)&.value value&.map { |asn1_int| asn1_int.value.to_i } end def find_optional_integer(tag) find_by_tag(tag)&.value&.at(0)&.value&.to_i end def find_boolean(tag) find_by_tag(tag)&.value || false end def find_time_milliseconds(tag) value = find_by_tag(tag)&.value&.at(0)&.value Time.at(value.to_i / 1000) if value end end end