Last active
December 21, 2023 18:28
-
-
Save anfedorov/446a78964e64689a09f82b9f1cbebf8b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# code mostly from by https://nathancahill.com/duo-cli | |
import plistlib | |
# enable backup in Duo app w/ encryption, set ascii password and enter here. | |
# if you do not want to write the password to disk, instead of saving this | |
# file, start iPython, copy the code, and type %paste into the prompt | |
password = b'xxx' | |
# off an encrypted backup of your phone, get the app's | |
# "Library/Preferences/com.duosecurity.DuoMobile.plist" | |
with open("com.duosecurity.DuoMobile.plist", "rb") as f: | |
plist = plistlib.load(f, fmt=plistlib.FMT_BINARY) | |
# create the decryption box | |
import nacl.secret | |
import nacl.pwhash | |
params = plist["kBackupEncryptionStoreDerivationParamsDictKey"] | |
salt = params["passwordSaltKey"] | |
opslimit = params["opsLimitKey"] | |
memlimit = params["memLimitKey"] | |
key = nacl.pwhash.argon2id.kdf( | |
nacl.secret.SecretBox.KEY_SIZE, | |
password, | |
salt, | |
opslimit=opslimit, | |
memlimit=memlimit, | |
) | |
box = nacl.secret.SecretBox(key) | |
# test that it works (i.e. that you got the password right) | |
import base64 | |
test_string = plist["kBackupEncryptionStoreEncryptedTestStringKey"] | |
def decrypt(c): | |
[cipher, nonce] = c.split(":") | |
cipher = base64.b64decode(cipher) | |
nonce = base64.b64decode(nonce) | |
return box.decrypt(cipher, nonce) | |
print('test decrypt', decrypt(test_string)) | |
# decrypt the OTP secrets and print them as URI's | |
import passlib.totp | |
[cipher, nonce] = encrypted_otp_string.split(":") | |
cipher = base64.b64decode(cipher) | |
nonce = base64.b64decode(nonce) | |
secret = box.decrypt(cipher, nonce) | |
otp = passlib.totp.TOTP(key=secret, format="raw") | |
def get_secret(k): | |
if 'encryptedOTPString' not in k: | |
return 'no otp string in this entry' | |
secret = decrypt(k['encryptedOTPString']) | |
otp = passlib.totp.TOTP(key=secret, format="raw") | |
return otp.to_uri(k['displayLabel'], k['serviceName']) | |
for k in ks: | |
print(get_secret(k)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment