Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

argon2: choose a steady parallelism value #3630

Merged
merged 5 commits into from
Nov 22, 2019
Merged

argon2: choose a steady parallelism value #3630

merged 5 commits into from
Nov 22, 2019

Conversation

jtolio
Copy link
Member

@jtolio jtolio commented Nov 21, 2019

Huge thanks to Least Authority, our security audit team, for finding and notifying us of this issue!!! This is almost certainly the cause of lots of bugs we’ve been unable to identify for some time now.

Problem

At the time of this writing, this line of code is how we turn a passphrase into a root encryption key:
https://github.com/storj/storj/blob/ee6c1ca/pkg/encryption/password.go#L40
keyData := argon2.IDKey(password, pathSalt, 1, uint32(64*memory.MiB/memory.KiB), uint8(runtime.GOMAXPROCS(-1)), 32)

The documentation for argon2’s IDKey says:

IDKey derives a key from the password, salt, and cost parameters using Argon2id returning a byte slice of length keyLen that can be used as cryptographic key. The CPU cost and parallelism degree must be greater than zero.

For example, you can get a derived key for e.g. AES-256 (which needs a 32-byte key) by doing:

key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)

The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number. If using that amount of memory (64 MB) is not possible in some contexts then the time parameter can be increased to compensate.

The time parameter specifies the number of passes over the memory and the memory parameter specifies the size of the memory in KiB. For example memory=64*1024 sets the memory cost to ~64 MB. The number of threads can be adjusted to the numbers of available CPUs. The cost parameters should be increased as memory latency and CPU parallelism increases. Remember to get a good random salt.

Emphasis mine. (runtime.GOMAXPROCS(-1) returns the number of CPUs available).

It turns out, argon2 generates different keys depending on the requested parallelism!!

https://play.golang.org/p/cyuYw2cXQxI

In our defense, I think the documentation for the argon2.IDKey method is really misleading, even though we probably should have checked this.

Implications

We generate keys from passphrases whenever someone calls SaltedKeyFromPassphrase in libuplink, or whenever someone runs uplink setup or gateway setup. This is the only time this matters.

However, if someone runs uplink setup on two different devices with the same project, API key, and passphrase, but the devices have a different number of CPU cores, they will not be able to encrypt or decrypt each installation’s data.

If instead, someone uses scopes/encryption accesses for sharing access, key generation is not involved, and this problem will not affect the user.

This is a broad problem that certainly affects all of our users if they ever use more than one device. A sample of Storj employees had surprisingly large variation in the number of cores people had (4, 8, 12, and 16 were all common).

Proposed solution

Uggghhhhh

Well, so it’s easy to fix for all new users going forward. We pick the argon2 parallelism statically.

For all existing users it’s hard. There is no default value that will broadly work for most people. Continuing to use CPU-specific settings means that decryption does not interoperate well.

We could give up on passphrase determinism (scopes are unaffected, and just require everyone to save their scopes), but then why have passphrases at all?

So, we have a couple of different users affected.

  • People using libuplink directly - To libuplink, we add a ProjectConfig volatile field where the user can set the Argon2 concurrency value with documentation and a warning. We default to, say, 8, but in the comments say that prior to version x, we used the number of CPU cores your computer had. If you need to decrypt data you uploaded, please set this value. They set the value going forward and everything keeps working. New users ignore it.
  • People using commandline tools - To the uplink CLI, we clean up our decryption errors significantly. If we get a decryption error, instead of log spew we say something nice about the encryption settings being wrong. We can even add: “Beta warning: prior to version X, we used the number of cores your computer had. If you just ran setup, try moving your existing configuration to a safe location and rerunning setup again with --pbkdf-concurrency 6” or something. Then, users of the uplink CLI could specify during setup what they wanted that value to be, and then it could get preserved to their configuration. It again would not affect imported scopes.

This change does everything but the error cleanup.

Code Review Checklist (to be filled out by reviewer)

  • NEW: Are there any Satellite database migrations? Are they forwards and backwards compatible?
  • Does the PR describe what changes are being made?
  • Does the PR describe why the changes are being made?
  • Does the code follow our style guide?
  • Does the code follow our testing guide?
  • Is the PR appropriately sized? (If it could be broken into smaller PRs it should be)
  • Does the new code have enough tests? (every PR should have tests or justification otherwise. Bug-fix PRs especially)
  • Does the new code have enough documentation that answers "how do I use it?" and "what does it do?"? (both source documentation and higher level, diagrams?)
  • Does any documentation need updating?
  • Do the database access patterns make sense?

