Skip to content

feat(crypto): mnemonic generation/encryption/decryption/storage#2014

Merged
shamardy merged 22 commits intodevfrom
feat-seed-gen
Apr 9, 2024
Merged

feat(crypto): mnemonic generation/encryption/decryption/storage#2014
shamardy merged 22 commits intodevfrom
feat-seed-gen

Conversation

@shamardy
Copy link
Copy Markdown
Collaborator

@shamardy shamardy commented Nov 24, 2023

#1939

Checklist

  • Seed generation using rust-bip39
  • Deriving a key from password using argon2
  • Encrypting the seed/mnemonic using AES-256-CBC and MAC. Crates used: aes, cbc, cipher, hmac
  • Saving encrypted seed on start if it's new or reading it if it was saved before, for non-wasm and wasm targets.

Todo:

Future:

  • Support different key derivations and encryption algorithms for the framework (SDK)

@shamardy shamardy marked this pull request as ready for review November 28, 2023 09:57
@shamardy shamardy requested a review from dimxy November 28, 2023 09:58
Copy link
Copy Markdown

@artemii235 artemii235 left a comment

Choose a reason for hiding this comment

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

Amazing work! I have a few minor comments 🙂

// The passphrase will then be encrypted and saved whether it was generated or provided.
let wallet_name = deserialize_config_field::<Option<String>>(&ctx, "wallet_name")?;

let passphrase = match (wallet_name, passphrase) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Relying on existence of wallet_name might a bit confusing (just IMHO/nitpick). What do you think about using more explicit config field like "use_encrypted_wallet"?

Copy link
Copy Markdown
Collaborator Author

@shamardy shamardy Nov 30, 2023

Choose a reason for hiding this comment

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

Do you mean that use_encrypted_wallet is a bool?

I wanted to cover the multi seed support for GUIs, so we need different wallet names as identifiers.

1 - For new wallet creation, only a wallet name and a password is passed, a seed is generated by mm2 and saved in wallet_name.dat
Screenshot 2023-11-30 at 11 10 29 AM
2 - If a wallet name already exists, we decrypt the wallet_name.dat using the password
Screenshot 2023-11-30 at 11 11 05 AM
Screenshot 2023-11-30 at 11 15 17 AM
3 - To import a wallet, we use wallet_name, passphrase and password
Screenshot 2023-11-30 at 11 18 39 AM
Screenshot 2023-11-30 at 11 18 26 AM
I guess I didn't cover the case where a seed file is uploaded, so it will be provided as encrypted to mm2, I can add this case I guess. Also, I can maybe use a struct for all the wallet data, so the parameter passed can look like the below:

"wallet": {
   "name": "wallet_name", // Optional for legacy operations where the passphrase is provided each time from GUIs
   "password": "PASSWORD",
   "passphrase": "PASSPHRASE" // This is an optional enum for either encrypted or plaintext
}

This will not be easy to use on cli though.

One more note: For GUIs to migrate, they will need to provide the seed phrase, wallet name, password from their files similar to how importing a wallet works. GUIs can then delete their seed file which uses different encryption algorithms that we plan to deprecate.

What is your opinion on all this @artemii235 :)

Copy link
Copy Markdown
Collaborator Author

@shamardy shamardy Nov 30, 2023

Choose a reason for hiding this comment

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

I just understood now that you meant to use use_encrypted_wallet in addition to wallet_name, Will this be needed if I used a struct as shown in the previous comment?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think it's not needed actually 🙂 Thanks for the detailed explanation!

Copy link
Copy Markdown
Collaborator Author

@shamardy shamardy Dec 1, 2023

Choose a reason for hiding this comment

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

Yeah, I also shouldn't use a struct as I suggested to maintain backward compatibility for the passphrase field. I will add the option to pass an encrypted passphrase for the case of uploading a seed file in next iteration, so I will leave this unresolved until then.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I will add the option to pass an encrypted passphrase for the case of uploading a seed file in next iteration, so I will leave this unresolved until then.

Done

@dimxy
Copy link
Copy Markdown
Collaborator

dimxy commented Nov 30, 2023

Do I understand it right that we sometimes call 'mnemonic' as 'passphrase' in the code (as I can see in bip39 'passphrase' is an optional extra string to additionally protect the mnemonic)?

Copy link
Copy Markdown

@laruh laruh left a comment

Choose a reason for hiding this comment

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

Impressive work!
I have just comment now

@shamardy
Copy link
Copy Markdown
Collaborator Author

Do I understand it right that we sometimes call 'mnemonic' as 'passphrase' in the code (as I can see in bip39 'passphrase' is an optional extra string to additionally protect the mnemonic)?

Yeah, passphrase is kept for backward compatibility since this is what is used in config. In lp_wallet.rs context passphrase is used, mnemonic is used in crypto crate. Do you think I need to modify any of the mnemonic or passphrase usages?

@dimxy
Copy link
Copy Markdown
Collaborator

dimxy commented Nov 30, 2023

Do I understand it right that we sometimes call 'mnemonic' as 'passphrase' in the code (as I can see in bip39 'passphrase' is an optional extra string to additionally protect the mnemonic)?

Yeah, passphrase is kept for backward compatibility since this is what is used in config. In lp_wallet.rs context passphrase is used, mnemonic is used in crypto crate. Do you think I need to modify any of the mnemonic or passphrase usages?

It was a bit confusing for me first time. Maybe we could make a separate PR for this

@shamardy
Copy link
Copy Markdown
Collaborator Author

It was a bit confusing for me first time. Maybe we could make a separate PR for this

I meant mnemonic named as passphrase. Not "bip39 passphrase", we don't use that at all. It is confusing 😕

Copy link
Copy Markdown

@artemii235 artemii235 left a comment

Choose a reason for hiding this comment

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

One more note

@shamardy
Copy link
Copy Markdown
Collaborator Author

shamardy commented Dec 1, 2023

I changed the default word count of mnemonics to 12 since this is what is used in GUIs and is sufficient. Thank you @dimxy for the hint.

artemii235
artemii235 previously approved these changes Dec 4, 2023
Copy link
Copy Markdown

@artemii235 artemii235 left a comment

Choose a reason for hiding this comment

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

🔥

MnemonicError(String),
#[display(fmt = "Error initializing crypto context: {}", _0)]
CryptoInitError(String),
Internal(String),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Maybe good to name internal errors everywhere identically ('InternalError')

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done

- Rename `SYMMETRIC_KEY_SEED` to `MASTER_NODE_HMAC_KEY` for clarity
- Change `initialize_wallet_passphrase` to take a reference to `MmArc`
- Remove unnecessary comments and fix some errors
Copy link
Copy Markdown

@onur-ozkan onur-ozkan left a comment

Choose a reason for hiding this comment

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

Left some dependency information (please note that I haven't checked for sec advisories on them).

doctest = false

[dependencies]
aes = "0.8.3"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Q: Can we use version 0.7.5 instead of duplicating this dependency?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I wanted to use the latest version for such an important encryption, after this #1957 is merged I can try to update 0.7.5 to 0.8.3 instead in librustzcash

onur-ozkan
onur-ozkan previously approved these changes Feb 18, 2024
Copy link
Copy Markdown

@onur-ozkan onur-ozkan left a comment

Choose a reason for hiding this comment

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

Thank you for the fixes.

@shamardy
Copy link
Copy Markdown
Collaborator Author

shamardy commented Apr 8, 2024

Copied none finished items in this checklist to the parent issue #1939 (comment). Will resolve conflicts then write docs for QA and merge this PR.

@shamardy
Copy link
Copy Markdown
Collaborator Author

shamardy commented Apr 9, 2024

@KomodoPlatform/qa This Pull Request introduces a new optional parameter, wallet_name, to the mm2 configuration. If this parameter is not provided, the passphrase parameter will function as before. If a passphrase is found, it will be used as the mnemonic without being stored in the mm2 storage/database. If a passphrase is not found, mm2 will start in no-login mode.

When wallet_name is used, wallet_password must also be provided. This leads to one of the following scenarios:

  1. If no passphrase is provided:

    1. mm2 checks if this wallet_name has been used before, which would mean that there is an encrypted passphrase saved for it. If it has been used, the seed is decrypted using the password and mm2 is initialized with it.
    2. If wallet_name has not been used before, mm2 generates a passphrase/mnemonic that is used to initialize mm2. The passphrase is then encrypted using the password and saved for future use with the same wallet_name as in point 1.1.
  2. If a passphrase is provided along with wallet_name and wallet_password:

    1. If it's a string as before, it means it's plaintext, the passphrase is used to initialize mm2. It's then encrypted using the password and saved under this wallet name. It can be reused as in point 1.1 without passing it again. This mode can be used to migrate passphrases from GUIs to mm2 to use its encryption by passing the passphrase as plaintext. This mode can also be used to import passphrases as plaintext.
    2. If the passphrase is passed as an encrypted passphrase (which is a JSON), this is for the upload seed file mode. This is a file that was previously exported using the new API method get_mnemonic. The passphrase is decrypted using the wallet_password and mm2 is initialized with it. It is then saved encrypted under the wallet_name for subsequent use as in point 1.1.

So, when wallet_name is passed, seed management is completely handled by mm2 instead of GUIs. If it's not passed, it's the legacy approach where seed is managed by GUIs and the passphrase/mnemonic is passed to mm2 during initialization.

A new API method called get_mnemonic has been added. The request can be for either the encrypted passphrase (for the GUI to export it as a file) or as a plaintext passphrase (for the GUI to display it). The requests/responses for both modes are:

Request for encrypted passphrase:

{
    "userpass": "{{userpass}}",
    "mmrpc": "2.0",
    "method": "get_mnemonic",
    "params": {
        "format": "encrypted"
    }
}

Response for encrypted passphrase request:

{
  "format": "encrypted",
  "encrypted_mnemonic_data": {
    // Encrypted data
  }
}

Request for plaintext passphrase:

{
    "userpass": "{{userpass}}",
    "mmrpc": "2.0",
    "method": "get_mnemonic",
    "params": {
        "format": "plaintext",
        "password": "password123" // The password used to encrypt the passphrase when the wallet was created
    }
}

Response for plaintext passphrase request:

{
  "format": "plaintext",
  "mnemonic": "your_mnemonic_here"
}

@shamardy shamardy merged commit ffe761e into dev Apr 9, 2024
@shamardy shamardy deleted the feat-seed-gen branch April 9, 2024 02:23
dimxy pushed a commit to dimxy/komodo-defi-framework that referenced this pull request Apr 9, 2024
* dev:
  feat(nft-swap): nft swap protocol v2 POC (GLEECBTC#2084)
  fix(zcoin): syncing and activation improvements (GLEECBTC#2089)
  feat(crypto): mnemonic generation/encryption/decryption/storage (GLEECBTC#2014)
  fix(eth): error handling in RPCs (GLEECBTC#2090)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants