Skip to content

Instantly share code, notes, and snippets.

@joostd
Created December 1, 2023 11:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joostd/49a35f6f5caccf3b4131c98e97c5089f to your computer and use it in GitHub Desktop.
Save joostd/49a35f6f5caccf3b4131c98e97c5089f to your computer and use it in GitHub Desktop.
Show attributes for a YubiKey PIV attestation certificate
#!/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