Last active
August 6, 2025 11:53
-
-
Save jmiserez/744116545d7f595923966f883c4f1b5d to your computer and use it in GitHub Desktop.
CyberChef: Parse Google Authenticator QR Codes (single & multi-account) using protobuf schema and show TOTP secrets as Base32
This file contains hidden or 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
How to parse Google Authenticator "Transfer Account" QR codes: | |
============================================================== | |
This method supports multi-account exports as well as single-account exports / QR codes. | |
Changelog: | |
=========== | |
2025-08-06: updated for multi-account exports | |
2023-08-11: added notes on security | |
IMPORTANT: | |
- Only use this if you know what you are doing. | |
- Make sure to securely delete the QR codes files and any copied otpauth-migration://offline?data= strings and TOTP secrets (clipboard, etc.) once done. | |
Step by step: | |
============== | |
1. In Google Authenticator > "Transfer Accounts" > "Export Accounts", choose the accounts you want to export. Store all the QR codes (as text or as a screenshot/photo). | |
2. Go to https://gchq.github.io/CyberChef/ | |
3. Download CyberChef (top left), extract .zip and open .html file. | |
4. Click "Options" in top right, then DISABLE the checkbox "Update the URL when the input or recipe changes" (see why in section "Notes on security") | |
5. Click "Load Recipe" in the top middle | |
6. Enter the following recipe: | |
Parse_QR_Code(false) | |
Regular_expression('User defined','otpauth-migration\\:\\/\\/offline\\?data\\=([^\\n]*)',true,false,true,false,false,false,'List capture groups') | |
URL_Decode(true) | |
Find_/_Replace({'option':'Extended (\\n, \\t, \\x...)','string':'\\n'},'',true,false,true,false) | |
From_Base64('A-Za-z0-9+/=',true,false) | |
Protobuf_Decode('syntax = "proto3";\n\noption java_package = "com.beemdevelopment.aegis";\noption java_outer_classname = "GoogleAuthProtos";\n\nmessage MigrationPayload {\n enum Algorithm {\n ALGORITHM_UNSPECIFIED = 0;\n ALGORITHM_SHA1 = 1;\n ALGORITHM_SHA256 = 2;\n ALGORITHM_SHA512 = 3;\n ALGORITHM_MD5 = 4;\n }\n\n enum DigitCount {\n DIGIT_COUNT_UNSPECIFIED = 0;\n DIGIT_COUNT_SIX = 1;\n DIGIT_COUNT_EIGHT = 2;\n }\n\n enum OtpType {\n OTP_TYPE_UNSPECIFIED = 0;\n OTP_TYPE_HOTP = 1;\n OTP_TYPE_TOTP = 2;\n }\n\n message OtpParameters {\n bytes secret = 1;\n string name = 2;\n string issuer = 3;\n Algorithm algorithm = 4;\n DigitCount digits = 5;\n OtpType type = 6;\n int64 counter = 7;\n }\n\n repeated OtpParameters otp_parameters = 1;\n int32 version = 2;\n int32 batch_size = 3;\n int32 batch_index = 4;\n int32 batch_id = 5;\n}',false,false) | |
Subsection('"secret": "(.*)",',true,true,false) | |
From_Base64('A-Za-z0-9+/=',true,false) | |
To_Base32('A-Z2-7=') | |
7. Click "Load" | |
8. Click "Open file as input" (top right) once for each QR code as the input file. | |
9. Your data will be shown fully decoded, with the "secret" field additionally decoded and reencoded in the Base32 format that is commonly required when entering TOTP secret keys somewhere else (e.g. Keepass). | |
Notes on multi-account backups: | |
================================ | |
- Google Authenticator generates QR codes with otpauth-migration://offline?data= URLs, these include all information including TOTP secrets. | |
- Google Authenticator uses the same .proto Schema for both single-account as well as multi-account exports. | |
- In the case of multi-account exports, multiple QR codes are generated by Google Authenticator due to QR code length limitations. Each QR code contains one or more complete Protobuf messages, and messages / accounts are never split across multiple QR codes. | |
- The 2 fields 'batchSize' and 'batchIndex' indicate how many QR codes were generated and which one is currently shown. | |
Notes on security: | |
=================== | |
For very small input files or when entering plain text, the CyberChef tool may append the input file to the fragment identifier of the URL in your browser (i.e. '?input=' in the '#' part of the URL). This information could remain in your local browser history, which is undesirable in the case of TOTP secrets. | |
To prevent such information leakage through the browser history either clear the history entries afterwards, or use a private browsing window for the whole process, or uncheck the CyberChef option 'Update the URL when the input or recipe changes' in the top-right settings before uploading the input file. In any case do not share CyberChef URLs directly with anyone as they may contain input, instead use the 'Save recipe' button. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment