11use clap:: Args ;
2+ use dialoguer:: Password ;
3+ use elliptic_curve:: zeroize:: Zeroizing ;
24use ic_agent:: { Identity as _, export:: Principal , identity:: BasicIdentity } ;
3- use icp:: { context:: Context , identity:: key} ;
5+ use icp:: { context:: Context , fs :: read_to_string , identity:: key, prelude :: * } ;
46use snafu:: { ResultExt , Snafu } ;
5- use tracing:: info;
7+ use tracing:: { info, warn } ;
68
7- use crate :: operations:: ii_poll;
9+ use crate :: { commands :: identity :: StorageMode , operations:: ii_poll} ;
810
911/// Link an Internet Identity to a new identity
1012#[ derive( Debug , Args ) ]
1113pub ( crate ) struct IiArgs {
1214 /// Name for the linked identity
1315 name : String ,
16+
17+ /// Where to store the session private key
18+ #[ arg( long, value_enum, default_value_t) ]
19+ storage : StorageMode ,
20+
21+ /// Read the storage password from a file instead of prompting (for --storage password)
22+ #[ arg( long, value_name = "FILE" ) ]
23+ storage_password_file : Option < PathBuf > ,
1424}
1525
1626pub ( crate ) async fn exec ( ctx : & Context , args : & IiArgs ) -> Result < ( ) , IiError > {
27+ let create_format = match args. storage {
28+ StorageMode :: Plaintext => key:: CreateFormat :: Plaintext ,
29+ StorageMode :: Keyring => key:: CreateFormat :: Keyring ,
30+ StorageMode :: Password => {
31+ let password = if let Some ( path) = & args. storage_password_file {
32+ read_to_string ( path)
33+ . context ( ReadStoragePasswordFileSnafu ) ?
34+ . trim ( )
35+ . to_string ( )
36+ } else {
37+ Password :: new ( )
38+ . with_prompt ( "Enter password to encrypt identity" )
39+ . with_confirmation ( "Confirm password" , "Passwords do not match" )
40+ . interact ( )
41+ . context ( StoragePasswordTermReadSnafu ) ?
42+ } ;
43+ key:: CreateFormat :: Pbes2 {
44+ password : Zeroizing :: new ( password) ,
45+ }
46+ }
47+ } ;
48+
1749 let secret_key = ic_ed25519:: PrivateKey :: generate ( ) ;
1850 let identity_key = key:: IdentityKey :: Ed25519 ( secret_key. clone ( ) ) ;
1951 let basic = BasicIdentity :: from_raw_key ( & secret_key. serialize_raw ( ) ) ;
@@ -29,18 +61,37 @@ pub(crate) async fn exec(ctx: &Context, args: &IiArgs) -> Result<(), IiError> {
2961 ctx. dirs
3062 . identity ( ) ?
3163 . with_write ( async |dirs| {
32- key:: link_ii_identity ( dirs, & args. name , identity_key, & chain, ii_principal)
64+ key:: link_ii_identity (
65+ dirs,
66+ & args. name ,
67+ identity_key,
68+ & chain,
69+ ii_principal,
70+ create_format,
71+ )
3372 } )
3473 . await ?
3574 . context ( LinkSnafu ) ?;
3675
3776 info ! ( "Identity `{}` linked to Internet Identity" , args. name) ;
3877
78+ if matches ! ( args. storage, StorageMode :: Plaintext ) {
79+ warn ! (
80+ "This identity is stored in plaintext and is not secure. Do not use it for anything of significant value."
81+ ) ;
82+ }
83+
3984 Ok ( ( ) )
4085}
4186
4287#[ derive( Debug , Snafu ) ]
4388pub ( crate ) enum IiError {
89+ #[ snafu( display( "failed to read storage password file" ) ) ]
90+ ReadStoragePasswordFile { source : icp:: fs:: IoError } ,
91+
92+ #[ snafu( display( "failed to read storage password from terminal" ) ) ]
93+ StoragePasswordTermRead { source : dialoguer:: Error } ,
94+
4495 #[ snafu( display( "failed during II authentication" ) ) ]
4596 Poll { source : ii_poll:: IiPollError } ,
4697
0 commit comments