# \[doc\] password encrypted encryption-key: How to decrypt it using the password?

**URL:** <https://forum.pydio.com/t/doc-password-encrypted-encryption-key-how-to-decrypt-it-using-the-password/3123>\
**Category:** Pydio Cells\
**Created:** [March 3, 2020, 6:09pm UTC](https://forum.pydio.com/t/doc-password-encrypted-encryption-key-how-to-decrypt-it-using-the-password/3123 "2020-03-03T18:09:14Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![drzraf](https://yyz2.discourse-cdn.com/flex032/user_avatar/forum.pydio.com/drzraf/32/1207_2.png) [@drzraf](https://forum.pydio.com/u/drzraf)\
**Post date:** [March 3, 2020, 6:09pm UTC](https://forum.pydio.com/t/doc-password-encrypted-encryption-key-how-to-decrypt-it-using-the-password/3123/1 "2020-03-03T18:09:15Z")

</div>

Export password-protected encryption key.  
I created a key and started using it for a given storage.  
I backup my key using the “Export key” feature. There, I’m asked a (possibly empty) passphrase.

For a given passphrase (and supposedly given encryption key) I always receive different encrypted base64 strings.

I wonder, **how is the key password-encrypted?**  
I would like to use `openssl` to decrypt it locally and be sure that the non-encrypted key is really always the same.

Thank you.

---

<div class="post-metadata">

**Author:** ![drzraf](https://yyz2.discourse-cdn.com/flex032/user_avatar/forum.pydio.com/drzraf/32/1207_2.png) [@drzraf](https://forum.pydio.com/u/drzraf)\
**Post date:** [March 5, 2020, 1:51pm UTC](https://forum.pydio.com/t/doc-password-encrypted-encryption-key-how-to-decrypt-it-using-the-password/3123/2 "2020-03-05T13:51:16Z")

</div>

As an additional question: Having the clear key (if possible) and assuming I get an encrypted file via  
`openstack object save <container> <file>`

Which `openssl` command would allow decryption?

---

<div class="post-metadata">

**Author:** ![zayn](https://avatars.discourse-cdn.com/v4/letter/z/a87d85/32.png) [@zayn](https://forum.pydio.com/u/zayn)\
**Post date:** [March 9, 2020, 8:16am UTC](https://forum.pydio.com/t/doc-password-encrypted-encryption-key-how-to-decrypt-it-using-the-password/3123/3 "2020-03-09T08:16:58Z")

</div>

Hi @drzraf,

You can find more information about encryption here: [https://pydio.com/en/docs/cells/v2/encryption](https://pydio.com/en/docs/cells/v2/encryption)  
If you think that we are missing details do not hesitate to tell us and we will update the doc consequently.

---

<div class="post-metadata">

**Author:** ![drzraf](https://yyz2.discourse-cdn.com/flex032/user_avatar/forum.pydio.com/drzraf/32/1207_2.png) [@drzraf](https://forum.pydio.com/u/drzraf)\
**Post date:** [March 9, 2020, 4:13pm UTC](https://forum.pydio.com/t/doc-password-encrypted-encryption-key-how-to-decrypt-it-using-the-password/3123/4 "2020-03-09T16:13:59Z")

</div>

Here are the three question I’m missing answers to from the documentation.

- For a given passphrase (and supposedly given encryption key) I always receive different encrypted base64 strings when I export the key: **Why?**
- **How is the key password-encrypted?**
- Could you provide a **sample `openssl`** command that, provided a plain-text key, would decrypt an encrypted file managed by Pydio?

---

<div class="post-metadata">

**Author:** ![drzraf](https://yyz2.discourse-cdn.com/flex032/user_avatar/forum.pydio.com/drzraf/32/1207_2.png) [@drzraf](https://forum.pydio.com/u/drzraf)\
**Post date:** [April 4, 2020, 3:49am UTC](https://forum.pydio.com/t/doc-password-encrypted-encryption-key-how-to-decrypt-it-using-the-password/3123/5 "2020-04-04T03:49:07Z")

</div>

### File Encryption key ([https://pydio.com/en/docs/cells/v2/encryption](https://pydio.com/en/docs/cells/v2/encryption))

For a **given file an unique random AES key of 256 bits is generated** and **encrypted with the encryption key of the datasource the file belongs to**. The datasource encryption keys are generated by the admin and are protected using the default keyring of the system Cells is installed in.

### SQL

1. `SELECT key\_data FROM idm\_user\_keys;  
This is my 80 characters base64-encoded of the 60 bytes _master key_. Right ?
2. `SELECT HEX(key_data), block_header_size, block_data_size FROM enc_node_keys NATURAL JOIN data_meta NATURAL JOIN enc_node_blocks WHERE data = '"test4.rtf"';`
  - `F2A703227282D4F6D54722FC134973652655A191E04F7D8ABDAEAB54B5F2BE90B00401AF4219BAA4AF7BEB1CBE00B3754388C26E0F9245D290972CAE` # This is the 60 bytes file-specific unique key (120 characters HEX-encoded) which, is _encrypted_ (using the data-source key).
  - `26 # block_header_size`
  - `1751 # block_data_size`  
These sole values are expected to allow me to decrypt the file, right?

3. `openstack object save pydio test4.rtf` to get the encrypted file (here on OVH Swift+S3). 1777 bytes = 1751 + 26

### Key decryption

According to [SetupEncryptMode](https://github.com/pydio/cells/blob/fcbbb18c7e1e034ce0958242244bd2e56c02af60/common/crypto/materials.go#L515) definition and [call](https://github.com/pydio/cells/blob/9f3e391784984337b037a82a33ee7b30cd308024/common/views/handler-encryption.go#L532), `encryptionKeyPlainBytes` is passed coming from [keyProtectionTool.GetDecrypted](https://github.com/pydio/cells/blob/53a979fa0626bd3b43273a77eba14e38bac23007/idm/key/tools.go#L92).  
The first 12 bytes being a nonce.

### Encrypted file format

The 1777 encrypted bytes are composed of:

- A **26 bytes clear-text header**
- The rest of **encrypted**  **data**
  - containing the a 16 bytes AES-GCM **tag** placed at the end of the data

- From [this code](https://github.com/pydio/cells/blob/fcbbb18c7e1e034ce0958242244bd2e56c02af60/common/crypto/materials.go#L658) I assume that the 12 bytes **nonce** is actually represented by the 12 first bytes of the **header**.

### Code

Contrary to `libressl`, `openssl enc` does not support AES-GCM but using Python `Cryptodome` should be.

```python
from Cryptodome.Cipher import AES
import binascii, base64, sys

master_key = base64.b64decode(sys.argv[1])
encrypted_file_key = binascii.unhexlify(sys.argv[2])
file_key_nonce, encrypted_file_key = encrypted_file_key[:12], encrypted_file_key[12:]
cipher = AES.new(master_key[0:32], AES.MODE_GCM, nonce = file_key_nonce)
decrypted_file_key = cipher.decrypt(encrypted_file_key) # = 48 bytes

HeaderSize = 26 # hardcoded
with open(sys.argv[3], "rb") as f:
    data = f.read()
header = data[:26]
nonce, tag = header[:12], data[-16:]
data = data[26:-16]
cipher = AES.new(decrypted_file_key[:32], AES.MODE_GCM, nonce)
cipher.decrypt_and_verify(data, tag)

```

### Run

`$ aes-256-gcm-decrypt "$K" "$FK" test4.rtf`

- With “$K” from `SELECT key_data from idm_user_key`
- And “$FK” the above file key from `SELECT hex(key_data) FROM enc_node_keys`
- `test4.rtf` the encrypted local file

=\> `ValueError: MAC check failed (Cryptodome/Cipher/_mode_gcm.py :: decrypt_and_verify)`

**The decryption failed**.  
Why?  
As you can see above I **trim both the master key and the decrypted file key** to their first 32 bytes but I feel bad about this. Is this the reason for the error? what the adequate use of these keys would be?

Thank you

(@charles?)

---

<div class="post-metadata">

**Author:** ![jabarkarim](https://yyz2.discourse-cdn.com/flex032/user_avatar/forum.pydio.com/jabarkarim/32/7_2.png) [@jabarkarim](https://forum.pydio.com/u/jabarkarim)\
**Post date:** [April 8, 2020, 5:45pm UTC](https://forum.pydio.com/t/doc-password-encrypted-encryption-key-how-to-decrypt-it-using-the-password/3123/6 "2020-04-08T17:45:19Z")

</div>

Hi drzaf,

Everything in the code seems good but I suspect you are working with the wrong master\_key content. Indeed make sure the content of $master\_key is the decrypted version of the key you exported from the admin interface. Which in your case I would call it the datasource key.

And to answer your questions:

For a given passphrase (and supposedly given encryption key) I always receive different encrypted base64 strings when I export the key: **Why?**

- Because a random parameter is used to cook the export output.

**How is the key password-encrypted?**

- I am not sure I understand the question.

Could you provide a **sample `openssl`** command that, provided a plain-text key, would decrypt an encrypted file managed by Pydio?

- I am affraid. In addition of the payload there are additional info that can be handled with a simple command

Regards

---

<div class="post-metadata">

**Author:** ![drzraf](https://yyz2.discourse-cdn.com/flex032/user_avatar/forum.pydio.com/drzraf/32/1207_2.png) [@drzraf](https://forum.pydio.com/u/drzraf)\
**Post date:** [April 8, 2020, 9:24pm UTC](https://forum.pydio.com/t/doc-password-encrypted-encryption-key-how-to-decrypt-it-using-the-password/3123/7 "2020-04-08T21:24:38Z")

</div>

Hi @jabarkarim,  
Let me first thank you for helping on this.

I obtain the master key using `SELECT key_data FROM idm_user_keys`. I only have one data-source, and thus only one master-key. Downloading the encrypted file (`test4.rtf`) using pydio UI brings a readable file so we can safely assume that there is no problem with the instance configuration.

This master key within the `{idm_user_keys}` table is 80 characters long and base64 encoded. Once decoded, this give me the **60 bytes representing the cleartext master key**.

1. Could you confirm that what I’m getting above is right?

2. Since `AES-256` expects a 32 bytes key, can you confirm that only the first 32 bytes of the cleartext master key are to be used to decrypt the file key?

3. Once the file-key (decrypted using the plaintext masterkey), is, once again, larger than 32 bytes (= 48). Could you confirm that I have to strip it again to only use the first 32 bytes?

4. About the master-key export, you said _output is cooked_. Can we say that it’s symmetrically encrypted using the “user-provided”, “per-export” password and a random salt? If yes, then that what’s the algorithm? (so that, with this information at hand, I could obtain back what’s in `{idm_user_keys}` in order to verify the process)

Thank you

---

<div class="post-metadata">

**Author:** ![drzraf](https://yyz2.discourse-cdn.com/flex032/user_avatar/forum.pydio.com/drzraf/32/1207_2.png) [@drzraf](https://forum.pydio.com/u/drzraf)\
**Post date:** [April 14, 2020, 3:04pm UTC](https://forum.pydio.com/t/doc-password-encrypted-encryption-key-how-to-decrypt-it-using-the-password/3123/8 "2020-04-14T15:04:28Z")

</div>

I’m sorry to insist (it’s a moot point to confirm that crypto works in a secure and standard way and that we could decrypt files if anything goes wrong).

I see that encryption use:

> <https://github.com/pydio/cells/blob/2c50df5b6b66b5a125b8b563f540a42842651eca/common/crypto/crypto.go#L68-L69>

  
And [https://golang.org/src/crypto/aes/cipher.go](https://golang.org/src/crypto/aes/cipher.go) shows that it’d fail if the key were not among 16, 24, 32 (and I guess it has always been that way).

```auto
func (kt *userKeyTool) keyByID(ctx context.Context, id string) ([]byte, error) {
   return base64.StdEncoding.DecodeString(rsp.Key.Content)
}
func (kt *userKeyTool) GetDecrypted(ctx context.Context, keyID string, encrypted []byte) ([]byte, error) {
   keyBytes, err := kt.keyByID(ctx, keyID)
   return crypto.Open(keyBytes, encrypted[:12], encrypted[12:])
}
func crypto.Open(key []byte, nonce []byte, cipherData []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	aesgcm, err := cipher.NewGCM(block)
	return aesgcm.Open(nil, nonce, cipherData, nil)
}
func aes.NewCipher(key []byte) (cipher.Block, error) {
	k := len(key)
       if k not in [16, 24, 32] return nil, KeySizeError(k)
}

```

As such when base64-decoding the 80-chars value from `{idm_user_keys}` I’ve 60 bytes and after subtracting 12 bytes of nonce, I’m still having **48 bytes** of master key instead of 32. What am I missing?

---

<div class="post-metadata">

**Author:** ![Michael\_sutherland](https://yyz2.discourse-cdn.com/flex032/user_avatar/forum.pydio.com/michael_sutherland/32/1160_2.png) [@Michael\_sutherland](https://forum.pydio.com/u/Michael_sutherland)\
**Post date:** [April 18, 2020, 9:42pm UTC](https://forum.pydio.com/t/doc-password-encrypted-encryption-key-how-to-decrypt-it-using-the-password/3123/9 "2020-04-18T21:42:08Z")

</div>

Hi drzraf

I am having problems with this also.

I have encrypted files locally that I want to decrypt with my key but no information anywhere on how this can be done. Have you had any luck?

---

<div class="post-metadata">

**Author:** ![drzraf](https://yyz2.discourse-cdn.com/flex032/user_avatar/forum.pydio.com/drzraf/32/1207_2.png) [@drzraf](https://forum.pydio.com/u/drzraf)\
**Post date:** [April 19, 2020, 4:01am UTC](https://forum.pydio.com/t/doc-password-encrypted-encryption-key-how-to-decrypt-it-using-the-password/3123/10 "2020-04-19T04:01:58Z")

</div>

De-obfuscating the encryption process does not seem like an easy task. As you can see, we are waiting for complementary answers from @jabarkarim.

---

<div class="post-metadata">

**Author:** ![jabarkarim](https://yyz2.discourse-cdn.com/flex032/user_avatar/forum.pydio.com/jabarkarim/32/7_2.png) [@jabarkarim](https://forum.pydio.com/u/jabarkarim)\
**Post date:** [April 20, 2020, 9:38am UTC](https://forum.pydio.com/t/doc-password-encrypted-encryption-key-how-to-decrypt-it-using-the-password/3123/11 "2020-04-20T09:38:58Z")

</div>

Hi,

I am sorry for the late answer

1. The master key you obtain by using `SELECT key_data FROM idm_user_keys` is encrypted. All master keys before they are saved in the database are encrypted with the same unique key. That key is randomly generated at start once for all and is saved in the system keychain. Allow me to call it the cells system key.  
Now I dont know a reliable way to obtain the cells system key, that’s why I suggest you extract the master key from the Cells admin interface. Note that what you extracted is the encrypted version of the master key but this time it’s encrypted with a derived key from the password you provided.  
So to get the plain version of the master key you have to AES-GCM-256 decrypt what you’ve extracted with key generated from the password you provided for extraction.  
Refer to the [crypto](https://github.com/pydio/cells/blob/master/common/crypto/crypto.go).KeyFromPassword method to obtain a derived key from a given password.

2. No you dont have to extract anything. If you follow the steps above you’ll obtain a 32 bytes clear master key.

3. Once again you dont have to strip anything.

4. Can we say that it’s symmetrically encrypted using the “user-provided”, “per-export” password and a random salt? - The answer is YES and the algorithm is AES-GCM-256

I hope it helps.

Regards

---

<div class="post-metadata">

**Author:** ![drzraf](https://yyz2.discourse-cdn.com/flex032/user_avatar/forum.pydio.com/drzraf/32/1207_2.png) [@drzraf](https://forum.pydio.com/u/drzraf)\
**Post date:** [April 20, 2020, 5:06pm UTC](https://forum.pydio.com/t/doc-password-encrypted-encryption-key-how-to-decrypt-it-using-the-password/3123/12 "2020-04-20T17:06:10Z")

</div>

Thanks to your, @jabarkarim, I finally got it working (using the exported master-key as you suggested).

> <https://gist.github.com/drzraf/9ca1ba06623e54f398d9c28dce6cf2f5#file-pydio-decrypt-py-L9-L13>

I hope this will help others pinpoint their encryption/decryption problems.

@charles: Interested in a MR for cell’s `./tools/*` ?

(BTW, I think internals of the encryption process needs more documentation)
