]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
lib:crypto: Have samba_gnutls_sp800_108_derive_key() support various output key lengths
authorJoseph Sutton <josephsutton@catalyst.net.nz>
Tue, 28 Nov 2023 23:44:10 +0000 (12:44 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Thu, 30 Nov 2023 00:02:33 +0000 (00:02 +0000)
View with ‘git show -b’.

Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
lib/crypto/gnutls_sp800_108.c
selftest/knownfail.d/gnutls-sp800-108 [deleted file]

index 7fd7461c9ebf59be2e10a184fc84df8738c3c4d6..505c0664db32859fb959316b091e8336e8b2a1ff 100644 (file)
@@ -72,6 +72,11 @@ static NTSTATUS samba_gnutls_sp800_108_derive_key_part(
        return NT_STATUS_OK;
 }
 
+static size_t ceiling_div(const size_t a, const size_t b)
+{
+       return a / b + (a % b != 0);
+}
+
 /**
  * @brief Derive a key using the NIST SP 800‐108 algorithm.
  *
@@ -113,24 +118,31 @@ NTSTATUS samba_gnutls_sp800_108_derive_key(
 {
        gnutls_hmac_hd_t hmac_hnd = NULL;
        const size_t digest_len = gnutls_hmac_get_len(algorithm);
-       uint8_t digest[digest_len];
-       uint32_t i = 1;
+       uint32_t i;
        uint32_t L = KO_len * 8;
+       size_t KO_idx;
        NTSTATUS status = NT_STATUS_OK;
        int rc;
 
-       if (KO_len > digest_len) {
-               DBG_ERR("KO_len[%zu] > digest_len[%zu]\n", KO_len, digest_len);
+       if (KO_len > UINT32_MAX / 8) {
+               /* The calculation of L has overflowed. */
                return NT_STATUS_INTERNAL_ERROR;
        }
 
-       switch (KO_len) {
-       case 16:
-       case 32:
-               break;
-       default:
-               DBG_ERR("KO_len[%zu] not supported\n", KO_len);
-               return NT_STATUS_INTERNAL_ERROR;
+       if (digest_len == 0) {
+               return NT_STATUS_HMAC_NOT_SUPPORTED;
+       }
+
+       {
+               const size_t n_iterations = ceiling_div(KO_len, digest_len);
+               /*
+                * To ensure that the counter values are distinct, n shall not
+                * be larger than 2ʳ−1, where r = 32. We have made sure that
+                * |KO| × 8 < 2³², and we know that n ≤ |KO| from its
+                * definition. Thus n ≤ |KO| ≤ |KO| × 8 < 2³², and so the
+                * requirement n ≤ 2³² − 1 must always hold.
+                */
+               SMB_ASSERT(n_iterations <= UINT32_MAX);
        }
 
        /*
@@ -146,26 +158,53 @@ NTSTATUS samba_gnutls_sp800_108_derive_key(
                                                NT_STATUS_HMAC_NOT_SUPPORTED);
        }
 
-       status = samba_gnutls_sp800_108_derive_key_part(hmac_hnd,
-                                                       Label,
-                                                       Label_len,
-                                                       Context,
-                                                       Context_len,
-                                                       L,
-                                                       i,
-                                                       digest);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto out;
+       /* (This loop would make an excellent candidate for parallelization.) */
+
+       for (KO_idx = 0, i = 1; KO_len - KO_idx >= digest_len;
+            KO_idx += digest_len, ++i)
+       {
+               status = samba_gnutls_sp800_108_derive_key_part(hmac_hnd,
+                                                               Label,
+                                                               Label_len,
+                                                               Context,
+                                                               Context_len,
+                                                               L,
+                                                               i,
+                                                               KO + KO_idx);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto out;
+               }
        }
 
-       memcpy(KO, digest, KO_len);
-
-       ZERO_ARRAY(digest);
+       if (KO_idx < KO_len) {
+               /* Get the last little bit. */
+               uint8_t digest[digest_len];
+               status = samba_gnutls_sp800_108_derive_key_part(hmac_hnd,
+                                                               Label,
+                                                               Label_len,
+                                                               Context,
+                                                               Context_len,
+                                                               L,
+                                                               i,
+                                                               digest);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto out;
+               }
+
+               memcpy(KO + KO_idx, digest, KO_len - KO_idx);
+
+               ZERO_ARRAY(digest);
+       }
 
 out:
        if (hmac_hnd != NULL) {
                gnutls_hmac_deinit(hmac_hnd, NULL);
        }
+       if (!NT_STATUS_IS_OK(status)) {
+               /* Hide the evidence. */
+               memset_s(KO, KO_len, 0, KO_idx);
+               ZERO_ARRAY_LEN(KO, KO_idx);
+       }
 
        return status;
 }
diff --git a/selftest/knownfail.d/gnutls-sp800-108 b/selftest/knownfail.d/gnutls-sp800-108
deleted file mode 100644 (file)
index b4f50c3..0000000
+++ /dev/null
@@ -1 +0,0 @@
-^samba\.unittests\.test_gnutls_sp800_108\.test_sp800_108_sha512\(none\)$