147 lines
5.0 KiB
Diff
147 lines
5.0 KiB
Diff
From 2ad81007d2371f536af9e231490357c928eca53a Mon Sep 17 00:00:00 2001
|
|
From: Chris Dunlap <cdunlap@llnl.gov>
|
|
Date: Wed, 2 Dec 2020 09:50:27 -0800
|
|
Subject: [PATCH 4/4] HKDF: Fix big-endian bug caused by size_t ptr cast
|
|
|
|
When Fedora updated to 0.5.14 and added the new test suite to their
|
|
rpm spec's %check, munge successfully built but its test suite failed
|
|
on s390x for hkdf_test:
|
|
|
|
> FAIL: hkdf_test
|
|
> ===============
|
|
> Failed to finalize HKDF MAC ctx for extraction
|
|
|
|
This is caused by the cast of prklenp from a size_t * to an int *
|
|
in _hkdf_extract().
|
|
|
|
On s390x, memory ordering is big-endian and size_t is an alias for
|
|
unsigned long. Thus, a ptr to an 8-byte size_t was being cast to a
|
|
ptr to a 4-byte int.
|
|
|
|
This worked on little-endian systems (of which all my test systems
|
|
had been) since the least-significant byte is stored at the smallest
|
|
memory address (the little end), and the stored value always fit
|
|
within 4 bytes. But on big-endian systems, the least-significant
|
|
byte is stored at the largest memory address (the big end) which
|
|
differs for 4-byte and 8-byte values.
|
|
|
|
Remove the cast by using an int variable as an intermediary.
|
|
|
|
Reference:
|
|
- https://fedoraproject.org/wiki/Architectures/s390x#Notes_for_application_developers_and_package_maintainers
|
|
- https://bugzilla.redhat.com/show_bug.cgi?id=1923337
|
|
- https://bugs.launchpad.net/bugs/1915457
|
|
- https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=982564
|
|
|
|
Tested:
|
|
- Arch Linux
|
|
- CentOS Stream 8, 8.3.2011, 7.9.2009, 6.10
|
|
- Debian sid, 10.8, 9.13, 8.11, 7.11, 6.0.10, 5.0.10, 4.0
|
|
- Fedora 33 [s390x, x86_64], 32, 31
|
|
- FreeBSD 12.2, 11.4
|
|
- NetBSD 9.1, 9.0, 8.1
|
|
- OpenBSD 6.8, 6.7, 6.6
|
|
- openSUSE 15.2, 15.1
|
|
- Raspberry Pi OS (Raspbian 10) [armv7l]
|
|
- Ubuntu 20.10, 20.04.2 LTS, 18.04.5 LTS, 16.04.7 LTS, 14.04.6 LTS, 12.04.5 LTS
|
|
|
|
Closes #91
|
|
---
|
|
src/common/hkdf.c | 30 ++++++++++++++++++++++++++----
|
|
1 file changed, 26 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/src/common/hkdf.c b/src/common/hkdf.c
|
|
index ac7ab6f..364f3e0 100644
|
|
--- a/src/common/hkdf.c
|
|
+++ b/src/common/hkdf.c
|
|
@@ -32,6 +32,7 @@
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
+#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
@@ -316,6 +317,7 @@ _hkdf_extract (hkdf_ctx_t *ctxp, void *prk, size_t *prklenp)
|
|
{
|
|
mac_ctx mac_ctx;
|
|
int mac_ctx_is_initialized = 0;
|
|
+ int prklen;
|
|
int rv = 0;
|
|
|
|
assert (ctxp != NULL);
|
|
@@ -325,6 +327,14 @@ _hkdf_extract (hkdf_ctx_t *ctxp, void *prk, size_t *prklenp)
|
|
assert (prklenp != NULL);
|
|
assert (*prklenp > 0);
|
|
|
|
+ /* Convert prklen size_t to int for the call to mac_final() since the parm
|
|
+ * is being passed as a ptr, and size of size_t and int may differ.
|
|
+ * *prklenp must be representable as an int because it was assigned
|
|
+ * (via ctxp->mdlen) by mac_size() which returns an int.
|
|
+ */
|
|
+ assert (*prklenp <= INT_MAX);
|
|
+ prklen = (int) *prklenp;
|
|
+
|
|
/* Compute the pseudorandom key.
|
|
* prk = HMAC (salt, ikm)
|
|
*/
|
|
@@ -340,7 +350,7 @@ _hkdf_extract (hkdf_ctx_t *ctxp, void *prk, size_t *prklenp)
|
|
log_msg (LOG_ERR, "Failed to update HKDF MAC ctx for extraction");
|
|
goto err;
|
|
}
|
|
- rv = mac_final (&mac_ctx, prk, (int *) prklenp);
|
|
+ rv = mac_final (&mac_ctx, prk, &prklen);
|
|
if (rv == -1) {
|
|
log_msg (LOG_ERR, "Failed to finalize HKDF MAC ctx for extraction");
|
|
goto err;
|
|
@@ -352,6 +362,12 @@ err:
|
|
return -1;
|
|
}
|
|
}
|
|
+ /* Update [prklenp] on success.
|
|
+ */
|
|
+ if (rv >= 0) {
|
|
+ assert (prklen >= 0);
|
|
+ *prklenp = (size_t) prklen;
|
|
+ }
|
|
return rv;
|
|
}
|
|
|
|
@@ -371,7 +387,7 @@ _hkdf_expand (hkdf_ctx_t *ctxp, const void *prk, size_t prklen,
|
|
unsigned char *dstptr;
|
|
size_t dstlen;
|
|
unsigned char *okm = NULL;
|
|
- size_t okmlen;
|
|
+ int okmlen;
|
|
int num_rounds;
|
|
const int max_rounds = 255;
|
|
unsigned char round;
|
|
@@ -390,8 +406,14 @@ _hkdf_expand (hkdf_ctx_t *ctxp, const void *prk, size_t prklen,
|
|
|
|
/* Allocate buffer for output keying material.
|
|
* The buffer size is equal to the size of the hash function output.
|
|
+ * Note that okmlen must be an int (and not size_t) for the call to
|
|
+ * mac_final() since the parm is being passed as a ptr, and size of
|
|
+ * size_t and int may differ.
|
|
+ * ctxp->mdlen must be representable as an int because it was assigned
|
|
+ * by mac_size() which returns an int.
|
|
*/
|
|
- okmlen = ctxp->mdlen;
|
|
+ assert (ctxp->mdlen <= INT_MAX);
|
|
+ okmlen = (int) ctxp->mdlen;
|
|
okm = calloc (1, okmlen);
|
|
if (okm == NULL) {
|
|
rv = -1;
|
|
@@ -448,7 +470,7 @@ _hkdf_expand (hkdf_ctx_t *ctxp, const void *prk, size_t prklen,
|
|
"for expansion round #%u", round);
|
|
goto err;
|
|
}
|
|
- rv = mac_final (&mac_ctx, okm, (int *) &okmlen);
|
|
+ rv = mac_final (&mac_ctx, okm, &okmlen);
|
|
if (rv == -1) {
|
|
log_msg (LOG_ERR,
|
|
"Failed to finalize HKDF MAC ctx "
|
|
--
|
|
2.30.0
|
|
|