mirror of https://gitlab.com/octospacc/SmolOTP.git
240 lines
5.8 KiB
C
240 lines
5.8 KiB
C
|
#include <ctype.h>
|
||
|
#include <stdbool.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <time.h>
|
||
|
|
||
|
#include <nds.h>
|
||
|
#include <fat.h>
|
||
|
|
||
|
#include "rfc4226.h"
|
||
|
#include "rfc6238.h"
|
||
|
#include "utils.h"
|
||
|
#include "parser.h"
|
||
|
#include "ini.h"
|
||
|
|
||
|
#define T0 0
|
||
|
#define DIGITS 6
|
||
|
#define VALIDITY 30
|
||
|
#define TIME 2
|
||
|
#define VERSION 1.0
|
||
|
|
||
|
#define AppConfigFile "/.AppData/SmolOTP/Config.ini"
|
||
|
#define MaxSecretLength 32
|
||
|
#define MaxSecretsCount 256
|
||
|
|
||
|
extern NODE *provider_list = NULL;
|
||
|
|
||
|
typedef struct AppConfigType {
|
||
|
int timezone;
|
||
|
bool useDaylightSavings;
|
||
|
int totpSecretsCount;
|
||
|
char totpNames[MaxSecretsCount][MaxSecretLength];
|
||
|
char totpSecrets[MaxSecretsCount][MaxSecretLength];
|
||
|
uint32_t totpResults[MaxSecretsCount];
|
||
|
} AppConfigType;
|
||
|
|
||
|
char currentTotpSecret[MaxSecretLength];
|
||
|
|
||
|
uint32_t getTotp(char secret[], int timezone)
|
||
|
{
|
||
|
size_t pos;
|
||
|
size_t len = strlen(secret);
|
||
|
size_t keylen;
|
||
|
time_t curtime;
|
||
|
uint8_t *keysec;
|
||
|
uint32_t result;
|
||
|
|
||
|
if (validate_b32key(secret, len, pos) == 1) {
|
||
|
// invalid secret
|
||
|
return UINT32_MAX;
|
||
|
} else {
|
||
|
keysec = (uint8_t *)secret;
|
||
|
keylen = decode_b32key(&keysec, len);
|
||
|
curtime = getTotpTime(T0, VALIDITY, timezone);
|
||
|
result = TOTP(keysec, keylen, curtime, DIGITS);
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void waitUserExit(int code)
|
||
|
{
|
||
|
int gamepadKeys;
|
||
|
while(1) {
|
||
|
swiWaitForVBlank();
|
||
|
scanKeys();
|
||
|
gamepadKeys = keysDown();
|
||
|
if (gamepadKeys & KEY_START || gamepadKeys & KEY_SELECT) {
|
||
|
exit(code);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void calcOtps(AppConfigType *AppConfig)
|
||
|
{
|
||
|
for (int i=0; i<AppConfig->totpSecretsCount; i++)
|
||
|
{
|
||
|
// we copy the secret in a temporary variable because the calculation corrupts it (?)
|
||
|
strcpy(currentTotpSecret, AppConfig->totpSecrets[i]);
|
||
|
AppConfig->totpResults[i] = getTotp(currentTotpSecret, AppConfig->timezone);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void printOtps(AppConfigType *AppConfig)
|
||
|
{
|
||
|
if (AppConfig->totpSecretsCount == 0)
|
||
|
{
|
||
|
iprintf("\x1b[5;0H No Secrets Defined in \n \"%s\"\n", AppConfigFile);
|
||
|
}
|
||
|
for (int i=0; i<AppConfig->totpSecretsCount; i++)
|
||
|
{
|
||
|
iprintf("\x1b[%d;0H %s\n", 5+(i*2), AppConfig->totpNames[i]);
|
||
|
|
||
|
if (AppConfig->totpResults[i] == UINT32_MAX) {
|
||
|
iprintf("\x1b[%d;23H INVALID\n", 5+(i*2));
|
||
|
} else {
|
||
|
iprintf("\x1b[%d;24H %06u\n", 5+(i*2), AppConfig->totpResults[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int HandleAppConfig(AppConfigType *AppConfig, const char* section, const char* name, const char* value)
|
||
|
{
|
||
|
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
|
||
|
if (MATCH("Options", "TimezoneOffset"))
|
||
|
{
|
||
|
AppConfig->timezone = atoi(value);
|
||
|
}
|
||
|
//else if (MATCH("Options", "UseDaylightSavingTime"))
|
||
|
//{
|
||
|
// AppConfig->useDaylightSavings = (strcmp(strlwr(value), "true") == 0 ? true : false);
|
||
|
//}
|
||
|
else if (strcmp(section, "Secrets") == 0)
|
||
|
{
|
||
|
strcpy(AppConfig->totpNames[AppConfig->totpSecretsCount], name);
|
||
|
strcpy(AppConfig->totpSecrets[AppConfig->totpSecretsCount], value);
|
||
|
AppConfig->totpSecretsCount++;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
void RewriteAppConfig(AppConfigType *AppConfig)
|
||
|
{
|
||
|
FILE *file = fopen(AppConfigFile, "w");
|
||
|
fprintf(file,
|
||
|
"\n[Options]\n"
|
||
|
"# Note: For now Daylight Saving Time is not implemented, so set your UTC offset to account for that"
|
||
|
"TimezoneOffset = %d\n"
|
||
|
"\n[Secrets]\n",
|
||
|
AppConfig->timezone);
|
||
|
for (int i=0; i<AppConfig->totpSecretsCount; i++)
|
||
|
{
|
||
|
fprintf(file, "%s = %s\n", AppConfig->totpNames[i], AppConfig->totpSecrets[i]);
|
||
|
}
|
||
|
fclose(file);
|
||
|
}
|
||
|
|
||
|
// TODO
|
||
|
/*bool isinDaylightSavings(int month, int day, int weekday, int hours, int minutes)
|
||
|
{
|
||
|
// TODO: handle daylight savings start/end dates with alternatives to the european standard
|
||
|
// https://en.m.wikipedia.org/wiki/Daylight_saving_time_by_country
|
||
|
//bool isSavingsMonth = (month >= 3 && month <= 10);
|
||
|
//return (isSavingsMonth && isSwitchDay weekday == && );
|
||
|
return false;
|
||
|
}*/
|
||
|
|
||
|
int main(void)
|
||
|
{
|
||
|
int totpViewStart = 0;
|
||
|
int totpViewEnd = 5;
|
||
|
int gamepadKeys;
|
||
|
time_t unixTime;
|
||
|
struct tm *timeStruct;
|
||
|
int year, month, day, weekday, hours, minutes, seconds;
|
||
|
bool inDaylightSavings;
|
||
|
char totpNames[MaxSecretsCount][MaxSecretLength];
|
||
|
char totpSecrets[MaxSecretsCount][MaxSecretLength];
|
||
|
uint32_t totpResults[MaxSecretsCount];
|
||
|
|
||
|
AppConfigType AppConfig = {
|
||
|
.timezone = 0,
|
||
|
.useDaylightSavings = false,
|
||
|
.totpSecretsCount = 0,
|
||
|
.totpNames = totpNames,
|
||
|
.totpSecrets = totpSecrets,
|
||
|
.totpResults = totpResults,
|
||
|
};
|
||
|
|
||
|
consoleDemoInit();
|
||
|
|
||
|
if (!fatInitDefault())
|
||
|
{
|
||
|
iprintf("fatInitDefault failure\nPress START to exit...\n");
|
||
|
waitUserExit(-1);
|
||
|
}
|
||
|
|
||
|
mkdir("/.AppData");
|
||
|
mkdir("/.AppData/SmolOTP");
|
||
|
|
||
|
if (ini_parse(AppConfigFile, HandleAppConfig, &AppConfig) < 0)
|
||
|
{
|
||
|
RewriteAppConfig(&AppConfig);
|
||
|
}
|
||
|
|
||
|
/*if (AppConfig.useDaylightSavings && isinDaylightSavings(month, day, weekday, hours, minutes))
|
||
|
{
|
||
|
inDaylightSavings = true;
|
||
|
AppConfig.timezone++;
|
||
|
}*/
|
||
|
|
||
|
calcOtps(&AppConfig);
|
||
|
printOtps(&AppConfig);
|
||
|
|
||
|
while(1)
|
||
|
{
|
||
|
unixTime = getUnixTime(AppConfig.timezone);
|
||
|
timeStruct = gmtime((const time_t *)&unixTime);
|
||
|
|
||
|
year = timeStruct->tm_year + 1900;
|
||
|
month = timeStruct->tm_mon;
|
||
|
day = timeStruct->tm_mday;
|
||
|
//weekday = timeStruct->tm_wday;
|
||
|
hours = timeStruct->tm_hour;
|
||
|
minutes = timeStruct->tm_min;
|
||
|
seconds = timeStruct->tm_sec;
|
||
|
|
||
|
iprintf("\x1b[0;0H UTC Time: %04d-%02d-%02d %02d:%02d:%02d\n (Timezone Offset: %s%d)\n\n--------------------------------",
|
||
|
year, month, day, hours, minutes, seconds,
|
||
|
(AppConfig.timezone >= 0 ? "+" : ""), AppConfig.timezone//,
|
||
|
//(AppConfig.useDaylightSavings ? "" : "DST Adjustments are Disabled.")
|
||
|
);
|
||
|
|
||
|
if (seconds % 30 == 0)
|
||
|
{
|
||
|
calcOtps(&AppConfig);
|
||
|
printOtps(&AppConfig);
|
||
|
}
|
||
|
|
||
|
swiWaitForVBlank();
|
||
|
scanKeys();
|
||
|
gamepadKeys = keysDown();
|
||
|
|
||
|
// TODO: add secrets encryption, and decryption via keypad combos
|
||
|
if (gamepadKeys & KEY_START || gamepadKeys & KEY_SELECT)
|
||
|
{
|
||
|
exit(0);
|
||
|
}
|
||
|
else if (totpViewStart > 0 && gamepadKeys & KEY_UP)
|
||
|
{
|
||
|
// scroll list up ...
|
||
|
}
|
||
|
else if (totpViewEnd < AppConfig.totpSecretsCount && gamepadKeys & KEY_DOWN)
|
||
|
{
|
||
|
// scroll list down ...
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|