forked from heeeyflo/authy-decryptor
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdecrypt.mjs
More file actions
84 lines (73 loc) · 2.67 KB
/
decrypt.mjs
File metadata and controls
84 lines (73 loc) · 2.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import { readFileSync } from "fs";
import { createDecipheriv, pbkdf2Sync } from "crypto";
import fs from 'fs';
import { v4 as uuidv4, v6 as uuidv6 } from 'uuid';
// Read the JSON from stdin, then filter out non-TOTP tokens
const contents = JSON.parse(
readFileSync(0, "utf8")
).authenticator_tokens.filter((t) => t.encrypted_seed !== undefined);
// The IV is static and equals 16 NULL bytes
let IV = Buffer.from("00000000000000000000000000000000", "hex");
const defaultIV = "00000000000000000000000000000000";
// Obtain your backup key from the environment variable
const backupKey = process.env.BACKUP_KEY;
/**
* Decrypts the seed using the backup key and the account's salt
* @param {String} seed Encrypted seed
* @param {String} salt Account salt
* @returns {String} Decrypted seed
*/
function decryptSync(seed, salt, iiv) {
// Authy uses PBKDF2 with PKCS5 padding and 100,000 iterations of SHA1.
// Here, we derive the key from the backup key and the account's salt
IV = Buffer.from(iiv, "hex");
const key = pbkdf2Sync(backupKey, salt, 100000, 32, "sha1");
// Then, we decrypt the seed using AES-256-CBC with the derived key and static IV
const decipher = createDecipheriv("aes-256-cbc", key, IV);
const ciphertext = Buffer.from(seed, "base64");
const decrypted = Buffer.concat([
decipher.update(ciphertext),
decipher.final(),
]);
// Return the decrypted seed as a UTF-8 string
return decrypted.toString("utf8");
}
// BitWarden format header
let tobewritten = {
"encrypted": false,
"folders": [
{
"id": uuidv4(),
"name": "Extracted from Authy"
}
],
"items": []
}
// Iterate over each token and decrypt the seed phrase, then output it to stdout, as well as the BitWarden JSON format
// along with the token's name and original name
for (const token of contents) {
if (token.unique_iv === undefined) {
token.unique_iv = defaultIV;
}
const decrypted = decryptSync(token.encrypted_seed, token.salt, token.unique_iv);
console.log(`${token.name} (${token.original_name})\t ${decrypted}`);
fs.appendFileSync('authyout.txt', `${token.name} (${token.original_name})\t ${decrypted}\n`);
tobewritten.items.push({
"id": uuidv4(),
"organizationId": null,
"folderId": tobewritten.folders[0].id,
"type": 1,
"reprompt": 0,
"name": token.name,
"notes": token.original_name,
"favorite": false,
"login": {
"username": null,
"password": null,
"totp": `otpauth://totp/${token.name}?secret=${decrypted}&digits=${token.digits}&period=30`
},
"collectionIds": null
})
}
// Write the BitWarden JSON to a file
fs.writeFileSync('authyout.json', JSON.stringify(tobewritten, null, 2));