+/* Return non-zero if the signed number is negative */
+static int is_negative(const void *number, size_t s)
+{
+ const unsigned char *n = number;
+ DECLARE_IS_ENDIAN;
+
+ return 0x80 & (IS_BIG_ENDIAN ? n[0] : n[s - 1]);
+}
+
+/* Check that all the bytes specified match the expected sign byte */
+static int check_sign_bytes(const unsigned char *p, size_t n, unsigned char s)
+{
+ size_t i;
+
+ for (i = 0; i < n; i++)
+ if (p[i] != s)
+ return 0;
+ return 1;
+}
+
+/*
+ * Copy an integer to another integer.
+ * Handle different length integers and signed and unsigned integers.
+ * Both integers are in native byte ordering.
+ */
+static int copy_integer(unsigned char *dest, size_t dest_len,
+ const unsigned char *src, size_t src_len,
+ unsigned char pad, int signed_int)
+{
+ size_t n;
+ DECLARE_IS_ENDIAN;
+
+ if (IS_BIG_ENDIAN) {
+ if (src_len < dest_len) {
+ n = dest_len - src_len;
+ memset(dest, pad, n);
+ memcpy(dest + n, src, src_len);
+ } else {
+ n = src_len - dest_len;
+ if (!check_sign_bytes(src, n, pad)
+ /*
+ * Shortening a signed value must retain the correct sign.
+ * Avoiding this kind of thing: -253 = 0xff03 -> 0x03 = 3
+ */
+ || (signed_int && ((pad ^ src[n]) & 0x80) != 0)) {
+ err_out_of_range;
+ return 0;
+ }
+ memcpy(dest, src + n, dest_len);
+ }
+ } else /* IS_LITTLE_ENDIAN */ {
+ if (src_len < dest_len) {
+ n = dest_len - src_len;
+ memset(dest + src_len, pad, n);
+ memcpy(dest, src, src_len);
+ } else {
+ n = src_len - dest_len;
+ if (!check_sign_bytes(src + dest_len, n, pad)
+ /*
+ * Shortening a signed value must retain the correct sign.
+ * Avoiding this kind of thing: 130 = 0x0082 -> 0x82 = -126
+ */
+ || (signed_int && ((pad ^ src[dest_len - 1]) & 0x80) != 0)) {
+ err_out_of_range;
+ return 0;
+ }
+ memcpy(dest, src, dest_len);
+ }
+ }
+ return 1;
+}
+
+/* Copy a signed number to a signed number of possibly different length */
+static int signed_from_signed(void *dest, size_t dest_len,
+ const void *src, size_t src_len)
+{
+ return copy_integer(dest, dest_len, src, src_len,
+ is_negative(src, src_len) ? 0xff : 0, 1);
+}
+
+/* Copy an unsigned number to a signed number of possibly different length */
+static int signed_from_unsigned(void *dest, size_t dest_len,
+ const void *src, size_t src_len)
+{
+ return copy_integer(dest, dest_len, src, src_len, 0, 1);
+}
+
+/* Copy a signed number to an unsigned number of possibly different length */
+static int unsigned_from_signed(void *dest, size_t dest_len,
+ const void *src, size_t src_len)
+{
+ if (is_negative(src, src_len)) {
+ err_unsigned_negative;
+ return 0;
+ }
+ return copy_integer(dest, dest_len, src, src_len, 0, 0);
+}
+
+/* Copy an unsigned number to an unsigned number of possibly different length */
+static int unsigned_from_unsigned(void *dest, size_t dest_len,
+ const void *src, size_t src_len)
+{
+ return copy_integer(dest, dest_len, src, src_len, 0, 0);
+}
+
+/* General purpose get integer parameter call that handles odd sizes */
+static int general_get_int(const OSSL_PARAM *p, void *val, size_t val_size)
+{
+ if (p->data_type == OSSL_PARAM_INTEGER)
+ return signed_from_signed(val, val_size, p->data, p->data_size);
+ if (p->data_type == OSSL_PARAM_UNSIGNED_INTEGER)
+ return signed_from_unsigned(val, val_size, p->data, p->data_size);
+ err_not_integer;
+ return 0;
+}
+
+/* General purpose set integer parameter call that handles odd sizes */
+static int general_set_int(OSSL_PARAM *p, void *val, size_t val_size)
+{
+ int r = 0;
+
+ p->return_size = val_size; /* Expected size */
+ if (p->data == NULL)
+ return 1;
+ if (p->data_type == OSSL_PARAM_INTEGER)
+ r = signed_from_signed(p->data, p->data_size, val, val_size);
+ else if (p->data_type == OSSL_PARAM_UNSIGNED_INTEGER)
+ r = unsigned_from_signed(p->data, p->data_size, val, val_size);
+ else
+ err_not_integer;
+ p->return_size = r ? p->data_size : val_size;
+ return r;
+}
+
+/* General purpose get unsigned integer parameter call that handles odd sizes */
+static int general_get_uint(const OSSL_PARAM *p, void *val, size_t val_size)
+{
+ if (p->data_type == OSSL_PARAM_INTEGER)
+ return unsigned_from_signed(val, val_size, p->data, p->data_size);
+ if (p->data_type == OSSL_PARAM_UNSIGNED_INTEGER)
+ return unsigned_from_unsigned(val, val_size, p->data, p->data_size);
+ err_not_integer;
+ return 0;
+}
+
+/* General purpose set unsigned integer parameter call that handles odd sizes */
+static int general_set_uint(OSSL_PARAM *p, void *val, size_t val_size)
+{
+ int r = 0;
+
+ p->return_size = val_size; /* Expected size */
+ if (p->data == NULL)
+ return 1;
+ if (p->data_type == OSSL_PARAM_INTEGER)
+ r = signed_from_unsigned(p->data, p->data_size, val, val_size);
+ else if (p->data_type == OSSL_PARAM_UNSIGNED_INTEGER)
+ r = unsigned_from_unsigned(p->data, p->data_size, val, val_size);
+ else
+ err_not_integer;
+ p->return_size = r ? p->data_size : val_size;
+ return r;
+}
+