Skip to content

Commit a77f0e7

Browse files
committed
FreeOTP importer
1 parent 84f922a commit a77f0e7

File tree

9 files changed

+76
-5
lines changed

9 files changed

+76
-5
lines changed

converters/freeotp.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import base64
2+
import sys
3+
import xml.etree.ElementTree as Et
4+
import json
5+
6+
7+
def fetch_json(filename):
8+
element_list = []
9+
tree = Et.parse(filename)
10+
for e in tree.findall("string"):
11+
element_list.append(json.loads(e.text.replace(""", "\"")))
12+
return element_list
13+
14+
15+
def object_to_cotp_json(d):
16+
final = []
17+
for element in d:
18+
if type(element) is dict:
19+
final.append(
20+
{
21+
'label': element['label'],
22+
'secret': byte_array_to_base32(element['secret']),
23+
'issuer': element['issuerExt'],
24+
'algorithm': element['algo'],
25+
'type': element['type'],
26+
'digits': int(element['digits']),
27+
'counter': element['counter'],
28+
}
29+
)
30+
return final
31+
32+
def byte_array_to_base32(secret: list):
33+
b = bytearray()
34+
for n in secret:
35+
# convert signed byte to unsigned
36+
b.append(n & 255)
37+
38+
return base64.b32encode(b).decode('utf-8')
39+
40+
def main():
41+
if len(sys.argv) != 3:
42+
print("Usage: python authy.py [INPUT_FILE] [OUTPUT_FILE]")
43+
return
44+
45+
data = fetch_json(sys.argv[1])
46+
output_file = open(sys.argv[2], 'w')
47+
output_file.write(json.dumps(object_to_cotp_json(data)))
48+
output_file.close()
49+
50+
51+
main()

converters/test_converters.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,37 @@
11
from authy import fetch_json as authy_fetch_json, object_to_cotp_json as authy_converter
22
from gauth import get_accounts as gauth_converter
33
from mauth import get_accounts as mauth_converter
4+
from freeotp import fetch_json as freeotp_fetch_json, object_to_cotp_json as freeotp_converter
45

56

67
def test_gauth_converter():
7-
assert gauth_converter("converters/example_databases/gauth_db.sqlite3") == [
8+
assert gauth_converter("example_databases/gauth.sqlite3") == [
89
{"label": "SHA512 10", "secret": "V24KSXF7TXJFGCKG35ZHQVQB4XZK26XPD7UCXU5GXU3POBTXAFLD47SA", "issuer": None,
910
"digits": 6, "type": "TOTP", "counter": 0, "algorithm": "SHA1"},
1011
{"label": "SHA512", "secret": "V24KSXF7TXJFGCKG35ZHQVQB4XZK26XPD7UCXU5GXU3POBTXAFLD47SA", "issuer": None,
1112
"digits": 6, "type": "TOTP", "counter": 0, "algorithm": "SHA1"}]
1213

1314

1415
def test_authy_converter():
15-
assert authy_converter(authy_fetch_json("converters/example_databases/authy_db.xml")) == [
16+
assert authy_converter(authy_fetch_json("example_databases/authy.xml")) == [
1617
{"label": "Test", "secret": "TLQEUBNC4ENYRDMLM2ZMQPN7PE272AW7", "issuer": "", "digits": 6, "type": "TOTP", "counter": 0, "algorithm": "SHA1"}]
1718

1819

1920
def test_mauth_converter():
20-
assert mauth_converter("converters/example_databases/microsoft_authenticator/PhoneFactor") == [
21+
assert mauth_converter("example_databases/microsoft_authenticator/PhoneFactor") == [
2122
{"label": "Test1", "secret": "XHRHVLZKO5YARFKEH65RFC7NVOOQCZXNHSCB2Y32OCV32ITSQOVV3DSD", "issuer": "Test1",
2223
"digits": 6, "type": "TOTP", "counter": 0, "algorithm": "SHA1"},
2324
{"label": "Test2", "secret": "XHRHVLZKO5YARFKEH65RFC7NVOOQCZXNHSCB2Y32OCV32ITSQOVV3DSD", "issuer": "Test2",
2425
"digits": 6, "type": "TOTP", "counter": 0, "algorithm": "SHA1"},
2526
{"label": "Test3", "secret": "XHRHVLZKO5YARFKEH65RFC7NVOOQCZXNHSCB2Y32OCV32ITSQOVV3DSD", "issuer": "Test3",
2627
"digits": 6, "type": "TOTP", "counter": 0, "algorithm": "SHA1"}
2728
]
29+
30+
def test_freeotp_converter():
31+
assert freeotp_converter(freeotp_fetch_json("example_databases/freeotp.xml")) == [
32+
{"label": "TOTP512", "secret": "3ZUNXX2SU6RZP4QFMS32YAILJFS2I2T2UZXYSHSX7IIMPAQZAC753NG2", "issuer": "", "algorithm": "SHA512", "type": "TOTP", "digits": 6, "counter": 0},
33+
{"label": "TOTP256", "secret": "T2POIVOD6PZYO5YUNIULXIXB3SNXT4GYLSLO5FUNLIXOEMQHY2R6QAGB", "issuer": "", "algorithm": "SHA256", "type": "TOTP", "digits": 8, "counter": 0},
34+
{"label": "HOTP1", "secret": "QEXCCTBU5DXXSFZQIYQAZ7BZUV5TM4EAXMDGSN4TPMR5BSFTFVEXWPMX", "issuer": "", "algorithm": "SHA1", "type": "HOTP", "digits": 8, "counter": 1},
35+
{"label": "Test3", "secret": "XHRHVLZKO5YARFKEH65RFC7NVOOQCZXNHSCB2Y32OCV32ITSQOVV3DSD", "issuer": "", "algorithm": "SHA256", "type": "TOTP", "digits": 8, "counter": 0},
36+
{"label": "TOTP1", "secret": "MC6SGRDYDMCZBEROCAESWB4KBNTGN76C3NPUNAZMIA5G2CNHRJBB7L6W", "issuer": "", "algorithm": "SHA1", "type": "TOTP", "digits": 8, "counter": 0}
37+
]

example_databases/freeotp.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
2+
<map>
3+
<string name="TOTP512">{&quot;algo&quot;:&quot;SHA512&quot;,&quot;counter&quot;:0,&quot;digits&quot;:6,&quot;issuerExt&quot;:&quot;&quot;,&quot;label&quot;:&quot;TOTP512&quot;,&quot;period&quot;:30,&quot;secret&quot;:[-34,104,-37,-33,82,-89,-93,-105,-14,5,100,-73,-84,1,11,73,101,-92,106,122,-90,111,-119,30,87,-6,16,-57,-126,25,0,-65,-35,-76,-38],&quot;type&quot;:&quot;TOTP&quot;}</string>
4+
<string name="TOTP256">{&quot;algo&quot;:&quot;SHA256&quot;,&quot;counter&quot;:0,&quot;digits&quot;:8,&quot;issuerExt&quot;:&quot;&quot;,&quot;label&quot;:&quot;TOTP256&quot;,&quot;period&quot;:30,&quot;secret&quot;:[-98,-98,-28,85,-61,-13,-13,-121,119,20,106,40,-69,-94,-31,-36,-101,121,-16,-40,92,-106,-18,-106,-115,90,46,-30,50,7,-58,-93,-24,0,-63],&quot;type&quot;:&quot;TOTP&quot;}</string>
5+
<string name="HOTP1">{&quot;algo&quot;:&quot;SHA1&quot;,&quot;counter&quot;:1,&quot;digits&quot;:8,&quot;issuerExt&quot;:&quot;&quot;,&quot;label&quot;:&quot;HOTP1&quot;,&quot;period&quot;:30,&quot;secret&quot;:[-127,46,33,76,52,-24,-17,121,23,48,70,32,12,-4,57,-91,123,54,112,-128,-69,6,105,55,-109,123,35,-48,-56,-77,45,73,123,61,-105],&quot;type&quot;:&quot;HOTP&quot;}</string>
6+
<string name="Test3">{&quot;algo&quot;:&quot;SHA256&quot;,&quot;counter&quot;:0,&quot;digits&quot;:8,&quot;issuerExt&quot;:&quot;&quot;,&quot;label&quot;:&quot;Test3&quot;,&quot;period&quot;:30,&quot;secret&quot;:[-71,-30,122,-81,42,119,112,8,-107,68,63,-69,18,-117,-19,-85,-99,1,102,-19,60,-124,29,99,122,112,-85,-67,34,114,-125,-85,93,-114,67],&quot;type&quot;:&quot;TOTP&quot;}</string>
7+
<string name="TOTP1">{&quot;algo&quot;:&quot;SHA1&quot;,&quot;counter&quot;:0,&quot;digits&quot;:8,&quot;issuerExt&quot;:&quot;&quot;,&quot;label&quot;:&quot;TOTP1&quot;,&quot;period&quot;:30,&quot;secret&quot;:[96,-67,35,68,120,27,5,-112,-110,46,16,9,43,7,-118,11,102,102,-1,-62,-37,95,70,-125,44,64,58,109,9,-89,-118,66,31,-81,-42],&quot;type&quot;:&quot;TOTP&quot;}</string>
8+
<string name="tokenOrder">[&quot;HOTP1&quot;,&quot;TOTP1&quot;,&quot;TOTP256&quot;,&quot;TOTP512&quot;,&quot;Test3&quot;]</string>
9+
</map>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"tokenOrder":[":TOTP512",":TOTP256",":TOTP1",":HOTP512",":HOTP256",":HOTP1"],"tokens":[{"algo":"SHA512","counter":0,"digits":6,"issuerExt":"","label":"{token.label}","period":30,"secret":[-34,104,-37,-33,82,-89,-93,-105,-14,5,100,-73,-84,1,11,73,101,-92,106,122,-90,111,-119,30,87,-6,16,-57,-126,25,0,-65,-35,-76,-38],"type":"TOTP"},{"algo":"SHA256","counter":0,"digits":6,"issuerExt":"","label":"{token.label}","period":30,"secret":[81,-41,94,-117,119,-106,122,-7,-121,-98,-21,-9,4,25,125,-29,107,-64,37,30,-19,45,72,112,21,-98,-98,13,-108,-122,-91,-95,-4,47,76],"type":"TOTP"},{"algo":"SHA1","counter":0,"digits":6,"issuerExt":"","label":"{token.label}","period":30,"secret":[92,64,-47,-93,-113,22,-72,101,78,-51,11,-52,-50,75,50,-47,11,31,35,-77,-7,-39,-53,-102,19,-34,-59,115,-36,53,-65,-34,-9,-40,29],"type":"TOTP"},{"algo":"SHA512","counter":4,"digits":6,"issuerExt":"","label":"{token.label}","period":30,"secret":[-38,54,-21,-26,91,6,-68,101,-12,-58,100,-99,68,-50,57,-122,50,112,84,-82,1,2,51,46,-89,-29,-28,25,-38,85,-104,31,74,98,-82],"type":"HOTP"},{"algo":"SHA256","counter":4,"digits":6,"issuerExt":"","label":"{token.label}","period":30,"secret":[-40,-75,-103,19,60,-104,-92,-54,-110,86,-102,24,115,-49,-103,88,47,-104,-92,2,-45,-45,-87,43,-108,34,96,91,29,41,-57,-17,31,91,3],"type":"HOTP"},{"algo":"SHA1","counter":5,"digits":6,"issuerExt":"","label":"{token.label}","period":30,"secret":[-57,14,67,0,-5,-99,-122,-116,102,-110,-111,43,4,126,-9,85,31,122,105,78,41,-109,-62,-76,-100,51,-3,95,124,28,-30,88,14,15,-24],"type":"HOTP"}]}
File renamed without changes.

converters/example_databases/microsoft_authenticator/PhoneFactor renamed to example_databases/microsoft_authenticator/PhoneFactor

File renamed without changes.

src/argument_functions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ pub fn import(matches: &ArgMatches) {
1313
"cotp" | "andotp" => importers::and_otp::import(path),
1414
"aegis" => importers::aegis::import(path),
1515
"freeotp+" => importers::freeotp_plus::import(path),
16-
"gauth" | "authy" | "mauth" => importers::converted::import(path),
16+
"gauth" | "authy" | "mauth" | "freeotp" => importers::converted::import(path),
1717
_ => {
1818
println!("Invalid argument: {}", app_name);
1919
return;

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ fn get_matches() -> ArgMatches{
266266
.short('a')
267267
.long("appname")
268268
.about("App from which you are importing the backup")
269-
.possible_values(&["cotp","andotp","aegis","freeotp+","gauth","authy","mauth"])
269+
.possible_values(&["cotp","andotp","aegis","freeotp+","freeotp","gauth","authy","mauth"])
270270
.takes_value(true)
271271
.required(true),
272272
)

0 commit comments

Comments
 (0)