decrypt keychain
Closed this issue · 6 comments
I've got struggle to decrypt the keychain using python pysodium.
This data are from a nextcloud dev instance - so leaking everything here is totally okey.
The cse_password SuperSicher12345
is used.
First, opening the session works as expected and I've got
cse = {
"keys": {
"CSEv1r1": "7758874be1c5dcc121c7991084f5ae006e3bcce83bcc3c639aab31e93657ab5fb9312070a76b32a5aebc10efc9bbbbf3fd395ab5cecaa8d7f82838f87dc2723eef512567271d6dd7a5fe1571ad33843f286dedee91fc9087dced3cd7bb9729491c959746a559d83a1d487d8e2a4cf5c5abe7b50d6cb594993a8b1408e2c0fe04212c797198834bc94a4e5760900c87612d6906022f1f35c520cdf456739ae84b2ccbfa26ea25117e3cbf6954940808f39a47975aa9f76373f6942fa8cb2dd4c3b0eb0b6b25d5101c231310d889e0f8ba125ef91ee84a0f560b15e7f303"
},
"success": True
}
But decrypting of the keychain failed https://git.mdns.eu/nextcloud/passwords-client/-/blob/master/src/Encryption/Keychain/CSEv1Keychain.js#L85-92
import pysodium
import binascii
out = {
"changed": False,
"failed": False,
"password": [
{
"client": "Passwords Session 26.01.23 18:56 - m@2a02:3032:208:5591:3ede:cf65:4c9:f45f",
"created": 1674759588,
"cseKey": "b15ac86a-5c0a-4ece-9c0a-03904d9ccc0a",
"cseType": "CSEv1r1",
"customFields": "9de8f88b7f89757d68de395d488eb2330262b610cd04b996bef566898e873e62e5018589d5c3968e97c7",
"editable": True,
"edited": 1674759588,
"favorite": False,
"folder": "00000000-0000-0000-0000-000000000000",
"hash": "77b85c9b988d2162ad36fb8620d0f684acbc8344",
"hidden": False,
"id": "6b1b8572-1daa-45b3-8df7-acad8af313c9",
"label": "3d325bc563054f80d188486d38f55261e10a47542212c9b9edf33a6bc647d4e332fd31e6fa07b03c77313419a859da9e735a3775c011a6",
"notes": "",
"password": "364847848e926377f924d8ccbbb720d3510b40c3b54d38a354b80a6f2633218c52035dcb15e28b3f32510c7091920118925ae7",
"revision": "08e51b8a-b1d7-4495-a2b2-3749d4636376",
"share": None,
"shared": False,
"sseType": "none",
"status": 2,
"statusCode": "BREACHED",
"trashed": False,
"updated": 1674759588,
"url": "",
"username": ""
}
]
}
cse = {
"keys": {
"CSEv1r1": "7758874be1c5dcc121c7991084f5ae006e3bcce83bcc3c639aab31e93657ab5fb9312070a76b32a5aebc10efc9bbbbf3fd395ab5cecaa8d7f82838f87dc2723eef512567271d6dd7a5fe1571ad33843f286dedee91fc9087dced3cd7bb9729491c959746a559d83a1d487d8e2a4cf5c5abe7b50d6cb594993a8b1408e2c0fe04212c797198834bc94a4e5760900c87612d6906022f1f35c520cdf456739ae84b2ccbfa26ea25117e3cbf6954940808f39a47975aa9f76373f6942fa8cb2dd4c3b0eb0b6b25d5101c231310d889e0f8ba125ef91ee84a0f560b15e7f303"
},
"success": True
}
vals = binascii.unhexlify(cse['keys']['CSEv1r1'])
salt = vals[0:pysodium.crypto_box_NONCEBYTES]
key = vals[pysodium.crypto_box_NONCEBYTES:-1]
passwordHash = pysodium.crypto_pwhash(
pysodium.crypto_box_SEEDBYTES,
'SuperSicher12345',
salt,
pysodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
pysodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
pysodium.crypto_pwhash_ALG_ARGON2ID13
)
results in an error about the salt
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/m/.local/lib/python3.10/site-packages/pysodium/__init__.py", line 62, in wrapper
return func(*args, **kwargs)
File "/home/m/.local/lib/python3.10/site-packages/pysodium/__init__.py", line 92, in wrapper
return func(*largs, **kwargs)
File "/home/m/.local/lib/python3.10/site-packages/pysodium/__init__.py", line 1048, in crypto_pwhash
if len(salt) != crypto_pwhash_SALTBYTES: raise ValueError("invalid salt")
ValueError: invalid salt
>>> salt
b'wX\x87K\xe1\xc5\xdc\xc1!\xc7\x99\x10\x84\xf5\xae\x00n;\xcc\xe8;\xcc<c'
>>> salt.hex()
'7758874be1c5dcc121c7991084f5ae006e3bcce83bcc3c63'
>>> len(salt)
24
Do you have any idea what's going wrong here?
Ok, I've found that it must use crypto_pwhash_SALTBYTES
and not crypto_box_NONCEBYTES
. That brings me one step further.
I still need some assistent if possible
import pysodium
import binascii
out = {
"changed": False,
"failed": False,
"password": [
{
"client": "Passwords Session 26.01.23 18:56 - m@2a02:3032:208:5591:3ede:cf65:4c9:f45f",
"created": 1674759588,
"cseKey": "b15ac86a-5c0a-4ece-9c0a-03904d9ccc0a",
"cseType": "CSEv1r1",
"customFields": "9de8f88b7f89757d68de395d488eb2330262b610cd04b996bef566898e873e62e5018589d5c3968e97c7",
"editable": True,
"edited": 1674759588,
"favorite": False,
"folder": "00000000-0000-0000-0000-000000000000",
"hash": "77b85c9b988d2162ad36fb8620d0f684acbc8344",
"hidden": False,
"id": "6b1b8572-1daa-45b3-8df7-acad8af313c9",
"label": "3d325bc563054f80d188486d38f55261e10a47542212c9b9edf33a6bc647d4e332fd31e6fa07b03c77313419a859da9e735a3775c011a6",
"notes": "",
"password": "364847848e926377f924d8ccbbb720d3510b40c3b54d38a354b80a6f2633218c52035dcb15e28b3f32510c7091920118925ae7",
"revision": "08e51b8a-b1d7-4495-a2b2-3749d4636376",
"share": None,
"shared": False,
"sseType": "none",
"status": 2,
"statusCode": "BREACHED",
"trashed": False,
"updated": 1674759588,
"url": "",
"username": ""
}
]
}
cse = {
"keys": {
"CSEv1r1": "7758874be1c5dcc121c7991084f5ae006e3bcce83bcc3c639aab31e93657ab5fb9312070a76b32a5aebc10efc9bbbbf3fd395ab5cecaa8d7f82838f87dc2723eef512567271d6dd7a5fe1571ad33843f286dedee91fc9087dced3cd7bb9729491c959746a559d83a1d487d8e2a4cf5c5abe7b50d6cb594993a8b1408e2c0fe04212c797198834bc94a4e5760900c87612d6906022f1f35c520cdf456739ae84b2ccbfa26ea25117e3cbf6954940808f39a47975aa9f76373f6942fa8cb2dd4c3b0eb0b6b25d5101c231310d889e0f8ba125ef91ee84a0f560b15e7f303"
},
"success": True
}
password='SuperSicher12345'
vals = binascii.unhexlify(cse['keys']['CSEv1r1'])
salt = vals[0:pysodium.crypto_pwhash_SALTBYTES]
text = vals[pysodium.crypto_pwhash_SALTBYTES:-1]
key = pysodium.crypto_pwhash(
pysodium.crypto_box_SEEDBYTES,
password,
salt,
pysodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
pysodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
pysodium.crypto_pwhash_ALG_ARGON2ID13
)
len(key) == pysodium.crypto_secretbox_KEYBYTES
nonce = text[0:pysodium.crypto_secretbox_NONCEBYTES]
len(nonce) == pysodium.crypto_secretbox_NONCEBYTES
ciphertext = text[pysodium.crypto_secretbox_NONCEBYTES:-1]
pysodium.crypto_secretbox_open(ciphertext, nonce, key)
the last step https://git.mdns.eu/nextcloud/passwords-client/-/blob/master/src/Encryption/Keychain/CSEv1Keychain.js#L161
failed. And I've no idea why.
>>> pysodium.crypto_secretbox_open(ciphertext, nonce, key)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/m/.local/lib/python3.10/site-packages/pysodium/__init__.py", line 683, in crypto_secretbox_open
__check(sodium.crypto_secretbox_open(msg, padded, ctypes.c_ulonglong(len(padded)), nonce, k))
File "/home/m/.local/lib/python3.10/site-packages/pysodium/__init__.py", line 274, in __check
raise ValueError
ValueError
I noticed also that the let expectedLength = sodium.crypto_secretbox_NONCEBYTES + sodium.crypto_secretbox_MACBYTES;
results in 40 in python
>>> pysodium.crypto_secretbox_NONCEBYTES + pysodium.crypto_secretbox_MACBYTES
40
that's much shorter as the encrypted/test
text = vals[pysodium.crypto_pwhash_SALTBYTES:-1]
len(text)
204
the exptectedLength is the minimum length. It should always be greater than that
Ha, found it.
The text and ciphertext are both too short. The text is missing the last character and the ciphertext is missing the last two characters.
You need to use text = vals[pysodium.crypto_pwhash_SALTBYTES:]
and ciphertext = text[pysodium.crypto_secretbox_NONCEBYTES:]
So no -1
, that seems to cut of the last character
vals = binascii.unhexlify(cse['keys']['CSEv1r1'])
salt = vals[0:pysodium.crypto_pwhash_SALTBYTES]
text = vals[pysodium.crypto_pwhash_SALTBYTES:]
key = pysodium.crypto_pwhash(
pysodium.crypto_box_SEEDBYTES,
password,
salt,
pysodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
pysodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
pysodium.crypto_pwhash_ALG_ARGON2ID13
)
len(key) == pysodium.crypto_secretbox_KEYBYTES
nonce = text[0:pysodium.crypto_secretbox_NONCEBYTES]
len(nonce) == pysodium.crypto_secretbox_NONCEBYTES
ciphertext = text[pysodium.crypto_secretbox_NONCEBYTES:]
result = pysodium.crypto_secretbox_open(ciphertext, nonce, key)
print(result)
Thanks, now everything works as expected!