cosmjs icon indicating copy to clipboard operation
cosmjs copied to clipboard

Generate and store salt

Open shapkarin opened this issue 2 years ago • 30 comments

Hello. It there any way to make salt configurable?

https://github.com/cosmos/cosmjs/blob/f198f0acc425051f0ebd71489d993d7851b48475/packages/proto-signing/src/wallet.ts#L15

I found a way to replace that file. And put salt to the env variable:

new webpack.NormalModuleReplacementPlugin(
  new RegExp('@cosmjs/proto-signing/build/wallet.js'),
  path.resolve(
    __dirname,
    'replaced/wallet.js'
  )
)

Of course someone can find the value in a builded js, but it looks harder.

shapkarin avatar Oct 17 '21 17:10 shapkarin

Hello. It there any way to make salt configurable?

Heyho! Why do you want to do that? I'd be curious about a detailed motivation and usecase description. If it makes sense to do, it should be trivial to make it configurable through proper APIs.

Of course someone can find the value in a builded js, but it looks harder.

Are you sure you're on the right path here? The salt is not a secret value. It's public and that's perfectly fine. It just makes rainbow attacks harder.

webmaster128 avatar Oct 17 '21 17:10 webmaster128

While wallet development, I decided that If It just makes rainbow attacks harder, why not make it a bit more harder with "hidden" salt from env variable.

shapkarin avatar Oct 17 '21 17:10 shapkarin

Okay, so one scenario could be this: All CosmJS users use the same salt. One can come up with a rainbow table of passwords that are all hashed with one salt in order to attack CosmJS users. Now what you can do is override the salt in each application. Still no need for env variables and hiding. Then you need application specific tables instead of lib specific tables, making them much harder to generate or less attractive.

I'm unsure if this is worth it. Changing the argon 2 parameters should be enough to get unique hashes and at the same time tune the difficulty of the KDF. Then if you push this computation to a WebWorker to not freeze the UI you can go really high with your params. Development effort is much more worth there. Also this heavily reduces the risk that an encrypted wallet cannot be decrypted anymore because you remain compatible with standard CosmJS tooling.

webmaster128 avatar Oct 17 '21 17:10 webmaster128

Then you need application specific tables instead of lib specific tables, making them much harder to generate or less attractive.

That was the goal. Instead of lib specific tables has different application specific tables so one table can't be used to any app.

shapkarin avatar Oct 17 '21 18:10 shapkarin

Yeah. This can be done and there is nothing wrong with it. But I highly recommend not making this a priority and don't implement costy and risky hacks for it.

I leave this ticket open to make the salt configurable. This is easy to implement.

webmaster128 avatar Oct 17 '21 18:10 webmaster128

I wonder if we could just store the salt together with all other argon2 options. The options need to be read anyways before the decryption. Then we can automatically generate a new salt for every encryption.

webmaster128 avatar Oct 17 '21 18:10 webmaster128

Agree, it could be an option with default value "The CosmJS salt.".

shapkarin avatar Oct 17 '21 18:10 shapkarin

@webmaster128 I don't have problems to provide a new PR. But not right now or today. Maybe tomorrow's evening.

shapkarin avatar Oct 17 '21 18:10 shapkarin

@webmaster128 maybe it will be ready even at tomorrows daytime. Looks easy to do. But next should be carefully rechecked at review.

shapkarin avatar Oct 17 '21 18:10 shapkarin

Alright, go ahead. No rush. I'll look at it then.

webmaster128 avatar Oct 17 '21 18:10 webmaster128

will try to add *.spec.ts if my PR will not include it, feel free to add yours.

shapkarin avatar Oct 17 '21 18:10 shapkarin

Looking at the new title, I just want to make sure we are on the same page:

  • Salt is randomly generated, not configured at encryption time
  • It is stored together with the other Argon2 options in a field salt?: string (hex or base64 encoded)
  • When decrypting, the salt is used. When non-existent, the default salt is used.

This means we don't have any additional work for the user of the library – just upgrade.

webmaster128 avatar Oct 18 '21 07:10 webmaster128

@webmaster128 so with random generated salt we will get not an app specific salt, but encryption specific salt and it should be a part of encrypted result. Right?

shapkarin avatar Oct 18 '21 11:10 shapkarin

we will get not an app specific salt, but encryption specific salt

Exactly. Better salting with no configuration.

and it should be a part of encrypted result

It would be stored next to the encrypted data in the config that stores the Argon2 options. The salt is unencrypted (can be public) but unique then.

webmaster128 avatar Oct 18 '21 11:10 webmaster128

