瀏覽代碼

add testdata + ehc decrypter

Noah Vogt 3 年之前
父節點
當前提交
acaac4b95f
共有 5 個文件被更改,包括 130 次插入1 次删除
  1. 2 1
      README.md
  2. 96 0
      src/decrypt.py
  3. 31 0
      testdata/emma-pearson-2-doses-valid.json
  4. 二進制
      testdata/emma-pearson-2-doses-valid.png
  5. 1 0
      testdata/emma-pearson-2-doses-valid.txt

+ 2 - 1
README.md

@@ -1 +1,2 @@
-# ehc-tools
+# ehc-tools
+This is a repository for tools regarding the European Health Certificate (Digital Green Pass).

+ 96 - 0
src/decrypt.py

@@ -0,0 +1,96 @@
+#!/usr/bin/env python3
+import json, sys, zlib, base45, cbor2, optparse, os
+
+# helper functions
+
+def verbosePrint(desc, text):
+    if verboseMode: print("\n[Verbose:] {}\n{}".format(desc, text))
+
+def checkFilePath(file):
+    if not( os.path.isfile(file) and os.access(file, os.R_OK)):
+        print('Error: File does not exist or is not readable/accessable')
+        exit()
+
+def getSecondParameter(shortOption, longOption):
+    successIndex = -10 # arbitrary number to check for changes later
+    args = sys.argv
+    for i in range((len(args))):
+        if args[i] == shortOption or args[i] == longOption:
+            successIndex = i + 1
+            break
+    if successIndex == -10 or successIndex + 1 > len(args):
+        print("Error: Please enter valid i/o parameters\nHint: run 'python decrypt.py -h'")
+        exit(1)
+    else:
+        return args[successIndex]
+
+# define cli options
+cli_parser = optparse.OptionParser()
+cli_parser.set_defaults(debug=False)
+cli_parser.add_option('-v', '--verbose', action='store_true', dest='verbose',
+    help='prints status messages to stdout')
+cli_parser.add_option('-i', '--image', action='store_true', dest='image',
+    help='specify an image as input with the next argument')
+cli_parser.add_option('-t', '--text', action='store_true', dest='text',
+    help='specify text as input with the next argument')
+cli_parser.add_option('-f', '--file', action='store_true', dest='file',
+    help='specify a text file as input with the next argument')
+(options, args) = cli_parser.parse_args()
+
+# parse cli options
+
+# check for verbose mode
+if options.verbose:
+    verboseMode = True
+    print('[Verbose:] verbose mode activated')
+else:
+    verboseMode = False
+
+# save/convert input to string 'qrdata'
+if options.image and options.text is None and options.file is None:
+    imageName = getSecondParameter('-i', '--image')
+    checkFilePath(imageName)
+
+    from pyzbar.pyzbar import decode
+    from PIL import Image
+
+    try:
+        img = Image.open(imageName)
+        decode = decode(img.convert('RGBA')) # convert to RBGA image to avoid PIL warning
+        qrdata = decode[0].data.decode('ascii')
+    except:
+        print("Error: Error while opening your image. Are you sure it is actually a QR Code?")
+        exit()
+elif options.text and options.image is None and options.file is None:
+    qrdata = getSecondParameter('-t', '--text')
+elif options.file and options.image is None and options.text is None:
+    fileName = getSecondParameter('-f', '--file')
+    checkFilePath(fileName)
+
+    with open(fileName, 'r') as fileopener:
+        qrdata = fileopener.read()
+else:
+    print("Error: Please specify (only one!) qr code i/o argument\nHint: run 'python decrypt.py -h'")
+    exit(1)
+
+# remove Context Identifier string (usually 'HCI:')
+qrdata = qrdata[qrdata.find(':') + 1:]
+verbosePrint('raw QR code data', qrdata)
+
+# decrypt payload
+try:
+    base45data = base45.b45decode(str(qrdata))
+    verbosePrint('Base45 decoded data', base45data)
+    zlibdata = zlib.decompress(base45data)
+    verbosePrint('Zlib decompressed data', base45data)
+    cosedata = cbor2.loads(zlibdata)
+    verbosePrint("COSE 'sign1' signed data", cosedata)
+    payload = cbor2.loads(cosedata.value[2])
+except:
+    print("Error: Error while decrypting your string. Are you sure you entered it correctly?")
+    exit(1)
+
+# print payload as formatted json
+prettyPayload = json.dumps(payload, indent=4, sort_keys=True, ensure_ascii=False).encode('utf8')
+if verboseMode: print('\n[Verbose:] formatted JSON data')
+print(prettyPayload.decode())

+ 31 - 0
testdata/emma-pearson-2-doses-valid.json

@@ -0,0 +1,31 @@
+{
+    "-260": {
+        "1": {
+            "dob": "1980-01-03",
+            "nam": {
+                "fn": "PEARSON",
+                "fnt": "PEARSON",
+                "gn": "EMMA",
+                "gnt": "EMMA"
+            },
+            "v": [
+                {
+                    "ci": "urn:uvci:01:FR:LXDMWSLQHR91#Y",
+                    "co": "FR",
+                    "dn": 2,
+                    "dt": "2021-06-22",
+                    "is": "CNAM",
+                    "ma": "ORG-100030215",
+                    "mp": "EU/1/20/1528",
+                    "sd": 2,
+                    "tg": "840539006",
+                    "vp": "J07BX03"
+                }
+            ],
+            "ver": "1.3.0"
+        }
+    },
+    "1": "CNAM",
+    "4": 1686693600,
+    "6": 1624622345
+}

二進制
testdata/emma-pearson-2-doses-valid.png


+ 1 - 0
testdata/emma-pearson-2-doses-valid.txt

@@ -0,0 +1 @@
+NC1:6BFOXN%TSMAHN-H6SKJPT.-7G2TZ978S8ELBXEJW.TFJTXG41UQR$TTSJHSOYUKK1JZZPQA36S4HZ6SH9X5Q9AIMZ5BTMUW5-5QNF6O MOL1YTUNCEDT1U4VC%UZNM1VA0T9U3QZIESH9UKPSH9WC5PF6846A$QY76SW6LK9KP5AUJIZI.EJJ14B2MZ8DC8CPQ1AX67PPDFPVX1R270:6NEQ0R6AOMUF5LDCPF5RBQ746B46O1N646RM9AL5CBVW566LH 469/9-3AKI63ZMLEQQ008RD$ZJ*DJWP42W5D 7$.QG75U9SR95Y16N1R99RCA7T5MN*4:ZJ::AV72UM97H98$QP3R8BHL45VZHM9VBYPKYPR3RJDP32SHJCLGU7.U95BF2VK*VWN4MOCT9OT$S4%IH9O74W -USNP861LYGC5GUI72-DFP7TPSRXTQ0WNHFLUJJ00+S7F5