diff options
| -rw-r--r-- | greenpass/tests/test_verifier.py | 7 | ||||
| -rw-r--r-- | greenpass/verifier.py | 95 | ||||
| -rw-r--r-- | greenpass/verifiers/base.py | 3 | ||||
| -rw-r--r-- | greenpass/verifiers/eu.py | 93 |
4 files changed, 103 insertions, 95 deletions
diff --git a/greenpass/tests/test_verifier.py b/greenpass/tests/test_verifier.py index 8d030c3..61f01fe 100644 --- a/greenpass/tests/test_verifier.py +++ b/greenpass/tests/test_verifier.py @@ -1,6 +1,7 @@ from pathlib import Path from ..verifier import GreenPassVerifier +from ..verifiers.eu import EuroVerifier SAMPLES_DIR = Path(__file__).parent / "samples" @@ -9,3 +10,9 @@ def test_ramzor_samples(): assert 1 == 1 # v = GreenPassVerifier.from_pdf(str(SAMPLES_DIR / "CovidCertificate_01.pdf")) # assert v.verify() is True + + +def test_eu_cert(): + data = b"HC1:NCFOXN%TSMAHN-H3TK0FWUPKPH1C-C-AH+UC1ROB.S4UOE+I$4QQFG.I5+TO Z0F/8X*G3M9FQH+4JZW49-A3VGNNVR*G0C7PHBO33-IFR7BS7B29BERJGMJ5UN2H30H3FCN+J3FMJ3QBVINOF7Y9BOJJM478UJ:G7H40NQ1:PI0EGO05AZKZ73423ZQT.EJFG37Y4JXITAFG.8H4PSC9EY8IA7BBIZ.CK+GX3E1.BLEELEACI8:NJC19BJCJA9GCHDGSUVB6DJ6SI4OIMEDTJCJKDLEDL9CWZJ$7K+ COCDL%Q:NK%9D$ZJ*DJWP42W5$3UHTP4SE61AQK9:XPX$79GP6GFINOWC51FD$W4SIO9:H12MTNP8EFQJLA-7/:FWKP/HLIJLKNF8JF172LNFPYS+F9XUVM-2DYCYOUG.ICOML2UCNGZ:VXQ3*WU/PMDENLUTG.J7/6RPF8W4LTVFMSM3WHLCV-GY92LDDI:HTYJDA1TWE:8RAEGD50N-9T2" + v = EuroVerifier(data) + assert v.verify() is True diff --git a/greenpass/verifier.py b/greenpass/verifier.py deleted file mode 100644 index b5182e1..0000000 --- a/greenpass/verifier.py +++ /dev/null @@ -1,95 +0,0 @@ -import base64 -import fitz -import json - -from io import BytesIO -from pathlib import Path - -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import padding, ec -from cryptography.exceptions import InvalidSignature -from PIL import Image -from pyzbar import pyzbar - - -class GreenPassVerifier(object): - def __init__(self, data_bytes): - self.validate_bytes(data_bytes) - - sig, self.payload = data_bytes.split(b"#", maxsplit=1) - self.signature = base64.decodebytes(sig) - self.data = json.loads(self.payload) - - self.validate_data() - self.details = self.get_details() - self.digest = self.get_digest() - - self.ec_cert = self.get_cert_path("IL-NB-DSC-01.pem") - self.rsa_cert = self.get_cert_path("RamzorQRPubKey.pem") - - def validate_data(self): - ct = self.data["ct"] - if ct not in (1, 2): - raise Exception(f"Unknown certificate type {ct=}") - - def get_cert_path(self, name): - return Path(__file__).absolute().parent / "certs" / name - - def get_details(self): - details = [] - data = self.data - if data["ct"] == 1: - for i in range(len(data["p"])): - details.append( - { - "id_num": data["p"][i]["idl"], - "valid_by": data["p"][i]["e"], - "cert_id": data["id"], - } - ) - elif data["ct"] == 2: - details.append( - { - "id_num": data["idl"], - "valid_by": data["e"], - "cert_id": data["id"], - } - ) - return details - - def get_digest(self): - ct = self.data["ct"] - if ct == 1: - digest = self.payload.decode().encode("utf8") - elif ct == 2: - h = hashes.Hash(hashes.SHA256()) - h.update(self.payload) - digest = h.finalize() - return digest - - def verify(self): - for d in self.details: - print(f"\tIsraeli ID Number {d['id_num']}") - print(f"\tID valid by {d['valid_by']}") - print(f"\tCert Unique ID {d['cert_id']}") - - certs = [ - [ - self.rsa_cert, - [ - padding.PKCS1v15(), - hashes.SHA256(), - ], - ], - [self.ec_cert, [ec.ECDSA(hashes.SHA256())]], - ] - for cert, method in certs: - with open(cert, "rb") as f: - k = serialization.load_pem_public_key(f.read()) - try: - k.verify(self.signature, self.digest, *method) - return True - except InvalidSignature: - pass - else: - return False diff --git a/greenpass/verifiers/base.py b/greenpass/verifiers/base.py index c8ccdf6..61689f6 100644 --- a/greenpass/verifiers/base.py +++ b/greenpass/verifiers/base.py @@ -4,3 +4,6 @@ class Verifier(object): def verify(self): raise NotImplementedError() + + def get_cert_path(self, name): + return Path(__file__).absolute().parent / "certs" / name diff --git a/greenpass/verifiers/eu.py b/greenpass/verifiers/eu.py index 2c89c2f..1337da5 100644 --- a/greenpass/verifiers/eu.py +++ b/greenpass/verifiers/eu.py @@ -1,6 +1,99 @@ +import base64 +import fitz +import json + +from io import BytesIO +from pathlib import Path + +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import padding, ec +from cryptography.exceptions import InvalidSignature +from PIL import Image +from pyzbar import pyzbar + from .base import Verifier +class GreenPassVerifier(object): + def __init__(self, data_bytes): + self.validate_bytes(data_bytes) + + sig, self.payload = data_bytes.split(b"#", maxsplit=1) + self.signature = base64.decodebytes(sig) + self.data = json.loads(self.payload) + + self.validate_data() + self.details = self.get_details() + self.digest = self.get_digest() + + self.ec_cert = self.get_cert_path("IL-NB-DSC-01.pem") + self.rsa_cert = self.get_cert_path("RamzorQRPubKey.pem") + + def validate_data(self): + ct = self.data["ct"] + if ct not in (1, 2): + raise Exception(f"Unknown certificate type {ct=}") + + def get_details(self): + details = [] + data = self.data + if data["ct"] == 1: + for i in range(len(data["p"])): + details.append( + { + "id_num": data["p"][i]["idl"], + "valid_by": data["p"][i]["e"], + "cert_id": data["id"], + } + ) + elif data["ct"] == 2: + details.append( + { + "id_num": data["idl"], + "valid_by": data["e"], + "cert_id": data["id"], + } + ) + return details + + def get_digest(self): + ct = self.data["ct"] + if ct == 1: + digest = self.payload.decode().encode("utf8") + elif ct == 2: + h = hashes.Hash(hashes.SHA256()) + h.update(self.payload) + digest = h.finalize() + return digest + + def verify(self): + for d in self.details: + print(f"\tIsraeli ID Number {d['id_num']}") + print(f"\tID valid by {d['valid_by']}") + print(f"\tCert Unique ID {d['cert_id']}") + + certs = [ + [ + self.rsa_cert, + [ + padding.PKCS1v15(), + hashes.SHA256(), + ], + ], + [self.ec_cert, [ec.ECDSA(hashes.SHA256())]], + ] + for cert, method in certs: + with open(cert, "rb") as f: + k = serialization.load_pem_public_key(f.read()) + try: + k.verify(self.signature, self.digest, *method) + return True + except InvalidSignature: + pass + else: + return False + + class EuroVerifier(Verifier): def verify(self): return False |
