diff --git a/docs/cli-reference/quill-generate.mdx b/docs/cli-reference/quill-generate.mdx index b06ce6b..ed61813 100644 --- a/docs/cli-reference/quill-generate.mdx +++ b/docs/cli-reference/quill-generate.mdx @@ -69,6 +69,6 @@ Most `quill` commands take a `--pem-file` parameter, for the key used to sign th If a password-protected key needs to be exported for use with another tool such as DFX, use [`quill decrypt-pem`]. -Technical notes: Passwords are run through `scrypt(r=8,p=1,n=131072,len=32)`, and then the file is encrypted with AES-256-CBC. +Technical notes: Passwords are run through `scrypt(r=8,p=1,n=2^17,len=32)`, and then the file is encrypted with AES-256-CBC. [`quill decrypt-pem`]: quill-decrypt-pem.mdx diff --git a/src/commands/generate.rs b/src/commands/generate.rs index 20a09e7..218a26b 100644 --- a/src/commands/generate.rs +++ b/src/commands/generate.rs @@ -1,5 +1,5 @@ use crate::{ - lib::{get_account_id, mnemonic_to_key, AnyhowResult}, + lib::{get_account_id, key_encryption_params, mnemonic_to_key, AnyhowResult}, read_file, }; use anyhow::{anyhow, bail, Context}; @@ -7,9 +7,9 @@ use bip39::{Language, Mnemonic}; use clap::{Parser, ValueEnum}; use dialoguer::Password; use ic_agent::{identity::Secp256k1Identity, Identity}; -use pkcs8::EncodePrivateKey; +use pkcs8::{EncodePrivateKey, EncryptedPrivateKeyInfo, PrivateKeyInfo}; use rand::{rngs::OsRng, thread_rng, RngCore}; -use sec1::LineEnding; +use sec1::{pem::PemLabel, LineEnding}; use std::{ io::{stdin, IsTerminal}, path::PathBuf, @@ -122,7 +122,15 @@ Copy this onto a piece of paper or external media and store it in a safe place." .with_confirmation("Re-enter password", "Passwords did not match") .interact()? }; - key.to_pkcs8_encrypted_pem(thread_rng(), password, LineEnding::default())? + let key_der = key.to_pkcs8_der()?; + let pki = PrivateKeyInfo::try_from(key_der.as_bytes())?; + let mut rng = thread_rng(); + let mut salt = [0u8; 16]; + rng.fill_bytes(&mut salt); + let mut iv = [0u8; 16]; + rng.fill_bytes(&mut iv); + let doc = pki.encrypt_with_params(key_encryption_params(&salt, &iv), password)?; + doc.to_pem(EncryptedPrivateKeyInfo::PEM_LABEL, LineEnding::default())? } }; std::fs::write(&opts.pem_file, &pem)?; diff --git a/src/lib/mod.rs b/src/lib/mod.rs index 6d8daf2..e6fe4dc 100644 --- a/src/lib/mod.rs +++ b/src/lib/mod.rs @@ -22,6 +22,7 @@ use ic_nns_constants::{ use icp_ledger::{AccountIdentifier, Subaccount}; use icrc_ledger_types::icrc1::account::Account; use k256::SecretKey; +use pkcs8::pkcs5::{pbes2::Parameters, scrypt::Params}; use ring::signature::Ed25519KeyPair; use serde_cbor::Value; @@ -610,6 +611,12 @@ pub fn e8s_to_tokens(e8s: Nat) -> BigDecimal { BigDecimal::new(e8s.0.into(), 8) } +pub fn key_encryption_params<'a>(salt: &'a [u8; 16], iv: &'a [u8; 16]) -> Parameters<'a> { + let scrypt_params = Params::new(17, 8, 1, 32).expect("valid scrypt Params consts"); + Parameters::scrypt_aes256cbc(scrypt_params, salt, iv) + .expect("valid PKCS5 encryption parameters") +} + #[cfg(test)] mod tests { use super::{ParsedAccount, ParsedSubaccount};