diff --git a/keyfinder.py b/keyfinder.py index c6143ef..3fe733b 100755 --- a/keyfinder.py +++ b/keyfinder.py @@ -11,6 +11,7 @@ import re import sys import urllib.parse +import xml import warnings import bs4 @@ -35,6 +36,9 @@ jrex_t = r'{[^{}]*"kty"[^}]*}' jrex = re.compile(jrex_t, flags=re.MULTILINE | re.DOTALL) +xrex_t = r"(?=())" +xrex = re.compile(xrex_t, flags=re.MULTILINE | re.DOTALL) + DNSPRE = "Private-key-format:" dups = set() @@ -170,7 +174,7 @@ def writeperr(perr, fragment, phash, verbose=True): if not os.path.isdir(perr): os.makedirs(perr) fn = f"{perr}/{binascii.hexlify(phash).decode()}" - pathlib.Path(fn).write_text(fragment, encoding="ascii") + pathlib.Path(fn).write_text(fragment, encoding="ascii", errors="replace") if verbose: short = binascii.hexlify(phash).decode()[0:16] print(f"Unparsable candidate {short}") @@ -270,6 +274,25 @@ def getjwk(kstr): return False +def getxkms(kstr): + try: + tree = xml.etree.ElementTree.fromstring(kstr) + except xml.etree.ElementTree.ParseError: + return None + n = tree.find("{*}Modulus") + e = tree.find("{*}Exponent") + d = tree.find("{*}D") + if n is not None and e is not None and d is not None: + n = n.text.replace(" ", "").replace("\n", "").replace("\r", "") + e = e.text.replace(" ", "").replace("\n", "").replace("\r", "") + d = d.text.replace(" ", "").replace("\n", "").replace("\r", "") + n = int.from_bytes(base64.b64decode(n), byteorder="big") + e = int.from_bytes(base64.b64decode(e), byteorder="big") + d = int.from_bytes(base64.b64decode(d), byteorder="big") + return makersa(n, e, d) + return None + + def findkeys(data, perr=None, usebk=False, verbose=False): datastr = data.decode(errors="replace", encoding="ascii") @@ -313,6 +336,21 @@ def findkeys(data, perr=None, usebk=False, verbose=False): if not ckey: writeperr(perr, jkey, phash, verbose=verbose) + xkeys = xrex.findall(datastr) + for xkey in xkeys: + phash = checkphash(xkey, verbose=verbose) + if not phash: + continue + + for kfilter in kfilters: + xfkey = kfilter(xkey) + ckey = getxkms(xfkey) + if ckey: + ckeys.append(ckey) + break + if not ckey: + writeperr(perr, xkey, phash, verbose=verbose) + if DNSPRE in datastr: dkeys = datastr.split(DNSPRE) for keyfrag in dkeys[1:]: diff --git a/runci.sh b/runci.sh index 5f634d4..047cafe 100755 --- a/runci.sh +++ b/runci.sh @@ -1,5 +1,5 @@ #!/bin/bash set -euo pipefail -ruff check --select=ALL --ignore=PTH,ANN,D,ERA,S310,T201,C,PLR,S501,FIX,TD,FBT,I001 keyfinder.py gitkeyfinder +ruff check --select=ALL --ignore=PTH,ANN,D,ERA,S310,T201,C,PLR,S501,FIX,TD,FBT,I001,S314 keyfinder.py gitkeyfinder python -m unittest -v diff --git a/tests/data/valid/xkms-2.0.xml b/tests/data/valid/xkms-2.0.xml new file mode 100644 index 0000000..41d00a9 --- /dev/null +++ b/tests/data/valid/xkms-2.0.xml @@ -0,0 +1,38 @@ + + + + + 0nIsmR+aVW2egl5MIfOKy4HuMKkk9AZ/IQuDLVPlhzOfgngjVQCjr8uvmnqtNu8HBupui8LgG + thO6U9D0CNT5mbmhIAErRADUMIAFsi7LzBarUvNWTqYNEJmcHsAUZdrdcDrkNnG7SzbuJx+GD + NiHKVDQggPBLc1XagW20RMvok= + + AQAB +

+ 7p05u5P4BO+aXdyD/6n31a4Dk9kC4Tv5fMbE15/ioPii9JwPU2J29qhO1QEqvgNwxv67w4jrC + 025Yz5LXgjziw== +

+ + 4ceKAtGgSJg8ddRxwz8OESXVOd1tlSHFu7Gqona3VxbrOONLZEbsnYA4dv4nI+pxl8PmUe5CP + gggGElx30OIuw== + + + ve9rEDQVfaBYCRTKAY2DGJT+hgZ881qxGjCCaXz8gdPIqts6m85KEcchkQ3vvvawI8aLIXdwW + TwSMLxac8y+Rw== + + + jW/x3ggx76gmn+3hAl3a0xUvORukjTrl4snOyg2ylsUNv8prrTrc+WGcfbaDEHXKiTc4bnTiX + He8m1pPEnz9Bw== + + + yxCo+k0v8n80Qeo2QAGKiwltLF+1ObyZ1TQg4chISWdfLD+j1nIKIs1miELdszjO/szLWMx5k + A3kOLi6jXsByw== + + + aeLWu8jh75/zRGdL6T1QFatvfH5uwHXQW4EeZJ00/P0lghEOvgNPWPGkjpaxNtW39GvaaWoJN + pilw9CFL2HHIVn1OVZyw5BDbotQty3lm66KL7qtrjqlqyPu5ARglGqTZIaRyP8LW6NAbkyxLP + npADVfHJuEePmooCmHbTValP0= + +