aboutsummaryrefslogtreecommitdiff
path: root/src/Program.cs
blob: 8895f8ecd37f184d5755917ac75499925d8f8696 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/*
* Copyright (c) 2023 Vaughn Nugent
* 
* Package: PkiAuthenticator
* File: Program.cs 
*
* PkiAuthenticator is free software: you can redistribute it and/or modify 
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 2 of the License,
* or (at your option) any later version.
*
* PkiAuthenticator is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License 
* along with PkiAuthenticator. If not, see http://www.gnu.org/licenses/.
*/

using System;

using VNLib.Utils.Logging;

using static PkiAuthenticator.Statics;

namespace PkiAuthenticator
{
    internal sealed class Program
    {
        public const string JWK_EXPORT_TEMPLATE = "You may copy your JWK public key\n\n{pk}\n";
        public const string TOKEN_PRINT_TEMPLATE = "You may copy your authentication token \n\n{tk}\n";
        public const string YUBIKEY_PIN_ENV_VAR_NAME = "YUBIKEY_PIN";
        public const string SOFTWARE_PASSWORD_VAR_NAME = "CERT_PASSWORD";
        public const string PEM_EXPORT_TEMPLATE = "You may copy your public key\n\n{cert}\n";

        const string HELP_MESSAGE = @$"
    vauth Copyright © Vaughn Nugent <vnpublic@proton.me> https://www.vaughnnugent.com/resources/software

    This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to
    redistribute it under certain conditions. See the license.txt file for more details.

    Usage: vauth <flags>

    A cross-platform hardware (YubiKey) or software backed authenticator for generating short lived 
    OTPs for VNLib.Plugins.Essentials.Accounts enabled servers. This tool generates a signed Json Web 
    Token (JWT) that can be used as a single factor authentication method for accounts that have a stored
    public key. Currently the plugin requires JSON Web Keys (JWK) format for public keys. It requires
    serial numbers, key-ids, and the public key itself, x509 is not used. You may use the --export
    flag to export this public key in the required JWK format. This tool currently supports YubiKey
    as a hardware authenticator, and PEM encoded x509 certificates as a software authenticator. You 
    may use this tool to list your connected YubiKey devices, and their serial numbers.
        
    Command flags:
        
        none/default            Genereates a signed OTP (one time password) Json Web Token
                                for authentication.

        -e, --export    <pem>   Writes the public key to the screen as a JWK, or optionally
                                PEM encoded by using the 'pem' keyword following -e.

        --list-devices          Lists the device information of all connected YubiKey devices.
                                (Ignores the --key flag)

        -h, --help              Prints this help message.

    Global flags:

        -u, --user      <uid>   The user-id (or email address) to specify during for 
                                authenticating. If not specified, uses the certificates CN
                                subject value.

        --software <cert file>  Runs the process using a software authenticator instead of 
                                a YubiKey hardware authenticator. The cert file must be a 
                                a valid x509 certificate with the public key. You must also
                                set the private key file path.

        --private-key   <file>  The path to the private key file, may be password protected.
                                This flag is only required in software mode.

        --password <password?>  Set this flag if your private key is password protected. 
                                The password string (utf8 decoded) used to decrypt the PEM 
                                private key file. WARNING! You should avoid setting your password 
                                after this flag unless you have cli history disabled, otherwise 
                                your password may be recovered from your shell history file. This 
                                allows you to automate the authentication process. NOTE: consider 
                                setting the {SOFTWARE_PASSWORD_VAR_NAME} environment variable before 
                                starting the process instead of supplying the password as a flag.

        --key        <serial>   Allows you to specify the serial number (int32) of the exact
                                YubiKey to connect to if multiple keys are connected. (PIV must
                                be enabled on the device)

        --pin    <device pin>   Allows you to specify your device's pin as an argument. 
                                WARNING! You should avoid using this flag unless you have cli
                                history disabled, otherwise your pin may be recovered from your 
                                history file. This allows you to automate the authentication 
                                process. NOTE: consider setting the {YUBIKEY_PIN_ENV_VAR_NAME} environment 
                                variable before starting the process instead.

        --piv-slot    <slot>    The hexadecimal YubiKey PIV slot number override to use, defaults 
                                to authentication (9a) slot.
        
        -s, --silent            Silences logs, only operation output is written to STDOUT. For pin-
                                required operations, a --pin flag must be set, or set the {YUBIKEY_PIN_ENV_VAR_NAME} 
                                env variable. If an op error occurs, an exit code is returned.

        -v, --verbose           Enables verbose logging to be written to STDOUT, is overridden
                                by silent mode, and will override -d debug mode.

        -d, --debug             Enables debug logging to be written to STDOUT, is overridden by 
                                silent mode.

    Environment Variables
        {SOFTWARE_PASSWORD_VAR_NAME}    The password used to decrypt the PEM encoded private key file in software mode
        
        {YUBIKEY_PIN_ENV_VAR_NAME}      Your secret pin used for protected yubikey operations
        

    This tool was created to quickly generate short lived One-Time-Passwords (OTP) or signed
    authentication tokens (JWT) for authenticating aginst PKI endpoints, using your YubiKey's
    authentication slot (0x9a). You may use this tool to automate a login process by using the 
    -s flag and specifying a pin with --pin (not recommended!), or setting the {YUBIKEY_PIN_ENV_VAR_NAME} 
    environment variable. 

    A software, x509 certificate file backed, mode is also supported by using the --software flag.
    The certificate file must be a PEM encoded certificate. You must also specify a PEM encoded private
    key file using the --private-key flag. This file may be encrypted, and you must specify a --password 
    flag. You may wait for a prompt, set the {SOFTWARE_PASSWORD_VAR_NAME} environment variable, or write it after 
    the --password argument: '--password my_unsecure_password'.

    Examples:
        
        OTP:
            vauth.exe                       # default cert CN usename
            vauth.exe -u 'name@example.com' # specify username
            vauth.exe --key 1111111         # specify hardware key serial numer
            vauth.exe -s > token.txt        # write token to a text file w/ silent mode
            vauth.exe --piv-slot 9C         # specify a differnt PIV slot on the yubikey (in hex)
            
            #software mode
            vauth.exe --software 'cert.pem' --private-key 'priv.pem'
            vauth.exe --software 'cert.pem' --private-key 'priv.pem' --password 'mypassword'
    
        Export public key:
            vauth.exe --export              # for JWK output
            vauth.exe --export pem          # for pem encoding

            #software
            vauth.exe --software cert.pem --export pem

        List devices:
            vauth.exe --list-devices        # only supported in hardware mode
";

        static int Main(string[] args)
        {
            if (CliArgs.HasArg("-h") || CliArgs.HasArg("--help"))
            {
                Console.WriteLine(HELP_MESSAGE);
                return 0;
            }

            Log.Information("vauth © 2023 Vaughn Nugent");

            int exitCode = 1;
            try
            {
                //Get software or hardware authenticator
                using IAuthenticator authenticator = CliArgs.HasArg("--software") ? new SoftwareAuthenticator() : new HardwareAuthenticator();

                //initialze the authenticator
                if (authenticator.Initialize())
                {
                    //Only continue if authenticator successfully initialized
                    if (CliArgs.HasArg("--list-devices"))
                    {
                        Log.Verbose("Gathering device information");

                        //List devices flag
                        exitCode = authenticator.ListDevices();
                    }
                    else if (CliArgs.HasArg("-e") || CliArgs.HasArg("--export"))
                    {
                        Log.Verbose("Exporting public key");

                        //Check for pem encoding flag
                        if (CliArgs.HasArg("pem"))
                        {
                            string pem = authenticator.ExportPem();
                            Log.Information(PEM_EXPORT_TEMPLATE, pem);
                            exitCode = 0;
                        }
                        else
                        {
                            //Print jwk
                            string? pupKey = authenticator.ExportJwk();

                            //May be null if the alg is not supported
                            if (pupKey == null)
                            {
                                Log.Error("The certificate does not use a supported algorithm");
                            }
                            else
                            {
                                //Print
                                Log.Information(JWK_EXPORT_TEMPLATE, pupKey);
                                exitCode = 0;
                            }
                        }
                    }
                    else
                    {
                        //Authenticate
                        exitCode = authenticator.GenerateOtp();
                    }
                }
            }
            catch(Exception ex)
            {
                if (Log.IsEnabled(LogLevel.Debug))
                {
                    Log.Error(ex);
                }
                else
                {
                    Log.Error("Operation failed. Reason: {ex}", ex.Message);
                }
            }

            Log.Verbose("Exiting...");

            return exitCode;
        }
    }
}