Change-Id: I6006da7d7980cda88f5f08ee759612df23a8132d
@jtolio jtolio requested review from zeebo and a team November 21, 2019 20:36
@cla-bot cla-bot bot added the cla-signed label Nov 21, 2019
@ghost ghost requested review from Qweder93 and removed request for a team November 21, 2019 20:36
@@ -25,19 +24,19 @@ func sha256hmac(key, data []byte) ([]byte, error) {

// DeriveRootKey derives a root key for some path using the salt for the bucket and
// a password from the user. See the password key derivation design doc.
func DeriveRootKey(password, salt []byte, path storj.Path) (*storj.Key, error) {
func DeriveRootKey(password, salt []byte, argon2Threads uint8) (*storj.Key, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the path changes the design (https://github.com/storj/storj/blob/master/docs/blueprints/password-key-derivation.md#design)

While it's not used, it exists to allow different root encryption keys at different paths inside of a bucket.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay nbd to bring it back. i wrongly assumed it was refactor cruft

Change-Id: Ied9039f9a9be1d0f6ff3c7d5c4839a83fc7b4b1f
@jtolio jtolio added Request Code Review Code review requested Reviewer Can Merge If all checks have passed, non-owner can merge PR labels Nov 22, 2019
zeebo
zeebo previously approved these changes Nov 22, 2019
Change-Id: I07288cd6cef32ba387f2f003febff5c297e50997
Change-Id: Icdbda8b709cc100a86f3859303c40edb8dff1e0f
@@ -40,6 +40,8 @@ type GatewayFlags struct {
uplink.Config

Version checker.Config

PBKDFConcurrency int `help:"please see <url>. default value recommended" default:"0"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to put an actual URL value here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, we also need to update the comment about which version numbers the problem applies to, once we have a version it no longer applies to. i'll submit a new pr with updates for both when that happens

@jtolio jtolio merged commit 031ba86 into master Nov 22, 2019
@jtolio jtolio deleted the jt/argon2 branch November 22, 2019 21:00
littleskunk pushed a commit that referenced this pull request Nov 25, 2019
* argon2: choose a steady parallelism value

Change-Id: I6006da7d7980cda88f5f08ee759612df23a8132d

* whoops, not cruft

Change-Id: Ied9039f9a9be1d0f6ff3c7d5c4839a83fc7b4b1f

* fix broken test file

Change-Id: I07288cd6cef32ba387f2f003febff5c297e50997

* fix linting error

Change-Id: Icdbda8b709cc100a86f3859303c40edb8dff1e0f
(cherry picked from commit 031ba86)
bryanchriswhite added a commit that referenced this pull request Nov 25, 2019
* storj/master: (63 commits)
  web/satellite:  token payments logic (#3581)
  satellite/metainfo: reduce pointerDB access for CommitObject (#3589)
  satellite/metainfo: Fix misspelling in comment (#3636)
  argon2: choose a steady parallelism value (#3630)
  satellitedb: add support to testplanet for cockroachdb (#3634)
  satellite/console/auth: return in error handle added (#3639)
  Make sed a little more cross platformable (#3629)
  web: ms edge support bug fixed (#3638)
  web/satellite: registration/welcome message fixed, usage-report url fixed, storj-sim fixed (#3622)
  web/satellite: fonts changed to Inter (#3620)
  storagenode/updater: read identity location from storagenode's config.yaml (#3607)
  cmd/segment-reaper: Implement bitmask type (#3626)
  storagenode/gracefulexit: improve logging (#3633)
  private/testplanet: add a mock referral manager server into testplanet (#3631)
  satellite/gracefulexit: refactor concurrency (#3624)
  pkg/pb/referralmanager: update to add satellite ID to Get Tokens request (#3625)
  satellite/metainfo: improve Loop comments (#3595)
  storagenode: add bandwidth metrics (#3623)
  satellite/console: Add security headers (#3615)
  satellite/payments: token deposit accept cents (#3628)
  ...
bryanchriswhite pushed a commit to bryanchriswhite/storj that referenced this pull request Oct 29, 2020
* argon2: choose a steady parallelism value

Change-Id: I6006da7d7980cda88f5f08ee759612df23a8132d

* whoops, not cruft

Change-Id: Ied9039f9a9be1d0f6ff3c7d5c4839a83fc7b4b1f

* fix broken test file

Change-Id: I07288cd6cef32ba387f2f003febff5c297e50997

* fix linting error

Change-Id: Icdbda8b709cc100a86f3859303c40edb8dff1e0f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla-signed Request Code Review Code review requested Reviewer Can Merge If all checks have passed, non-owner can merge PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants