Skip to content

Commit

Permalink
Merge pull request #21 from GurpreetKang/Password_Protected_Encrypted…
Browse files Browse the repository at this point in the history
…_JSON_Export

Add Support For Decrypting Password Protected Encrypted JSON Export
  • Loading branch information
GurpreetKang authored Oct 17, 2022
2 parents 6a5f757 + 03d01f9 commit 8c2e0e9
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 8 deletions.
41 changes: 33 additions & 8 deletions BitwardenDecrypt.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
#
# Note: BitwardenDecrypt does not work with Bitwarden Encrypted JSON Exports.
# These exports lack the Protected Symmetric Key needed to decrypt entries.
# Password Protected Encrypted JSON Exports are supported.
#
# Attachments are not supported (they are not stored locally in data.json).
#
#
# Outputs JSON containing:
Expand Down Expand Up @@ -297,10 +300,10 @@ def isUUID(value):
def checkFileFormatVersion(options):

options.account = {}
email = ""
kdfIterations = ""
encKey = ""
encPrivateKey = ""
email = None
kdfIterations = None
encKey = None
encPrivateKey = None

try:
with open(options.inputfile) as f:
Expand All @@ -313,7 +316,17 @@ def checkFileFormatVersion(options):
sys.exit(1)


if datafile.get("userEmail") is None:
# Check if datafile is a password protected excrypted json export.
if datafile.get("encrypted") and datafile.get("passwordProtected"):
options.fileformat = "EncryptedJSON"

# Email address is used as the salt in data.json, in password protected excrypted json exports there is an explicit salt key/value (and no email).
email = datafile.get("salt")
kdfIterations = int(datafile.get("kdfIterations"))
encKey = datafile.get("encKeyValidation_DO_NOT_EDIT")

# Check if data.json is new/old format.
elif datafile.get("userEmail") is None:
options.fileformat = "NEW"
accounts = []

Expand Down Expand Up @@ -380,8 +393,11 @@ def decryptBitwardenJSON(options):

email, kdfIterations, encKey, encPrivateKey = checkFileFormatVersion(options)

# Set prompt text for when entering password.
prompt_text = "EncryptedJSON" if options.fileformat == "EncryptedJSON" else email

getBitwardenSecrets(email, \
getpass.getpass(prompt = f"Enter Password ({email}):").encode("utf-8"), \
getpass.getpass(prompt = f"Enter Password ({prompt_text}):").encode("utf-8"), \
kdfIterations, \
encKey, \
encPrivateKey)
Expand All @@ -391,9 +407,18 @@ def decryptBitwardenJSON(options):

# RegEx to find CipherString
regexPattern = re.compile(r"\d\.[^,]+\|[^,]+=+")

if (options.fileformat == "EncryptedJSON"):
EncryptedJSON = datafile.get("data")

encKey = BitwardenSecrets['StretchedEncryptionKey']
macKey = BitwardenSecrets['StretchedMACKey']

decryptedEntries = OrderedDict(json.loads(decryptCipherString(EncryptedJSON, encKey, macKey)))


# data.json file format changed in v1.30+
if (options.fileformat == "NEW"):
elif (options.fileformat == "NEW"):
# data.json file format changed in v1.30+

datafile = datafile[options.account['UUID']]
organizationKeys = datafile['keys']['organizationKeys']['encrypted']
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ https://bitwarden.com/help/data-storage/#on-your-local-machine
*Note: BitwardenDecrypt does not work with Bitwarden Encrypted JSON Exports.<br/>
These exports lack the Protected Symmetric Key needed to decrypt entries.*

*Password Protected Encrypted JSON Exports* are supported.
Currently these can only be created using the [Bitwarden CLI](https://bitwarden.com/help/cli/#export).


<br/>

Outputs JSON containing:
Expand Down Expand Up @@ -61,8 +65,10 @@ https://paypal.me/GurpreetKang

## Limitations

- Attachments are not supported (they are not stored locally in data.json)
- Does not work with Bitwarden Encrypted JSON Exports.
<br/>*These exports lack the Protected Symmetric Key needed to decrypt entries.*
<br/>(Password Protected Encrypted JSON Exports are now supported)
- ~~No validation of the CipherString.
I.e. No verification of the MAC before decrypting.~~ Now verifies the MAC.
- Can only decrypt EncryptionType: 2 (AesCbc256_HmacSha256_B64). At the time of writing this is the default used for all entries in the personal vault.
Expand Down

0 comments on commit 8c2e0e9

Please sign in to comment.