Line data Source code
1 : #include "fdctl.h"
2 :
3 : #include "../../disco/keyguard/fd_keyload.h"
4 :
5 : #include <fcntl.h>
6 : #include <unistd.h>
7 : #include <sys/stat.h>
8 : #include <sys/random.h>
9 :
10 : typedef enum {
11 : CMD_NEW_IDENTITY,
12 : CMD_NEW_VOTE_ACCOUNT,
13 : CMD_PUBKEY,
14 : } cmd_type_t;
15 :
16 : void
17 : keys_cmd_args( int * pargc,
18 : char *** pargv,
19 0 : args_t * args) {
20 0 : if( FD_UNLIKELY( *pargc < 2 ) ) goto err;
21 :
22 0 : if( FD_LIKELY( !strcmp( *pargv[ 0 ], "new" ) ) ) {
23 0 : (*pargc)--;
24 0 : (*pargv)++;
25 0 : if( FD_LIKELY( !strcmp( *pargv[ 0 ], "identity" ) ) ) args->keys.cmd = CMD_NEW_IDENTITY;
26 0 : else if( FD_LIKELY( !strcmp( *pargv[ 0 ], "vote" ) ) ) args->keys.cmd = CMD_NEW_VOTE_ACCOUNT;
27 0 : }
28 0 : else if( FD_LIKELY( !strcmp( *pargv[ 0 ], "pubkey" ) ) ) {
29 0 : (*pargc)--;
30 0 : (*pargv)++;
31 0 : if( FD_UNLIKELY( *pargc < 1 ) ) goto err;
32 0 : args->keys.cmd = CMD_PUBKEY;
33 0 : fd_memcpy( args->keys.file_path, *pargv[ 0 ], sizeof( args->keys.file_path ) );
34 0 : }
35 0 : else goto err;
36 :
37 0 : (*pargc)--;
38 0 : (*pargv)++;
39 :
40 0 : return;
41 :
42 0 : err:
43 0 : FD_LOG_ERR(( "unrecognized subcommand `%s`\nusage:\n"
44 0 : " keys new identity\n"
45 0 : " keys new vote\n"
46 0 : " keys pubkey <path-to-keyfile>\n",
47 0 : *pargv[0] ));
48 0 : }
49 :
50 : void FD_FN_SENSITIVE
51 : generate_keypair( char const * keyfile,
52 : config_t * const config,
53 0 : int use_grnd_random ) {
54 0 : uint flags = use_grnd_random ? GRND_RANDOM : 0U;
55 :
56 0 : uchar keypair[ 64 ];
57 0 : long bytes_produced = 0L;
58 0 : while( FD_LIKELY( bytes_produced<32L ) ) {
59 0 : long n = getrandom( keypair+bytes_produced, (ulong)(32-bytes_produced), flags );
60 0 : if( FD_UNLIKELY( -1==n ) ) FD_LOG_ERR(( "could not create keypair, getrandom() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
61 0 : bytes_produced += n;
62 0 : }
63 :
64 0 : fd_sha512_t _sha[ 1 ];
65 0 : fd_sha512_t * sha = fd_sha512_join( fd_sha512_new( _sha ) );
66 0 : if( FD_UNLIKELY( !sha ) ) FD_LOG_ERR(( "could not create keypair, fd_sha512 join failed" ));
67 0 : fd_ed25519_public_from_private( keypair+32UL, keypair, sha );
68 :
69 : /* Switch to non-root uid/gid for file creation. Permissions checks
70 : are still done as root. */
71 0 : gid_t gid = getgid();
72 0 : uid_t uid = getuid();
73 0 : if( FD_LIKELY( !gid && setegid( config->gid ) ) ) FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
74 0 : if( FD_LIKELY( !uid && seteuid( config->uid ) ) ) FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
75 :
76 : /* find last `/` in keyfile and zero it */
77 0 : char keyfile_copy[ PATH_MAX ] = {0};
78 0 : strncpy( keyfile_copy, keyfile, sizeof( keyfile_copy )-1UL );
79 0 : char * last_slash = strrchr( keyfile_copy, '/' );
80 0 : if( FD_LIKELY( last_slash ) ) {
81 0 : *last_slash = '\0';
82 0 : mkdir_all( keyfile_copy, config->uid, config->gid );
83 0 : }
84 :
85 0 : int fd = open( keyfile, O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR );
86 0 : if( FD_UNLIKELY( -1==fd ) ) {
87 0 : if( FD_LIKELY( errno==EEXIST ) ) FD_LOG_ERR(( "could not create keypair as the keyfile `%s` already exists", keyfile ));
88 0 : else FD_LOG_ERR(( "could not create keypair, open(%s) failed (%i-%s)", keyfile, errno, fd_io_strerror( errno ) ));
89 0 : }
90 :
91 0 : if( FD_UNLIKELY( write( fd, "[", 1 )!=1L ) ) FD_LOG_ERR(( "could not create keypair, write() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
92 :
93 0 : for( ulong i=0UL; i<64UL; i++ ) {
94 0 : if( FD_LIKELY( i ) ) {
95 0 : if( FD_UNLIKELY( write( fd, ",", 1 )!=1L ) ) FD_LOG_ERR(( "could not create keypair, write() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
96 0 : }
97 :
98 0 : char digits[ 4 ];
99 0 : ulong digits_len;
100 0 : FD_TEST( fd_cstr_printf_check( digits, sizeof( digits ), &digits_len, "%d", keypair[ i ] ) );
101 0 : if( FD_UNLIKELY( write( fd, digits, digits_len )!=(long)digits_len ) ) FD_LOG_ERR(( "could not create keypair, write() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
102 0 : }
103 :
104 0 : if( FD_UNLIKELY( write( fd, "]", 1 )!=1L ) ) FD_LOG_ERR(( "could not create keypair, write() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
105 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "could not create keypair, close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
106 :
107 0 : FD_LOG_NOTICE(( "successfully created keypair in `%s`", keyfile ));
108 :
109 0 : if( FD_UNLIKELY( seteuid( uid ) ) ) FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
110 0 : if( FD_UNLIKELY( setegid( gid ) ) ) FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
111 :
112 0 : fd_memset_explicit( keypair, 0, 64UL );
113 0 : }
114 :
115 : void
116 0 : keys_pubkey( const char * file_path ) {
117 0 : uchar const * pubkey = fd_keyload_load( file_path, 1 );
118 0 : char pubkey_str[ FD_BASE58_ENCODED_32_SZ ];
119 0 : fd_base58_encode_32( pubkey, NULL, pubkey_str );
120 0 : FD_LOG_STDOUT(( "%s\n", pubkey_str ));
121 0 : }
122 :
123 : void
124 : keys_cmd_fn( args_t * args,
125 0 : config_t * const config ) {
126 0 : if( FD_LIKELY( args->keys.cmd == CMD_NEW_IDENTITY ) ) {
127 0 : generate_keypair( config->consensus.identity_path, config, 1 );
128 0 : } else if( FD_LIKELY( args->keys.cmd == CMD_NEW_VOTE_ACCOUNT ) ) {
129 0 : if( FD_UNLIKELY( !strcmp( config->consensus.vote_account_path, "" ) ) )
130 0 : FD_LOG_ERR(( "Cannot create a vote account keypair because your validator is not configured "
131 0 : "to vote. Please set [consensus.vote_account_path] in your configuration file." ));
132 :
133 0 : generate_keypair( config->consensus.vote_account_path, config, 1 );
134 0 : } else if( FD_LIKELY( args->keys.cmd == CMD_PUBKEY ) ) {
135 0 : keys_pubkey( args->keys.file_path );
136 0 : } else {
137 0 : FD_LOG_ERR(( "unknown key type `%lu`", args->keys.cmd ));
138 0 : }
139 0 : }
|