/**************************************************************************** * * 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; }