summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2021-12-31 11:45:52 +0200
committerYuval Adam <_@yuv.al>2021-12-31 11:45:52 +0200
commit2679e6c3ebc181677acdd077df409bf73b7dc048 (patch)
tree771ff696194fb777c5cfe5615675351914679314
parentabef9a61beef6f55772a1230b0d520009227ce5e (diff)
Lots of refactoring WIP
-rw-r--r--README.md23
-rw-r--r--greenpass/certificate.py58
-rw-r--r--greenpass/cli.py7
-rw-r--r--greenpass/tests/samples/CovidCertificate_01.pdfbin0 -> 535274 bytes
-rw-r--r--greenpass/tests/samples/CovidCertificate_02.pdfbin0 -> 544440 bytes
-rw-r--r--greenpass/tests/samples/CovidCertificate_2_vac.pdfbin0 -> 544572 bytes
-rw-r--r--greenpass/tests/samples/CovidCertificate_3_vac.pdfbin0 -> 545364 bytes
-rw-r--r--greenpass/tests/test_verifier.py5
-rw-r--r--greenpass/verifier.py32
-rw-r--r--greenpass/verifiers/eu.py2
-rw-r--r--greenpass/verifiers/il.py2
11 files changed, 79 insertions, 50 deletions
diff --git a/README.md b/README.md
index 22c7c7f..e75e1ef 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,11 @@
-# Green Pass - התו הירוק
+# Green Pass
-Signature verification for the Green Pass (התו הירוק).
+COVID pass verification library and CLI. Implements verification for:
-As (not really) specified in https://github.com/MohGovIL/Ramzor
+ - [EU Digital COVID certificates](https://ec.europa.eu/health/ehealth/covid-19_en)
+ - [Legacy Israeli Green Pass certificates](https://github.com/MohGovIL/Ramzor)
-## Verification Script
-
-A pythonic implementation of the verification process can be found in [`verify.py`](verify.py).
-
-### Setup
+## Setup
Install [`pipenv`](https://pipenv.pypa.io/en/latest/) and sync the required dependencies:
@@ -16,11 +13,11 @@ Install [`pipenv`](https://pipenv.pypa.io/en/latest/) and sync the required depe
$ pipenv sync
```
-### Usage
+### CLI Usage
-Generate a Green Pass or Vaccination Certificate at https://corona.health.gov.il/green-pass/
+The CLI tool accepts PDF files, PNG images and raw QR code decoded contents.
-#### Option 1 - from PDF
+#### PDF Certificates
Directly verify your `GreenPass.pdf` or `VaccinationCertificate.pdf`:
@@ -28,7 +25,7 @@ Directly verify your `GreenPass.pdf` or `VaccinationCertificate.pdf`:
$ pipenv run greenpass -p GreenPass.pdf
```
-#### Option 2 - from QR code image
+#### QR Code Images
Save the QR code as a `.png` file (e.g. from screenshot) and execute:
@@ -36,7 +33,7 @@ Save the QR code as a `.png` file (e.g. from screenshot) and execute:
$ pipenv run greenpass -i green_pass_image.png
```
-#### Option 3 - from QR code decoded textual content
+#### Decoded QR Code Content
Decode the QR code payload yourself, put it in a txt file and then execute:
diff --git a/greenpass/certificate.py b/greenpass/certificate.py
new file mode 100644
index 0000000..37d97d2
--- /dev/null
+++ b/greenpass/certificate.py
@@ -0,0 +1,58 @@
+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 .verifiers.il import IsraeliVerifier
+from .verifiers.eu import EuroVerifier
+
+
+class CertificateData(object):
+ def __init__(self, data):
+ self.data = data_bytes
+
+ @classmethod
+ def from_payload(cls, path):
+ with open(path, "rb") as f:
+ return cls(f.read().strip())
+
+ @classmethod
+ def from_qr(cls, path):
+ return cls(pyzbar.decode(Image.open(path))[0].data)
+
+ @classmethod
+ def from_pdf(cls, path):
+ doc = fitz.open(path)
+ for i in range(len(doc)):
+ for img in doc.get_page_images(i):
+ xref, width = img[0], img[2]
+ try:
+ img = fitz.Pixmap(doc, xref)
+ data = img.tobytes(output="png")
+ with open(f"/tmp/greenpass/{xref}.png", "wb") as f:
+ f.write(data)
+ return cls.from_qr(BytesIO(data))
+ except IndexError:
+ pass
+ else:
+ raise Exception("No QR found")
+
+ def verify(self):
+ # EU certs start with HC1
+ if self.data.startswith("HC1"):
+ return EuroVerifier(self.data)
+ else:
+ try:
+ # Legacy IL certs split the sig and the payload with '#'
+ _sig, _payload = self.data.split(b"#", maxsplit=1)
+ return IsraeliVerifier(self.data)
+ except ValueError:
+ raise Exception(f"Unknown certificate data: {self.data}")
diff --git a/greenpass/cli.py b/greenpass/cli.py
index 165ae38..6df49d0 100644
--- a/greenpass/cli.py
+++ b/greenpass/cli.py
@@ -1,5 +1,6 @@
import click
+from .certificate import CertificateData
from .verifier import GreenPassVerifier
@@ -19,11 +20,11 @@ from .verifier import GreenPassVerifier
)
def verify(pdf_path="", image_path="", txt_path=""):
if image_path:
- verifier = GreenPassVerifier.from_qr(image_path)
+ data = CertificateData.from_qr(image_path)
elif pdf_path:
- verifier = GreenPassVerifier.from_pdf(pdf_path)
+ data = CertificateData.from_pdf(pdf_path)
elif txt_path:
- verifier = GreenPassVerifier.from_payload(txt_path)
+ data = CertificateData.from_payload(txt_path)
else:
ctx = click.get_current_context()
click.echo(ctx.get_help())
diff --git a/greenpass/tests/samples/CovidCertificate_01.pdf b/greenpass/tests/samples/CovidCertificate_01.pdf
new file mode 100644
index 0000000..66b8763
--- /dev/null
+++ b/greenpass/tests/samples/CovidCertificate_01.pdf
Binary files differ
diff --git a/greenpass/tests/samples/CovidCertificate_02.pdf b/greenpass/tests/samples/CovidCertificate_02.pdf
new file mode 100644
index 0000000..20b041c
--- /dev/null
+++ b/greenpass/tests/samples/CovidCertificate_02.pdf
Binary files differ
diff --git a/greenpass/tests/samples/CovidCertificate_2_vac.pdf b/greenpass/tests/samples/CovidCertificate_2_vac.pdf
new file mode 100644
index 0000000..b6436d3
--- /dev/null
+++ b/greenpass/tests/samples/CovidCertificate_2_vac.pdf
Binary files differ
diff --git a/greenpass/tests/samples/CovidCertificate_3_vac.pdf b/greenpass/tests/samples/CovidCertificate_3_vac.pdf
new file mode 100644
index 0000000..88d1f07
--- /dev/null
+++ b/greenpass/tests/samples/CovidCertificate_3_vac.pdf
Binary files differ
diff --git a/greenpass/tests/test_verifier.py b/greenpass/tests/test_verifier.py
index 2a44b3e..ae58efc 100644
--- a/greenpass/tests/test_verifier.py
+++ b/greenpass/tests/test_verifier.py
@@ -5,5 +5,6 @@ from ..verifier import GreenPassVerifier
SAMPLES_DIR = Path(__file__).parent / "samples"
-def test_good():
- assert 1 == 1
+def test_ramzor_samples():
+ v = GreenPassVerifier.from_pdf(str(SAMPLES_DIR / "CovidCertificate_01.pdf"))
+ assert v.verify() is True
diff --git a/greenpass/verifier.py b/greenpass/verifier.py
index ec9cf69..abe6a4b 100644
--- a/greenpass/verifier.py
+++ b/greenpass/verifier.py
@@ -27,41 +27,9 @@ class GreenPassVerifier(object):
self.ec_cert = self.get_cert_path("IL-NB-DSC-01.pem")
self.rsa_cert = self.get_cert_path("RamzorQRPubKey.pem")
- @classmethod
- def from_payload(cls, path):
- with open(path, "rb") as f:
- return cls(f.read().strip())
-
- @classmethod
- def from_qr(cls, path):
- return cls(pyzbar.decode(Image.open(path))[0].data)
-
- @classmethod
- def from_pdf(cls, path):
- doc = fitz.open(path)
- for i in range(len(doc)):
- for img in doc.get_page_images(i):
- xref, width = img[0], img[2]
- try:
- img = fitz.Pixmap(doc, xref)
- data = img.tobytes(output="png")
- with open(f"/tmp/greenpass/{xref}.png", "wb") as f:
- f.write(data)
- return cls.from_qr(BytesIO(data))
- except IndexError:
- pass
- else:
- raise Exception("No QR found")
-
def validate_bytes(self, bs):
if bs.decode().startswith("GreenPass"):
raise Exception("Green pass QR code contains no signature to verify")
- # click.secho(
- # "⚠️ ",
- # fg="yellow",
- # bold=True,
- # )
- # click.get_current_context().exit()
def validate_data(self):
ct = self.data["ct"]
diff --git a/greenpass/verifiers/eu.py b/greenpass/verifiers/eu.py
new file mode 100644
index 0000000..20ac69e
--- /dev/null
+++ b/greenpass/verifiers/eu.py
@@ -0,0 +1,2 @@
+class EuroVerifier(object):
+ pass
diff --git a/greenpass/verifiers/il.py b/greenpass/verifiers/il.py
new file mode 100644
index 0000000..c221e91
--- /dev/null
+++ b/greenpass/verifiers/il.py
@@ -0,0 +1,2 @@
+class IsraeliVerifier(object):
+ pass