Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libcotp for openSUSE:Factory checked in at 2025-12-22 22:52:30 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libcotp (Old) and /work/SRC/openSUSE:Factory/.libcotp.new.1928 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libcotp" Mon Dec 22 22:52:30 2025 rev:15 rq:1324209 version:3.1.1 Changes: -------- --- /work/SRC/openSUSE:Factory/libcotp/libcotp.changes 2024-10-11 17:02:18.358637648 +0200 +++ /work/SRC/openSUSE:Factory/.libcotp.new.1928/libcotp.changes 2025-12-22 22:56:12.806272288 +0100 @@ -1,0 +2,15 @@ +Mon Dec 22 16:42:11 UTC 2025 - Paolo Stivanin <[email protected]> + +- Update to 3.1.1: + * Security + + Memory sanitization: Sensitive cryptographic data is now + securely cleared from memory + + Buffer safety: Fixed potential overflow in base32 decoding + * Improvements + + Type safety: Migrated to unsigned integers for crypto operations + + Performance: Optimized base32 length calculations and removed + redundant calls + + Code quality: Simplified token formatting and enhanced + error handling + +------------------------------------------------------------------- Old: ---- v3.1.0.tar.gz v3.1.0.tar.gz.asc New: ---- v3.1.1.tar.gz v3.1.1.tar.gz.asc ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libcotp.spec ++++++ --- /var/tmp/diff_new_pack.RV3O3a/_old 2025-12-22 22:56:15.350377210 +0100 +++ /var/tmp/diff_new_pack.RV3O3a/_new 2025-12-22 22:56:15.394379024 +0100 @@ -1,7 +1,7 @@ # # spec file for package libcotp # -# Copyright (c) 2024 SUSE LLC +# Copyright (c) 2025 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -24,7 +24,7 @@ %define libsoname %{name}3 Name: libcotp -Version: 3.1.0 +Version: 3.1.1 Release: 0 Summary: C library for generating TOTP and HOTP License: Apache-2.0 ++++++ v3.1.0.tar.gz -> v3.1.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libcotp-3.1.0/CMakeLists.txt new/libcotp-3.1.1/CMakeLists.txt --- old/libcotp-3.1.0/CMakeLists.txt 2024-10-04 13:42:33.000000000 +0200 +++ new/libcotp-3.1.1/CMakeLists.txt 2025-09-24 13:31:18.000000000 +0200 @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.16) -project(cotp VERSION "3.1.0" LANGUAGES "C") +project(cotp VERSION "3.1.1" LANGUAGES "C") set(CMAKE_C_STANDARD 11) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libcotp-3.1.0/src/otp.c new/libcotp-3.1.1/src/otp.c --- old/libcotp-3.1.0/src/otp.c 2024-10-04 13:42:33.000000000 +0200 +++ new/libcotp-3.1.1/src/otp.c 2025-09-24 13:31:18.000000000 +0200 @@ -6,6 +6,24 @@ #include "whmac.h" #include "cotp.h" +static void secure_memzero(void *p, size_t n) { + volatile unsigned char *vp = (volatile unsigned char *)p; + while (n--) { + *vp++ = 0; + } +} + +static size_t b32_decoded_len_from_str(const char *s) { + if (!s) return 0; + size_t chars = 0; + for (const char *p = s; *p; ++p) { + if (*p != '=' && *p != ' ') { + ++chars; + } + } + return (chars * 5) / 8; // floor +} + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define REVERSE_BYTES(C, C_reverse_byte_order) \ for (int j = 0, i = 7; j < 8; j++, i--) { \ @@ -83,9 +101,11 @@ return NULL; } + size_t dlen = whmac_getlen(hd); int tk = truncate (hmac, digits, hd); whmac_freehandle (hd); + secure_memzero(hmac, dlen); free (hmac); *err_code = NO_ERROR; @@ -183,10 +203,12 @@ char *totp = get_steam_code (hmac, hd); + size_t dlen = whmac_getlen(hd); whmac_freehandle (hd); *err_code = NO_ERROR; + secure_memzero(hmac, dlen); free(hmac); return totp; @@ -234,18 +256,19 @@ get_steam_code (const unsigned char *hmac, whmac_handle_t *hd) { - int offset = (hmac[whmac_getlen(hd)-1] & 0x0f); + size_t hlen = whmac_getlen(hd); + int offset = (hmac[hlen-1] & 0x0f); // Starting from the offset, take the successive 4 bytes while stripping the topmost bit to prevent it being handled as a signed integer - int bin_code = ((hmac[offset] & 0x7f) << 24) | ((hmac[offset + 1] & 0xff) << 16) | ((hmac[offset + 2] & 0xff) << 8) | ((hmac[offset + 3] & 0xff)); + uint32_t bin_code = ((uint32_t)(hmac[offset] & 0x7f) << 24) | ((uint32_t)(hmac[offset + 1] & 0xff) << 16) | ((uint32_t)(hmac[offset + 2] & 0xff) << 8) | ((uint32_t)(hmac[offset + 3] & 0xff)); const char steam_alphabet[] = "23456789BCDFGHJKMNPQRTVWXY"; char code[6]; size_t steam_alphabet_len = strlen (steam_alphabet); for (int i = 0; i < 5; i++) { - int mod = (int)(bin_code % steam_alphabet_len); - bin_code = (int)(bin_code / steam_alphabet_len); + uint32_t mod = bin_code % (uint32_t)steam_alphabet_len; + bin_code = bin_code / (uint32_t)steam_alphabet_len; code[i] = steam_alphabet[mod]; } code[5] = '\0'; @@ -260,13 +283,17 @@ whmac_handle_t *hd) { // take the lower four bits of the last byte - int offset = hmac[whmac_getlen(hd) - 1] & 0x0f; + size_t hlen = whmac_getlen(hd); + int offset = hmac[hlen - 1] & 0x0f; // Starting from the offset, take the successive 4 bytes while stripping the topmost bit to prevent it being handled as a signed integer - int bin_code = ((hmac[offset] & 0x7f) << 24) | ((hmac[offset + 1] & 0xff) << 16) | ((hmac[offset + 2] & 0xff) << 8) | ((hmac[offset + 3] & 0xff)); + uint32_t bin_code = ((uint32_t)(hmac[offset] & 0x7f) << 24) | ((uint32_t)(hmac[offset + 1] & 0xff) << 16) | ((uint32_t)(hmac[offset + 2] & 0xff) << 8) | ((uint32_t)(hmac[offset + 3] & 0xff)); - long long int DIGITS_POWER[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000}; - int token = (int)(bin_code % DIGITS_POWER[digits_length]); + uint64_t mod = 1; + for (int i = 0; i < digits_length; ++i) { + mod *= 10ULL; + } + int token = (int)(((uint64_t)bin_code) % mod); return token; } @@ -277,13 +304,13 @@ long C, whmac_handle_t *hd) { - size_t secret_len = (size_t)((strlen(K) + 1.6 - 1) / 1.6); - char *normalized_K = normalize_secret (K); if (normalized_K == NULL) { return NULL; } + size_t secret_len = b32_decoded_len_from_str(normalized_K); + cotp_error_t err; unsigned char *secret = base32_decode (normalized_K, strlen(normalized_K), &err); free (normalized_K); @@ -297,6 +324,7 @@ err = whmac_setkey (hd, secret, secret_len); if (err) { fprintf (stderr, "Error while setting the cipher key.\n"); + secure_memzero(secret, secret_len); free (secret); return NULL; } @@ -306,6 +334,7 @@ unsigned char *hmac = calloc (dlen, 1); if (hmac == NULL) { fprintf (stderr, "Error allocating memory"); + secure_memzero(secret, secret_len); free (secret); return NULL; } @@ -313,10 +342,13 @@ ssize_t flen = whmac_finalize (hd, hmac, dlen); if (flen < 0) { fprintf (stderr, "Error getting digest\n"); + secure_memzero(hmac, dlen); free (hmac); + secure_memzero(secret, secret_len); free (secret); return NULL; } + secure_memzero(secret, secret_len); free (secret); return hmac; @@ -331,9 +363,8 @@ if (token == NULL) { return NULL; } - char fmt[6]; - sprintf (fmt, "%%0%dd", digits_length); - snprintf (token, digits_length + 1, fmt, tk); + // Print with leading zeros without building an intermediate format string + snprintf (token, digits_length + 1, "%0*d", digits_length, tk); return token; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libcotp-3.1.0/src/utils/base32.c new/libcotp-3.1.1/src/utils/base32.c --- old/libcotp-3.1.0/src/utils/base32.c 2024-10-04 13:42:33.000000000 +0200 +++ new/libcotp-3.1.1/src/utils/base32.c 2025-09-24 13:31:18.000000000 +0200 @@ -153,7 +153,7 @@ bits_left += BITS_PER_BYTE - BITS_PER_B32_BLOCK; } } - decoded_data[output_length-1] = '\0'; + decoded_data[output_length] = '\0'; free (user_data);
