Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dg2, dg7 and dg11 #3

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
python_requires=">=3.9.11",
install_requires=[
'asn1crypto>=1.5.1',
'cryptography>=41.0.3'
'cryptography==41.0.3'
],
extras_require={
'tests': [
Expand Down
34 changes: 13 additions & 21 deletions src/pymrtd/ef/__init__.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,28 @@
from .base import (
ElementaryFile,
ElementaryFileError,
LDSVersionInfo
)
from .base import ElementaryFile, ElementaryFileError, LDSVersionInfo

from .dg import (
DataGroup,
DataGroupNumber,
DG1,
DG14,
DG15
)
from .dg import DataGroup, DataGroupNumber, DG1, DG2, DG14, DG15

from .mrz import (
MachineReadableZone
)
from .mrz import MachineReadableZone

from .sod import (
SOD,
SODError
)
from .dg2 import DataGroup2

from .sod import SOD, SODError

from .errors import NFCPassportReaderError
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you revert include style back to original.


__all__ = [
"DataGroup",
"DataGroupNumber",
"DG1",
"DG2",
"DG14",
"DG15",
"ElementaryFile",
"ElementaryFileError",
"LDSVersionInfo",
"MachineReadableZone",
"DataGroup2",
"SOD",
"SODError"
]
"SODError",
"NFCPassportReaderError",
]
150 changes: 83 additions & 67 deletions src/pymrtd/ef/dg.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,96 +2,98 @@
from asn1crypto.util import int_from_bytes
from asn1crypto.keys import PublicKeyInfo
from pymrtd.pki import keys, oids
from typing import Union #pylint: disable=wrong-import-order
from typing import Union # pylint: disable=wrong-import-order

from .base import ElementaryFile
from .mrz import MachineReadableZone
from .dg2 import DataGroup2


class ActiveAuthenticationInfoId(asn1.ObjectIdentifier):
_map = {
oids.id_icao_mrtd_security_aaProtocolObject: 'aa_info',
oids.id_icao_mrtd_security_aaProtocolObject: "aa_info",
}


class ActiveAuthenticationInfo(asn1.Sequence):
_fields = [
('protocol', ActiveAuthenticationInfoId),
('version', asn1.Integer),
('signature_algorithm', keys.SignatureAlgorithmId)
("protocol", ActiveAuthenticationInfoId),
("version", asn1.Integer),
("signature_algorithm", keys.SignatureAlgorithmId),
]


class ChipAuthenticationInfoId(asn1.ObjectIdentifier):
_map = {
oids.id_CA_DH_3DES_CBC_CBC : 'ca_dh_3des_cbc_cbc',
oids.id_CA_DH_AES_CBC_CMAC_128 : 'ca_dh_aes_cbc_cmac_128',
oids.id_CA_DH_AES_CBC_CMAC_192 : 'ca_dh_aes_cbc_cmac_192',
oids.id_CA_DH_AES_CBC_CMAC_256 : 'ca_dh_aes_cbc_cmac_256',
oids.id_CA_ECDH_3DES_CBC_CBC : 'ca_ecdh_3des_cbc_cbc',
oids.id_CA_ECDH_AES_CBC_CMAC_128 : 'ca_ecdh_aes_cbc_cmac_128',
oids.id_CA_ECDH_AES_CBC_CMAC_192 : 'ca_ecdh_aes_cbc_cmac_192',
oids.id_CA_ECDH_AES_CBC_CMAC_256 : 'ca_ecdh_aes_cbc_cmac_256'
oids.id_CA_DH_3DES_CBC_CBC: "ca_dh_3des_cbc_cbc",
oids.id_CA_DH_AES_CBC_CMAC_128: "ca_dh_aes_cbc_cmac_128",
oids.id_CA_DH_AES_CBC_CMAC_192: "ca_dh_aes_cbc_cmac_192",
oids.id_CA_DH_AES_CBC_CMAC_256: "ca_dh_aes_cbc_cmac_256",
oids.id_CA_ECDH_3DES_CBC_CBC: "ca_ecdh_3des_cbc_cbc",
oids.id_CA_ECDH_AES_CBC_CMAC_128: "ca_ecdh_aes_cbc_cmac_128",
oids.id_CA_ECDH_AES_CBC_CMAC_192: "ca_ecdh_aes_cbc_cmac_192",
oids.id_CA_ECDH_AES_CBC_CMAC_256: "ca_ecdh_aes_cbc_cmac_256",
}


class ChipAuthenticationInfo(asn1.Sequence):
_fields = [
('protocol', ChipAuthenticationInfoId),
('version', asn1.Integer),
('key_id', asn1.Integer, {'optional': True})
("protocol", ChipAuthenticationInfoId),
("version", asn1.Integer),
("key_id", asn1.Integer, {"optional": True}),
]


class ChipAuthenticationPublicKeyInfoId(asn1.ObjectIdentifier):
_map = {
oids.id_PK_DH : 'pk_dh',
oids.id_PK_ECDH : 'pk_ecdh'
}
_map = {oids.id_PK_DH: "pk_dh", oids.id_PK_ECDH: "pk_ecdh"}


class ChipAuthenticationPublicKeyInfo(asn1.Sequence):
_fields = [
('protocol', ChipAuthenticationPublicKeyInfoId),
('chip_auth_public_key', PublicKeyInfo),
('key_id', asn1.Integer, {'optional': True})
("protocol", ChipAuthenticationPublicKeyInfoId),
("chip_auth_public_key", PublicKeyInfo),
("key_id", asn1.Integer, {"optional": True}),
]


class DefaultSecurityInfo(asn1.Sequence):
_fields = [
('protocol', asn1.ObjectIdentifier),
('required_data', asn1.Any),
('optional', asn1.Any, {'optional': True})
("protocol", asn1.ObjectIdentifier),
("required_data", asn1.Any),
("optional", asn1.Any, {"optional": True}),
]


class SecurityInfo(asn1.Choice):
_alternatives = [
('security_info', DefaultSecurityInfo),
('aa_info', ActiveAuthenticationInfo),
('chip_auth_info', ChipAuthenticationInfo),
('chip_auth_pub_key_info', ChipAuthenticationPublicKeyInfo)
#Note: Missing PACEDomainParameterInfo and PACEInfo
("security_info", DefaultSecurityInfo),
("aa_info", ActiveAuthenticationInfo),
("chip_auth_info", ChipAuthenticationInfo),
("chip_auth_pub_key_info", ChipAuthenticationPublicKeyInfo),
# Note: Missing PACEDomainParameterInfo and PACEInfo
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use single quote mark.

]

def validate(self, class_, tag, contents):
""" this function select proper SecurityInfo choice index based on OID """
"""this function select proper SecurityInfo choice index based on OID"""
oid = asn1.ObjectIdentifier.load(contents).dotted

self._choice = 0
for index, info in enumerate(self._alternatives):
toidm = info[1]._fields[0][1]._map #pylint: disable=protected-access
toidm = info[1]._fields[0][1]._map # pylint: disable=protected-access
if toidm is not None and oid in toidm:
self._choice = index
return

def parse(self):
if self._parsed is None:
super().parse()
if self.name == 'aa_info' or self.name == 'chip_auth_info':
if self._parsed['version'].native != 1:
from asn1crypto._types import type_name #pylint: disable=import-outside-toplevel
raise ValueError(f'{type_name(self._parsed)} version != 1')
if self.name == "aa_info" or self.name == "chip_auth_info":
if self._parsed["version"].native != 1:
from asn1crypto._types import (
type_name,
) # pylint: disable=import-outside-toplevel

raise ValueError(f"{type_name(self._parsed)} version != 1")
return self._parsed


Expand All @@ -100,26 +102,26 @@ class SecurityInfos(asn1.SetOf):


class DataGroupNumber(asn1.Integer):
min = 1 # DG min value
max = 16 # DG max value
min = 1 # DG min value
max = 16 # DG max value

_map = {
1: 'EF.DG1',
2: 'EF.DG2',
3: 'EF.DG3',
4: 'EF.DG4',
5: 'EF.DG5',
6: 'EF.DG6',
7: 'EF.DG7',
8: 'EF.DG8',
9: 'EF.DG9',
10: 'EF.DG10',
11: 'EF.DG11',
12: 'EF.DG12',
13: 'EF.DG13',
14: 'EF.DG14',
15: 'EF.DG15',
16: 'EF.DG16'
1: "EF.DG1",
2: "EF.DG2",
3: "EF.DG3",
4: "EF.DG4",
5: "EF.DG5",
6: "EF.DG6",
7: "EF.DG7",
8: "EF.DG8",
9: "EF.DG9",
10: "EF.DG10",
11: "EF.DG11",
12: "EF.DG12",
13: "EF.DG13",
14: "EF.DG14",
15: "EF.DG15",
16: "EF.DG16",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use single quote mark.

}

@property
Expand All @@ -138,12 +140,12 @@ def __ne__(self, other) -> bool:

def set(self, value):
if isinstance(value, int):
if value == 21: # DG2 tag
if value == 21: # DG2 tag
value = 2
elif value == 22: # DG4 tag
elif value == 22: # DG4 tag
value = 4
elif value not in DataGroupNumber._map:
raise ValueError('Invalid data group number')
raise ValueError("Invalid data group number")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use single quote mark.

super().set(value)

def __hash__(self) -> int:
Expand All @@ -159,8 +161,9 @@ def __str__(self):
Returns string representation of self i.e. EF.DG<No>(fp=XXXXXXXXXXXXXXXX)
"""
if self._str_rep is None:
self._str_rep = super().__str__()\
.replace("EF(", f'{self.number.native}(', 1)
self._str_rep = (
super().__str__().replace("EF(", f"{self.number.native}(", 1)
)
return self._str_rep

@property
Expand All @@ -178,7 +181,20 @@ def mrz(self) -> MachineReadableZone:

@property
def native(self):
return { 'mrz': self.mrz.native }
return {"mrz": self.mrz.native}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use single quote mark.



class DG2(DataGroup):
tag = 21
_content_spec = DataGroup2

@property
def portrait(self) -> DataGroup2:
return self.content

@property
def native(self):
return {"portrait": self.portrait}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, use single quote mark.



class DG14(DataGroup):
Expand All @@ -187,7 +203,7 @@ class DG14(DataGroup):

@property
def aaInfo(self) -> Union[ActiveAuthenticationInfo, None]:
''' Returns ActiveAuthenticationInfo if in list otherwise None. '''
"""Returns ActiveAuthenticationInfo if in list otherwise None."""

# Loop over list of SecurityInfo objects and try to find ActiveAuthentication object
# Should contain only one ActiveAuthenticationInfo
Expand All @@ -198,14 +214,14 @@ def aaInfo(self) -> Union[ActiveAuthenticationInfo, None]:

@property
def aaSignatureAlgo(self) -> keys.SignatureAlgorithm:
''' Returns SignatureAlgorithm object or None if DG doesn't contain one. '''
"""Returns SignatureAlgorithm object or None if DG doesn't contain one."""

aai = self.aaInfo
if aai is None:
return None

# Get signature algorithm
return keys.SignatureAlgorithm({ 'algorithm' : aai.native['signature_algorithm'] })
return keys.SignatureAlgorithm({"algorithm": aai.native["signature_algorithm"]})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use single quote mark.



class DG15(DataGroup):
Expand All @@ -215,12 +231,12 @@ class DG15(DataGroup):

@property
def aaPublicKeyInfo(self) -> PublicKeyInfo:
''' Returns active authentication public key info '''
"""Returns active authentication public key info"""
return self.content

@property
def aaPublicKey(self) -> keys.AAPublicKey:
''' Returns active authentication public key '''
if not hasattr(self, '_aakey'):
"""Returns active authentication public key"""
if not hasattr(self, "_aakey"):
self._aakey = keys.AAPublicKey.load(self.aaPublicKeyInfo.dump())
return self._aakey
Loading
Loading