From 6646edc6e113984f5f4d0b2811b608dcdd997878 Mon Sep 17 00:00:00 2001 From: Ruben Vermeersch Date: Thu, 3 Aug 2017 08:04:26 +0200 Subject: [PATCH] Initial commit --- .gitignore | 3 + convert.py | 198 +++++++++++++++++++++++++++++++++++++++++++++++++++++ dump.py | 6 ++ in.kdbx | Bin 0 -> 1118 bytes 4 files changed, 207 insertions(+) create mode 100644 .gitignore create mode 100644 convert.py create mode 100644 dump.py create mode 100644 in.kdbx diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bc564cb --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.*.swp +/env +*.1pif diff --git a/convert.py b/convert.py new file mode 100644 index 0000000..6291ce5 --- /dev/null +++ b/convert.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python + +import os +import datetime +import shutil +import json +import pprint + +from pykeepass import PyKeePass +from urlparse import urlparse + +#until = 281 +until = 20000 + +shutil.copyfile("in.kdbx", "out.kdbx") + +kp = PyKeePass("out.kdbx", password="test") + +groupLabels = { + "passwords.Password": "Passwords", + "webforms.WebForm": "Logins", + "wallet.membership.Membership": "Memberships", + "securenotes.SecureNote": "Notes", + "wallet.government.Passport": "Passports", + "wallet.computer.UnixServer": "Servers", + "wallet.computer.Router": "Routers", + "wallet.financial.BankAccountUS": "Bank Accounts", + "wallet.financial.CreditCard": "Credit Cards", + "wallet.computer.License": "Licenses", +} +groups = {} + +def getGroup(item): + group = groups.get(item["typeName"]) + if group: + return group + + label = groupLabels.get(item["typeName"]) + if not label: + pprint.pprint(item) + raise Exception("Unknown type name {}".format(item["typeName"])) + + group = kp.add_group(kp.root_group, label) + groups[item["typeName"]] = group + return group + +def getField(item, designation): + secure = item["secureContents"] + if "fields" in secure: + for field in secure["fields"]: + d = field.get("designation") + if d == designation: + return field["value"] + + return None + + +with open("in.1pif/data.1pif", "r") as fp: + data = fp.read().strip().split("***5642bee8-a5ff-11dc-8314-0800200c9a66***") + +done = 0 +for line in data: + if line.strip() == "": + continue + + item = json.loads(line.strip()) + if item.get("trashed"): + continue + + group = getGroup(item) + if done == until - 1: + pprint.pprint(item) + pprint.pprint(group) + + entry = kp.add_entry(group, item["title"], "", "") + secure = item["secureContents"] + + # Username + if "username" in secure: + entry.username = secure["username"] + else: + entry.username = getField(item, "username") + + # Password + if "password" in secure: + entry.password = secure["password"] + else: + entry.password = getField(item, "password") + + # Other web fields + if "fields" in secure: + for field in secure["fields"]: + d = field.get("designation") + if d != "username" and d != "password": + entry.set_custom_property("Web field: {}".format(field["name"]), field["value"]) + + # Password history + if "passwordHistory" in secure: + for p in secure["passwordHistory"]: + d = datetime.datetime.fromtimestamp(p["time"]) + entry.set_custom_property("Password history ({})".format(d), p["value"]) + + # Find URL in fields + if not entry.url: + if "htmlAction" in secure: + entry.url = secure["htmlAction"] + + # Membership fields + if "membership_no" in secure and not entry.username: + entry.username = secure["membership_no"] + + # Passport fields + if "number" in secure and not entry.username: + entry.username = secure["number"] + + # Router fields + if "network_name" in secure and not entry.username: + entry.username = secure["network_name"] + if "wireless_password" in secure and not entry.password: + entry.password = secure["wireless_password"] + + # Bank account + if "iban" in secure and not entry.username: + entry.username = secure["iban"] + if "swift" in secure and not entry.username: + entry.username = secure["swift"] + if "routingNo" in secure and not entry.username: + entry.username = secure["routingNo"] + if "accountNo" in secure and not entry.username: + entry.username = secure["accountNo"] + if "telephonePin" in secure and not entry.password: + entry.password = secure["telephonePin"] + + # Credit card + if "ccnum" in secure and not entry.username: + entry.username = secure["ccnum"] + if "pin" in secure and not entry.password: + entry.password = secure["pin"] + + # Sections + if "sections" in secure: + for s in secure["sections"]: + t = s["title"] + if "fields" in s: + for f in s["fields"]: + v = f.get("v") + if not v: + continue + k = f["k"] + ft = "{} - {}".format(t, f["t"]) + if t == "": + ft = f["t"] + if k == "string" or k == "concealed" or k == "menu" or k == "cctype" or k == "monthYear": + entry.set_custom_property(ft, str(v)) + elif k == "date": + d = datetime.datetime.fromtimestamp(v) + entry.set_custom_property(ft, str(d)) + else: + raise Exception("Unknown k: {}".format(k)) + + # Notes + if "notesPlain" in secure: + entry.notes = secure["notesPlain"] + + # URLs + settings = { + "Allow": [], + "Deny": [], + "Realm": "", + } + applySettings = False + + if "location" in item: + entry.url = item["location"] + if "URLs" in secure: + for u in secure["URLs"]: + if not entry.url: + entry.url = u["url"] + url = urlparse(u["url"]) + settings["Allow"].append(url.hostname) + applySettings = True + + if applySettings: + settings["Allow"] = list(set(settings["Allow"])) + entry.set_custom_property("KeePassHttp Settings", json.dumps(settings)) + + + # Dates + entry.ctime = datetime.datetime.fromtimestamp(item["createdAt"]) + entry.mtime = datetime.datetime.fromtimestamp(item["updatedAt"]) + + done = done + 1 + if done >= until: + print("Halting at {}".format(done)) + print("-------------------------------------------------------------------------") + break + +kp.save() diff --git a/dump.py b/dump.py new file mode 100644 index 0000000..cc7c60e --- /dev/null +++ b/dump.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python + +from pykeepass import PyKeePass + +kp = PyKeePass("out.kdbx", password="test") +kp.dump_xml("out.xml") diff --git a/in.kdbx b/in.kdbx new file mode 100644 index 0000000000000000000000000000000000000000..1a19f813b14f639d0dbc2a6d917826e21e12a5d4 GIT binary patch literal 1118 zcmV-k1flx_*`k_f`%AR}00RI55CAd3^5(yBLr}h01tDtuTK@wC0096100bZawirW~ zagg5*v))_%nC%V7fi-{#B$NWk*5(G(d07BD1t0(p2vzk$s;pNdQ|Kki#2E21xi*;D zp?g>HmTqDBdU}rr2mqjl0RR91000LN09j#eZ?9WWLs2&SmyA7xq6i=WNf^Z46QV}( zO!SZ_>OrP{2_SBlbv%CRl4L@?wXVZ{2_OJFF6o+W2Ah3`5T_Fb)FVrhL<*p%`_ecF zkS_85$PoJq1ONg60000401XNa3Uly^_x+>u($(j@lQG)s?A<%Ju?Sw?8_>88432?^ z)NBNlVkr2PAkUAgm_&cN_i;JnAJB-^teU*_->#BjYl>E! zyc-YWE|=3Y53zm9goiKDIRd?wg2|FseR$d1^4Kt#N7 zP|3g(`9JzI?v@lzg0UfRl}aZDbU}*VYMFXVgRAR+p%z1EV3FZAeBT}j^ z%6(9NCsoC`txGtzy{T7Ztwi<$QzvM1e@L9Z>bm^$JmX7`%0v>;Ffp>rpe%<6)U=4f zR_V>XY3LsB>2sUP`%KWVL=`?7L^sC9t%<|riMQNKv?TETBk(<;qN?-Mp3Ft~fGHY0 zIU6kgBy~i4r+?nu-HJxw=s2%pGM_``wKzQNAF>BSf4qeCGt~wz5Q12hpgMncR$9V~ zek(z6HN#4^TB~%=HEkFvG7_pqQiWX38A!n+rRaEJ_vRd%Ai+Yc`@I_PeiWd1WK{T1 zxP}E~ax85gH<{Ln<&d`vUPiQ>ggsYfZ)I-Ap&3XGtjXO?O7@KSukiDG1|@110g5Kv z+@`UU{x5V}z8}o3(#uYok>l;}^P8kkM#FUZ4}h)}?v^=gjEPB|o&( zEg-s{%-?6nyP_A?L!?Dq4rx=iX$D$6n!g)ZK+Ca(pA%XZTr=cpPqK?$8=sWi)i*#q k<=hcR4L5M~tS-q#?oXOY0+5qS{mKohF8U#*I?wus;MTSq^8f$< literal 0 HcmV?d00001