Created
December 1, 2023 11:17
-
-
Save joostd/49a35f6f5caccf3b4131c98e97c5089f to your computer and use it in GitHub Desktop.
Show attributes for a YubiKey PIV attestation certificate
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# Show attributes for a YubiKey PIV attestation certificate | |
# | |
# Use ykman to generate a PIV attestation certificate for a slot (for instance 9a): | |
# ykman piv keys attest 9a attestation.pem | |
# | |
# To show the attributes in the generated attestation certificate: | |
# ykman script ./check_yubikey_attestation.py attestation.pem | |
from cryptography import x509 | |
from cryptography.hazmat.backends import default_backend | |
import sys | |
# NOTE: uses PEP 634: Structural Pattern Matching | |
# Requires Python 3.10 | |
assert sys.version_info >= (3, 10) | |
# https://developers.yubico.com/PIV/Introduction/PIV_attestation.html | |
class OID: | |
FIRMWARE_VERSION = "1.3.6.1.4.1.41482.3.3" # Octet String | |
SERIAL_NUMBER = "1.3.6.1.4.1.41482.3.7" # Integer | |
PIN_TOUCH_POLICY = "1.3.6.1.4.1.41482.3.8" # | |
FORM_FACTOR = "1.3.6.1.4.1.41482.3.9" # | |
FIPS_CERTIFIED = "1.3.6.1.4.1.41482.3.10" # | |
CSPN_CERTIFIED = "1.3.6.1.4.1.41482.3.11" # | |
pin_policies = [ "undefined", "never", "once", "always" ] | |
touch_policies = [ "undefined", "never", "always", "cache" ] | |
form_factors = [ "undefined", "USB-A Keychain", "USB-A Nano", "USB-C Keychain", "USB-C Nano", "Lightning and USB-C" ] | |
def list_extensions(cert_path): | |
with open(cert_path, 'rb') as cert_file: | |
cert_data = cert_file.read() | |
cert = x509.load_pem_x509_certificate(cert_data, default_backend()) | |
print(f"Subject : { cert.subject.rfc4514_string() }") | |
for ext in cert.extensions: | |
v = ext.value.public_bytes() | |
#assert len(v) == l | |
match ext.oid.dotted_string: | |
case OID.FIRMWARE_VERSION: | |
assert len(v) == 3 | |
print(f" firmware_version : {v[0]}.{v[1]}.{v[2]}") | |
case OID.SERIAL_NUMBER: | |
t, l, *v = v # ASN.1 encoding | |
assert t == 2 # Integer | |
assert l == 4 # 4 octets | |
assert len(v) == 4 | |
serial = int.from_bytes(v, "big") | |
print(f" serial_number : {serial} ({ hex(serial) })") | |
case OID.PIN_TOUCH_POLICY: | |
(pin,touch) = v | |
print(f" policy : PIN={ pin_policies[pin] } Touch={ touch_policies[touch] }") | |
case OID.FORM_FACTOR: | |
ff = v[0] | |
print(f" form factor : { form_factors[ff&0x7f] } { 'FIPS' if ff&0x80 else '' }") | |
case OID.FIPS_CERTIFIED: | |
print(f" FIPS Certified : Yes") | |
case OID.CSPN_CERTIFIED: | |
print(f" CSPN Certified : Yes") | |
case _: | |
print(f" {ext.oid.dotted_string} : value { hex(int.from_bytes(v, 'big')) }") | |
certificate_path = './attestation.pem' | |
if len(sys.argv) > 1: | |
certificate_path = sys.argv[1] | |
else: | |
print(f"usage: {sys.argv[0]} <attestation certificate>") | |
exit() | |
list_extensions(certificate_path) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment