diff --git a/README.md b/README.md
index b129e0e..baac059 100644
--- a/README.md
+++ b/README.md
@@ -6,36 +6,80 @@ You can extract the actual TOTP seed and use it with apps like [KeePassXC](https
## Prequisites
-For this to work, you need to extract 2 things - [SSAID](https://developer.android.com/reference/android/provider/Settings.Secure#ANDROID_ID) and encrypted seed.
+For this to work, you need to extract 3 things - [SSAID](https://developer.android.com/reference/android/provider/Settings.Secure#ANDROID_ID), UUID and encrypted seed.
This needs root access on your Android device.
### SSAID
-Run this command in a rooted shell:
+#### Android 8.0+
+For Android 8.0+ SSAID is unique for every installed app, to get value for FTM run this command in a rooted shell:
```
# grep com.fortinet /data/system/users/0/settings_ssaid.xml
```
Output should look like this:
```xml
-
+
```
-Copy the value from quotes and paste it to the script as `android_ssaid`.
+#### Android versions before 8.0
+
+For previous versions of Android SSAID is the same for all apps to get it run following command
+```
+# grep android_id /data/system/users/0/settings_secure.xml
+```
+
+```xml
+
+```
+
+Copy the value from quotes and paste it to the script as `DEVICE_ID`.
+
+### UUID
+
+The encrypted UUID is stored in the UUID key of the XML file stored at /data/data/com.fortinet.android.ftm/shared_prefs/FortiToken_SharedPrefs_NAME.xml.
+
+```
+grep UUID /data/data/com.fortinet.android.ftm/shared_prefs/FortiToken_SharedPrefs_NAME.xml
+ N7gAr30eX72sR2owbVR4WrFiw4e3ignGBO6IcgA4qJjvBYjZvIxZXIMTHOix8QDt
+```
+
+Copy the value paste it to the script as `UUID`.
### Seed
The seed is stored in app's database: `/data/data/com.fortinet.android.ftm/databases/FortiToken.db`
-You can copy the file and open it with an SQLite3 editor, or run this command: (I know it's ugly, but does the job)
+You can copy the file and open it with an SQLite3 editor,
+
+```
+$ sudo sqlite3 /data/data/com.fortinet.android.ftm/databases/FortiToken.db 'SELECT seed FROM Account WHERE type="totp"'
+MNmAN7drtlNJxjFqo5bgSN/DZcdWVK9Qv1YyUP3OjuJkDXgV06siQYlQfO0678Lg
+```
+
+or run this command: (I know it's ugly, but does the job)
```
# grep -Eao 'totp.{64}' /data/data/com.fortinet.android.ftm/databases/FortiToken.db | cut -c5-
+MNmAN7drtlNJxjFqo5bgSN/DZcdWVK9Qv1YyUP3OjuJkDXgV06siQYlQfO0678Lg
```
-Copy the output and paste it as `seed`.
+
+Copy the output and paste it as `SEED`.
+
## Usage
Install requirements with `pip3 install -U -r requirements.txt`, then run with `python3 generate.py`.
+```
+$ python3 generate.py
+UUID KEY: eefd7d4837294e94unknown
+UUID: bbc350195b88433dbcc7365cdbd130e5
+SEED KEY: eefd7d4837294e94unknownbbc350195b88433dbcc7365cdbd130e5
+TOTP SECRET: DEADBEEFDEADBEEFDEADBEEFDEADBEEF
+Current TOTP: 779726
+```
+
+Printed TOTP SECRET is base32-encoded and can be used to setup TOTP codes in other authenticator applications like: KeePassXC, andOTP, SailOTP, Numberstation. Make sure to set the period to *60 seconds*.
+
## Disclaimer
All product and company names are trademarks™ or registered® trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.
diff --git a/generate.py b/generate.py
old mode 100644
new mode 100755
index e43844d..62f74c9
--- a/generate.py
+++ b/generate.py
@@ -1,18 +1,53 @@
-from Crypto.Cipher import AES
-from Crypto.Util.Padding import pad, unpad
-from pyotp import TOTP
-import base64
+#!/bin/env python3
+#
+# This code is based on article
+# https://huggable.tech/blog/extracting-fortitoken-mobile-totp-secret
+#
import hashlib
+import base64
+import qrcode
+from Cryptodome.Cipher import AES
+from pyotp import TOTP
+
+SEED = 'MNmAN7drtlNJxjFqo5bgSN/DZcdWVK9Qv1YyUP3OjuJkDXgV06siQYlQfO0678Lg'
+UUID = 'N7gAr30eX72sR2owbVR4WrFiw4e3ignGBO6IcgA4qJjvBYjZvIxZXIMTHOix8QDt'
+DEVICE_ID = 'eefd7d4837294e94'
+
+SERIAL = 'TOKENSERIALunknown'
+
+
+def unpad(s):
+ return s[0:-ord(s[-1])]
+
+
+def decrypt(cipher, key):
+ sha256 = hashlib.sha256()
+ sha256.update(bytes(key, 'utf-8'))
+ digest = sha256.digest()
+ iv = bytes([0] * 16)
+ aes = AES.new(digest, AES.MODE_CBC, iv)
+ decrypted = aes.decrypt(base64.b64decode(cipher))
+ return unpad(str(decrypted, "utf-8"))
+
+
+uuid_key = DEVICE_ID + SERIAL[11:]
+print("UUID KEY: %s" % uuid_key)
+decoded_uuid = decrypt(UUID, uuid_key)
+print("UUID: %s" % decoded_uuid)
+
+seed_decryption_key = uuid_key + decoded_uuid
+print("SEED KEY: %s" % seed_decryption_key)
+decrypted_seed = decrypt(SEED, seed_decryption_key)
+totp_secret = bytes.fromhex(decrypted_seed)
-# fill these with values explained in README.md
-android_ssaid = b''
-seed = b''
+totp_secret_encoded = str(base64.b32encode(totp_secret), "utf-8")
+print("TOTP SECRET: %s" % totp_secret_encoded)
-key = hashlib.sha256(android_ssaid + b'unknownnull').digest()
-aes = AES.new(key, AES.MODE_CBC, b'\x00'*16)
-data = unpad(aes.decrypt(base64.b64decode(seed)), 8)
-data = base64.b32encode(bytes.fromhex(data.decode('utf-8')))
+totp = TOTP(totp_secret_encoded, interval=60)
+print("Current TOTP: %s" % totp.now())
-totp = TOTP(data, interval = 60)
-print(totp.now())
+str=totp.provisioning_uri(name='vpn', issuer_name='fortivpn')
+img=qrcode.make(str)
+type(img)
+img.show()
diff --git a/requirements.txt b/requirements.txt
index 0d7e176..cc30850 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,5 @@
-pycryptodome==3.10.1
+pycryptodome==3.11.0
pyotp==2.6.0
+pycryptodomex==3.16.0
+qrcode==7.3.1
+pillow==9.3.0