diff --git a/convert.py b/convert.py index 68cb16a..0efdb01 100755 --- a/convert.py +++ b/convert.py @@ -88,6 +88,11 @@ for item in opif: # Tags kp.set_tags(item.get_tags()) + # TOTP + totps = item.get_totps() + if totps: + for totp in totps: + kp.add_totp(totp[0], title=totp[1]) @@ -110,10 +115,6 @@ for item in opif: if new_password: entry.password = new_password - # TOTP - totp = item.get_totp() - if totp: - kp.add_totp(totp) # Other web fields if "fields" in secure: diff --git a/kpwriter.py b/kpwriter.py index 6616a01..8443c86 100644 --- a/kpwriter.py +++ b/kpwriter.py @@ -32,12 +32,21 @@ class KpWriter: def set_tags(self, tag_list): self.current_entry.tags = tag_list - def add_totp(self, init_string, otp_url=None): + def add_totp(self, init_string, otp_url=None, title=""): if not otp_url: otp_url = "otpauth://totp/Sample:username?secret={}&algorithm=SHA1&digits=6&period=30&issuer=Sample".format(quote_plus(init_string)) - # TODO: Support multiple / don't overwrite - self.set_prop("TimeOtp-Secret-Base32", init_string, True) - self.set_prop("otp", otp_url) + + # It's possible to define multiple OTP-secrets in 1P7, so let's not lose one + suffix = "" + suffix_ctr = 1 + while self.current_entry.get_custom_property("otp{}".format(suffix)): + suffix_ctr += 1 + suffix = "_{}".format(suffix_ctr) + + self.set_prop("TimeOtp-Secret-Base32{}".format(suffix), init_string, True) + self.set_prop("otp{}".format(suffix), otp_url) + if len(title) > 0: + self.set_prop("otp_title{}".format(suffix), title) def set_prop(self, key, value, protected=False): self.current_entry.set_custom_property(key, value) diff --git a/onepif/OnepifEntry.py b/onepif/OnepifEntry.py index cea7c47..01931a9 100644 --- a/onepif/OnepifEntry.py +++ b/onepif/OnepifEntry.py @@ -41,15 +41,21 @@ class OnepifEntry(): return [] return self.raw["openContents"]["tags"] - def get_totp(self): + def get_totps(self): + totp_fields = [] if "sections" in self.raw["secureContents"]: for section in self.raw["secureContents"]["sections"]: if "fields" not in section: continue for field in section["fields"]: if field["n"][:5] == "TOTP_": - return field["v"] - return None + totp_fields.append([ + field["v"], + field["t"], # Custom title, if set (isn't displayed in 1P) + ]) + if len(totp_fields) == 0: + return None + return totp_fields def is_trash(self): if "trashed" in self.raw: