The PyMRTD is python implementation of ICAO 9303 standard. It implements only parts of standard needed for Port server. That is, parsing (re-serializing) some of MRTD logical data structures (LDS) files and verifying eMRTD trustchain including verification of signature made by MRTD (AA signature). This library doesn't provide APIs and functionalities needed to send commands to and extract data from MRTD.
Library is divided into two modules:
- ef - Elementary file. This module defines LDS data structures e.g.: MRZ, EF.SOD, EF.DG1, EF.DG14 and EF.DG15.
- pki - eMRTD Public Key Infrastructure:
- eMRTD trustchain public key certificates e.g.: CSCA, DSC and master list signer certificate
- Certificate revocation list (CRL)
- CSCA master list
- Functions and procedures to verify digital signatures including RSA ISO9796-2 DSS1
-
Python 3.9 or higher.
Check this website for installation guidelines.
pip3 install asn1crypto
pip3 install cryptography
All data structures that can be parsed from raw byte array (SOD, DG1, CscaCertificate etc...) have defined
static member function load
and member function dump
to serialize data back to byte array (following the interface of library asn1crypto).
Example of loading CscaCertificate from file:
f = open('csca.cer', 'rb')
csca = CscaCertificate.load(f.read())
print(csca.issuerCountry)
print(csca.subjectKey.hex())
print(csca.fingerprint)
print(csca.dump().hex())
All certificate classes (Certificate, CscaCertificate, DocumentSignerCertificate, MasterListSignerCertificate) and also classes implementing MRTD CMS data structure (RFC 5652) have member function verify
defined which verifies digital signature made over an object.
Classes implementing class Certificate
has also defined member function isValidOn(datetime)
which returns True
if certificate is valid on particular date and time.
Example of validating MRTD trustchain:
# 1. Parse SOD and get signing certificates (DSC, CSCA)
sod = SOD.load(...)
if len(sod.dscCertificates) == 0: # SOD is not required to store it's signer DSC certificate.
raise Exception("Can't verify SOD, no DSC found")
dsc = sod.dscCertificates[0] # SOD can store more than 1 DSC certificate by definition
csca = fetchCscaForDsc(sod.dscCertificates[0])
if csca is None:
raise Exception("Can't verify DSC, no CSCA found")
# 2. Validate trust chain by verifying digital signatures and expiration time of certificates
if not csca.isValidOn(utils.time_now()):
raise Exception("CSCA has expired")
if not dsc.isValidOn(utils.time_now()):
raise Exception("DSC has expired")
try:
for si in sod.signers:
# Note: certificate conformance check (checkConformance) is not done by default
# because not all countries follow the standard strictly
dsc.verify(issuing_cert=csca, checkConformance=True/False)
sod.verify(si=si, issuerCert=dsc)
return success
except:
raise Exception("MRTD turstchain verification failed")
Example of verifying MRTD digital signature:
sod = SOD.load(...)
dg15 = DG15.load(...)
# First verify DG15 was issued by country
# Note: SOD object should be validated into trustchain at this point
if not sod.ldsSecurityObject.contains(dg15):
raise Exception("Can't verify signature, invalid EF.DG15 file")
# If ECC signature, get ECC signature algorithm from EF.DG14 file
sigAlgo = None
if dg15.aaPublicKey.isEcKey():
dg14 = SOD.load(...)
if not sod.ldsSecurityObject.contains(dg14): # Verify EF.DG14 was issued by country
raise Exception("Can't verify signature, invalid EF.DG14 file")
elif dg14.aaSignatureAlgo is None: # sanity check
raise Exception("Missing ActiveAuthenticationInfo in DG14 file")
sigAlgo = dg14.aaSignatureAlgo
# Verify signature made by MRTD
try:
dg15.aaPublicKey.verifySignature(msg, sig, sigAlgo):
catch:
raise Exception("Signature verification failed")