txt_db pkcs7 pkcs12 ui kdf store property \
md2 md4 md5 sha mdc2 ml_kem hmac ripemd whrlpool poly1305 \
siphash sm3 des aes rc2 rc4 rc5 idea aria bf cast camellia \
- seed sm4 chacha modes bn ec rsa dsa dh sm2 dso engine \
+ seed sm4 chacha modes fn bn ec rsa dsa dh sm2 dso engine \
err comp http ocsp cms ts srp cmac ct async ess crmf cmp encode_decode \
ffc hpke thread lms ml_dsa slh_dsa
--- /dev/null
+$LIBCRYPTO=../../libcrypto
+$LIBFIPS=../../providers/libfips.a
+LIBS=$LIBCRYPTO
+
+$COMMON=fn_lib.c
+
+SOURCE[$LIBCRYPTO]=$COMMON
+SOURCE[$LIBFIPS]=$COMMON
--- /dev/null
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdbool.h>
+#include <limits.h>
+#include <openssl/opensslconf.h>
+#include <openssl/crypto.h>
+#include "internal/common.h"
+#include "fn_local.h"
+
+static OSSL_FN *ossl_fn_new_internal(size_t limbs, bool securely)
+{
+ /*
+ * Since the number of limbs is represented as an 'int' in OSSL_FN,
+ * we must ensure that the desired size isn't larger than can be
+ * represented.
+ */
+ if (ossl_unlikely(limbs >= INT_MAX))
+ return NULL;
+
+ /* Total size of the whole OSSL_FN, in bytes */
+ size_t totalsize = sizeof(OSSL_FN) + limbs * sizeof(OSSL_FN_ULONG);
+
+ /*
+ * If the size ends up being smaller than the bookkeeping struct,
+ * we know that the size calculation has wrapped around and is
+ * therefore invalid. Also, we don't allow zero byte numbers.
+ */
+ if (totalsize <= sizeof(OSSL_FN))
+ return NULL;
+
+ OSSL_FN *ret = NULL;
+
+ if (securely)
+ ret = OPENSSL_secure_zalloc(totalsize);
+ else
+ ret = OPENSSL_zalloc(totalsize);
+
+ if (ret != NULL) {
+ ret->dsize = (int)limbs;
+ ret->is_dynamically_allocated = 1;
+ ret->is_securely_allocated = securely;
+ }
+ return ret;
+}
+
+static void ossl_fn_free_internal(OSSL_FN *f, bool clear)
+{
+ if (f == NULL)
+ return;
+
+ size_t limbssize = f->dsize * sizeof(OSSL_FN_ULONG);
+ size_t totalsize = limbssize + sizeof(OSSL_FN);
+
+ if (f->is_dynamically_allocated) {
+ if (f->is_securely_allocated)
+ OPENSSL_secure_clear_free(f, totalsize);
+ else if (clear)
+ OPENSSL_clear_free(f, totalsize);
+ else
+ OPENSSL_free(f);
+ } else if (clear) {
+ OPENSSL_cleanse(f->d, limbssize);
+ }
+}
+
+OSSL_FN *OSSL_FN_new_limbs(size_t size)
+{
+ return ossl_fn_new_internal(size, false);
+}
+
+OSSL_FN *OSSL_FN_secure_new_limbs(size_t size)
+{
+ return ossl_fn_new_internal(size, true);
+}
+
+static size_t bytes_to_limbs(size_t size)
+{
+ return (size + sizeof(OSSL_FN_ULONG) - 1) / sizeof(OSSL_FN_ULONG);
+}
+
+OSSL_FN *OSSL_FN_new_bytes(size_t size)
+{
+ return OSSL_FN_new_limbs(bytes_to_limbs(size));
+}
+
+OSSL_FN *OSSL_FN_secure_new_bytes(size_t size)
+{
+ return OSSL_FN_secure_new_limbs(bytes_to_limbs(size));
+}
+
+static size_t bits_to_bytes(size_t size)
+{
+ return (size + 7) / 8;
+}
+
+OSSL_FN *OSSL_FN_new_bits(size_t size)
+{
+ return OSSL_FN_new_bytes(bits_to_bytes(size));
+}
+
+OSSL_FN *OSSL_FN_secure_new_bits(size_t size)
+{
+ return OSSL_FN_secure_new_bytes(bits_to_bytes(size));
+}
+
+void OSSL_FN_free(OSSL_FN *f)
+{
+ ossl_fn_free_internal(f, false);
+}
+
+void OSSL_FN_clear_free(OSSL_FN *f)
+{
+ ossl_fn_free_internal(f, true);
+}
--- /dev/null
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OSSL_CRYPTO_FN_LOCAL_H
+# define OSSL_CRYPTO_FN_LOCAL_H
+
+# include <openssl/opensslconf.h>
+# include "crypto/fn.h"
+
+struct ossl_fn_st {
+ /* Flag: alloced with OSSL_FN_new() or OSSL_FN_secure_new() */
+ unsigned int is_dynamically_allocated : 1;
+ /* Flag: alloced with OSSL_FN_secure_new() */
+ unsigned int is_securely_allocated : 1;
+
+ /*
+ * The d array, with its size in number of OSSL_FN_ULONG.
+ * This stores the number itself.
+ *
+ * Note: |dsize| is an int, because it turns out that some lower level
+ * (possibly assembler) functions expect that type (especially, that
+ * type size).
+ * This deviates from the design in doc/designs/fixed-size-large-numbers.md
+ */
+ int dsize;
+ OSSL_FN_ULONG d[];
+};
+
+#endif
--- /dev/null
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OPENSSL_FN_H
+# define OPENSSL_FN_H
+# pragma once
+
+# include <stddef.h>
+# include <openssl/opensslconf.h>
+# include <openssl/bn_limbs.h>
+# include "crypto/types.h"
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+/*
+ * @type OSSL_FN_ULONG is the type for the OSSL_FN limb. It's made to be
+ * compatible with BN_ULONG (quite literally).
+ *
+ * @def OSSL_FN_BYTES is defined with the size of OSSL_FN_ULONG, measured in
+ * bytes. This is mainly useful where 'sizeof(OSSL_FN_ULONG)' isn't suitable,
+ * such as the C pre-processor.
+ */
+
+# ifdef BN_ULONG
+typedef BN_ULONG OSSL_FN_ULONG;
+# define OSSL_FN_BYTES BN_BYTES
+# endif
+
+# ifndef OSSL_FN_BYTES
+# error "OpenSSL doesn't support large numbers on this platform"
+# endif
+
+/*
+ * For practical reasons, we allow allocating OSSL_FNs in terms of limbs (what
+ * the BIGNUM library calls "words"), bytes and bits. The number of bytes and
+ * bits are rounded up to the number of limbs that can fit them.
+ */
+
+/**
+ * Allocate an OSSL_FN in memory.
+ *
+ * @param[in] size The number of limbs for the number itself.
+ * There's an additional few bytes allocated for bookkeeping.
+ * @returns an OSSL_FN instance.
+ * @retval NULL on error.
+ */
+OSSL_FN *OSSL_FN_new_limbs(size_t size);
+
+/**
+ * Allocate an OSSL_FN in secure memory.
+ *
+ * @param[in] size The number of limbs for the number itself.
+ * There's an additional few bytes allocated for bookkeeping.
+ * @returns an OSSL_FN instance.
+ * @retval NULL on error.
+ */
+OSSL_FN *OSSL_FN_secure_new_limbs(size_t size);
+
+/**
+ * Allocate an OSSL_FN in memory.
+ *
+ * @param[in] size The number of bytes for the number itself.
+ * There's an additional few bytes allocated for bookkeeping.
+ * @returns an OSSL_FN instance.
+ * @retval NULL on error.
+ */
+OSSL_FN *OSSL_FN_new_bytes(size_t size);
+
+/**
+ * Allocate an OSSL_FN in secure memory.
+ *
+ * @param[in] size The number of bytes for the number itself.
+ * There's an additional few bytes allocated for bookkeeping.
+ * @returns an OSSL_FN instance.
+ * @retval NULL on error.
+ */
+OSSL_FN *OSSL_FN_secure_new_bytes(size_t size);
+
+/**
+ * Allocate an OSSL_FN in memory.
+ *
+ * @param[in] size The number of bits for the number itself.
+ * There's an additional few bytes allocated for bookkeeping.
+ * @returns an OSSL_FN instance.
+ * @retval NULL on error.
+ */
+OSSL_FN *OSSL_FN_new_bits(size_t size);
+
+/**
+ * Allocate an OSSL_FN in secure memory.
+ *
+ * @param[in] size The number of bits for the number itself.
+ * There's an additional few bytes allocated for bookkeeping.
+ * @returns an OSSL_FN instance.
+ * @retval NULL on error.
+ */
+OSSL_FN *OSSL_FN_secure_new_bits(size_t size);
+
+/**
+ * Free an OSSL_FN instance if it was dynamically allocated.
+ * Free it securely if it was allocated securely.
+ *
+ * @param[in] f The OSSL_FN instance to be freed.
+ */
+void OSSL_FN_free(OSSL_FN *f);
+
+/**
+ * Cleanse and free an OSSL_FN instance if it was dynamically allocated.
+ * Cleanse and free it securely if it was allocated securely.
+ * Merely cleanse it if it was not dynamically allocated.
+ *
+ * @param[in] f The OSSL_FN instance to be freed.
+ */
+void OSSL_FN_clear_free(OSSL_FN *f);
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif
# define OSSL_CRYPTO_TYPES_H
# pragma once
+/* At some point in the future, this may move to include/openssl/types.h */
+typedef struct ossl_fn_st OSSL_FN;
+
# ifdef OPENSSL_NO_DEPRECATED_3_0
typedef struct rsa_st RSA;
typedef struct rsa_meth_st RSA_METHOD;
# ifndef OPENSSL_NO_STDIO
# include <stdio.h>
# endif
-# include <openssl/opensslconf.h>
# include <openssl/types.h>
# include <openssl/crypto.h>
# include <openssl/bnerr.h>
+# include <openssl/bn_limbs.h>
#ifdef __cplusplus
extern "C" {
#endif
-/*
- * 64-bit processor with LP64 ABI
- */
-# ifdef SIXTY_FOUR_BIT_LONG
-# define BN_ULONG unsigned long
-# define BN_BYTES 8
-# endif
-
-/*
- * 64-bit processor other than LP64 ABI
- */
-# ifdef SIXTY_FOUR_BIT
-# define BN_ULONG unsigned long long
-# define BN_BYTES 8
-# endif
-
-# ifdef THIRTY_TWO_BIT
-# define BN_ULONG unsigned int
-# define BN_BYTES 4
-# endif
-
# define BN_BITS2 (BN_BYTES * 8)
# define BN_BITS (BN_BITS2 * 2)
# define BN_TBIT ((BN_ULONG)1 << (BN_BITS2 - 1))
--- /dev/null
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/**
+ * @file Defines the type of large integer limbs.
+ *
+ * The large number is composed of words, the size of which is assumed to
+ * be optimal for the platform it's built for. In many large number texts,
+ * these words are called "limb". The BIGNUM library also calls this "word".
+ *
+ * In OpenSSL code, the BIGNUM "limb" is represented with the type macro
+ * BN_ULONG.
+ */
+
+#ifndef OPENSSL_BN_LIMBS_H
+# define OPENSSL_BN_LIMBS_H
+# pragma once
+
+# include <openssl/opensslconf.h>
+
+/*
+ * 64-bit processor with LP64 ABI
+ */
+# ifdef SIXTY_FOUR_BIT_LONG
+# define BN_ULONG unsigned long
+# define BN_BYTES 8
+# endif
+
+/*
+ * 64-bit processor other than LP64 ABI
+ */
+# ifdef SIXTY_FOUR_BIT
+# define BN_ULONG unsigned long long
+# define BN_BYTES 8
+# endif
+
+# ifdef THIRTY_TWO_BIT
+# define BN_ULONG unsigned int
+# define BN_BYTES 4
+# endif
+
+#endif
DEPEND[rsa_x931_test]=../libcrypto.a libtestutil.a
ENDIF
+ PROGRAMS{noinst}=fn_internal_test
+ SOURCE[fn_internal_test]=fn_internal_test.c
+ INCLUDE[fn_internal_test]=.. ../include ../crypto/fn ../apps/include
+ DEPEND[fn_internal_test]=../libcrypto.a libtestutil.a
+
SOURCE[bn_internal_test]=bn_internal_test.c
INCLUDE[bn_internal_test]=.. ../include ../crypto/bn ../apps/include
DEPEND[bn_internal_test]=../libcrypto.a libtestutil.a
--- /dev/null
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/**
+ * @file Internal tests of OSSL_FN
+ *
+ * This tests OSSL_FN internals only, i.e. anything that requires including
+ * ../crypto/fn/fn_local.h, such as introspection.
+ */
+
+#include "crypto/fn.h"
+#include "fn_local.h"
+#include "testutil.h"
+
+static int test_struct(void)
+{
+ TEST_note("OSSL_FN struct is %zu bytes\n", sizeof(OSSL_FN));
+ TEST_note("OSSL_FN 'd' array starts at offset %zu\n", offsetof(OSSL_FN, d));
+
+ /*
+ * Note: The working theory for the moment is that the 'd' array *must*
+ * align with the end of the OSSL_FN struct.
+ * If it turns out that this isn't the case, we can choose to run
+ * TEST_size_t_eq() for display purposes, but ignore its result and
+ * return 1.
+ */
+ return TEST_size_t_eq(sizeof(OSSL_FN), offsetof(OSSL_FN, d));
+}
+
+static int test_alloc(void)
+{
+ int ret = 1;
+ OSSL_FN *f = NULL;
+
+ /*
+ * OSSL_FN_new_bits() calls OSSL_FN_new_bytes(), which calls
+ * OSSL_FN_new_limbs(), so we're exercising all three in one go.
+ *
+ * The curious size formula is there to check that the number of bits that
+ * is passed in gets properly rounded up to the number of limbs they fit
+ * into.
+ * This formula aims for two limbs (each of which is at least 32 bits),
+ * shaving off 17 bits for demonstration purposes.
+ */
+ if (!TEST_ptr(f = OSSL_FN_new_bits(sizeof(OSSL_FN_ULONG) * 16 - 17))
+ || !TEST_uint_eq(f->is_dynamically_allocated, 1)
+ || !TEST_uint_eq(f->is_securely_allocated, 0)
+ || !TEST_int_eq(f->dsize, 2)
+ || !TEST_size_t_eq(f->d[0], 0)
+ || !TEST_size_t_eq(f->d[1], 0))
+ ret = 0;
+ OSSL_FN_free(f);
+
+ return ret;
+}
+
+static int test_secure_alloc(void)
+{
+ int ret = 1;
+ OSSL_FN *f = NULL;
+
+ /*
+ * OSSL_FN_secure_new_bits() calls OSSL_FN_secure_new_bytes(), which calls
+ * OSSL_FN_secure_new_limbs(), so we're exercising all three in one go.
+ *
+ * The curious size formula is there to check that the number of bits that
+ * is passed in gets properly rounded up to the number of limbs they fit
+ * into.
+ * This formula aims for two limbs (each of which is at least 32 bits),
+ * shaving off 17 bits for demonstration purposes.
+ */
+ if (!TEST_ptr(f = OSSL_FN_secure_new_bits(sizeof(OSSL_FN_ULONG) * 16 - 17))
+ || !TEST_uint_eq(f->is_dynamically_allocated, 1)
+ || !TEST_uint_eq(f->is_securely_allocated, 1)
+ || !TEST_int_eq(f->dsize, 2)
+ || !TEST_size_t_eq(f->d[0], 0)
+ || !TEST_size_t_eq(f->d[1], 0))
+ ret = 0;
+ OSSL_FN_free(f);
+
+ return ret;
+}
+
+int setup_tests(void)
+{
+ ADD_TEST(test_struct);
+ ADD_TEST(test_alloc);
+ ADD_TEST(test_secure_alloc);
+
+ return 1;
+}
--- /dev/null
+#! /usr/bin/env perl
+# Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the Apache License 2.0 (the "License"). You may not use
+# this file except in compliance with the License. You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+use strict;
+use OpenSSL::Test; # get 'plan'
+use OpenSSL::Test::Simple;
+use OpenSSL::Test::Utils;
+
+setup("test_internal_fn");
+
+plan skip_all => "This test is unsupported in a shared library build on Windows"
+ if $^O eq 'MSWin32' && !disabled("shared");
+
+simple_test("test_internal_fn", "fn_internal_test");