Add optional two-factor authentication primitives
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
add_library(bongoauthsecurity SHARED
|
||||
authsecurity.c)
|
||||
|
||||
target_link_libraries(bongoauthsecurity
|
||||
Argon2::Argon2
|
||||
LibGcrypt::LibGcrypt
|
||||
OATH::OATH)
|
||||
|
||||
install(TARGETS bongoauthsecurity DESTINATION ${LIB_INSTALL_DIR})
|
||||
StrictCompile()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_executable(auth-security-test tests/authsecurity-test.c)
|
||||
target_link_libraries(auth-security-test PRIVATE bongoauthsecurity OATH::OATH)
|
||||
add_test(NAME auth-security COMMAND auth-security-test)
|
||||
endif()
|
||||
@@ -0,0 +1,173 @@
|
||||
/****************************************************************************
|
||||
* <Novell-copyright>
|
||||
* 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.
|
||||
* </Novell-copyright>
|
||||
****************************************************************************/
|
||||
|
||||
#include <argon2.h>
|
||||
#include <gcrypt.h>
|
||||
#include <liboath/oath.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <bongoauthsecurity.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/****************************************************************************
|
||||
* <Novell-copyright>
|
||||
* 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.
|
||||
*
|
||||
* To contact Novell about this file by physical or electronic mail, you
|
||||
* may find current contact information at www.novell.com.
|
||||
* </Novell-copyright>
|
||||
****************************************************************************/
|
||||
|
||||
#include <liboath/oath.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <bongoauthsecurity.h>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
char secret[BONGO_AUTH_TOTP_SECRET_SIZE];
|
||||
char credential[BONGO_AUTH_CREDENTIAL_SIZE];
|
||||
char second[BONGO_AUTH_CREDENTIAL_SIZE];
|
||||
char hash[BONGO_AUTH_HASH_SIZE];
|
||||
char otp[7];
|
||||
char *decoded = NULL;
|
||||
size_t decoded_size = 0;
|
||||
time_t now = 1234567890;
|
||||
int result = 0;
|
||||
|
||||
if (BongoAuthSecurityInit() != 0)
|
||||
return 1;
|
||||
if (BongoAuthGenerateTotpSecret(secret, sizeof(secret)) != 0 ||
|
||||
oath_base32_decode(secret, strlen(secret), &decoded,
|
||||
&decoded_size) != OATH_OK)
|
||||
result = 2;
|
||||
else if (oath_totp_generate(decoded, decoded_size, now, 30, 0, 6, otp) != OATH_OK ||
|
||||
!BongoAuthVerifyTotp(secret, otp, now) ||
|
||||
BongoAuthVerifyTotp(secret, "00000x", now))
|
||||
result = 3;
|
||||
free(decoded);
|
||||
if (!result && (BongoAuthGenerateCredential(credential, sizeof(credential)) != 0 ||
|
||||
BongoAuthGenerateCredential(second, sizeof(second)) != 0 ||
|
||||
strcmp(credential, second) == 0 || strlen(credential) != 39))
|
||||
result = 4;
|
||||
if (!result && (BongoAuthHashCredential(credential, hash, sizeof(hash)) != 0 ||
|
||||
strncmp(hash, "$argon2id$", 10) != 0 ||
|
||||
!BongoAuthVerifyCredential(hash, credential) ||
|
||||
BongoAuthVerifyCredential(hash, second)))
|
||||
result = 5;
|
||||
BongoAuthSecurityDone();
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user