Blob Blame History Raw
From bf170f9894baf94be84c6255521625e64299259b Mon Sep 17 00:00:00 2001
From: Nicolai Stange <nstange@suse.de>
Date: Mon, 29 Nov 2021 10:08:06 +0100
Subject: [PATCH] crypto: ecdh - implement FIPS PCT
References: jsc#SLE-21132,bsc#1191256,bsc#1207184
Patch-mainline: Never, not upstreamable

SP800-56Arev3, 5.6.2.1.4 ("Owner Assurance of Pair-wise Consistency")
requires that a pair-wise consistency check needs to be conducted on a
keypair. A pair-wise consistency test (PCT) is meant to ensure that a
some provided public key is indeed associated with the given private one.
As the kernel's ECDH implementation always computes the public key from
the private one, this is guaranteed already as per the API. However, in the
course of the certification process, there had been a lengthy discussion
regarding this topic, with the result that a PCT is nonetheless mandatory.
As the only user of the in-kernel ECDH is bluetooth, performance certainly
isn't super critical. Simply implement a PCT for ECDH and move on. As
mandated by SP800-56Arev3, 5.6.2.1.4, the PCT involves recomputing the
public key and comparing it against the one under test.

Signed-off-by: Nicolai Stange <nstange@suse.de>
---
 crypto/ecdh.c |   31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

--- a/crypto/ecdh.c
+++ b/crypto/ecdh.c
@@ -10,6 +10,7 @@
 #include <crypto/kpp.h>
 #include <crypto/ecdh.h>
 #include <linux/scatterlist.h>
+#include <linux/fips.h>
 #include "ecc.h"
 
 struct ecdh_ctx {
@@ -94,6 +95,36 @@ static int ecdh_compute_value(struct kpp
 				       ctx->private_key, public_key);
 		buf = public_key;
 		nbytes = public_key_sz;
+
+		/*
+		 * SP800-56Arev3, 5.6.2.1.4: ("Owner Assurance of
+		 * Pair-wise Consistency"): recompute the public key
+		 * and check if the results match.
+		 */
+		if (fips_enabled) {
+			u64 *public_key_pct;
+
+			if (ret < 0)
+				goto free_all;
+
+			public_key_pct = kmalloc(public_key_sz, GFP_KERNEL);
+			if (!public_key_pct) {
+				ret = -ENOMEM;
+				goto free_all;
+			}
+
+			ret = ecc_make_pub_key(ctx->curve_id, ctx->ndigits,
+					       ctx->private_key,
+					       public_key_pct);
+			if (ret < 0) {
+				kfree(public_key_pct);
+				goto free_all;
+			}
+
+			if (memcmp(public_key, public_key_pct, public_key_sz))
+				panic("ECDH PCT failed in FIPS mode");
+			kfree(public_key_pct);
+		}
 	}
 
 	if (ret < 0)