/****************************************************************************
*
* Copyright (c) 2001 Novell, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This program 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 this program; if not, contact Novell, Inc.
*
****************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#define TOTP_SECRET_BYTES 20U
#define CREDENTIAL_BYTES 20U
#define ARGON_TIME_COST 3U
#define ARGON_MEMORY_COST 32768U
#define ARGON_PARALLELISM 1U
#define ARGON_SALT_BYTES 16U
#define ARGON_HASH_BYTES 32U
static void
SecureClear(void *buffer, size_t length)
{
volatile unsigned char *cursor = buffer;
while (length-- > 0)
*cursor++ = 0;
}
int
BongoAuthSecurityInit(void)
{
if (!gcry_check_version(NULL))
return -1;
return oath_init() == OATH_OK ? 0 : -1;
}
void
BongoAuthSecurityDone(void)
{
oath_done();
}
int
BongoAuthGenerateTotpSecret(char *result, size_t result_size)
{
unsigned char random[TOTP_SECRET_BYTES];
char *encoded = NULL;
size_t encoded_size = 0;
int status = -1;
if (!result || result_size == 0)
return -1;
gcry_randomize(random, sizeof(random), GCRY_STRONG_RANDOM);
if (oath_base32_encode((const char *)random, sizeof(random), &encoded,
&encoded_size) == OATH_OK && encoded &&
encoded_size < result_size) {
memcpy(result, encoded, encoded_size);
result[encoded_size] = '\0';
status = 0;
}
if (encoded) {
SecureClear(encoded, encoded_size);
free(encoded);
}
SecureClear(random, sizeof(random));
return status;
}
int
BongoAuthVerifyTotp(const char *secret, const char *otp, time_t now)
{
char *decoded = NULL;
size_t decoded_size = 0;
int status;
size_t i;
if (!secret || !otp || strlen(otp) != 6)
return 0;
for (i = 0; otp[i]; i++)
if (!isdigit((unsigned char)otp[i]))
return 0;
if (oath_base32_decode(secret, strlen(secret), &decoded,
&decoded_size) != OATH_OK || !decoded)
return 0;
status = oath_totp_validate(decoded, decoded_size, now,
OATH_TOTP_DEFAULT_TIME_STEP_SIZE,
OATH_TOTP_DEFAULT_START_TIME, 1, otp);
SecureClear(decoded, decoded_size);
free(decoded);
return status >= 0;
}
int
BongoAuthGenerateCredential(char *result, size_t result_size)
{
unsigned char random[CREDENTIAL_BYTES];
char *encoded = NULL;
size_t encoded_size = 0;
size_t i;
size_t output = 0;
int status = -1;
if (!result || result_size < BONGO_AUTH_CREDENTIAL_SIZE)
return -1;
gcry_randomize(random, sizeof(random), GCRY_STRONG_RANDOM);
if (oath_base32_encode((const char *)random, sizeof(random), &encoded,
&encoded_size) != OATH_OK || !encoded)
goto cleanup;
for (i = 0; i < encoded_size && encoded[i] != '='; i++) {
if (i > 0 && i % 4 == 0)
result[output++] = '-';
result[output++] = (char)tolower((unsigned char)encoded[i]);
}
result[output] = '\0';
status = 0;
cleanup:
if (encoded) {
SecureClear(encoded, encoded_size);
free(encoded);
}
SecureClear(random, sizeof(random));
return status;
}
int
BongoAuthHashCredential(const char *credential, char *result,
size_t result_size)
{
unsigned char salt[ARGON_SALT_BYTES];
size_t required;
int status;
if (!credential || !credential[0] || !result)
return -1;
required = argon2_encodedlen(ARGON_TIME_COST, ARGON_MEMORY_COST,
ARGON_PARALLELISM, ARGON_SALT_BYTES,
ARGON_HASH_BYTES, Argon2_id);
if (result_size < required)
return -1;
gcry_randomize(salt, sizeof(salt), GCRY_STRONG_RANDOM);
status = argon2id_hash_encoded(ARGON_TIME_COST, ARGON_MEMORY_COST,
ARGON_PARALLELISM, credential,
strlen(credential), salt, sizeof(salt),
ARGON_HASH_BYTES, result, result_size);
SecureClear(salt, sizeof(salt));
return status == ARGON2_OK ? 0 : -1;
}
int
BongoAuthVerifyCredential(const char *encoded, const char *credential)
{
if (!encoded || !credential)
return 0;
return argon2id_verify(encoded, credential, strlen(credential)) == ARGON2_OK;
}