Thanks. Got that. To create random salt for each encryption and store it next to a cipher.

shapkarin avatar Oct 18 '21 11:10 shapkarin

@webmaster128 it should be just serialized.salt?

here is the example:

const serialized = {
  type: "directsecp256k1hdwallet-v1",
  kdf: {
    algorithm: "argon2id",
    params: {
      outputLength: 32,
      opsLimit: 24,
      memLimitKib: 12 * 1024,
    },
  },
  encryption: {
    algorithm: "xchacha20poly1305-ietf",
  },
  data: "",
  salt: "",
}

Here is tests. Will improve them and change it('') titles to lowercased, as all spec.

shapkarin avatar Oct 18 '21 21:10 shapkarin

The steps are done as follows:

  1. User provides password string, possibly low entropy
  2. The KDF turns this string into a byte series using a hard to bruteforce algorithm that is configurable. This KDF may or may not have a salt. A output length for the encryption key is configured.
  3. The encryption is performed using the encryption key from 2.

Step 3 does not know where the encryption key comes from. It does not know which KDF is used and what a KDF even is. It does not know what a salt is because encryption has no salt.

So the salt clearly belongs to the KDF step. And it is algorithm specific. For this reason I would pout it in kdf.params and Argon2idOptions.

webmaster128 avatar Oct 18 '21 21:10 webmaster128

What do you think about to code that? Does't looks like I have a lot of time for that. In any case will back to it this week, please mark this issue in a commit #903

shapkarin avatar Oct 18 '21 22:10 shapkarin

What do you think about to code that? Does't looks like I have a lot of time for that.

So far I only see two tests. Without a (draft) PR it's hard to see what you did.

But if you don't make it, I'll implement this at some point.

webmaster128 avatar Oct 19 '21 11:10 webmaster128

@webmaster128 I got emergency things to do. In any case I'm glad of the participation, feel free to implement. Tests are not so good, at least the second one.

shapkarin avatar Oct 19 '21 12:10 shapkarin

@webmaster128 I removed second test, and change the first one.

shapkarin avatar Oct 19 '21 12:10 shapkarin

@webmaster128 please, have a check my PR: added backward compatibility test: serialize with current DirectSecp256k1HdWallet version, and deserialize with the updated one. Please, review it. Maybe I need to add some other cases for other packages?

shapkarin avatar Oct 19 '21 15:10 shapkarin

@webmaster128 link https://github.com/cosmos/cosmjs/pull/906/

shapkarin avatar Oct 19 '21 15:10 shapkarin

Nice start, but no implementation in #906.

I started working on this myself now. It's a bit tricky becasue of the API design that allows KDF execution on a different thread (e.g. WebWorker). Will keep you updated on progress.

webmaster128 avatar Oct 24 '21 21:10 webmaster128

@webmaster128 I appreciate that, thanks. I can't find any time for that.

shapkarin avatar Oct 25 '21 13:10 shapkarin

@webmaster128 hello, can you please closer describe the problem? I will try to find some time for the help.

shapkarin avatar Nov 12 '21 07:11 shapkarin

@webmaster128 hello I will try to prepare the PR. I guess that I can just use something like:

import { Random } from '@cosmjs/crypto'

const salt = Random.getBytes(sodium.crypto_pwhash_SALTBYTES);

shapkarin avatar Mar 12 '22 19:03 shapkarin

@webmaster128 it may be like that

But next I should return the salt itself with https://github.com/cosmos/cosmjs/blob/663d39c7bb645a8a420ef8e2c7444ccf68e8d718/packages/crypto/src/libsodium.ts#L46 and it use libsodium-wrappers package

shapkarin avatar Mar 12 '22 20:03 shapkarin

@webmaster128 hello. random salt is already used in the wallet module https://github.com/cosmos/cosmjs/blob/486ab65ed9d0d935c3f15760e64cbf707a85a1b4/packages/proto-signing/src/wallet.ts#L55-L72, but not in the directsecp256k1hdwallet https://github.com/cosmos/cosmjs/blob/fcf01d4b09ed2cda3edff5e5d75f3670d22ddd77/packages/proto-signing/src/directsecp256k1hdwallet.ts#L294-L298 https://github.com/cosmos/cosmjs/blob/fcf01d4b09ed2cda3edff5e5d75f3670d22ddd77/packages/proto-signing/src/wallet.ts#L26-L36

shapkarin avatar Mar 21 '22 02:03 shapkarin

@webmaster128 hello. I finally found time to make that contribution better. xchacha20poly1305Ietf salt also gets type Uint8Array right now.

shapkarin avatar Mar 24 '22 08:03 shapkarin