Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
Ğ
Ğcli-v2s
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Nicolas80
Ğcli-v2s
Commits
fb218187
Commit
fb218187
authored
6 months ago
by
Nicolas80
Browse files
Options
Downloads
Patches
Plain Diff
Revert "Added support for the different SecretFormat within the Vault"
This reverts commit
fd92df0c
.
parent
2b8dac2d
No related branches found
No related tags found
No related merge requests found
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
src/commands/vault.rs
+21
-121
21 additions, 121 deletions
src/commands/vault.rs
src/keys.rs
+11
-38
11 additions, 38 deletions
src/keys.rs
with
32 additions
and
159 deletions
src/commands/vault.rs
+
21
−
121
View file @
fb218187
use
crate
::
*
;
use
age
::
secrecy
::
Secret
;
use
std
::
io
::{
Read
,
Write
};
use
std
::
path
::
PathBuf
;
/// define universal dividends subcommands
#[derive(Clone,
Default,
Debug,
clap::Parser)]
...
...
@@ -13,18 +12,8 @@ pub enum Subcommand {
Where
,
/// Generate a mnemonic
Generate
,
/// Import key from (substrate)mnemonic or other format with interactive prompt
Import
{
/// Secret key format (substrate, seed, cesium)
#[clap(short
=
'S'
,
long,
required
=
false
,
default_value
=
SecretFormat::Substrate)]
secret_format
:
SecretFormat
,
},
}
struct
VaultItem
{
secret_format
:
SecretFormat
,
secret
:
keys
::
Secret
,
keypair
:
KeyPair
,
/// Import mnemonic with interactive prompt
Import
,
}
// encrypt input with passphrase
...
...
@@ -73,14 +62,11 @@ pub fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliError>
let
mnemonic
=
bip39
::
Mnemonic
::
generate
(
12
)
.unwrap
();
println!
(
"{mnemonic}"
);
}
Subcommand
::
Import
{
secret_format
,
}
=>
{
let
vault_item
=
prompt_secret_and_compute_vault_item
(
secret_format
)
?
;
Subcommand
::
Import
=>
{
let
mnemonic
=
rpassword
::
prompt_password
(
"Mnemonic: "
)
?
;
println!
(
"Enter password to protect the key"
);
let
password
=
rpassword
::
prompt_password
(
"Password: "
)
?
;
let
address
=
store_
vault_item
(
&
data
,
vault_item
,
password
)
?
;
let
address
=
store_
mnemonic
(
&
data
,
&
mnemonic
,
password
)
?
;
println!
(
"Stored secret for {address}"
);
}
};
...
...
@@ -88,120 +74,34 @@ pub fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliError>
Ok
(())
}
fn
create_vault_item
<
F
,
P
>
(
secret_format
:
SecretFormat
,
prompt_fn
:
F
)
->
Result
<
VaultItem
,
GcliError
>
where
F
:
Fn
()
->
(
keys
::
Secret
,
P
),
P
:
Into
<
KeyPair
>
,
{
let
(
secret
,
pair
)
=
prompt_fn
();
Ok
(
VaultItem
{
secret_format
,
secret
,
keypair
:
pair
.into
(),
})
}
fn
prompt_secret_and_compute_vault_item
(
secret_format
:
SecretFormat
)
->
Result
<
VaultItem
,
GcliError
>
{
match
secret_format
{
SecretFormat
::
Substrate
=>
create_vault_item
(
secret_format
,
prompt_secret_substrate_and_compute_keypair
),
SecretFormat
::
Seed
=>
create_vault_item
(
secret_format
,
prompt_seed_and_compute_keypair
),
SecretFormat
::
Cesium
=>
create_vault_item
(
secret_format
,
prompt_secret_cesium_and_compute_keypair
),
SecretFormat
::
Predefined
=>
create_vault_item
(
secret_format
,
prompt_predefined_and_compute_keypair
),
}
}
/// store VaultItem protected with password
fn
store_vault_item
(
/// store mnemonic protected with password
pub
fn
store_mnemonic
(
data
:
&
Data
,
vault_item
:
VaultItem
,
mnemonic
:
&
str
,
password
:
String
,
)
->
Result
<
AccountId
,
GcliError
>
{
let
keypair
=
vault_item
.keypair
;
let
address
=
keypair
.address
();
// write encrypted secret in file identified by address pubkey and secret_format
let
path
=
get_vault_key_path
(
data
,
vault_item
.secret_format
,
address
.to_string
());
// check validity by deriving keypair
let
keypair
=
pair_from_str
(
mnemonic
)
?
;
let
address
=
keypair
.public
();
// write encrypted mnemonic in file identified by pubkey
let
path
=
data
.project_dir
.data_dir
()
.join
(
address
.to_string
());
let
mut
file
=
std
::
fs
::
File
::
create
(
path
)
?
;
match
vault_item
.secret
{
keys
::
Secret
::
SimpleSecret
(
secret
)
=>
{
file
.write_all
(
&
encrypt
(
secret
.as_bytes
(),
password
)
.map_err
(|
e
|
anyhow!
(
e
))
?
[
..
])
?
;
}
keys
::
Secret
::
DualSecret
(
id
,
pwd
)
=>
{
//Making a simple separation of the 2 parts with a newline character
let
secret
=
format!
(
"{id}
\n
{pwd}"
);
file
.write_all
(
&
encrypt
(
secret
.as_bytes
(),
password
)
.map_err
(|
e
|
anyhow!
(
e
))
?
[
..
])
?
;
}
}
Ok
(
address
)
}
fn
get_vault_key_path
(
data
:
&
Data
,
secret_format
:
SecretFormat
,
address
:
String
)
->
PathBuf
{
let
format_str
:
&
'static
str
=
From
::
from
(
secret_format
);
data
.project_dir
.data_dir
()
.join
(
format!
(
"{}-{}"
,
address
,
format_str
))
file
.write_all
(
&
encrypt
(
mnemonic
.as_bytes
(),
password
)
.map_err
(|
e
|
anyhow!
(
e
))
?
[
..
])
?
;
Ok
(
keypair
.public
()
.into
())
}
/// look for different possible paths for vault keys and return both format and path
fn
find_vault_key
(
data
:
&
Data
,
address
:
String
)
->
Result
<
Option
<
(
SecretFormat
,
PathBuf
)
>
,
GcliError
>
{
let
mut
secret_format
=
SecretFormat
::
Substrate
;
let
mut
path
=
get_vault_key_path
(
data
,
secret_format
,
address
.to_string
());
//Also checking for old file name without secret_format which would be for (default) substrate key
if
!
path
.exists
()
{
path
=
data
.project_dir
.data_dir
()
.join
(
address
.to_string
());
}
if
!
path
.exists
()
{
secret_format
=
SecretFormat
::
Seed
;
path
=
get_vault_key_path
(
data
,
secret_format
,
address
.to_string
());
}
if
!
path
.exists
()
{
secret_format
=
SecretFormat
::
Cesium
;
path
=
get_vault_key_path
(
data
,
secret_format
,
address
.to_string
());
}
if
!
path
.exists
()
{
secret_format
=
SecretFormat
::
Predefined
;
path
=
get_vault_key_path
(
data
,
secret_format
,
address
.to_string
());
}
/// try get secret in keystore
pub
fn
try_fetch_secret
(
data
:
&
Data
,
address
:
AccountId
)
->
Result
<
Option
<
String
>
,
GcliError
>
{
let
path
=
data
.project_dir
.data_dir
()
.join
(
address
.to_string
());
if
path
.exists
()
{
Ok
(
Some
((
secret_format
,
path
)))
}
else
{
Ok
(
None
)
}
}
/// try to get secret in keystore, prompt for the password and compute the keypair
pub
fn
try_fetch_key_pair
(
data
:
&
Data
,
address
:
AccountId
)
->
Result
<
Option
<
KeyPair
>
,
GcliError
>
{
if
let
Some
((
secret_format
,
path
))
=
find_vault_key
(
data
,
address
.to_string
())
?
{
println!
(
"Enter password to unlock account {address}"
);
let
password
=
rpassword
::
prompt_password
(
"Password: "
)
?
;
let
mut
file
=
std
::
fs
::
OpenOptions
::
new
()
.read
(
true
)
.open
(
path
)
?
;
let
mut
cypher
=
vec!
[];
file
.read_to_end
(
&
mut
cypher
)
?
;
let
secret_vec
=
decrypt
(
&
cypher
,
password
)
.map_err
(|
e
|
GcliError
::
Input
(
e
.to_string
()))
?
;
let
secret
=
String
::
from_utf8
(
secret_vec
)
.map_err
(|
e
|
anyhow!
(
e
))
?
;
//Still need to handle different secret formats
match
secret_format
{
SecretFormat
::
Substrate
=>
{
Ok
(
Some
(
pair_from_str
(
&
secret
)
?
.into
()))
}
SecretFormat
::
Seed
=>
{
Ok
(
Some
(
pair_from_seed
(
&
secret
)
?
.into
()))
}
SecretFormat
::
Cesium
=>
{
let
mut
lines
=
secret
.lines
();
//Un-wrapping the 2 secrets from each line
let
id
=
lines
.next
()
.unwrap
();
let
pwd
=
lines
.next
()
.unwrap
();
Ok
(
Some
(
pair_from_cesium
(
id
.to_string
(),
pwd
.to_string
())
.into
()))
}
SecretFormat
::
Predefined
=>
{
Ok
(
Some
(
pair_from_predefined
(
&
secret
)
?
.into
()))
}
}
let
secret
=
decrypt
(
&
cypher
,
password
)
.map_err
(|
e
|
GcliError
::
Input
(
e
.to_string
()))
?
;
let
secretstr
=
String
::
from_utf8
(
secret
)
.map_err
(|
e
|
anyhow!
(
e
))
?
;
Ok
(
Some
(
secretstr
))
}
else
{
Ok
(
None
)
}
...
...
This diff is collapsed.
Click to expand it.
src/keys.rs
+
11
−
38
View file @
fb218187
use
sp_core
::
sr25519
::
Pair
;
use
crate
::
*
;
use
sr25519
::
Pair
as
Sr25519Pair
;
...
...
@@ -18,12 +17,6 @@ pub enum SecretFormat {
/// Cesium (scrypt + nacl)
Cesium
,
}
pub
enum
Secret
{
SimpleSecret
(
String
),
DualSecret
(
String
,
String
),
}
impl
FromStr
for
SecretFormat
{
type
Err
=
std
::
io
::
Error
;
...
...
@@ -159,15 +152,10 @@ pub fn pair_from_cesium(id: String, pwd: String) -> nacl::sign::Keypair {
/// ask user to input a secret
pub
fn
prompt_secret_substrate
()
->
Sr25519Pair
{
// Only interested in the keypair which is the second element of the tuple
prompt_secret_substrate_and_compute_keypair
()
.1
}
pub
fn
prompt_secret_substrate_and_compute_keypair
()
->
(
Secret
,
Sr25519Pair
)
{
loop
{
let
mnemonic
=
rpassword
::
prompt_password
(
"Mnemonic: "
)
.unwrap
();
match
pair_from_str
(
&
mnemonic
)
{
Ok
(
pair
)
=>
return
(
Secret
::
SimpleSecret
(
mnemonic
),
pair
)
,
let
mnemonic
=
&
rpassword
::
prompt_password
(
"Mnemonic: "
)
.unwrap
();
match
pair_from_str
(
mnemonic
)
{
Ok
(
pair
)
=>
return
pair
,
Err
(
_
)
=>
println!
(
"Invalid secret"
),
}
}
...
...
@@ -175,27 +163,17 @@ pub fn prompt_secret_substrate_and_compute_keypair() -> (Secret, Sr25519Pair) {
/// ask user pass (Cesium format)
pub
fn
prompt_secret_cesium
()
->
nacl
::
sign
::
Keypair
{
// Only interested in the keypair which is the second element of the tuple
prompt_secret_cesium_and_compute_keypair
()
.1
}
pub
fn
prompt_secret_cesium_and_compute_keypair
()
->
(
Secret
,
nacl
::
sign
::
Keypair
)
{
let
id
=
rpassword
::
prompt_password
(
"Cesium id: "
)
.unwrap
();
let
pwd
=
rpassword
::
prompt_password
(
"Cesium password: "
)
.unwrap
();
(
Secret
::
DualSecret
(
id
.clone
(),
pwd
.clone
()),
pair_from_cesium
(
id
,
pwd
)
)
pair_from_cesium
(
id
,
pwd
)
}
/// ask user to input a seed
pub
fn
prompt_seed
()
->
Sr25519Pair
{
// Only interested in the keypair which is the second element of the tuple
prompt_seed_and_compute_keypair
()
.1
}
pub
fn
prompt_seed_and_compute_keypair
()
->
(
Secret
,
Pair
)
{
loop
{
let
seed
=
rpassword
::
prompt_password
(
"Seed: "
)
.unwrap
();
match
pair_from_seed
(
&
seed
)
{
Ok
(
pair
)
=>
return
(
Secret
::
SimpleSecret
(
seed
),
pair
)
,
let
seed
=
&
rpassword
::
prompt_password
(
"Seed: "
)
.unwrap
();
match
pair_from_seed
(
seed
)
{
Ok
(
pair
)
=>
return
pair
,
Err
(
_
)
=>
println!
(
"Invalid seed"
),
}
}
...
...
@@ -203,13 +181,8 @@ pub fn prompt_seed_and_compute_keypair() -> (Secret,Pair) {
/// ask user pass (Cesium format)
pub
fn
prompt_predefined
()
->
Sr25519Pair
{
// Only interested in the keypair which is the second element of the tuple
prompt_predefined_and_compute_keypair
()
.1
}
pub
fn
prompt_predefined_and_compute_keypair
()
->
(
Secret
,
Sr25519Pair
)
{
let
deriv
=
rpassword
::
prompt_password
(
"Enter derivation path: "
)
.unwrap
();
(
Secret
::
SimpleSecret
(
deriv
.clone
()),
pair_from_predefined
(
&
deriv
)
.expect
(
"invalid secret"
)
)
pair_from_predefined
(
&
deriv
)
.expect
(
"invalid secret"
)
}
/// ask user secret in relevant format
...
...
@@ -231,9 +204,9 @@ pub fn fetch_or_get_keypair(data: &Data, address: Option<AccountId>) -> Result<K
return
Ok
(
pair_from_predefined
(
d
)
.unwrap
()
.into
());
};
// look for corresponding
KeyPair
in keystore
if
let
Some
(
key_pair
)
=
commands
::
vault
::
try_fetch_
key_pair
(
data
,
address
)
?
{
return
Ok
(
key
_
pair
);
// look for corresponding
secret
in keystore
if
let
Some
(
secret
)
=
commands
::
vault
::
try_fetch_
secret
(
data
,
address
)
?
{
return
get_
keypair
(
SecretFormat
::
Substrate
,
Some
(
&
secret
)
);
};
}
// at the moment, there is no way to confg gcli to use an other kind of secret
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment