└── Google_Authenticator
├── Google_Authenticator.vcproj
├── crypto.cpp
├── crypto.h
├── doublylinkedlist.cpp
├── doublylinkedlist.h
├── globals.h
├── main.cpp
├── utilities.cpp
└── utilities.h
/Google_Authenticator/Google_Authenticator.vcproj:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
26 |
29 |
32 |
35 |
38 |
41 |
52 |
55 |
58 |
61 |
68 |
71 |
74 |
77 |
80 |
83 |
86 |
89 |
90 |
98 |
101 |
104 |
107 |
110 |
113 |
124 |
127 |
130 |
133 |
142 |
145 |
148 |
151 |
154 |
157 |
160 |
163 |
164 |
165 |
166 |
167 |
168 |
173 |
176 |
177 |
180 |
181 |
184 |
185 |
188 |
189 |
190 |
195 |
198 |
199 |
202 |
203 |
206 |
207 |
210 |
211 |
212 |
217 |
218 |
219 |
220 |
221 |
222 |
--------------------------------------------------------------------------------
/Google_Authenticator/crypto.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Google Authenticator generates one-time passwords for Google accounts.
3 | Copyright (C) 2019 Eric Kutcher
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | #include "crypto.h"
20 |
21 | #include "globals.h"
22 | #include "utilities.h"
23 |
24 | #define ENCRYPT_BLOCK_SIZE 16
25 |
26 | HCRYPTKEY g_hKey = NULL;
27 | HCRYPTPROV g_hCryptProv = NULL;
28 |
29 | void CleanupCrypto()
30 | {
31 | if ( g_hKey != NULL )
32 | {
33 | CryptDestroyKey( g_hKey );
34 | }
35 |
36 | if ( g_hCryptProv != NULL )
37 | {
38 | CryptReleaseContext( g_hCryptProv, 0 );
39 | }
40 | }
41 |
42 | void InitializeCrypto()
43 | {
44 | CryptAcquireContext( &g_hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT );
45 | }
46 |
47 | void GenerateKeyFile( char *file_path )
48 | {
49 | AES_256_KEY_BLOB aes256kb;
50 |
51 | if ( g_hKey != NULL )
52 | {
53 | CryptDestroyKey( g_hKey );
54 | g_hKey = NULL;
55 | }
56 |
57 | if ( CryptGenKey( g_hCryptProv, CALG_AES_256, CRYPT_EXPORTABLE, &g_hKey ) )
58 | {
59 | DWORD kb_length;
60 | if ( CryptExportKey( g_hKey, NULL, PLAINTEXTKEYBLOB, 0, ( BYTE * )&aes256kb, &kb_length ) )
61 | {
62 | HANDLE hFile = CreateFileA( file_path, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
63 | if ( hFile != INVALID_HANDLE_VALUE )
64 | {
65 | if ( aes256kb.len == 32 )
66 | {
67 | DWORD write;
68 | WriteFile( hFile, aes256kb.key, aes256kb.len, &write, NULL );
69 | }
70 | /*else
71 | {
72 | _printf( "Invalid key length.\r\n" );
73 | }*/
74 |
75 | CloseHandle( hFile );
76 | }
77 | /*else
78 | {
79 | _printf( "Unable to open key file for writing.\r\n" );
80 | }*/
81 | }
82 | /*else
83 | {
84 | _printf( "Unable to export key file.\r\n" );
85 | }*/
86 | }
87 | /*else
88 | {
89 | _printf( "Unable to generate key value.\r\n" );
90 | }*/
91 | }
92 |
93 | bool LoadKeyFile( char *file_path )
94 | {
95 | bool ret = false;
96 |
97 | HANDLE hFile = CreateFileA( file_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
98 | if ( hFile != INVALID_HANDLE_VALUE )
99 | {
100 | if ( GetFileSize( hFile, NULL ) == 32 )
101 | {
102 | AES_256_KEY_BLOB aes256kb;
103 | aes256kb.header.bType = PLAINTEXTKEYBLOB;
104 | aes256kb.header.bVersion = CUR_BLOB_VERSION;
105 | aes256kb.header.reserved = 0;
106 | aes256kb.header.aiKeyAlg = CALG_AES_256;
107 |
108 | ReadFile( hFile, aes256kb.key, 32, &aes256kb.len, NULL );
109 |
110 | if ( g_hKey != NULL )
111 | {
112 | CryptDestroyKey( g_hKey );
113 | g_hKey = NULL;
114 | }
115 |
116 | if ( CryptImportKey( g_hCryptProv, ( BYTE * )&aes256kb, sizeof( AES_256_KEY_BLOB ), NULL, 0, &g_hKey ) )
117 | {
118 | ret = true;
119 | }
120 | /*else
121 | {
122 | _printf( "Unable to import key file.\r\n" );
123 | }*/
124 | }
125 | /*else
126 | {
127 | _printf( "Invalid key length.\r\n" );
128 | }*/
129 |
130 | CloseHandle( hFile );
131 | }
132 |
133 | return ret;
134 | }
135 |
136 | void DecryptData( char *file_path )
137 | {
138 | PBYTE pbBuffer = NULL;
139 | DWORD dwBlockLen;
140 | DWORD dwBufferLen;
141 |
142 | dwBlockLen = 1023 * ENCRYPT_BLOCK_SIZE;
143 |
144 | dwBufferLen = dwBlockLen + ENCRYPT_BLOCK_SIZE;
145 |
146 | HANDLE hFile = CreateFileA( file_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
147 | if ( hFile != INVALID_HANDLE_VALUE )
148 | {
149 | DWORD read;
150 |
151 | pbBuffer = ( BYTE * )GlobalAlloc( GMEM_FIXED, dwBufferLen );
152 | if ( pbBuffer != NULL )
153 | {
154 | DoublyLinkedList *list_item = g_list;
155 | DoublyLinkedList *last_list_item = g_list;
156 | DWORD dwBufferOffset = 0;
157 |
158 | pbBuffer = ( BYTE * )GlobalAlloc( GMEM_FIXED, dwBufferLen );
159 | if ( pbBuffer != NULL )
160 | {
161 | BOOL fEOF = FALSE;
162 | do
163 | {
164 | ReadFile( hFile, pbBuffer, dwBufferLen, &read, NULL );
165 |
166 | if ( read < dwBufferLen )
167 | {
168 | fEOF = TRUE;
169 | }
170 |
171 | if ( !CryptDecrypt( g_hKey, NULL, fEOF, 0, pbBuffer, &read ) )
172 | {
173 | break;
174 | }
175 |
176 | BYTE *p = pbBuffer;
177 | dwBufferOffset = 0;
178 |
179 | while ( dwBufferOffset < read )
180 | {
181 | AUTH_INFO *ai = ( AUTH_INFO * )GlobalAlloc( GPTR, sizeof( AUTH_INFO ) );
182 |
183 | dwBufferOffset += sizeof( unsigned int );
184 | if ( dwBufferOffset >= read ) { goto CLEANUP; }
185 | memcpy_s( &ai->username_length, sizeof( unsigned int ), p, sizeof( unsigned int ) );
186 | p += sizeof( unsigned int );
187 |
188 | dwBufferOffset += ai->username_length;
189 | if ( dwBufferOffset >= read ) { goto CLEANUP; }
190 | ai->username = ( char * )GlobalAlloc( GMEM_FIXED, ai->username_length + 1 );
191 | memcpy_s( ai->username, ai->username_length + 1, p, ai->username_length );
192 | ai->username[ ai->username_length ] = 0; // Sanity.
193 | p += ai->username_length;
194 |
195 | dwBufferOffset += sizeof( unsigned int );
196 | if ( dwBufferOffset >= read ) { goto CLEANUP; }
197 | memcpy_s( &ai->key_length, sizeof( unsigned int ), p, sizeof( unsigned int ) );
198 | p += sizeof( unsigned int );
199 |
200 | dwBufferOffset += ai->key_length;
201 | if ( dwBufferOffset > read ) { goto CLEANUP; }
202 | ai->key = ( char * )GlobalAlloc( GMEM_FIXED, ai->key_length + 1 );
203 | memcpy_s( ai->key, ai->key_length + 1, p, ai->key_length );
204 | ai->key[ ai->key_length ] = 0; // Sanity.
205 | p += ai->key_length;
206 |
207 | ai->code = -1;
208 |
209 | DoublyLinkedList *dll = DLL_CreateNode( ( void * )ai );
210 | DLL_AddNode( &g_list, dll, -1 );
211 |
212 | ++g_list_count;
213 |
214 | continue;
215 |
216 | CLEANUP:
217 |
218 | GlobalFree( ai->username );
219 | GlobalFree( ai->key );
220 | GlobalFree( ai );
221 | }
222 | }
223 | while ( !fEOF );
224 |
225 | GlobalFree( pbBuffer );
226 | }
227 |
228 | CloseHandle( hFile );
229 | }
230 | }
231 | }
232 |
233 | void EncryptData( char *file_path )
234 | {
235 | PBYTE pbBuffer = NULL;
236 | DWORD dwBlockLen;
237 | DWORD dwBufferLen;
238 |
239 | dwBlockLen = 1023 * ENCRYPT_BLOCK_SIZE;
240 | dwBufferLen = dwBlockLen + ENCRYPT_BLOCK_SIZE;
241 |
242 | HANDLE hFile = CreateFileA( file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
243 | if ( hFile != INVALID_HANDLE_VALUE )
244 | {
245 | DWORD write;
246 |
247 | pbBuffer = ( BYTE * )GlobalAlloc( GMEM_FIXED, dwBufferLen );
248 | if ( pbBuffer != NULL )
249 | {
250 | DoublyLinkedList *list_item;
251 | DoublyLinkedList *last_list_item = g_list;
252 | DWORD dwBufferOffset = 0;
253 |
254 | BOOL fEOF = FALSE;
255 | do
256 | {
257 | list_item = last_list_item;
258 |
259 | while ( list_item != NULL )
260 | {
261 | AUTH_INFO *ai = ( AUTH_INFO * )list_item->data;
262 | if ( ai != NULL )
263 | {
264 | if ( dwBufferOffset + sizeof( unsigned int ) + ai->username_length + sizeof( unsigned int ) + ai->key_length <= dwBlockLen )
265 | {
266 | memcpy_s( pbBuffer + dwBufferOffset, dwBufferLen - dwBufferOffset, &ai->username_length, sizeof( unsigned int ) );
267 | dwBufferOffset += sizeof( unsigned int );
268 | if ( ai->username_length > 0 )
269 | {
270 | memcpy_s( pbBuffer + dwBufferOffset, dwBufferLen - dwBufferOffset, ai->username, ai->username_length );
271 | dwBufferOffset += ai->username_length;
272 | }
273 | memcpy_s( pbBuffer + dwBufferOffset, dwBufferLen - dwBufferOffset, &ai->key_length, sizeof( unsigned int ) );
274 | dwBufferOffset += sizeof( unsigned int );
275 | if ( ai->key_length > 0 )
276 | {
277 | memcpy_s( pbBuffer + dwBufferOffset, dwBufferLen - dwBufferOffset, ai->key, ai->key_length );
278 | dwBufferOffset += ai->key_length;
279 | }
280 | }
281 | else
282 | {
283 | break;
284 | }
285 | }
286 |
287 | list_item = list_item->next;
288 | last_list_item = list_item;
289 | }
290 |
291 | if ( list_item == NULL )
292 | {
293 | fEOF = TRUE;
294 | }
295 |
296 | if ( !CryptEncrypt( g_hKey, NULL, fEOF, 0, pbBuffer, &dwBufferOffset, dwBufferLen ) )
297 | {
298 | break;
299 | }
300 |
301 | WriteFile( hFile, pbBuffer, dwBufferOffset, &write, NULL );
302 |
303 | dwBufferOffset = 0;
304 | }
305 | while( !fEOF );
306 |
307 | GlobalFree( pbBuffer );
308 | }
309 |
310 | CloseHandle( hFile );
311 | }
312 | }
313 |
314 | unsigned long GetTOTP( char *key, unsigned int key_length )
315 | {
316 | unsigned long code = -1;
317 |
318 | HCRYPTPROV hProv = NULL;
319 | HCRYPTHASH hHash = NULL;
320 | HCRYPTKEY hKey = NULL;
321 | HCRYPTHASH hHmacHash = NULL;
322 | PBYTE pbHash = NULL;
323 | DWORD dwDataLen = 0;
324 | HMAC_INFO HmacInfo;
325 |
326 | KEY_BLOB *kb = NULL;
327 |
328 | unsigned char *dkey = ( unsigned char * )GlobalAlloc( GMEM_FIXED, sizeof( unsigned char ) * key_length );
329 | unsigned int dkey_length = base32_decode( ( unsigned char * )key, dkey );
330 |
331 | if ( dkey_length == 0 )
332 | {
333 | goto CLEANUP;
334 | }
335 |
336 | ZeroMemory( &HmacInfo, sizeof( HmacInfo ) );
337 | HmacInfo.HashAlgid = CALG_SHA1;
338 |
339 | if ( !CryptAcquireContext( &hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_NEWKEYSET ) )
340 | {
341 | goto CLEANUP;
342 | }
343 |
344 | DWORD kbSize = sizeof( KEY_BLOB ) + dkey_length;
345 |
346 | kb = ( KEY_BLOB * )GlobalAlloc( GMEM_FIXED, kbSize );
347 | kb->header.bType = PLAINTEXTKEYBLOB;
348 | kb->header.bVersion = CUR_BLOB_VERSION;
349 | kb->header.reserved = 0;
350 | kb->header.aiKeyAlg = CALG_RC2;
351 | memcpy( &kb->key, dkey, dkey_length );
352 | kb->len = dkey_length;
353 |
354 | if ( !CryptImportKey( hProv, ( BYTE * )kb, kbSize, 0, CRYPT_IPSEC_HMAC_KEY, &hKey ) )
355 | {
356 | goto CLEANUP;
357 | }
358 |
359 | if ( !CryptCreateHash( hProv, CALG_HMAC, hKey, 0, &hHmacHash ) )
360 | {
361 | goto CLEANUP;
362 | }
363 |
364 | if ( !CryptSetHashParam( hHmacHash, HP_HMAC_INFO, ( BYTE * )&HmacInfo, 0 ) )
365 | {
366 | goto CLEANUP;
367 | }
368 |
369 | unsigned long data = GetUnixTimestamp() / 30;
370 |
371 | BYTE cdata[ 8 ];
372 | ZeroMemory( &cdata, sizeof( cdata ) );
373 |
374 | cdata[ 7 ] = ( BYTE )( data & 0xFF );
375 | cdata[ 6 ] = ( BYTE )( ( data & 0xFF00 ) >> 8 );
376 | cdata[ 5 ] = ( BYTE )( ( data & 0xFF0000 ) >> 16 );
377 | cdata[ 4 ] = ( BYTE )( ( data & 0xFF000000 ) >> 24 );
378 |
379 | if ( !CryptHashData( hHmacHash, cdata, sizeof( cdata ), 0 ) )
380 | {
381 | goto CLEANUP;
382 | }
383 |
384 | if ( !CryptGetHashParam( hHmacHash, HP_HASHVAL, NULL, &dwDataLen, 0 ) )
385 | {
386 | goto CLEANUP;
387 | }
388 |
389 | pbHash = ( BYTE * )GlobalAlloc( GMEM_FIXED, dwDataLen );
390 | if ( pbHash == NULL )
391 | {
392 | goto CLEANUP;
393 | }
394 |
395 | if ( !CryptGetHashParam( hHmacHash, HP_HASHVAL, pbHash, &dwDataLen, 0 ) )
396 | {
397 | goto CLEANUP;
398 | }
399 |
400 | unsigned char offset = pbHash[ dwDataLen - 1 ] & 0x0F;
401 |
402 | code = ( ( pbHash[ offset + 0 ] & 0x7F ) << 24 ) |
403 | ( ( pbHash[ offset + 1 ] & 0xFF ) << 16 ) |
404 | ( ( pbHash[ offset + 2 ] & 0xFF ) << 8 ) |
405 | ( pbHash[ offset + 3 ] & 0xFF );
406 |
407 | code %= 1000000;
408 |
409 | CLEANUP:
410 |
411 | // Free resources.
412 | if ( hHmacHash ) { CryptDestroyHash( hHmacHash ); }
413 | if ( hKey ) { CryptDestroyKey( hKey ); }
414 | if ( hHash ) { CryptDestroyHash( hHash ); }
415 | if ( hProv ) { CryptReleaseContext( hProv, 0 ); }
416 | if ( pbHash ) { GlobalFree( pbHash ); }
417 | if ( dkey ) { GlobalFree( dkey ); }
418 | if ( kb ) { GlobalFree( kb ); }
419 |
420 | return code;
421 | }
422 |
--------------------------------------------------------------------------------
/Google_Authenticator/crypto.h:
--------------------------------------------------------------------------------
1 | /*
2 | Google Authenticator generates one-time passwords for Google accounts.
3 | Copyright (C) 2019 Eric Kutcher
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | #ifndef _CRYPTO_H
20 | #define _CRYPTO_H
21 |
22 | #define STRICT
23 | #define WIN32_LEAN_AND_MEAN
24 |
25 | #include
26 | #include
27 |
28 | struct AES_256_KEY_BLOB
29 | {
30 | BLOBHEADER header;
31 | DWORD len;
32 | BYTE key[ 32 ];
33 | };
34 |
35 | struct KEY_BLOB
36 | {
37 | BLOBHEADER header;
38 | DWORD len;
39 | BYTE *key;
40 | };
41 |
42 | void InitializeCrypto();
43 | void CleanupCrypto();
44 |
45 | void GenerateKeyFile( char *file_path );
46 | bool LoadKeyFile( char *file_path );
47 | void EncryptData( char *file_path );
48 | void DecryptData( char *file_path );
49 |
50 | unsigned long GetTOTP( char *key, unsigned int key_length );
51 |
52 | #endif
53 |
--------------------------------------------------------------------------------
/Google_Authenticator/doublylinkedlist.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Google Authenticator generates one-time passwords for Google accounts.
3 | Copyright (C) 2019 Eric Kutcher
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | #include "doublylinkedlist.h"
20 |
21 | #define STRICT
22 | #define WIN32_LEAN_AND_MEAN
23 | #include
24 |
25 | DoublyLinkedList *DLL_CreateNode( void *data )
26 | {
27 | DoublyLinkedList *dll = ( DoublyLinkedList * )GlobalAlloc( GPTR, sizeof( DoublyLinkedList ) );
28 | if ( dll != NULL )
29 | {
30 | dll->next = NULL;
31 | dll->prev = NULL;
32 | dll->data = data;
33 | }
34 |
35 | return dll;
36 | }
37 |
38 | void DLL_RemoveNode( DoublyLinkedList **head, DoublyLinkedList *node )
39 | {
40 | if ( *head != NULL && node != NULL )
41 | {
42 | if ( node == *head ) // Node is the head.
43 | {
44 | if ( node->next != NULL ) // See if we can make a new head.
45 | {
46 | if ( node->next != node->prev ) // Make sure the new tail's previous value isn't itself.
47 | {
48 | node->next->prev = node->prev; // Set the new tail.
49 | }
50 | else
51 | {
52 | node->next->prev = NULL;
53 | }
54 |
55 | *head = node->next;
56 | }
57 | else // No head exists now.
58 | {
59 | *head = NULL;
60 | }
61 | }
62 | else if ( node->next == NULL ) // Node is a tail.
63 | {
64 | if ( node->prev != NULL ) // This should always be the case so long as node != head.
65 | {
66 | if ( node->prev != *head ) // Make sure the node's previous value is not the head.
67 | {
68 | if ( ( *head )->prev == node ) // Make sure the head list actually contains the node we're removing.
69 | {
70 | ( *head )->prev = node->prev; // Set the new tail.
71 | }
72 |
73 | node->prev->next = NULL;
74 | }
75 | else // All that exists now is the head.
76 | {
77 | ( *head )->next = NULL;
78 | ( *head )->prev = NULL;
79 | }
80 | }
81 | }
82 | else if ( node->next != NULL && node->prev != NULL ) // Node is between two other nodes.
83 | {
84 | node->prev->next = node->next;
85 | node->next->prev = node->prev;
86 | }
87 |
88 | node->next = NULL;
89 | node->prev = NULL;
90 | }
91 | }
92 |
93 | void DLL_AddNode( DoublyLinkedList **head, DoublyLinkedList *node, int position )
94 | {
95 | if ( node == NULL )
96 | {
97 | return;
98 | }
99 |
100 | if ( *head == NULL )
101 | {
102 | *head = node;
103 | return;
104 | }
105 |
106 | if ( position < 0 ) // Insert node as the new tail.
107 | {
108 | node->next = NULL;
109 |
110 | if ( ( *head )->prev != NULL ) // Head has a tail.
111 | {
112 | node->prev = ( *head )->prev;
113 | ( *head )->prev->next = node;
114 | }
115 | else // Head has no tail.
116 | {
117 | node->prev = *head;
118 | ( *head )->next = node;
119 | }
120 |
121 | ( *head )->prev = node;
122 | }
123 | else if ( position == 0 ) // Insert node as the new head.
124 | {
125 | node->next = *head;
126 | node->prev = ( *head )->prev;
127 | ( *head )->prev = node;
128 |
129 | *head = node; // Set the new head.
130 | }
131 | else
132 | {
133 | int count = 0;
134 | DoublyLinkedList *last_node = *head;
135 | DoublyLinkedList *current_node = ( *head )->next;
136 | while ( current_node != NULL )
137 | {
138 | if ( ++count == position )
139 | {
140 | node->next = current_node;
141 | node->prev = last_node;
142 | last_node->next = node;
143 | return;
144 | }
145 |
146 | last_node = current_node;
147 | current_node = current_node->next;
148 | }
149 |
150 | // The position is at the end of the list. Add node as the new tail.
151 | if ( current_node == NULL && ++count == position )
152 | {
153 | node->next = current_node;
154 | node->prev = last_node;
155 | last_node->next = node;
156 |
157 | ( *head )->prev = node;
158 | }
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/Google_Authenticator/doublylinkedlist.h:
--------------------------------------------------------------------------------
1 | /*
2 | Google Authenticator generates one-time passwords for Google accounts.
3 | Copyright (C) 2019 Eric Kutcher
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | #ifndef _DOUBLYLINKEDLIST_H
20 | #define _DOUBLYLINKEDLIST_H
21 |
22 | struct DoublyLinkedList
23 | {
24 | DoublyLinkedList *prev;
25 | DoublyLinkedList *next;
26 | void *data;
27 | };
28 |
29 |
30 | DoublyLinkedList *DLL_CreateNode( void *data );
31 |
32 | void DLL_RemoveNode( DoublyLinkedList **head, DoublyLinkedList *node );
33 | void DLL_AddNode( DoublyLinkedList **head, DoublyLinkedList *node, int position );
34 |
35 | #endif
36 |
--------------------------------------------------------------------------------
/Google_Authenticator/globals.h:
--------------------------------------------------------------------------------
1 | /*
2 | Google Authenticator generates one-time passwords for Google accounts.
3 | Copyright (C) 2019 Eric Kutcher
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | #ifndef _GLOBALS_H
20 | #define _GLOBALS_H
21 |
22 | // Pretty window.
23 | #pragma comment( linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"" )
24 |
25 | #define STRICT
26 | #define WIN32_LEAN_AND_MEAN
27 |
28 | #include
29 | #include
30 |
31 | #include "doublylinkedlist.h"
32 |
33 | #define CONSOLE_BUFFER_SIZE MAX_PATH
34 |
35 | #define LEFT_PADDING 5
36 | #define TOP_PADDING 5
37 |
38 | #define HEADER_HEIGHT ( TOP_PADDING + 7 )
39 | #define TIMER_OFFSET ( HEADER_HEIGHT + 1 )
40 | #define LIST_OFFSET ( TIMER_OFFSET + 2 )
41 | #define SELECTION_OFFSET ( LIST_OFFSET + 2 )
42 | #define LIST_MOD_HEIGHT 3
43 |
44 | #define VISIBLE_LINES 10
45 |
46 | #define FRAME_HEIGHT ( SELECTION_OFFSET + VISIBLE_LINES + LIST_MOD_HEIGHT )
47 |
48 | #define INPUT_OFFSET ( FRAME_HEIGHT + 2 )
49 |
50 | struct AUTH_INFO
51 | {
52 | char *key;
53 | char *username;
54 | unsigned int code;
55 | unsigned int key_length;
56 | unsigned int username_length;
57 | };
58 |
59 | extern DoublyLinkedList *g_list;
60 | extern int g_list_count;
61 |
62 | extern HANDLE g_hInput;
63 | extern HANDLE g_hOutput[ 2 ];
64 | extern unsigned char current_console;
65 |
66 | #endif
67 |
--------------------------------------------------------------------------------
/Google_Authenticator/main.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Google Authenticator generates one-time passwords for Google accounts.
3 | Copyright (C) 2019 Eric Kutcher
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | #include
20 |
21 | #include "globals.h"
22 | #include "utilities.h"
23 |
24 | #include "crypto.h"
25 |
26 | HANDLE g_hInput = NULL;
27 | HANDLE g_hOutput[ 2 ] = { NULL };
28 | unsigned char current_console = 0;
29 |
30 | char g_console_buffer[ CONSOLE_BUFFER_SIZE + 1 ];
31 |
32 | unsigned char g_status = 0;
33 |
34 | bool g_timers_running = false;
35 | HANDLE g_timer_semaphore = NULL;
36 |
37 | CRITICAL_SECTION console_cs;
38 |
39 | DoublyLinkedList *g_list = NULL;
40 | DoublyLinkedList *g_first_visible = NULL;
41 | DoublyLinkedList *g_selected_item = NULL;
42 |
43 | short g_selection_offset = 0;
44 | int g_selected_index = 0;
45 | int g_list_count = 0;
46 |
47 | char g_save_path[ MAX_PATH ];
48 | char g_key_path[ MAX_PATH ];
49 |
50 | // Ordered by month.
51 | wchar_t *month_string_table[ 12 ] =
52 | {
53 | L"January",
54 | L"February",
55 | L"March",
56 | L"April",
57 | L"May",
58 | L"June",
59 | L"July",
60 | L"August",
61 | L"September",
62 | L"October",
63 | L"November",
64 | L"December"
65 | };
66 |
67 | // Ordered by day.
68 | wchar_t *day_string_table[ 7 ] =
69 | {
70 | L"Sunday",
71 | L"Monday",
72 | L"Tuesday",
73 | L"Wednesday",
74 | L"Thursday",
75 | L"Friday",
76 | L"Saturday"
77 | };
78 |
79 | void WriteCharInfo( HANDLE output, char *str, WORD width, SHORT x, SHORT y, WORD attributes )
80 | {
81 | CHAR_INFO ci[ 64 * VISIBLE_LINES ] = { NULL };
82 |
83 | WORD lines = 0;
84 |
85 | if ( str != NULL && width > 0 )
86 | {
87 | for ( WORD i = 0; i < width; )
88 | {
89 | if ( *str != NULL )
90 | {
91 | if ( *str == '\n' )
92 | {
93 | if ( *( str + 1 ) == NULL )
94 | {
95 | break;
96 | }
97 |
98 | i = 0;
99 | ++lines;
100 | ++str;
101 |
102 | continue;
103 | }
104 | else
105 | {
106 | ci[ ( lines * width ) + i ].Char.AsciiChar = *str++;
107 | ci[ ( lines * width ) + i ].Attributes = attributes;
108 | }
109 | }
110 | else
111 | {
112 | break;
113 | }
114 |
115 | ++i;
116 | }
117 |
118 | COORD bs;
119 | bs.X = width;
120 | bs.Y = 1 + lines;
121 |
122 | COORD bc;
123 | bc.X = 0;
124 | bc.Y = 0;
125 |
126 | SMALL_RECT sr;
127 | sr.Left = x;
128 | sr.Top = y;
129 | sr.Right = x + ( width - 1 );
130 | sr.Bottom = y + lines;
131 |
132 | WriteConsoleOutputA( output, ci, bs, bc, &sr );
133 | }
134 | }
135 |
136 | BOOL WINAPI ConsoleHandler( DWORD signal )
137 | {
138 | if ( signal == CTRL_C_EVENT || signal == CTRL_CLOSE_EVENT || CTRL_LOGOFF_EVENT || CTRL_SHUTDOWN_EVENT )
139 | {
140 | g_status = 1;
141 | }
142 |
143 | return TRUE;
144 | }
145 |
146 | void ClearConsole( HANDLE hConsole )
147 | {
148 | COORD ccp = { 0, 0 };
149 | DWORD written;
150 | CONSOLE_SCREEN_BUFFER_INFO csbi;
151 |
152 | GetConsoleScreenBufferInfo( hConsole, &csbi );
153 |
154 | FillConsoleOutputCharacterA( hConsole, ' ', csbi.dwSize.X * csbi.dwSize.Y, ccp, &written );
155 |
156 | SetConsoleCursorPosition( hConsole, ccp );
157 | }
158 |
159 | void UpdateList()
160 | {
161 | DoublyLinkedList *dll = g_list;
162 |
163 | while ( dll != NULL )
164 | {
165 | AUTH_INFO *ai = ( AUTH_INFO * )dll->data;
166 |
167 | if ( ai != NULL )
168 | {
169 | ai->code = GetTOTP( ai->key, ai->key_length );
170 | }
171 |
172 | dll = dll->next;
173 | }
174 | }
175 |
176 | void CleanupList()
177 | {
178 | while ( g_list != NULL )
179 | {
180 | DoublyLinkedList *del_node = g_list;
181 |
182 | g_list = g_list->next;
183 |
184 | AUTH_INFO *ai = ( AUTH_INFO * )del_node->data;
185 | if ( ai != NULL )
186 | {
187 | GlobalFree( ai->username );
188 | GlobalFree( ai->key );
189 | GlobalFree( ai );
190 | }
191 |
192 | GlobalFree( del_node );
193 | }
194 |
195 | g_list_count = 0;
196 | g_selection_offset = 0;
197 | g_selected_index = 0;
198 | g_first_visible = NULL;
199 | g_selected_item = NULL;
200 | }
201 |
202 | void EnableTimer( bool timer_state )
203 | {
204 | // Trigger the timers out of their infinite wait.
205 | if ( timer_state )
206 | {
207 | if ( !g_timers_running )
208 | {
209 | g_timers_running = true;
210 |
211 | if ( g_timer_semaphore != NULL )
212 | {
213 | ReleaseSemaphore( g_timer_semaphore, 1, NULL );
214 | }
215 | }
216 | }
217 | else // Let the timers complete their current task and then wait indefinitely.
218 | {
219 | g_timers_running = false;
220 | }
221 | }
222 |
223 | void PrintFrame( HANDLE hOutput, WORD attributes )
224 | {
225 | DWORD written;
226 |
227 | COORD ccp;
228 | ccp.X = 0;
229 | ccp.Y = TOP_PADDING;
230 | SetConsoleCursorPosition( hOutput, ccp );
231 |
232 | _printf( "\xC9\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" \
233 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" \
234 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" \
235 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBB\r\n" );
236 | _printf( "\xBA%*s\xBA\r\n", 62, "" );
237 | _printf( "\xBA " );
238 | SetConsoleTextAttribute( hOutput, FOREGROUND_BLUE | FOREGROUND_INTENSITY );
239 | WriteConsoleA( hOutput, "G", 1, &written, NULL );
240 | SetConsoleTextAttribute( hOutput, FOREGROUND_RED | FOREGROUND_INTENSITY );
241 | WriteConsoleA( hOutput, "o", 1, &written, NULL );
242 | SetConsoleTextAttribute( hOutput, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY );
243 | WriteConsoleA( hOutput, "o", 1, &written, NULL );
244 | SetConsoleTextAttribute( hOutput, FOREGROUND_BLUE | FOREGROUND_INTENSITY );
245 | WriteConsoleA( hOutput, "g", 1, &written, NULL );
246 | SetConsoleTextAttribute( hOutput, FOREGROUND_GREEN | FOREGROUND_INTENSITY );
247 | WriteConsoleA( hOutput, "l", 1, &written, NULL );
248 | SetConsoleTextAttribute( hOutput, FOREGROUND_RED | FOREGROUND_INTENSITY );
249 | WriteConsoleA( hOutput, "e", 1, &written, NULL );
250 | SetConsoleTextAttribute( hOutput, attributes );
251 | WriteConsoleA( hOutput, " Authenticator", 14, &written, NULL );
252 | _printf( " \xBA\r\n" );
253 | _printf( "\xBA%*s\xBA\r\n", 62, "" );
254 | _printf( "\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xD1" \
255 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xD1" \
256 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xD1" \
257 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xB9\r\n" );
258 | _printf( "\xBA Ctrl + [N]ew \xB3 Ctrl + [O]pen \xB3 Ctrl + [S]ave \xB3 Ctrl + [Q]uit \xBA\r\n" );
259 | _printf( "\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCF" \
260 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCF" \
261 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCF" \
262 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xB9\r\n" );
263 |
264 | for ( char i = 0; i < VISIBLE_LINES + 6; ++i )
265 | {
266 | _printf( "\xBA%62s\xBA\r\n", "" );
267 | }
268 |
269 | _printf( "\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xD1" \
270 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xD1" \
271 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xD1" \
272 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xB9\r\n" );
273 | _printf( "\xBA [A]dd \xB3 [E]dit \xB3 [R]emove \xB3 Ctrl + [C]opy \xBA\r\n" );
274 | _printf( "\xC8\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCF" \
275 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCF" \
276 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCF" \
277 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBC" );
278 | }
279 |
280 | void PrintList()
281 | {
282 | DWORD written;
283 |
284 | char buffer[ 255 ];
285 | int buffer_length;
286 |
287 | DoublyLinkedList *dll = g_first_visible;
288 |
289 | COORD ccp;
290 | ccp.X = LEFT_PADDING + 1;
291 |
292 | CONSOLE_CURSOR_INFO cci;
293 | GetConsoleCursorInfo( g_hOutput[ 0 ], &cci );
294 | if ( cci.bVisible == FALSE )
295 | {
296 | current_console = ( current_console == 0 ? 1 : 0 );
297 | }
298 |
299 | CONSOLE_SCREEN_BUFFER_INFO csbi;
300 | GetConsoleScreenBufferInfo( g_hOutput[ current_console ], &csbi );
301 |
302 | if ( g_list_count > 0 )
303 | {
304 | EnableTimer( true );
305 |
306 | WriteCharInfo( g_hOutput[ current_console ], "Username", 8, LEFT_PADDING + 5, LIST_OFFSET, FOREGROUND_INTENSITY );
307 | WriteCharInfo( g_hOutput[ current_console ], "One-Time Password", 17, LEFT_PADDING + 42, LIST_OFFSET, FOREGROUND_INTENSITY );
308 |
309 | ccp.Y = SELECTION_OFFSET;
310 |
311 | for ( char i = 0; i < VISIBLE_LINES; ++i )
312 | {
313 | ccp.X = LEFT_PADDING + 1;
314 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', 60, ccp, &written );
315 |
316 | if ( dll != NULL )
317 | {
318 | AUTH_INFO *ai = ( AUTH_INFO * )dll->data;
319 |
320 | if ( ai != NULL )
321 | {
322 | if ( dll == g_selected_item )
323 | {
324 | ccp.X = LEFT_PADDING + 2;
325 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], '>', 1, ccp, &written );
326 | }
327 |
328 | buffer_length = sprintf_s( buffer, 255, "%s", ai->username );
329 | if ( buffer_length > 32 )
330 | {
331 | memcpy_s( buffer + 32, 255 - 32, "...", 3 );
332 | buffer_length = 35;
333 | }
334 |
335 | ccp.X = LEFT_PADDING + 5;
336 |
337 | WriteCharInfo( g_hOutput[ current_console ], buffer, buffer_length, ccp.X, ccp.Y, ( dll == g_selected_item ? FOREGROUND_GREEN | FOREGROUND_INTENSITY : csbi.wAttributes ) );
338 |
339 | // MSB should not be set.
340 | if ( ai->code & 0xF0000000 )
341 | {
342 | memcpy_s( buffer, 255, "BAD KEY", 7 );
343 | buffer_length = 7;
344 |
345 | ccp.X = LEFT_PADDING + 52;
346 | }
347 | else
348 | {
349 | buffer_length = sprintf_s( buffer, 255, "%06lu", ai->code );
350 | ccp.X = LEFT_PADDING + 53;
351 | }
352 |
353 | WriteCharInfo( g_hOutput[ current_console ], buffer, buffer_length, ccp.X, ccp.Y, ( dll == g_selected_item ? FOREGROUND_GREEN | FOREGROUND_INTENSITY : csbi.wAttributes ) );
354 | }
355 |
356 | dll = dll->next;
357 | }
358 |
359 | ++ccp.Y;
360 | }
361 |
362 | ccp.X = LEFT_PADDING + 61;
363 | ccp.Y = SELECTION_OFFSET;
364 | //if ( ( g_selected_index + 1 ) > VISIBLE_LINES || ( g_selected_index > 0 && g_selection_offset == 0 ) )
365 | if ( g_first_visible != g_list )
366 | {
367 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ':', 1, ccp, &written );
368 | }
369 | else
370 | {
371 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', 1, ccp, &written );
372 | }
373 |
374 | ccp.Y = SELECTION_OFFSET + VISIBLE_LINES - 1;
375 | int offset = g_list_count - g_selected_index;
376 | if ( g_list_count <= VISIBLE_LINES ||
377 | ( offset <= VISIBLE_LINES && ( VISIBLE_LINES - offset ) == g_selection_offset ) )
378 | {
379 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', 1, ccp, &written );
380 | }
381 | else
382 | {
383 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ':', 1, ccp, &written );
384 | }
385 | }
386 | else
387 | {
388 | EnableTimer( false );
389 |
390 | // Clear the line that shows the time.
391 | ccp.Y = TIMER_OFFSET;
392 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', 60, ccp, &written );
393 |
394 | // Clear the line that shows Username and One-Time Password.
395 | ccp.Y = LIST_OFFSET;
396 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', 60, ccp, &written );
397 |
398 | // Clear the usernames and passwords.
399 | ccp.Y = SELECTION_OFFSET;
400 | for ( char i = 0; i < VISIBLE_LINES; ++i )
401 | {
402 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', 60, ccp, &written );
403 | ++ccp.Y;
404 | }
405 | }
406 |
407 | SetConsoleActiveScreenBuffer( g_hOutput[ current_console ] );
408 | }
409 |
410 | void RefreshLine( AUTH_INFO *ai, short line, bool selected )
411 | {
412 | if ( ai != NULL )
413 | {
414 | char buffer[ 255 ];
415 | COORD ccp;
416 | DWORD written;
417 | CONSOLE_SCREEN_BUFFER_INFO csbi;
418 |
419 | GetConsoleScreenBufferInfo( g_hOutput[ current_console ], &csbi );
420 |
421 | ccp.X = LEFT_PADDING + 1;
422 | ccp.Y = line;
423 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', 60, ccp, &written );
424 |
425 | int buffer_length = sprintf_s( buffer, 255, "%s", ai->username );
426 | if ( buffer_length > 32 )
427 | {
428 | memcpy_s( buffer + 32, 255 - 32, "...", 3 );
429 | buffer_length = 35;
430 | }
431 |
432 | WriteCharInfo( g_hOutput[ current_console ], buffer, buffer_length, LEFT_PADDING + 5, line, ( selected ? FOREGROUND_GREEN | FOREGROUND_INTENSITY : csbi.wAttributes ) );
433 |
434 | // MSB should not be set.
435 | if ( ai->code & 0xF0000000 )
436 | {
437 | memcpy_s( buffer, 255, "BAD KEY", 7 );
438 | buffer_length = 7;
439 | ccp.X = LEFT_PADDING + 52;
440 | }
441 | else
442 | {
443 | buffer_length = sprintf_s( buffer, 255, "%06lu", ai->code );
444 | ccp.X = LEFT_PADDING + 53;
445 | }
446 |
447 | WriteCharInfo( g_hOutput[ current_console ], buffer, buffer_length, ccp.X, line, ( selected ? FOREGROUND_GREEN | FOREGROUND_INTENSITY : csbi.wAttributes ) );
448 |
449 | if ( selected )
450 | {
451 | ccp.X = LEFT_PADDING + 2;
452 | ccp.Y = SELECTION_OFFSET + g_selection_offset;
453 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], '>', 1, ccp, &written );
454 | }
455 | }
456 | }
457 |
458 | DWORD WINAPI UpdateWindow( LPVOID WorkThreadContext )
459 | {
460 | bool run_timer = g_timers_running;
461 | bool updated = false;
462 |
463 | char time_buf[ 11 ];
464 |
465 | CONSOLE_SCREEN_BUFFER_INFO csbi;
466 | GetConsoleScreenBufferInfo( g_hOutput[ current_console ], &csbi );
467 |
468 | while ( g_status != 1 )
469 | {
470 | WaitForSingleObject( g_timer_semaphore, ( run_timer ? 1000 : INFINITE ) );
471 |
472 | if ( g_status == 1 )
473 | {
474 | break;
475 | }
476 |
477 | EnterCriticalSection( &console_cs );
478 |
479 | // This will allow the timer to go through at least one loop after it's been disabled (g_timers_running == false).
480 | run_timer = g_timers_running;
481 |
482 | if ( run_timer )
483 | {
484 | COORD ccp;
485 | ccp.X = LEFT_PADDING + 1;
486 | ccp.Y = TIMER_OFFSET;
487 |
488 | if ( g_list_count > 0 )
489 | {
490 | unsigned long ts = 30 - ( GetUnixTimestamp() % 30 );
491 |
492 | if ( run_timer )
493 | {
494 | WriteCharInfo( g_hOutput[ 0 ], "Time remaining: ", 16, LEFT_PADDING + 19, TIMER_OFFSET, csbi.wAttributes );
495 | WriteCharInfo( g_hOutput[ 1 ], "Time remaining: ", 16, LEFT_PADDING + 19, TIMER_OFFSET, csbi.wAttributes );
496 |
497 | WORD attributes = FOREGROUND_INTENSITY;
498 | if ( ts > 5 && ts <= 10 )
499 | {
500 | attributes |= FOREGROUND_RED | FOREGROUND_GREEN;
501 | }
502 | else if ( ts > 0 && ts <= 5 )
503 | {
504 | attributes |= FOREGROUND_RED;
505 | }
506 | else
507 | {
508 | attributes |= FOREGROUND_GREEN;
509 | }
510 | int time_buf_length = sprintf_s( time_buf, 11, "%02lu", ts );
511 | WriteCharInfo( g_hOutput[ 0 ], time_buf, time_buf_length, LEFT_PADDING + 19 + 16, TIMER_OFFSET, attributes );
512 | WriteCharInfo( g_hOutput[ 1 ], time_buf, time_buf_length, LEFT_PADDING + 19 + 16, TIMER_OFFSET, attributes );
513 |
514 | WriteCharInfo( g_hOutput[ 0 ], " seconds", 8, LEFT_PADDING + 19 + 16 + time_buf_length, TIMER_OFFSET, csbi.wAttributes );
515 | WriteCharInfo( g_hOutput[ 1 ], " seconds", 8, LEFT_PADDING + 19 + 16 + time_buf_length, TIMER_OFFSET, csbi.wAttributes );
516 | }
517 |
518 | // Attempt to update the list in case the timer skipped a second.
519 | if ( ts >= 25 )
520 | {
521 | if ( !updated )
522 | {
523 | updated = true;
524 |
525 | UpdateList();
526 |
527 | PrintList();
528 | }
529 | }
530 | else
531 | {
532 | updated = false;
533 | }
534 | }
535 | else
536 | {
537 | run_timer = g_timers_running = false;
538 | }
539 | }
540 |
541 | LeaveCriticalSection( &console_cs );
542 | }
543 |
544 | CloseHandle( g_timer_semaphore );
545 | g_timer_semaphore = NULL;
546 |
547 | ExitThread( 0 );
548 | return 0;
549 | }
550 |
551 | void EnableCursor( BOOL enable )
552 | {
553 | CONSOLE_CURSOR_INFO cci;
554 |
555 | GetConsoleCursorInfo( g_hOutput[ 0 ], &cci );
556 | cci.bVisible = enable;
557 | SetConsoleCursorInfo( g_hOutput[ 0 ], &cci );
558 |
559 | GetConsoleCursorInfo( g_hOutput[ 1 ], &cci );
560 | cci.bVisible = enable;
561 | SetConsoleCursorInfo( g_hOutput[ 1 ], &cci );
562 | }
563 |
564 | void FillConsoleInputLine( char *input )
565 | {
566 | if ( input != NULL )
567 | {
568 | size_t input_length = strlen( input );
569 | INPUT_RECORD *pir = ( INPUT_RECORD * )GlobalAlloc( GMEM_FIXED, sizeof( INPUT_RECORD ) * input_length );
570 |
571 | char *c = input;
572 | INPUT_RECORD *tir = pir;
573 | for ( unsigned int i = 0; i < input_length; ++i )
574 | {
575 | tir->EventType = KEY_EVENT;
576 | tir->Event.KeyEvent.bKeyDown = TRUE;
577 | tir->Event.KeyEvent.dwControlKeyState = 0;
578 | tir->Event.KeyEvent.wRepeatCount = 1;
579 | tir->Event.KeyEvent.uChar.AsciiChar = *c;
580 | tir->Event.KeyEvent.wVirtualKeyCode = VkKeyScanA( *c );
581 | tir->Event.KeyEvent.wVirtualScanCode = MapVirtualKey( tir->Event.KeyEvent.wVirtualKeyCode, MAPVK_VK_TO_VSC );
582 |
583 | ++tir;
584 | ++c;
585 | }
586 |
587 | DWORD write;
588 | WriteConsoleInputA( g_hInput, pir, ( DWORD )input_length, &write );
589 |
590 | GlobalFree( pir );
591 | }
592 | }
593 |
594 | int main( int argc, char *argv[] )
595 | {
596 | SYSTEMTIME compile_time;
597 | memset( &compile_time, 0, sizeof( SYSTEMTIME ) );
598 | HINSTANCE hInstance = GetModuleHandle( NULL );
599 | if ( hInstance != NULL )
600 | {
601 | IMAGE_DOS_HEADER *idh = ( IMAGE_DOS_HEADER * )hInstance;
602 | IMAGE_NT_HEADERS *inth = ( IMAGE_NT_HEADERS * )( ( BYTE * )idh + idh->e_lfanew );
603 |
604 | UnixTimeToSystemTime( inth->FileHeader.TimeDateStamp, &compile_time );
605 | }
606 |
607 | InitializeCrypto();
608 |
609 | g_timer_semaphore = CreateSemaphore( NULL, 0, 1, NULL );
610 |
611 | HANDLE timer_handle = CreateThread( NULL, 0, UpdateWindow, NULL, 0, NULL );
612 | SetThreadPriority( timer_handle, THREAD_PRIORITY_LOWEST );
613 | CloseHandle( timer_handle );
614 |
615 |
616 | g_first_visible = g_list;
617 | g_selected_item = g_list;
618 |
619 | DWORD written;
620 | DWORD read;
621 | COORD ccp;
622 | INPUT_RECORD ir[ 1 ];
623 | CONSOLE_SCREEN_BUFFER_INFO csbi;
624 |
625 | InitializeCriticalSection( &console_cs );
626 |
627 | // Set our console to receive Ctrl + x key presses.
628 | CONSOLE_READCONSOLE_CONTROL crcc;
629 | crcc.nLength = sizeof( CONSOLE_READCONSOLE_CONTROL );
630 | crcc.nInitialChars = 0;
631 | crcc.dwCtrlWakeupMask = 0xFFFFFFFF;
632 | crcc.dwControlKeyState = 0;
633 |
634 | SetConsoleCtrlHandler( ConsoleHandler, TRUE );
635 |
636 | g_hInput = GetStdHandle( STD_INPUT_HANDLE );
637 |
638 | // Save the current console buffer. We'll restore it when we're done.
639 | HANDLE g_hOutput_old = GetStdHandle( STD_OUTPUT_HANDLE );
640 |
641 | g_hOutput[ 0 ] = CreateConsoleScreenBuffer( GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CONSOLE_TEXTMODE_BUFFER, NULL );
642 | g_hOutput[ 1 ] = CreateConsoleScreenBuffer( GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CONSOLE_TEXTMODE_BUFFER, NULL );
643 |
644 | SHORT width = LEFT_PADDING + LEFT_PADDING + 64;
645 | SHORT height = INPUT_OFFSET + TOP_PADDING;
646 |
647 | // Window size
648 | SMALL_RECT src;
649 | src.Top = 0;
650 | src.Left = 0;
651 | src.Right = width;
652 | src.Bottom = height;
653 |
654 | // Buffer size
655 | COORD csize;
656 | csize.X = width + 1;
657 | csize.Y = height + 1;
658 |
659 | csize.X += 1000;
660 | csize.Y += 1000;
661 | BOOL test2 = SetConsoleScreenBufferSize( g_hOutput[ 0 ], csize );
662 | SetConsoleScreenBufferSize( g_hOutput[ 1 ], csize );
663 |
664 | BOOL test = SetConsoleWindowInfo( g_hOutput[ 0 ], TRUE, &src );
665 | SetConsoleWindowInfo( g_hOutput[ 1 ], TRUE, &src );
666 |
667 | csize.X -= 1000;
668 | csize.Y -= 1000;
669 | test = SetConsoleScreenBufferSize( g_hOutput[ 0 ], csize );
670 | SetConsoleScreenBufferSize( g_hOutput[ 1 ], csize );
671 |
672 |
673 | HWND hWnd_console = GetConsoleWindow();
674 | SetWindowLongPtr( hWnd_console, GWL_STYLE, GetWindowLongPtr( hWnd_console, GWL_STYLE ) & ~WS_MAXIMIZEBOX & ~WS_SIZEBOX );
675 |
676 |
677 | /////
678 |
679 | GetConsoleScreenBufferInfo( g_hOutput[ 0 ], &csbi );
680 |
681 | PrintFrame( g_hOutput[ 0 ], csbi.wAttributes );
682 | current_console = 1;
683 | PrintFrame( g_hOutput[ 1 ], csbi.wAttributes );
684 | current_console = 0;
685 |
686 | SetConsoleActiveScreenBuffer( g_hOutput[ 0 ] );
687 |
688 | char loaded_args = 0; // 1 = key and database loaded, 2 = key loaded
689 | if ( argc > 1 )
690 | {
691 | if ( GetFileAttributesA( argv[ 1 ] ) != INVALID_FILE_ATTRIBUTES )
692 | {
693 | GetFullPathNameA( argv[ 1 ], MAX_PATH, g_save_path, NULL );
694 |
695 | loaded_args = 1;
696 | }
697 | }
698 |
699 | if ( loaded_args == 1 && argc > 2 )
700 | {
701 | if ( GetFileAttributesA( argv[ 2 ] ) != INVALID_FILE_ATTRIBUTES )
702 | {
703 | GetFullPathNameA( argv[ 2 ], MAX_PATH, g_key_path, NULL );
704 |
705 | if ( LoadKeyFile( g_key_path ) )
706 | {
707 | DecryptData( g_save_path );
708 |
709 | g_selection_offset = 0;
710 | g_selected_index = 0;
711 | g_first_visible = g_list;
712 | g_selected_item = g_list;
713 |
714 | loaded_args = 2;
715 | }
716 | }
717 | }
718 |
719 | ///////////////////////////////
720 |
721 | do
722 | {
723 | g_status = 0;
724 |
725 | EnterCriticalSection( &console_cs );
726 |
727 | ClearConsole( g_hOutput[ current_console ] );
728 | PrintFrame( g_hOutput[ current_console ], csbi.wAttributes );
729 |
730 | if ( loaded_args == 2 )
731 | {
732 | UpdateList();
733 |
734 | PrintList();
735 | }
736 | else
737 | {
738 | g_save_path[ 0 ] = 0; // Sanity.
739 | g_key_path[ 0 ] = 0; // Sanity.
740 | }
741 |
742 | LeaveCriticalSection( &console_cs );
743 |
744 | loaded_args = 0;
745 |
746 | EnableCursor( FALSE );
747 |
748 | BOOL read_console;
749 | do
750 | {
751 | SetConsoleMode( g_hInput, ENABLE_WINDOW_INPUT );
752 | read_console = ReadConsoleInputW( g_hInput, ir, 1, &read );
753 |
754 | if ( ir[ 0 ].EventType == KEY_EVENT )
755 | {
756 | KEY_EVENT_RECORD ker = ir[ 0 ].Event.KeyEvent;
757 |
758 | DWORD ctrl_down = ( ker.dwControlKeyState & ( LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED ) );
759 |
760 | if ( ker.bKeyDown )
761 | {
762 | if ( ctrl_down )
763 | {
764 | if ( ker.wVirtualKeyCode == 'A' )
765 | {
766 | wchar_t msg[ 512 ];
767 | _snwprintf_s( msg, 512, L"Google Authenticator is made free under the GPLv3 license.\r\n\r\n" \
768 | L"Version 1.0.0.1 (%u-bit)\r\n\r\n" \
769 | L"Built on %s, %s %d, %04d %d:%02d:%02d %s (UTC)\r\n\r\n" \
770 | L"Copyright \xA9 2019 Eric Kutcher",
771 | #ifdef _WIN64
772 | 64,
773 | #else
774 | 32,
775 | #endif
776 | ( compile_time.wDayOfWeek > 6 ? L"" : day_string_table[ compile_time.wDayOfWeek ] ),
777 | ( ( compile_time.wMonth > 12 || compile_time.wMonth < 1 ) ? L"" : month_string_table[ compile_time.wMonth - 1 ] ),
778 | compile_time.wDay,
779 | compile_time.wYear,
780 | ( compile_time.wHour > 12 ? compile_time.wHour - 12 : ( compile_time.wHour != 0 ? compile_time.wHour : 12 ) ),
781 | compile_time.wMinute,
782 | compile_time.wSecond,
783 | ( compile_time.wHour >= 12 ? L"PM" : L"AM" ) );
784 |
785 | MessageBoxW( hWnd_console, msg, L"Google Authenticator", MB_APPLMODAL | MB_ICONINFORMATION );
786 | }
787 | else if ( ker.wVirtualKeyCode == 'N' )
788 | {
789 | g_status = 2; // New instance
790 |
791 | break;
792 | }
793 | else if ( ker.wVirtualKeyCode == 'Q' )
794 | {
795 | g_status = 1; // Quit
796 |
797 | break;
798 | }
799 | else if ( ker.wVirtualKeyCode == 'O' )
800 | {
801 | EnableCursor( TRUE );
802 |
803 | ccp.X = 2;
804 | ccp.Y = INPUT_OFFSET;
805 | SetConsoleCursorPosition( g_hOutput[ current_console ], ccp );
806 |
807 | GetConsoleScreenBufferInfo( g_hOutput[ current_console ], &csbi );
808 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', csbi.dwSize.X * csbi.dwSize.Y, ccp, &written );
809 |
810 | _printf( "Open database file: " );
811 |
812 | SetConsoleMode( g_hInput, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT );
813 | ReadConsoleA( g_hInput, g_console_buffer, CONSOLE_BUFFER_SIZE + 1, &read, NULL );
814 |
815 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', csbi.dwSize.X * csbi.dwSize.Y, ccp, &written );
816 |
817 | if ( read > 2 )
818 | {
819 | read -= 2;
820 |
821 | g_console_buffer[ read ] = 0;
822 |
823 | if ( GetFileAttributesA( g_console_buffer ) != INVALID_FILE_ATTRIBUTES )
824 | {
825 | GetFullPathNameA( g_console_buffer, MAX_PATH, g_save_path, NULL );
826 |
827 | ccp.X = 2;
828 | ccp.Y = INPUT_OFFSET;
829 | SetConsoleCursorPosition( g_hOutput[ current_console ], ccp );
830 |
831 | _printf( "Load key file: " );
832 |
833 | SetConsoleMode( g_hInput, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT );
834 | ReadConsoleA( g_hInput, g_console_buffer, CONSOLE_BUFFER_SIZE + 1, &read, NULL );
835 |
836 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', csbi.dwSize.X * csbi.dwSize.Y, ccp, &written );
837 |
838 | bool read_key = false;
839 |
840 | if ( read > 2 )
841 | {
842 | read -= 2;
843 |
844 | g_console_buffer[ read ] = 0;
845 |
846 | if ( GetFileAttributesA( g_console_buffer ) != INVALID_FILE_ATTRIBUTES )
847 | {
848 | GetFullPathNameA( g_console_buffer, MAX_PATH, g_key_path, NULL );
849 |
850 | if ( LoadKeyFile( g_key_path ) )
851 | {
852 | read_key = true;
853 |
854 | CleanupList();
855 |
856 | DecryptData( g_save_path );
857 |
858 | g_selection_offset = 0;
859 | g_selected_index = 0;
860 | g_first_visible = g_list;
861 | g_selected_item = g_list;
862 |
863 | EnterCriticalSection( &console_cs );
864 |
865 | PrintList();
866 |
867 | LeaveCriticalSection( &console_cs );
868 | }
869 | }
870 | }
871 |
872 | if ( !read_key )
873 | {
874 | g_save_path[ 0 ] = 0; // Sanity.
875 | g_key_path[ 0 ] = 0; // Sanity.
876 | }
877 | }
878 | }
879 |
880 | EnableCursor( FALSE );
881 | }
882 | else if ( ker.wVirtualKeyCode == 'S' )
883 | {
884 | EnableCursor( TRUE );
885 |
886 | ccp.X = 2;
887 | ccp.Y = INPUT_OFFSET;
888 | SetConsoleCursorPosition( g_hOutput[ current_console ], ccp );
889 |
890 | GetConsoleScreenBufferInfo( g_hOutput[ current_console ], &csbi );
891 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', csbi.dwSize.X * csbi.dwSize.Y, ccp, &written );
892 |
893 | if ( g_key_path[ 0 ] == NULL )
894 | {
895 | _printf( "Generate key file: " );
896 |
897 | SetConsoleMode( g_hInput, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT );
898 | ReadConsoleA( g_hInput, g_console_buffer, CONSOLE_BUFFER_SIZE + 1, &read, NULL );
899 |
900 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', csbi.dwSize.X * csbi.dwSize.Y, ccp, &written );
901 |
902 | if ( read > 2 )
903 | {
904 | read -= 2;
905 |
906 | g_console_buffer[ read ] = 0;
907 |
908 | GetFullPathNameA( g_console_buffer, MAX_PATH, g_key_path, NULL );
909 |
910 | GenerateKeyFile( g_key_path );
911 | }
912 |
913 | ccp.X = 2;
914 | ccp.Y = INPUT_OFFSET;
915 | SetConsoleCursorPosition( g_hOutput[ current_console ], ccp );
916 |
917 | GetConsoleScreenBufferInfo( g_hOutput[ current_console ], &csbi );
918 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', csbi.dwSize.X * csbi.dwSize.Y, ccp, &written );
919 | }
920 |
921 | if ( g_key_path[ 0 ] != NULL )
922 | {
923 | _printf( "Save database file: " );
924 |
925 | FillConsoleInputLine( g_save_path );
926 |
927 | SetConsoleMode( g_hInput, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT );
928 | ReadConsoleA( g_hInput, g_console_buffer, CONSOLE_BUFFER_SIZE + 1, &read, NULL );
929 |
930 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', csbi.dwSize.X * csbi.dwSize.Y, ccp, &written );
931 |
932 | if ( read > 2 )
933 | {
934 | read -= 2;
935 |
936 | g_console_buffer[ read ] = 0;
937 |
938 | GetFullPathNameA( g_console_buffer, MAX_PATH, g_save_path, NULL );
939 |
940 | EncryptData( g_save_path );
941 | }
942 | }
943 |
944 | EnableCursor( FALSE );
945 | }
946 | else if ( ker.wVirtualKeyCode == 'C' )
947 | {
948 | if ( g_selected_item != NULL && g_selected_item->data != NULL )
949 | {
950 | AUTH_INFO *ai = ( AUTH_INFO * )g_selected_item->data;
951 |
952 | if ( OpenClipboard( NULL ) )
953 | {
954 | EmptyClipboard();
955 |
956 | // Allocate a global memory object for the text.
957 | HGLOBAL hglbCopy = GlobalAlloc( GMEM_MOVEABLE, sizeof( char ) * 11 );
958 | if ( hglbCopy != NULL )
959 | {
960 | // Lock the handle and copy the text to the buffer. lptstrCopy doesn't get freed.
961 | char *lptstrCopy = ( char * )GlobalLock( hglbCopy );
962 | if ( lptstrCopy != NULL )
963 | {
964 | if ( ai->code >= 1000000 )
965 | {
966 | memcpy_s( lptstrCopy, 11, "BAD KEY\0", 8 );
967 | }
968 | else
969 | {
970 | sprintf_s( lptstrCopy, 11, "%06lu", ai->code );
971 | }
972 | }
973 |
974 | GlobalUnlock( hglbCopy );
975 |
976 | if ( SetClipboardData( CF_TEXT, hglbCopy ) == NULL )
977 | {
978 | GlobalFree( hglbCopy ); // Only free this Global memory if SetClipboardData fails.
979 | }
980 |
981 | CloseClipboard();
982 | }
983 | }
984 | }
985 | }
986 | }
987 | else if ( ker.wVirtualKeyCode == VK_UP )
988 | {
989 | if ( g_selected_index > 0 )
990 | {
991 | --g_selected_index;
992 | DoublyLinkedList *last_selected_item = g_selected_item;
993 | g_selected_item = g_selected_item->prev;
994 |
995 | GetConsoleScreenBufferInfo( g_hOutput[ current_console ], &csbi );
996 |
997 | EnterCriticalSection( &console_cs );
998 |
999 | if ( g_selection_offset == 0 )
1000 | {
1001 | g_first_visible = g_first_visible->prev;
1002 | PrintList();
1003 | }
1004 |
1005 | RefreshLine( ( AUTH_INFO * )last_selected_item->data, SELECTION_OFFSET + g_selection_offset, false );
1006 |
1007 | if ( g_selection_offset > 0 )
1008 | {
1009 | --g_selection_offset;
1010 | }
1011 |
1012 | RefreshLine( ( AUTH_INFO * )g_selected_item->data, SELECTION_OFFSET + g_selection_offset, true );
1013 |
1014 | LeaveCriticalSection( &console_cs );
1015 | }
1016 | }
1017 | else if ( ker.wVirtualKeyCode == VK_DOWN )
1018 | {
1019 | if ( g_selected_index < g_list_count - 1 )
1020 | {
1021 | ++g_selected_index;
1022 | DoublyLinkedList *last_selected_item = g_selected_item;
1023 | g_selected_item = g_selected_item->next;
1024 |
1025 | GetConsoleScreenBufferInfo( g_hOutput[ current_console ], &csbi );
1026 |
1027 | EnterCriticalSection( &console_cs );
1028 |
1029 | if ( g_selection_offset >= VISIBLE_LINES - 1 )
1030 | {
1031 | g_first_visible = g_first_visible->next;
1032 | PrintList();
1033 | }
1034 |
1035 | RefreshLine( ( AUTH_INFO * )last_selected_item->data, SELECTION_OFFSET + g_selection_offset, false );
1036 |
1037 | if ( g_selection_offset < VISIBLE_LINES - 1 )
1038 | {
1039 | ++g_selection_offset;
1040 | }
1041 |
1042 | RefreshLine( ( AUTH_INFO * )g_selected_item->data, SELECTION_OFFSET + g_selection_offset, true );
1043 |
1044 | LeaveCriticalSection( &console_cs );
1045 | }
1046 | }
1047 | else if ( ker.wVirtualKeyCode == 'A' )
1048 | {
1049 | EnableCursor( TRUE );
1050 |
1051 | GetConsoleScreenBufferInfo( g_hOutput[ current_console ], &csbi );
1052 |
1053 | ccp.X = 2;
1054 | ccp.Y = INPUT_OFFSET;
1055 | SetConsoleCursorPosition( g_hOutput[ current_console ], ccp );
1056 |
1057 | _printf( "Add username: " );
1058 |
1059 | SetConsoleMode( g_hInput, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT );
1060 | ReadConsoleA( g_hInput, g_console_buffer, CONSOLE_BUFFER_SIZE + 1, &read, NULL );
1061 |
1062 | if ( read > 2 )
1063 | {
1064 | read -= 2;
1065 |
1066 | unsigned int username_length = min( read, 254 );
1067 | char *username = ( char * )GlobalAlloc( GMEM_FIXED, sizeof( char ) * ( username_length + 1 ) );
1068 | memcpy_s( username, sizeof( char ) * ( username_length + 1 ), g_console_buffer, username_length );
1069 | username[ username_length ] = 0; // Sanity.
1070 |
1071 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', csbi.dwSize.X * csbi.dwSize.Y, ccp, &written );
1072 |
1073 | ccp.X = 2;
1074 | ccp.Y = INPUT_OFFSET;
1075 | SetConsoleCursorPosition( g_hOutput[ current_console ], ccp );
1076 |
1077 | _printf( "Add key: " );
1078 |
1079 | SetConsoleMode( g_hInput, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT );
1080 | ReadConsoleA( g_hInput, g_console_buffer, CONSOLE_BUFFER_SIZE + 1, &read, NULL );
1081 |
1082 | if ( read > 2 )
1083 | {
1084 | read -= 2;
1085 |
1086 | unsigned int key_length = min( read, 254 );
1087 | char *key = ( char * )GlobalAlloc( GMEM_FIXED, sizeof( char ) * ( key_length + 1 ) );
1088 | memcpy_s( key, sizeof( char ) * ( key_length + 1 ), g_console_buffer, key_length );
1089 | key[ key_length ] = 0; // Sanity.
1090 |
1091 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', csbi.dwSize.X * csbi.dwSize.Y, ccp, &written );
1092 |
1093 | EnterCriticalSection( &console_cs );
1094 |
1095 | AUTH_INFO *ai = ( AUTH_INFO * )GlobalAlloc( GMEM_FIXED, sizeof( AUTH_INFO ) );
1096 | ai->username = username;
1097 | ai->username_length = username_length;
1098 | ai->key = key;
1099 | ai->key_length = key_length;
1100 | ai->code = GetTOTP( ai->key, ai->key_length );
1101 |
1102 | DoublyLinkedList *dll = DLL_CreateNode( ( void * )ai );
1103 | DLL_AddNode( &g_list, dll, -1 );
1104 |
1105 | if ( g_list_count == 0 )
1106 | {
1107 | g_first_visible = g_list;
1108 | g_selected_item = g_list;
1109 | }
1110 |
1111 | ++g_list_count;
1112 |
1113 | PrintList();
1114 |
1115 | LeaveCriticalSection( &console_cs );
1116 | }
1117 | else
1118 | {
1119 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', csbi.dwSize.X * csbi.dwSize.Y, ccp, &written );
1120 |
1121 | GlobalFree( username );
1122 | }
1123 | }
1124 | else
1125 | {
1126 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', csbi.dwSize.X * csbi.dwSize.Y, ccp, &written );
1127 | }
1128 |
1129 | EnableCursor( FALSE );
1130 | }
1131 | else if ( ker.wVirtualKeyCode == 'E' )
1132 | {
1133 | if ( g_selected_item != NULL && g_selected_item->data != NULL )
1134 | {
1135 | AUTH_INFO *ai = ( AUTH_INFO * )g_selected_item->data;
1136 |
1137 | EnableCursor( TRUE );
1138 |
1139 | GetConsoleScreenBufferInfo( g_hOutput[ current_console ], &csbi );
1140 |
1141 | ccp.X = 2;
1142 | ccp.Y = INPUT_OFFSET;
1143 | SetConsoleCursorPosition( g_hOutput[ current_console ], ccp );
1144 |
1145 | _printf( "Edit username: " );
1146 |
1147 | FillConsoleInputLine( ai->username );
1148 |
1149 | SetConsoleMode( g_hInput, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT );
1150 | ReadConsoleA( g_hInput, g_console_buffer, CONSOLE_BUFFER_SIZE + 1, &read, NULL );
1151 |
1152 | if ( read > 2 )
1153 | {
1154 | read -= 2;
1155 |
1156 | unsigned int username_length = min( read, 254 );
1157 | char *username = ( char * )GlobalAlloc( GMEM_FIXED, sizeof( char ) * ( username_length + 1 ) );
1158 | memcpy_s( username, sizeof( char ) * ( username_length + 1 ), g_console_buffer, username_length );
1159 | username[ username_length ] = 0; // Sanity.
1160 |
1161 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', csbi.dwSize.X * csbi.dwSize.Y, ccp, &written );
1162 |
1163 | ccp.X = 2;
1164 | ccp.Y = INPUT_OFFSET;
1165 | SetConsoleCursorPosition( g_hOutput[ current_console ], ccp );
1166 |
1167 | _printf( "Edit key: " );
1168 |
1169 | FillConsoleInputLine( ai->key );
1170 |
1171 | SetConsoleMode( g_hInput, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT );
1172 | ReadConsoleA( g_hInput, g_console_buffer, CONSOLE_BUFFER_SIZE + 1, &read, NULL );
1173 |
1174 | if ( read > 2 )
1175 | {
1176 | read -= 2;
1177 |
1178 | unsigned int key_length = min( read, 254 );
1179 | char *key = ( char * )GlobalAlloc( GMEM_FIXED, sizeof( char ) * ( key_length + 1 ) );
1180 | memcpy_s( key, sizeof( char ) * ( key_length + 1 ), g_console_buffer, key_length );
1181 | key[ key_length ] = 0; // Sanity.
1182 |
1183 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', csbi.dwSize.X * csbi.dwSize.Y, ccp, &written );
1184 |
1185 | EnterCriticalSection( &console_cs );
1186 |
1187 | char *tmp = ai->username;
1188 | ai->username = username;
1189 | GlobalFree( tmp );
1190 | ai->username_length = username_length;
1191 |
1192 | tmp = ai->key;
1193 | ai->key = key;
1194 | GlobalFree( tmp );
1195 | ai->key_length = key_length;
1196 | ai->code = GetTOTP( ai->key, ai->key_length );
1197 |
1198 | RefreshLine( ai, SELECTION_OFFSET + g_selection_offset, true );
1199 |
1200 | LeaveCriticalSection( &console_cs );
1201 | }
1202 | else
1203 | {
1204 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', csbi.dwSize.X * csbi.dwSize.Y, ccp, &written );
1205 | }
1206 | }
1207 | else
1208 | {
1209 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', csbi.dwSize.X * csbi.dwSize.Y, ccp, &written );
1210 | }
1211 |
1212 | EnableCursor( FALSE );
1213 | }
1214 | }
1215 | else if ( ker.wVirtualKeyCode == 'R' )
1216 | {
1217 | if ( g_selected_item != NULL )
1218 | {
1219 | EnableCursor( TRUE );
1220 |
1221 | ccp.X = 2;
1222 | ccp.Y = INPUT_OFFSET;
1223 | SetConsoleCursorPosition( g_hOutput[ current_console ], ccp );
1224 |
1225 | _printf( "Are you sure you want to remove the selected entry? (Y/N): " );
1226 |
1227 | SetConsoleMode( g_hInput, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT );
1228 | ReadConsoleA( g_hInput, g_console_buffer, CONSOLE_BUFFER_SIZE + 1, &read, NULL );
1229 |
1230 | EnableCursor( FALSE );
1231 |
1232 | FillConsoleOutputCharacterA( g_hOutput[ current_console ], ' ', csbi.dwSize.X * csbi.dwSize.Y, ccp, &written );
1233 |
1234 | if ( read == 3 && g_console_buffer[ 0 ] == 'Y' || g_console_buffer[ 0 ] == 'y' )
1235 | {
1236 | EnterCriticalSection( &console_cs );
1237 |
1238 | DoublyLinkedList *tmp_dll = NULL;
1239 |
1240 | if ( g_first_visible != g_list )
1241 | {
1242 | int offset = g_list_count - g_selected_index;
1243 | if ( offset <= VISIBLE_LINES &&
1244 | ( VISIBLE_LINES - offset ) == g_selection_offset )
1245 | {
1246 | tmp_dll = g_selected_item->prev;
1247 |
1248 | --g_selected_index;
1249 |
1250 | g_first_visible = g_first_visible->prev;
1251 | }
1252 | else
1253 | {
1254 | tmp_dll = g_selected_item->next;
1255 | }
1256 | }
1257 | else
1258 | {
1259 | if ( g_selected_item == g_first_visible )
1260 | {
1261 | g_first_visible = g_first_visible->next;
1262 | }
1263 |
1264 | if ( g_selected_item->next == NULL )
1265 | {
1266 | if ( g_selected_index > 0 )
1267 | {
1268 | --g_selected_index;
1269 | --g_selection_offset;
1270 | }
1271 |
1272 | tmp_dll = g_selected_item->prev;
1273 | }
1274 | else
1275 | {
1276 | tmp_dll = g_selected_item->next;
1277 | }
1278 | }
1279 |
1280 | --g_list_count;
1281 | DLL_RemoveNode( &g_list, g_selected_item );
1282 |
1283 | AUTH_INFO *ai = ( AUTH_INFO * )g_selected_item->data;
1284 | GlobalFree( ai->username );
1285 | GlobalFree( ai->key );
1286 | GlobalFree( ai );
1287 | GlobalFree( g_selected_item );
1288 |
1289 | g_selected_item = tmp_dll;
1290 |
1291 | PrintList();
1292 |
1293 | LeaveCriticalSection( &console_cs );
1294 | }
1295 | }
1296 | }
1297 | }
1298 | }
1299 | }
1300 | while ( read_console );
1301 |
1302 | // Show the console cursor position.
1303 | EnableCursor( TRUE );
1304 |
1305 | EnterCriticalSection( &console_cs );
1306 | EnableTimer( false );
1307 |
1308 | CleanupList();
1309 |
1310 | LeaveCriticalSection( &console_cs );
1311 | }
1312 | while ( g_status == 2 );
1313 |
1314 | // Exit our timer thread if it's active.
1315 | if ( g_timer_semaphore != NULL )
1316 | {
1317 | ReleaseSemaphore( g_timer_semaphore, 1, NULL );
1318 | }
1319 |
1320 | // Restore the old buffer.
1321 | SetConsoleActiveScreenBuffer( g_hOutput_old );
1322 |
1323 | CloseHandle( g_hOutput[ 0 ] );
1324 | CloseHandle( g_hOutput[ 1 ] );
1325 |
1326 | DeleteCriticalSection( &console_cs );
1327 |
1328 | CleanupCrypto();
1329 |
1330 | return 0;
1331 | }
1332 |
--------------------------------------------------------------------------------
/Google_Authenticator/utilities.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Google Authenticator generates one-time passwords for Google accounts.
3 | Copyright (C) 2019 Eric Kutcher
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | #include "globals.h"
20 | #include "utilities.h"
21 |
22 | #include
23 |
24 | #define FILETIME_TICKS_PER_SECOND 10000000LL
25 |
26 | #define decode_char( c ) ( ( c >= 'A' && c <= 'Z' ) ? c - 'A' : ( ( c >= '2' && c <= '7' ) ? ( c - '2' ) + 26 : -1 ) )
27 |
28 | int decode_sequence( unsigned char *str_in, unsigned char *str_out )
29 | {
30 | static char offset_map[] = { 3, -2, 1, -4, -1, 2, -3, 0 };
31 |
32 | str_out[ 0 ] = 0;
33 |
34 | for ( char block = 0, octet = 0; block < 8; ++block, octet = ( block * 5 ) / 8 )
35 | {
36 | int c = decode_char( str_in[ block ] );
37 | if ( c < 0 )
38 | {
39 | return octet;
40 | }
41 |
42 | if ( offset_map[ block ] < 0 )
43 | {
44 | str_out[ octet ] |= ( c >> -offset_map[ block ] );
45 | str_out[ octet + 1 ] = c << ( 8 + offset_map[ block ] );
46 | }
47 | else
48 | {
49 | str_out[ octet ] |= ( c << offset_map[ block ] );
50 | }
51 | }
52 |
53 | return 5;
54 | }
55 |
56 | unsigned int base32_decode( unsigned char *str_in, unsigned char *str_out )
57 | {
58 | unsigned int written = 0;
59 |
60 | if ( str_in != NULL )
61 | {
62 | for ( unsigned int i = 0, j = 0; ; i += 8, j += 5 )
63 | {
64 | int n = decode_sequence( &str_in[ i ], &str_out[ j ] );
65 |
66 | written += n;
67 |
68 | if ( n < 5 )
69 | {
70 | break;
71 | }
72 | }
73 | }
74 |
75 | return written;
76 | }
77 |
78 | unsigned long GetUnixTimestamp()
79 | {
80 | FILETIME ft;
81 | GetSystemTimeAsFileTime( &ft );
82 |
83 | // Convert the time into a 32bit Unix timestamp.
84 | ULARGE_INTEGER ts;
85 | ts.HighPart = ft.dwHighDateTime;
86 | ts.LowPart = ft.dwLowDateTime;
87 |
88 | return ( unsigned long )( ( ts.QuadPart - ( 11644473600000 * 10000 ) ) / FILETIME_TICKS_PER_SECOND );
89 | }
90 |
91 | void UnixTimeToSystemTime( DWORD t, SYSTEMTIME *st )
92 | {
93 | FILETIME ft;
94 | LARGE_INTEGER li;
95 | li.QuadPart = Int32x32To64( t, 10000000 ) + 116444736000000000;
96 |
97 | ft.dwLowDateTime = li.LowPart;
98 | ft.dwHighDateTime = li.HighPart;
99 |
100 | FileTimeToSystemTime( &ft, st );
101 | }
102 |
103 | int _printf( const char *_Format, ... )
104 | {
105 | int ret = -1;
106 |
107 | DWORD written;
108 | va_list arglist;
109 |
110 | va_start( arglist, _Format );
111 |
112 | char buffer[ 8192 ];
113 |
114 | int buffer_length = _vsnprintf_s( buffer, 8192, _Format, arglist );
115 |
116 | va_end( arglist );
117 |
118 | CONSOLE_SCREEN_BUFFER_INFO csbi;
119 | GetConsoleScreenBufferInfo( g_hOutput[ current_console ], &csbi );
120 |
121 | csbi.dwCursorPosition.X += LEFT_PADDING;
122 | SetConsoleCursorPosition( g_hOutput[ current_console ], csbi.dwCursorPosition );
123 |
124 | if ( buffer_length >= 0 && WriteConsoleA( g_hOutput[ current_console ], buffer, buffer_length, &written, NULL ) )
125 | {
126 | ret = written;
127 | }
128 |
129 | return ret;
130 | }
131 |
--------------------------------------------------------------------------------
/Google_Authenticator/utilities.h:
--------------------------------------------------------------------------------
1 | /*
2 | Google Authenticator generates one-time passwords for Google accounts.
3 | Copyright (C) 2019 Eric Kutcher
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | #ifndef _UTILITIES_H
20 | #define _UTILITIES_H
21 |
22 | unsigned int base32_decode( unsigned char *str_in, unsigned char *str_out );
23 |
24 | unsigned long GetUnixTimestamp();
25 | void UnixTimeToSystemTime( DWORD t, SYSTEMTIME *st );
26 |
27 | int _printf( const char *_Format, ... );
28 |
29 | #endif
30 |
--------------------------------------------------------------------------------