From patchwork Fri Jan 10 01:03:03 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 13933361 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 101321422AB for ; Fri, 10 Jan 2025 01:03:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736471016; cv=none; b=gQzu7L7amtyUaGdaGQEu7Rn60bXDrmfkBHQtKjurcEqeFaM3E93hLnbQ0XE493LPLgClxh8TZH9qeYBaaMmg8A18ZWaKc0Pdwqgxf/GOYHz+pnCR2i0aH7olVNi5SJexgUQz/mhSIZ4GSDNsIYQJM3nt9TW89iD82t3xMPdYlDM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736471016; c=relaxed/simple; bh=MaxCbLX24ue4X68UTRZOsEko64m40xqyXHZD2xlCEk0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jne+AQ93TFYugb4GDxyTYGtzeu1cWzR3BRhAr5fcwFH2x5eBliHekon3NM9TkcrY8Hi0QmgqulJ1lPD0ZAJFNaqM2M2P8A32oa5a+MbkTuuPGK0TluhrFWAkIQFhkQ/8PTZthgfOjIKck44/E75UIPIy/5x9Fc+9D2+8iv6S3Sk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=jJPhJYUe; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="jJPhJYUe" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1736471014; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=jUzBwBZc7jiA0yAEpdkGwHRc8LI1eQmwaezuF6/wCmw=; b=jJPhJYUe47IZDGHwbNE4EfLOknLUl0qp9KZ6nLhA0SzeK+JKDNF5UpmnxhwyI+uUL1+DAt Cj0Pj/psMiO1qqR06qHZtDRfCGtVeDAUI1qZdgodifjFSxYDm3CZRxePdApXWPevgE+ATP 0b/2Ia5DBUDmVnOSVYtcR0+gWaqI2Z4= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-529-VLgKQHyPNxCrGzeDt1f_mg-1; Thu, 09 Jan 2025 20:03:29 -0500 X-MC-Unique: VLgKQHyPNxCrGzeDt1f_mg-1 X-Mimecast-MFC-AGG-ID: VLgKQHyPNxCrGzeDt1f_mg Received: from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.15]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 9AC3319560A1; Fri, 10 Jan 2025 01:03:27 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.42.28.12]) by mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 35582195E3E0; Fri, 10 Jan 2025 01:03:22 +0000 (UTC) From: David Howells To: Herbert Xu , Chuck Lever Cc: David Howells , Trond Myklebust , "David S. Miller" , Marc Dionne , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 1/8] crypto/krb5: Add some constants out of sunrpc headers Date: Fri, 10 Jan 2025 01:03:03 +0000 Message-ID: <20250110010313.1471063-2-dhowells@redhat.com> In-Reply-To: <20250110010313.1471063-1-dhowells@redhat.com> References: <20250110010313.1471063-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.15 Add some constants from the sunrpc headers. Signed-off-by: David Howells cc: Herbert Xu cc: "David S. Miller" cc: Chuck Lever cc: Marc Dionne cc: Eric Dumazet cc: Jakub Kicinski cc: Paolo Abeni cc: Simon Horman cc: linux-afs@lists.infradead.org cc: linux-nfs@vger.kernel.org cc: linux-crypto@vger.kernel.org cc: netdev@vger.kernel.org --- include/crypto/krb5.h | 50 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 include/crypto/krb5.h diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h new file mode 100644 index 000000000000..05e80fad2b38 --- /dev/null +++ b/include/crypto/krb5.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Kerberos 5 crypto + * + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#ifndef _CRYPTO_KRB5_H +#define _CRYPTO_KRB5_H + +/* per Kerberos v5 protocol spec crypto types from the wire. + * these get mapped to linux kernel crypto routines. + */ +#define KRB5_ENCTYPE_NULL 0x0000 +#define KRB5_ENCTYPE_DES_CBC_CRC 0x0001 /* DES cbc mode with CRC-32 */ +#define KRB5_ENCTYPE_DES_CBC_MD4 0x0002 /* DES cbc mode with RSA-MD4 */ +#define KRB5_ENCTYPE_DES_CBC_MD5 0x0003 /* DES cbc mode with RSA-MD5 */ +#define KRB5_ENCTYPE_DES_CBC_RAW 0x0004 /* DES cbc mode raw */ +/* XXX deprecated? */ +#define KRB5_ENCTYPE_DES3_CBC_SHA 0x0005 /* DES-3 cbc mode with NIST-SHA */ +#define KRB5_ENCTYPE_DES3_CBC_RAW 0x0006 /* DES-3 cbc mode raw */ +#define KRB5_ENCTYPE_DES_HMAC_SHA1 0x0008 +#define KRB5_ENCTYPE_DES3_CBC_SHA1 0x0010 +#define KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96 0x0011 +#define KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96 0x0012 +#define KRB5_ENCTYPE_ARCFOUR_HMAC 0x0017 +#define KRB5_ENCTYPE_ARCFOUR_HMAC_EXP 0x0018 +#define KRB5_ENCTYPE_UNKNOWN 0x01ff + +#define KRB5_CKSUMTYPE_CRC32 0x0001 +#define KRB5_CKSUMTYPE_RSA_MD4 0x0002 +#define KRB5_CKSUMTYPE_RSA_MD4_DES 0x0003 +#define KRB5_CKSUMTYPE_DESCBC 0x0004 +#define KRB5_CKSUMTYPE_RSA_MD5 0x0007 +#define KRB5_CKSUMTYPE_RSA_MD5_DES 0x0008 +#define KRB5_CKSUMTYPE_NIST_SHA 0x0009 +#define KRB5_CKSUMTYPE_HMAC_SHA1_DES3 0x000c +#define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128 0x000f +#define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256 0x0010 +#define KRB5_CKSUMTYPE_HMAC_MD5_ARCFOUR -138 /* Microsoft md5 hmac cksumtype */ + +/* + * Constants used for key derivation + */ +/* from rfc3961 */ +#define KEY_USAGE_SEED_CHECKSUM (0x99) +#define KEY_USAGE_SEED_ENCRYPTION (0xAA) +#define KEY_USAGE_SEED_INTEGRITY (0x55) + +#endif /* _CRYPTO_KRB5_H */ From patchwork Fri Jan 10 01:03:04 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 13933362 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B708A3D0D5 for ; Fri, 10 Jan 2025 01:03:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736471029; cv=none; b=tXNdKGPlXfWKn3SRXOnsgpbAYh+NxCZGjQ3HkEKyTeMybpkHZkbcjbDnncQqCu53Yhi3tk4O4rhBxTeOCqCME2BDyJHVOkDA8IOA0FwnExDsMJu9xTcUrYadOU0zPjVhGrgHJzi0u9yrpE9RC5wKAVSmk+ovIfo3KgLtuXsioIE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736471029; c=relaxed/simple; bh=RPfM5LjZZOFsG8gjcXyCo990DO+P/1q0PB0O1n/leyY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cK2dsV2RIfgGAVlb6SNdmd2zyD0ygzlKGWovGfhpcpKy8hOw21j9EVGJRYLowvVGCRXcXTHBcQgDl/6QS9GXf8NVkwOnQ99pJQUbsDoZ0MqH2/F2h66so3cX9XTn9ZGXWq8SGfZ034WtRlKwb5WD/diV34/99hVxiDOHZRANt3U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=C+R/GQUF; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="C+R/GQUF" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1736471022; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=2hh0cMxiexoevL5t0JC/q8EfOKA+/qJFwx3hcexeI9o=; b=C+R/GQUFWua7ixdtAACmi/KuGVnqAi74M9lGVh4av/s1pOD1fytpBpg6saaJ6cPP8dmQoS yKAcOyzn9apZ4NXpVX3CPbvDi7p3iy/J9QY4h5O1jHm/ceKWkycrJYs4AFsqZ2hjSZxqkA CFE8LRqvBAH5GUf+w98sQFl3jXvo0Z8= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-628-RvW84z04NkOqZBC__2EiBA-1; Thu, 09 Jan 2025 20:03:37 -0500 X-MC-Unique: RvW84z04NkOqZBC__2EiBA-1 X-Mimecast-MFC-AGG-ID: RvW84z04NkOqZBC__2EiBA Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 602C919560B4; Fri, 10 Jan 2025 01:03:34 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.42.28.12]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 599C630001BE; Fri, 10 Jan 2025 01:03:28 +0000 (UTC) From: David Howells To: Herbert Xu , Chuck Lever Cc: David Howells , Trond Myklebust , "David S. Miller" , Marc Dionne , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 2/8] crypto/krb5: Provide Kerberos 5 crypto through AEAD API Date: Fri, 10 Jan 2025 01:03:04 +0000 Message-ID: <20250110010313.1471063-3-dhowells@redhat.com> In-Reply-To: <20250110010313.1471063-1-dhowells@redhat.com> References: <20250110010313.1471063-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Use the AEAD crypto API to provide Kerberos 5 crypto, plus some supplementary library functions that lie outside of the AEAD API. The crypto algorithms only perform the actual crypto operations; they do not do any laying out of the message and nor do they insert any metadata or padding. Everything is done by dead-reckoning as the AEAD API does not provide a useful way to pass the extra parameters required. When setting the key on a crypto algorithm, setkey takes a composite structure consisting of an indication of the mode of transformation to be applied to the message (checksum only or full encryption); the usage type to be used in deriving the keys; an indicator indicating what key is being presented (K0 or Kc/Ke+Ki); and the material for those key(s). Based on this, the setkey code allocates and keys the appropriate ciphers and hashes. When dispatching a request, both checksumming (MIC) and encryption use the encrypt and decrypt methods. A source message, prelaid out with confounders or other metadata inserted is provided in the source buffer. The cryptolen indicates the amount of source message data, not including the trailer after the data (which includes the integrity checksum) and not including any associated data. Associated data is only used by checksumming encrypt/decrypt. The associated data is added to the checksum hash before the data in the message, but does not occupy any part of the output message. Authentication tags are not used at all and should cause EINVAL if used (a later patch does that). For the moment, the kerberos encryption algorithms use separate hash and cipher algorithms internally, but should really use dual hash+cipher and cipher+hash algorithms if possible to avoid doing these in series. Offload off this may be possible through something like the Intel QAT. To help with managing the layout, a number of functions are also provided: (1) crypto_krb5_find_enctype() - Find the definition of an encoding type by protocol number. This provides the name of the algorithm along with a host of parameter values, such as key sizes, block size, etc.. Note that the enctype wraps the aead_alg struct so that the crypto routines can use container_of() to find it. (2) crypto_krb5_how_much_buffer() - Determine how much bufferage is needed for a certain amount of data. (3) crypto_krb5_how_much_data() - Determine how much data will fit into a certain amount of buffer. (4) crypto_krb5_where_is_the_data() - Determine where in a decrypted message the data is and how much data there is. This may, in future, need to access the contents of the decrypted message to access metadata. (5) crypto_krb5_confound_buffer() - Insert the confounder in the buffer at the appropriate place - or generate one randomly. This may, in future, be combined with something that inserts padding and metadata. And to help with key derivation: (6) crypto_krb5_calc_PRFplus() - Calculate the PRF+ function. This patch includes the following features: (1) The PRF+ function from RFC4402 used in deriving keys. (2) The RFC3961 simplified crypto profile for Kerberos 5 rfc3961 with the pseudo-random function, PRF(), from section 5.3 and the key derivation function, DK() from section 5.1. (3) Message encryption and decryption according to RFC3961 sec 5.3. (4) Message checksumming and verification according to RFC3961 sec 5.4. (5) The aes128-cts-hmac-sha1-96 and aes256-cts-hmac-sha1-96 enctypes from RFC3962, using the RFC3961 kerberos 5 simplified crypto scheme. (6) The aes128-cts-hmac-sha256-128 and aes256-cts-hmac-sha384-192 enctypes from RFC8009 (which override the rfc3961 kerberos 5 simplified crypto scheme). (7) The camellia128-cts-cmac and camellia256-cts-cmac enctypes from RFC6803. Note that the test vectors in rfc6803 for encryption are incomplete, lacking the key usage number needed to derive Ke and Ki, and there are errata for this: https://www.rfc-editor.org/errata_search.php?rfc=6803 (8) Functions to find out about the layout of the crypto section. One calculates, for a given size of data, how big a buffer will be required to hold it and where the data will be within it. The other calculates, for an amount of buffer, what's the maximum size of data that will fit therein, and where it will start. (9) Self-testing infrastructure to test the pseudo-random function, key derivation, encryption and checksumming. Signed-off-by: David Howells cc: Herbert Xu cc: "David S. Miller" cc: Chuck Lever cc: Marc Dionne cc: Eric Dumazet cc: Jakub Kicinski cc: Paolo Abeni cc: Simon Horman cc: linux-afs@lists.infradead.org cc: linux-nfs@vger.kernel.org cc: linux-crypto@vger.kernel.org cc: netdev@vger.kernel.org --- crypto/Kconfig | 1 + crypto/Makefile | 2 + crypto/krb5/Kconfig | 24 + crypto/krb5/Makefile | 17 + crypto/krb5/internal.h | 162 ++++++ crypto/krb5/kdf.c | 334 +++++++++++++ crypto/krb5/krb5_aead.c | 456 +++++++++++++++++ crypto/krb5/rfc3961_simplified.c | 815 +++++++++++++++++++++++++++++++ crypto/krb5/rfc6803_camellia.c | 190 +++++++ crypto/krb5/rfc8009_aes2.c | 394 +++++++++++++++ crypto/krb5/selftest.c | 533 ++++++++++++++++++++ crypto/krb5/selftest_data.c | 370 ++++++++++++++ include/crypto/krb5.h | 101 +++- 13 files changed, 3397 insertions(+), 2 deletions(-) create mode 100644 crypto/krb5/Kconfig create mode 100644 crypto/krb5/Makefile create mode 100644 crypto/krb5/internal.h create mode 100644 crypto/krb5/kdf.c create mode 100644 crypto/krb5/krb5_aead.c create mode 100644 crypto/krb5/rfc3961_simplified.c create mode 100644 crypto/krb5/rfc6803_camellia.c create mode 100644 crypto/krb5/rfc8009_aes2.c create mode 100644 crypto/krb5/selftest.c create mode 100644 crypto/krb5/selftest_data.c diff --git a/crypto/Kconfig b/crypto/Kconfig index 6b0bfbccac08..7f1a60065dc1 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -1477,5 +1477,6 @@ endif source "drivers/crypto/Kconfig" source "crypto/asymmetric_keys/Kconfig" source "certs/Kconfig" +source "crypto/krb5/Kconfig" endif # if CRYPTO diff --git a/crypto/Makefile b/crypto/Makefile index 77abca715445..2d1463581d10 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -213,3 +213,5 @@ obj-$(CONFIG_CRYPTO_SIMD) += crypto_simd.o # Key derivation function # obj-$(CONFIG_CRYPTO_KDF800108_CTR) += kdf_sp800108.o + +obj-$(CONFIG_CRYPTO_KRB5) += krb5/ diff --git a/crypto/krb5/Kconfig b/crypto/krb5/Kconfig new file mode 100644 index 000000000000..0f8df83ff4fd --- /dev/null +++ b/crypto/krb5/Kconfig @@ -0,0 +1,24 @@ +config CRYPTO_KRB5 + tristate "Kerberos 5 crypto" + select CRYPTO_MANAGER + select CRYPTO_SKCIPHER + select CRYPTO_HASH_INFO + select CRYPTO_HMAC + select CRYPTO_CMAC + select CRYPTO_SHA1 + select CRYPTO_SHA256 + select CRYPTO_SHA512 + select CRYPTO_CBC + select CRYPTO_CTS + select CRYPTO_AES + select CRYPTO_CAMELLIA + help + Provide a library for provision of Kerberos-5-based crypto. This is + intended for network filesystems to use. + +config CRYPTO_KRB5_SELFTESTS + bool "Kerberos 5 crypto selftests" + depends on CRYPTO_KRB5 + help + Turn on some self-testing for the kerberos 5 crypto functions. These + will be performed on module load or boot, if compiled in. diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile new file mode 100644 index 000000000000..65cb211aebeb --- /dev/null +++ b/crypto/krb5/Makefile @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for asymmetric cryptographic keys +# + +krb5-y += \ + kdf.o \ + krb5_aead.o \ + rfc3961_simplified.o \ + rfc6803_camellia.o \ + rfc8009_aes2.o + +krb5-$(CONFIG_CRYPTO_KRB5_SELFTESTS) += \ + selftest.o \ + selftest_data.o + +obj-$(CONFIG_CRYPTO_KRB5) += krb5.o diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h new file mode 100644 index 000000000000..6ae7ff7008a8 --- /dev/null +++ b/crypto/krb5/internal.h @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Kerberos5 crypto internals + * + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include +#include +#include +#include +#include + +/* + * Checksum, encryption and integrity keyed algorithms for Kerberos encryption + * or checksumming. + */ +struct krb5_ctx { + struct crypto_sync_skcipher *Ke; /* Encryption key (fully encrypted) */ + union { + struct crypto_shash *Ki; /* Integrity key (fully encrypted) */ + struct crypto_shash *Kc; /* Checksum key (checksummed only) */ + }; +}; + +static inline struct krb5_ctx *crypto_krb5_ctx(struct crypto_aead *tfm) +{ + return crypto_aead_ctx(tfm); +} + +/* + * Profile used for key derivation and encryption. + */ +struct krb5_crypto_profile { + /* Pseudo-random function */ + int (*calc_PRF)(const struct krb5_enctype *krb5, + const struct krb5_buffer *protocol_key, + const struct krb5_buffer *octet_string, + struct krb5_buffer *result, + gfp_t gfp); + + /* Checksum key derivation */ + int (*calc_Kc)(const struct krb5_enctype *krb5, + const struct krb5_buffer *TK, + const struct krb5_buffer *usage_constant, + struct krb5_buffer *Kc, + gfp_t gfp); + + /* Encryption key derivation */ + int (*calc_Ke)(const struct krb5_enctype *krb5, + const struct krb5_buffer *TK, + const struct krb5_buffer *usage_constant, + struct krb5_buffer *Ke, + gfp_t gfp); + + /* Integrity key derivation */ + int (*calc_Ki)(const struct krb5_enctype *krb5, + const struct krb5_buffer *TK, + const struct krb5_buffer *usage_constant, + struct krb5_buffer *Ki, + gfp_t gfp); +}; + +/* + * Crypto size/alignment rounding convenience macros. + */ +#define crypto_roundup(X) ((unsigned int)round_up((X), CRYPTO_MINALIGN)) + +#define krb5_shash_size(TFM) \ + crypto_roundup(sizeof(struct shash_desc) + crypto_shash_descsize(TFM)) +#define krb5_skcipher_size(TFM) \ + crypto_roundup(sizeof(struct skcipher_request) + crypto_skcipher_reqsize(TFM)) +#define krb5_digest_size(TFM) \ + crypto_roundup(crypto_shash_digestsize(TFM)) +#define krb5_sync_skcipher_size(TFM) \ + krb5_skcipher_size(&(TFM)->base) +#define krb5_sync_skcipher_ivsize(TFM) \ + crypto_roundup(crypto_sync_skcipher_ivsize(TFM)) +#define round16(x) (((x) + 15) & ~15) + +/* + * Self-testing data. + */ +struct krb5_prf_test { + u32 etype; + const char *name, *key, *octet, *prf; +}; + +struct krb5_key_test_one { + u32 use; + const char *key; +}; + +struct krb5_key_test { + u32 etype; + const char *name, *key; + struct krb5_key_test_one Kc, Ke, Ki; +}; + +struct krb5_enc_test { + u32 etype; + const char *name, *plain, *conf, *key, *ct; +}; + +struct krb5_mic_test { + u32 etype; + const char *name, *plain, *key, *mic; +}; + +/* + * kdf.c + */ +int krb5_setkey(struct crypto_aead *aead, const u8 *key, unsigned int keylen); +int krb5_derive_Kc(const struct krb5_enctype *krb5, const struct krb5_buffer *TK, + u32 usage, struct krb5_buffer *key, gfp_t gfp); +int krb5_derive_Ke(const struct krb5_enctype *krb5, const struct krb5_buffer *TK, + u32 usage, struct krb5_buffer *key, gfp_t gfp); +int krb5_derive_Ki(const struct krb5_enctype *krb5, const struct krb5_buffer *TK, + u32 usage, struct krb5_buffer *key, gfp_t gfp); + +/* + * rfc3961_simplified.c + */ +extern const struct krb5_crypto_profile rfc3961_simplified_profile; + +size_t sg_count(struct scatterlist *sg, int *_nents); +int crypto_shash_update_sg(struct shash_desc *desc, struct scatterlist *sg, + size_t offset, size_t len); +int rfc3961_get_mic(struct aead_request *req); +int rfc3961_verify_mic(struct aead_request *req); +int rfc3961_aead_encrypt(struct aead_request *req); +int rfc3961_aead_decrypt(struct aead_request *req); + +/* + * rfc6803_camellia.c + */ +extern const struct krb5_crypto_profile rfc6803_crypto_profile; + +/* + * rfc8009_aes2.c + */ +extern const struct krb5_crypto_profile rfc8009_crypto_profile; + +int rfc8009_aead_encrypt(struct aead_request *req); +int rfc8009_aead_decrypt(struct aead_request *req); + +/* + * selftest.c + */ +#ifdef CONFIG_CRYPTO_KRB5_SELFTESTS +int krb5_selftest(void); +#else +static inline int krb5_selftest(void) { return 0; } +#endif + +/* + * selftest_data.c + */ +extern const struct krb5_prf_test krb5_prf_tests[]; +extern const struct krb5_key_test krb5_key_tests[]; +extern const struct krb5_enc_test krb5_enc_tests[]; +extern const struct krb5_mic_test krb5_mic_tests[]; diff --git a/crypto/krb5/kdf.c b/crypto/krb5/kdf.c new file mode 100644 index 000000000000..b29e69f3d7f0 --- /dev/null +++ b/crypto/krb5/kdf.c @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Kerberos key derivation. + * + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include "internal.h" + +/** + * crypto_krb5_calc_PRFplus - Calculate PRF+ [RFC4402] + * @krb5: The encryption type to use + * @K: The protocol key for the pseudo-random function + * @L: The length of the output + * @S: The input octet string + * @result: Result buffer, sized to krb5->prf_len + * @gfp: Allocation restrictions + * + * Calculate the kerberos pseudo-random function, PRF+() by the following + * method: + * + * PRF+(K, L, S) = truncate(L, T1 || T2 || .. || Tn) + * Tn = PRF(K, n || S) + * [rfc4402 sec 2] + */ +int crypto_krb5_calc_PRFplus(const struct krb5_enctype *krb5, + const struct krb5_buffer *K, + unsigned int L, + const struct krb5_buffer *S, + struct krb5_buffer *result, + gfp_t gfp) +{ + struct krb5_buffer T_series, Tn, n_S; + void *buffer; + int ret, n = 1; + + Tn.len = krb5->prf_len; + T_series.len = 0; + n_S.len = 4 + S->len; + + buffer = kzalloc(round16(L + Tn.len) + round16(n_S.len), gfp); + if (!buffer) + return -ENOMEM; + + T_series.data = buffer; + n_S.data = buffer + round16(L + Tn.len); + memcpy(n_S.data + 4, S->data, S->len); + + while (T_series.len < L) { + *(__be32 *)(n_S.data) = htonl(n); + Tn.data = T_series.data + Tn.len * (n - 1); + ret = krb5->profile->calc_PRF(krb5, K, &n_S, &Tn, gfp); + if (ret < 0) + goto err; + T_series.len += Tn.len; + n++; + } + + /* Truncate to L */ + memcpy(result->data, T_series.data, L); + ret = 0; + +err: + kfree_sensitive(buffer); + return ret; +} +EXPORT_SYMBOL(crypto_krb5_calc_PRFplus); + +/** + * krb5_derive_Kc - Derive key Kc and install into a hash + * @krb5: The encryption type to use + * @TK: The base key + * @usage: The key usage number + * @key: Prepped (temporary) buffer to store the key into + * @gfp: Allocation restrictions + * + * Derive the Kerberos Kc checksumming key. The key is stored into the + * prepared buffer. + */ +int krb5_derive_Kc(const struct krb5_enctype *krb5, const struct krb5_buffer *TK, + u32 usage, struct krb5_buffer *key, gfp_t gfp) +{ + u8 buf[5] __aligned(CRYPTO_MINALIGN); + struct krb5_buffer usage_constant = { .len = 5, .data = buf }; + + *(__be32 *)buf = cpu_to_be32(usage); + buf[4] = KEY_USAGE_SEED_CHECKSUM; + + key->len = krb5->Kc_len; + return krb5->profile->calc_Kc(krb5, TK, &usage_constant, key, gfp); +} + +static int krb5_get_Kc(const struct krb5_enctype *krb5, const struct krb5_buffer *key, + struct krb5_ctx *ctx, gfp_t gfp) +{ + struct crypto_shash *shash; + + shash = crypto_alloc_shash(krb5->cksum_name, 0, 0); + if (IS_ERR(shash)) + return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash); + ctx->Kc = shash; + return crypto_shash_setkey(shash, key->data, key->len); +} + +/** + * krb5_derive_Ke - Derive key Ke and install into an skcipher + * @krb5: The encryption type to use + * @TK: The base key + * @usage: The key usage number + * @key: Prepped (temporary) buffer to store the key into + * @gfp: Allocation restrictions + * + * Derive the Kerberos Ke encryption key. The key is stored into the prepared + * buffer. + */ +int krb5_derive_Ke(const struct krb5_enctype *krb5, const struct krb5_buffer *TK, + u32 usage, struct krb5_buffer *key, gfp_t gfp) +{ + u8 buf[5] __aligned(CRYPTO_MINALIGN); + struct krb5_buffer usage_constant = { .len = 5, .data = buf }; + + *(__be32 *)buf = cpu_to_be32(usage); + buf[4] = KEY_USAGE_SEED_ENCRYPTION; + + key->len = krb5->Ke_len; + return krb5->profile->calc_Ke(krb5, TK, &usage_constant, key, gfp); +} + +static int krb5_get_Ke(const struct krb5_enctype *krb5, const struct krb5_buffer *key, + struct krb5_ctx *ctx, gfp_t gfp) +{ + struct crypto_sync_skcipher *ci; + + ci = crypto_alloc_sync_skcipher(krb5->encrypt_name, 0, 0); + if (IS_ERR(ci)) + return (PTR_ERR(ci) == -ENOENT) ? -ENOPKG : PTR_ERR(ci); + ctx->Ke = ci; + return crypto_sync_skcipher_setkey(ci, key->data, key->len); +} + +/** + * krb5_derive_Ki - Derive key Ki and install into a hash + * @krb5: The encryption type to use + * @TK: The base key + * @usage: The key usage number + * @key: Prepped (temporary) buffer to store the key into + * @gfp: Allocation restrictions + * + * Derive the Kerberos Ki integrity checksum key. The key is stored into the + * prepared buffer. + */ +int krb5_derive_Ki(const struct krb5_enctype *krb5, const struct krb5_buffer *TK, + u32 usage, struct krb5_buffer *key, gfp_t gfp) +{ + u8 buf[5] __aligned(CRYPTO_MINALIGN); + struct krb5_buffer usage_constant = { .len = 5, .data = buf }; + + *(__be32 *)buf = cpu_to_be32(usage); + buf[4] = KEY_USAGE_SEED_INTEGRITY; + + key->len = krb5->Ki_len; + return krb5->profile->calc_Ki(krb5, TK, &usage_constant, key, gfp); +} + +static int krb5_get_Ki(const struct krb5_enctype *krb5, const struct krb5_buffer *key, + struct krb5_ctx *ctx, gfp_t gfp) +{ + struct crypto_shash *shash; + + shash = crypto_alloc_shash(krb5->cksum_name, 0, 0); + if (IS_ERR(shash)) + return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash); + ctx->Ki = shash; + return crypto_shash_setkey(shash, key->data, key->len); +} + +/** + * krb5_setkey - Use the key material to derive keys + * @aead: The crypto algorithm context to configure + * @key: Compound of the key material and config parameters + * @keylen: Length of @key buffer + * + * Use the key material and configuration parameters to derive the appropriate + * keys for encrypting and/or checksumming. The key material must be in one of + * the following formats: + * + * Checksummed mode: + * + * KRB5_CHECKSUM_MODE (__be32) || usage (__be32) || transport key data (TK) + * KRB5_CHECKSUM_MODE_KC (__be32) || usage (__be32) || Kc + * + * Full encryption mode: + * + * KRB5_ENCRYPT_MODE (__be32) || usage (__be32) || transport key data (TK) + * KRB5_ENCRYPT_MODE_KEKI (__be32) || usage (__be32) || Ke || Ki + */ +int krb5_setkey(struct crypto_aead *aead, const u8 *key, unsigned int keylen) +{ + const struct krb5_enctype *krb5 = crypto_krb5_enctype(aead); + struct krb5_buffer TK, keybuf = {}, Kn; + struct krb5_ctx *ctx = crypto_krb5_ctx(aead); + enum krb5_crypto_mode mode; + gfp_t gfp = GFP_NOFS; // TODO: The crypto API should provide this. + u32 usage; + int ret; + + pr_debug("setkey %x %*phN\n", keylen, keylen, key); + + if (keylen < 12) + return -EINVAL; + + switch (mode) { + case KRB5_CHECKSUM_MODE: + case KRB5_ENCRYPT_MODE: + keybuf.data = kzalloc(krb5->key_bytes, gfp); + if (!keybuf.data) + return -ENOMEM; + break; + default: + break; + } + + mode = get_unaligned_be32((__be32 *)key); + key += 4; + usage = get_unaligned_be32((__be32 *)key); + key += 4; + keylen -= 8; + + switch (mode) { + case KRB5_CHECKSUM_MODE: + if (keylen != krb5->key_len) + return -EINVAL; + TK.data = (u8 *)key; + TK.len = keylen; + ret = krb5_derive_Kc(krb5, &TK, usage, &keybuf, gfp); + if (ret < 0) + goto err; + ret = krb5_get_Kc(krb5, &keybuf, ctx, gfp); + if (ret < 0) + goto err; + break; + + case KRB5_CHECKSUM_MODE_KC: + if (keylen != krb5->Kc_len) + return -EINVAL; + Kn.data = (u8 *)key; + Kn.len = keylen; + ret = krb5_get_Kc(krb5, &Kn, ctx, gfp); + if (ret < 0) + goto err; + break; + + case KRB5_ENCRYPT_MODE: + if (keylen != krb5->key_len) + return -EINVAL; + TK.data = (u8 *)key; + TK.len = keylen; + ret = krb5_derive_Ke(krb5, &TK, usage, &keybuf, gfp); + if (ret < 0) { + pr_err("get_Ke failed %d\n", ret); + goto err; + } + ret = krb5_get_Ke(krb5, &keybuf, ctx, gfp); + if (ret < 0) { + pr_err("get_Ke failed %d\n", ret); + goto err; + } + ret = krb5_derive_Ki(krb5, &TK, usage, &keybuf, gfp); + if (ret < 0) { + pr_err("get_Ki failed %d\n", ret); + goto err; + } + ret = krb5_get_Ki(krb5, &keybuf, ctx, gfp); + if (ret < 0) { + pr_err("get_Ki failed %d\n", ret); + goto err; + } + break; + + case KRB5_ENCRYPT_MODE_KEKI: + if (keylen != krb5->Ke_len + krb5->Ki_len) + return -EINVAL; + Kn.data = (u8 *)key; + Kn.len = krb5->Ke_len; + ret = krb5_get_Ke(krb5, &Kn, ctx, gfp); + if (ret < 0) { + pr_err("get_Ke failed %d\n", ret); + goto err; + } + Kn.data = (u8 *)key + krb5->Ke_len; + Kn.len = krb5->Ki_len; + ret = krb5_get_Ki(krb5, &Kn, ctx, gfp); + if (ret < 0) { + pr_err("get_Ki failed %d\n", ret); + goto err; + } + break; + + default: + ret = -EINVAL; + break; + } + + if (ctx->Ke && crypto_sync_skcipher_blocksize(ctx->Ke) != krb5->block_len) { + pr_notice("skcipher inconsistent with krb5 table %u!=%u\n", + crypto_sync_skcipher_blocksize(ctx->Ke), krb5->block_len); + ret = -EINVAL; + goto err; + } + + if (ctx->Ki && crypto_shash_digestsize(ctx->Ki) < krb5->cksum_len) { + pr_notice("hash inconsistent with krb5 table %u!=%u\n", + crypto_shash_digestsize(ctx->Ki), krb5->cksum_len); + ret = -EINVAL; + goto err; + } + +out: + kfree(keybuf.data); + return ret; + +err: + if (ctx->Ke) + crypto_free_sync_skcipher(ctx->Ke); + if (ctx->Ki) + crypto_free_shash(ctx->Ki); + goto out; +} diff --git a/crypto/krb5/krb5_aead.c b/crypto/krb5/krb5_aead.c new file mode 100644 index 000000000000..2c8b3921e976 --- /dev/null +++ b/crypto/krb5/krb5_aead.c @@ -0,0 +1,456 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Kerberos 5 crypto library. + * + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include "internal.h" + +#include +#include +#include + +/** + * crypto_krb5_how_much_buffer - Work out how much buffer is required for an amount of data + * @krb5: The encoding to use. + * @mode: The mode in which to operated (checksum/encrypt) + * @data_size: How much data we want to allow for + * @_offset: Where to place the offset into the buffer + * + * Calculate how much buffer space is required to wrap a given amount of data. + * This allows for a confounder, padding and checksum as appropriate. The + * amount of buffer required is returned and the offset into the buffer at + * which the data will start is placed in *_offset. + */ +size_t crypto_krb5_how_much_buffer(const struct krb5_enctype *krb5, + enum krb5_crypto_mode mode, + size_t data_size, size_t *_offset) +{ + switch (mode) { + case KRB5_CHECKSUM_MODE: + case KRB5_CHECKSUM_MODE_KC: + *_offset = krb5->cksum_len; + return krb5->cksum_len + data_size; + + case KRB5_ENCRYPT_MODE: + case KRB5_ENCRYPT_MODE_KEKI: + *_offset = krb5->conf_len; + return krb5->conf_len + data_size + krb5->cksum_len; + + default: + WARN_ON(1); + *_offset = 0; + return 0; + } +} +EXPORT_SYMBOL(crypto_krb5_how_much_buffer); + +/** + * crypto_krb5_how_much_data - Work out how much data can fit in an amount of buffer + * @krb5: The encoding to use. + * @mode: The mode in which to operated (checksum/encrypt) + * @_buffer_size: How much buffer we want to allow for (may be reduced) + * @_offset: Where to place the offset into the buffer + * + * Calculate how much data can be fitted into given amount of buffer. This + * allows for a confounder, padding and checksum as appropriate. The amount of + * data that will fit is returned, the amount of buffer required is shrunk to + * allow for alignment and the offset into the buffer at which the data will + * start is placed in *_offset. + */ +size_t crypto_krb5_how_much_data(const struct krb5_enctype *krb5, + enum krb5_crypto_mode mode, + size_t *_buffer_size, size_t *_offset) +{ + size_t buffer_size = *_buffer_size, data_size; + + switch (mode) { + case KRB5_CHECKSUM_MODE: + if (WARN_ON(buffer_size < krb5->cksum_len + 1)) + goto bad; + *_offset = krb5->cksum_len; + return buffer_size - krb5->cksum_len; + + case KRB5_ENCRYPT_MODE: + if (WARN_ON(buffer_size < krb5->conf_len + 1 + krb5->cksum_len)) + goto bad; + data_size = buffer_size - krb5->cksum_len; + *_offset = krb5->conf_len; + return data_size - krb5->conf_len; + + default: + WARN_ON(1); + goto bad; + } + +bad: + *_offset = 0; + return 0; +} +EXPORT_SYMBOL(crypto_krb5_how_much_data); + +/** + * crypto_krb5_where_is_the_data - Find the data in a decrypted message + * @krb5: The encoding to use. + * @mode: Mode of operation + * @_offset: Offset of the secure blob in the buffer; updated to data offset. + * @_len: The length of the secure blob; updated to data length. + * + * Find the offset and size of the data in a secure message so that this + * information can be used in the metadata buffer which will get added to the + * digest by crypto_krb5_verify_mic(). + */ +void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, + enum krb5_crypto_mode mode, + size_t *_offset, size_t *_len) +{ + switch (mode) { + case KRB5_CHECKSUM_MODE: + case KRB5_CHECKSUM_MODE_KC: + *_offset += krb5->cksum_len; + *_len -= krb5->cksum_len; + return; + case KRB5_ENCRYPT_MODE: + case KRB5_ENCRYPT_MODE_KEKI: + *_offset += krb5->conf_len; + *_len -= krb5->conf_len + krb5->cksum_len; + return; + default: + WARN_ON_ONCE(1); + return; + } +} +EXPORT_SYMBOL(crypto_krb5_where_is_the_data); + +/* + * crypto_krb5_confound_buffer - Insert confounder + * @krb5: The encoding type + * @sg: The buffer holding the message + * @nr_sg: Number of segments in @sg + * @confounder: The confounder to insert (or NULL for random confounder) + * @conf_len: Length of @confounder content (if not NULL) + * @msg_offset: Offset in buffer of start of message + * + * Insert an appropriately-sized confounder into a buffer at the correct place + * with respect to the start of the message. + */ +int crypto_krb5_confound_buffer(const struct krb5_enctype *krb5, + struct scatterlist *sg, unsigned int nr_sg, + const u8 *confounder, size_t conf_len, + size_t msg_offset) +{ + size_t done; + void *buffer = NULL; + + if (!confounder) { + buffer = kmalloc(krb5->conf_len, GFP_NOFS); + if (!buffer) + return -ENOMEM; + get_random_bytes(buffer, krb5->conf_len); + confounder = buffer; + } else { + if (WARN_ON(conf_len != krb5->conf_len)) + return -EFAULT; + } + + done = sg_pcopy_from_buffer(sg, nr_sg, confounder, krb5->conf_len, + msg_offset); + kfree(buffer); + return (done == krb5->conf_len) ? 0 : -EFAULT; +} +EXPORT_SYMBOL(crypto_krb5_confound_buffer); + +/* + * Table of supported Kerberos-V encryption types and their parameters. + */ +static struct krb5_enctype krb5_enctypes[] = { + { + .etype = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96, + .ctype = KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128, + .name = "aes128-cts-hmac-sha1-96", + .encrypt_name = "cts(cbc(aes))", + .cksum_name = "hmac(sha1)", + .hash_name = "sha1", + .key_bytes = 16, + .key_len = 16, + .Kc_len = 16, + .Ke_len = 16, + .Ki_len = 16, + .block_len = 16, + .conf_len = 16, + .cksum_len = 12, + .hash_len = 20, + .prf_len = 16, + .keyed_cksum = true, + .random_to_key = NULL, /* Identity */ + .profile = &rfc3961_simplified_profile, + + .aead.setkey = krb5_setkey, + .aead.setauthsize = NULL, + .aead.encrypt = rfc3961_aead_encrypt, + .aead.decrypt = rfc3961_aead_decrypt, + .aead.ivsize = 0, + .aead.maxauthsize = 0, + .aead.chunksize = 16, + + .aead.base.cra_flags = CRYPTO_ALG_ALLOCATES_MEMORY, + .aead.base.cra_blocksize = 1, + .aead.base.cra_ctxsize = sizeof(struct krb5_ctx), + .aead.base.cra_alignmask = 0, + .aead.base.cra_priority = 100, + .aead.base.cra_name = "krb5-aes128-cts-hmac-sha1-96", + .aead.base.cra_driver_name = "krb5-aes128-cts-hmac-sha1-96-generic", + .aead.base.cra_module = THIS_MODULE, + }, { + .etype = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96, + .ctype = KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256, + .name = "aes256-cts-hmac-sha1-96", + .encrypt_name = "cts(cbc(aes))", + .cksum_name = "hmac(sha1)", + .hash_name = "sha1", + .key_bytes = 32, + .key_len = 32, + .Kc_len = 32, + .Ke_len = 32, + .Ki_len = 32, + .block_len = 16, + .conf_len = 16, + .cksum_len = 12, + .hash_len = 20, + .prf_len = 16, + .keyed_cksum = true, + .random_to_key = NULL, /* Identity */ + .profile = &rfc3961_simplified_profile, + + .aead.setkey = krb5_setkey, + .aead.setauthsize = NULL, + .aead.encrypt = rfc3961_aead_encrypt, + .aead.decrypt = rfc3961_aead_decrypt, + .aead.ivsize = 0, + .aead.maxauthsize = 0, + .aead.chunksize = 16, + + .aead.base.cra_flags = CRYPTO_ALG_ALLOCATES_MEMORY, + .aead.base.cra_blocksize = 1, + .aead.base.cra_ctxsize = sizeof(struct krb5_ctx), + .aead.base.cra_alignmask = 0, + .aead.base.cra_priority = 100, + .aead.base.cra_name = "krb5-aes256-cts-hmac-sha1-96", + .aead.base.cra_driver_name = "krb5-aes256-cts-hmac-sha1-96-generic", + .aead.base.cra_module = THIS_MODULE, + }, { + .etype = KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC, + .ctype = KRB5_CKSUMTYPE_CMAC_CAMELLIA128, + .name = "camellia128-cts-cmac", + .encrypt_name = "cts(cbc(camellia))", + .cksum_name = "cmac(camellia)", + .hash_name = NULL, + .key_bytes = 16, + .key_len = 16, + .Kc_len = 16, + .Ke_len = 16, + .Ki_len = 16, + .block_len = 16, + .conf_len = 16, + .cksum_len = 16, + .hash_len = 16, + .prf_len = 16, + .keyed_cksum = true, + .random_to_key = NULL, /* Identity */ + .profile = &rfc6803_crypto_profile, + + .aead.setkey = krb5_setkey, + .aead.setauthsize = NULL, + .aead.encrypt = rfc3961_aead_encrypt, + .aead.decrypt = rfc3961_aead_decrypt, + .aead.ivsize = 0, + .aead.maxauthsize = 0, + .aead.chunksize = 16, + + .aead.base.cra_flags = CRYPTO_ALG_ALLOCATES_MEMORY, + .aead.base.cra_blocksize = 1, + .aead.base.cra_ctxsize = sizeof(struct krb5_ctx), + .aead.base.cra_alignmask = 0, + .aead.base.cra_priority = 100, + .aead.base.cra_name = "krb5-camellia128-cts-cmac", + .aead.base.cra_driver_name = "krb5-camellia128-cts-cmac-generic", + .aead.base.cra_module = THIS_MODULE, + }, { + .etype = KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC, + .ctype = KRB5_CKSUMTYPE_CMAC_CAMELLIA256, + .name = "camellia256-cts-cmac", + .encrypt_name = "cts(cbc(camellia))", + .cksum_name = "cmac(camellia)", + .hash_name = NULL, + .key_bytes = 32, + .key_len = 32, + .Kc_len = 32, + .Ke_len = 32, + .Ki_len = 32, + .block_len = 16, + .conf_len = 16, + .cksum_len = 16, + .hash_len = 16, + .prf_len = 16, + .keyed_cksum = true, + .random_to_key = NULL, /* Identity */ + .profile = &rfc6803_crypto_profile, + + .aead.setkey = krb5_setkey, + .aead.setauthsize = NULL, + .aead.encrypt = rfc3961_aead_encrypt, + .aead.decrypt = rfc3961_aead_decrypt, + .aead.ivsize = 0, + .aead.maxauthsize = 0, + .aead.chunksize = 16, + + .aead.base.cra_flags = CRYPTO_ALG_ALLOCATES_MEMORY, + .aead.base.cra_blocksize = 1, + .aead.base.cra_ctxsize = sizeof(struct krb5_ctx), + .aead.base.cra_alignmask = 0, + .aead.base.cra_priority = 100, + .aead.base.cra_name = "krb5-camellia256-cts-cmac", + .aead.base.cra_driver_name = "krb5-camellia256-cts-cmac-generic", + .aead.base.cra_module = THIS_MODULE, + }, { + .etype = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128, + .ctype = KRB5_CKSUMTYPE_HMAC_SHA256_128_AES128, + .name = "aes128-cts-hmac-sha256-128", + .encrypt_name = "cts(cbc(aes))", + .cksum_name = "hmac(sha256)", + .hash_name = "sha256", + .key_bytes = 16, + .key_len = 16, + .Kc_len = 16, + .Ke_len = 16, + .Ki_len = 16, + .block_len = 16, + .conf_len = 16, + .cksum_len = 16, + .hash_len = 20, + .prf_len = 32, + .keyed_cksum = true, + .random_to_key = NULL, /* Identity */ + .profile = &rfc8009_crypto_profile, + + .aead.setkey = krb5_setkey, + .aead.setauthsize = NULL, + .aead.encrypt = rfc8009_aead_encrypt, + .aead.decrypt = rfc8009_aead_decrypt, + .aead.ivsize = 0, + .aead.maxauthsize = 0, + .aead.chunksize = 16, + + .aead.base.cra_flags = CRYPTO_ALG_ALLOCATES_MEMORY, + .aead.base.cra_blocksize = 1, + .aead.base.cra_ctxsize = sizeof(struct krb5_ctx), + .aead.base.cra_alignmask = 0, + .aead.base.cra_priority = 100, + .aead.base.cra_name = "krb5-aes128-cts-hmac-sha256-128", + .aead.base.cra_driver_name = "krb5-aes128-cts-hmac-sha256-128generic", + .aead.base.cra_module = THIS_MODULE, + }, { + .etype = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192, + .ctype = KRB5_CKSUMTYPE_HMAC_SHA384_192_AES256, + .name = "aes256-cts-hmac-sha384-192", + .encrypt_name = "cts(cbc(aes))", + .cksum_name = "hmac(sha384)", + .hash_name = "sha384", + .key_bytes = 32, + .key_len = 32, + .Kc_len = 24, + .Ke_len = 32, + .Ki_len = 24, + .block_len = 16, + .conf_len = 16, + .cksum_len = 24, + .hash_len = 20, + .prf_len = 48, + .keyed_cksum = true, + .random_to_key = NULL, /* Identity */ + .profile = &rfc8009_crypto_profile, + + .aead.setkey = krb5_setkey, + .aead.setauthsize = NULL, + .aead.encrypt = rfc8009_aead_encrypt, + .aead.decrypt = rfc8009_aead_decrypt, + .aead.ivsize = 0, + .aead.maxauthsize = 0, + .aead.chunksize = 16, + + .aead.base.cra_flags = CRYPTO_ALG_ALLOCATES_MEMORY, + .aead.base.cra_blocksize = 1, + .aead.base.cra_ctxsize = sizeof(struct krb5_ctx), + .aead.base.cra_alignmask = 0, + .aead.base.cra_priority = 100, + .aead.base.cra_name = "krb5-aes256-cts-hmac-sha384-192", + .aead.base.cra_driver_name = "krb5-aes256-cts-hmac-sha384-192-generic", + .aead.base.cra_module = THIS_MODULE, + }, +}; + +/** + * crypto_krb5_find_enctype - Find the handler for a Kerberos5 encryption type + * @enctype: The standard Kerberos encryption type number + * + * Look up a Kerberos encryption type by number. If successful, returns a + * pointer to the type description; returns NULL otherwise. + */ +const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype) +{ + const struct krb5_enctype *krb5; + size_t i; + + for (i = 0; i < ARRAY_SIZE(krb5_enctypes); i++) { + krb5 = &krb5_enctypes[i]; + if (krb5->etype == enctype) + return krb5; + } + + return NULL; +} +EXPORT_SYMBOL(crypto_krb5_find_enctype); + +static int __init krb5_module_init(void) +{ + int i, ret, count = ARRAY_SIZE(krb5_enctypes); + + for (i = 0; i < count; i++) { + ret = crypto_register_aead(&krb5_enctypes[i].aead); + if (ret) + goto err; + } + + ret = krb5_selftest(); + if (ret < 0) + goto err; + return 0; + +err: + for (--i; i >= 0; --i) + crypto_unregister_aead(&krb5_enctypes[i].aead); + return ret; +} + +static void __exit krb5_module_exit(void) +{ + for (int i = 0; i < ARRAY_SIZE(krb5_enctypes); i++) + crypto_unregister_aead(&krb5_enctypes[i].aead); +} + +subsys_initcall(krb5_module_init); +module_exit(krb5_module_exit); + +MODULE_DESCRIPTION("Kerberos 5 crypto"); +MODULE_AUTHOR("Red Hat, Inc."); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CRYPTO("krb5"); diff --git a/crypto/krb5/rfc3961_simplified.c b/crypto/krb5/rfc3961_simplified.c new file mode 100644 index 000000000000..d5f0837bd424 --- /dev/null +++ b/crypto/krb5/rfc3961_simplified.c @@ -0,0 +1,815 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* rfc3961 Kerberos 5 simplified crypto profile. + * + * Parts borrowed from net/sunrpc/auth_gss/. + */ +/* + * COPYRIGHT (c) 2008 + * The Regents of the University of Michigan + * ALL RIGHTS RESERVED + * + * Permission is granted to use, copy, create derivative works + * and redistribute this software and such derivative works + * for any purpose, so long as the name of The University of + * Michigan is not used in any advertising or publicity + * pertaining to the use of distribution of this software + * without specific, written prior authorization. If the + * above copyright notice or any other identification of the + * University of Michigan is included in any copy of any + * portion of this software, then the disclaimer below must + * also be included. + * + * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION + * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY + * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF + * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING + * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE + * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR + * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING + * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN + * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include "internal.h" + +size_t sg_count(struct scatterlist *sg, int *_nents) +{ + size_t total = 0; + int nents = 0; + + for (; sg; sg = sg_next(sg)) { + nents++; + total += sg->length; + } + + *_nents = nents; + return total; +} + +int crypto_shash_update_sg(struct shash_desc *desc, struct scatterlist *sg, + size_t offset, size_t len) +{ + do { + int ret; + + if (offset < sg->length) { + struct page *page = sg_page(sg); + void *p = kmap_local_page(page); + void *q = p + sg->offset + offset; + size_t seg = min_t(size_t, len, sg->length - offset); + + ret = crypto_shash_update(desc, q, seg); + kunmap_local(p); + if (ret < 0) + return ret; + len -= seg; + offset = 0; + } else { + offset -= sg->length; + } + } while (len > 0 && (sg = sg_next(sg))); + return 0; +} + +/* Maximum blocksize for the supported crypto algorithms */ +#define KRB5_MAX_BLOCKSIZE (16) + +static int rfc3961_do_encrypt(struct crypto_sync_skcipher *tfm, void *iv, + const struct krb5_buffer *in, struct krb5_buffer *out) +{ + struct scatterlist sg[1]; + u8 local_iv[KRB5_MAX_BLOCKSIZE] __aligned(KRB5_MAX_BLOCKSIZE) = {0}; + SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm); + int ret; + + if (WARN_ON(in->len != out->len)) + return -EINVAL; + if (out->len % crypto_sync_skcipher_blocksize(tfm) != 0) + return -EINVAL; + + if (crypto_sync_skcipher_ivsize(tfm) > KRB5_MAX_BLOCKSIZE) + return -EINVAL; + + if (iv) + memcpy(local_iv, iv, crypto_sync_skcipher_ivsize(tfm)); + + memcpy(out->data, in->data, out->len); + sg_init_one(sg, out->data, out->len); + + skcipher_request_set_sync_tfm(req, tfm); + skcipher_request_set_callback(req, 0, NULL, NULL); + skcipher_request_set_crypt(req, sg, sg, out->len, local_iv); + + ret = crypto_skcipher_encrypt(req); + skcipher_request_zero(req); + return ret; +} + +/* + * Calculate an unkeyed basic hash. + */ +static int rfc3961_calc_H(const struct krb5_enctype *krb5, + const struct krb5_buffer *data, + struct krb5_buffer *digest, + gfp_t gfp) +{ + struct crypto_shash *tfm; + struct shash_desc *desc; + size_t desc_size; + int ret = -ENOMEM; + + tfm = crypto_alloc_shash(krb5->hash_name, 0, 0); + if (IS_ERR(tfm)) + return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + + desc = kzalloc(desc_size, gfp); + if (!desc) + goto error_tfm; + + digest->len = crypto_shash_digestsize(tfm); + digest->data = kzalloc(digest->len, gfp); + if (!digest->data) + goto error_desc; + + desc->tfm = tfm; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error_digest; + + ret = crypto_shash_finup(desc, data->data, data->len, digest->data); + if (ret < 0) + goto error_digest; + + goto error_desc; + +error_digest: + kfree_sensitive(digest->data); +error_desc: + kfree_sensitive(desc); +error_tfm: + crypto_free_shash(tfm); + return ret; +} + +/* + * This is the n-fold function as described in rfc3961, sec 5.1 + * Taken from MIT Kerberos and modified. + */ +static void rfc3961_nfold(const struct krb5_buffer *source, struct krb5_buffer *result) +{ + const u8 *in = source->data; + u8 *out = result->data; + unsigned long ulcm; + unsigned int inbits, outbits; + int byte, i, msbit; + + /* the code below is more readable if I make these bytes instead of bits */ + inbits = source->len; + outbits = result->len; + + /* first compute lcm(n,k) */ + ulcm = lcm(inbits, outbits); + + /* now do the real work */ + memset(out, 0, outbits); + byte = 0; + + /* this will end up cycling through k lcm(k,n)/k times, which + * is correct. + */ + for (i = ulcm-1; i >= 0; i--) { + /* compute the msbit in k which gets added into this byte */ + msbit = ( + /* first, start with the msbit in the first, + * unrotated byte + */ + ((inbits << 3) - 1) + + /* then, for each byte, shift to the right + * for each repetition + */ + (((inbits << 3) + 13) * (i/inbits)) + + /* last, pick out the correct byte within + * that shifted repetition + */ + ((inbits - (i % inbits)) << 3) + ) % (inbits << 3); + + /* pull out the byte value itself */ + byte += (((in[((inbits - 1) - (msbit >> 3)) % inbits] << 8) | + (in[((inbits) - (msbit >> 3)) % inbits])) + >> ((msbit & 7) + 1)) & 0xff; + + /* do the addition */ + byte += out[i % outbits]; + out[i % outbits] = byte & 0xff; + + /* keep around the carry bit, if any */ + byte >>= 8; + } + + /* if there's a carry bit left over, add it back in */ + if (byte) { + for (i = outbits - 1; i >= 0; i--) { + /* do the addition */ + byte += out[i]; + out[i] = byte & 0xff; + + /* keep around the carry bit, if any */ + byte >>= 8; + } + } +} + +/* + * Calculate a derived key, DK(Base Key, Well-Known Constant) + * + * DK(Key, Constant) = random-to-key(DR(Key, Constant)) + * DR(Key, Constant) = k-truncate(E(Key, Constant, initial-cipher-state)) + * K1 = E(Key, n-fold(Constant), initial-cipher-state) + * K2 = E(Key, K1, initial-cipher-state) + * K3 = E(Key, K2, initial-cipher-state) + * K4 = ... + * DR(Key, Constant) = k-truncate(K1 | K2 | K3 | K4 ...) + * [rfc3961 sec 5.1] + */ +static int rfc3961_calc_DK(const struct krb5_enctype *krb5, + const struct krb5_buffer *inkey, + const struct krb5_buffer *in_constant, + struct krb5_buffer *result, + gfp_t gfp) +{ + unsigned int blocksize, keybytes, keylength, n; + struct krb5_buffer inblock, outblock, rawkey; + struct crypto_sync_skcipher *cipher; + int ret = -EINVAL; + + blocksize = krb5->block_len; + keybytes = krb5->key_bytes; + keylength = krb5->key_len; + + if (inkey->len != keylength || result->len != keylength) + return -EINVAL; + if (!krb5->random_to_key && result->len != keybytes) + ret = -EINVAL; + + cipher = crypto_alloc_sync_skcipher(krb5->encrypt_name, 0, 0); + if (IS_ERR(cipher)) { + ret = (PTR_ERR(cipher) == -ENOENT) ? -ENOPKG : PTR_ERR(cipher); + goto err_return; + } + ret = crypto_sync_skcipher_setkey(cipher, inkey->data, inkey->len); + if (ret < 0) + goto err_free_cipher; + + ret = -ENOMEM; + inblock.data = kzalloc(blocksize * 2 + keybytes, gfp); + if (!inblock.data) + goto err_free_cipher; + + inblock.len = blocksize; + outblock.data = inblock.data + blocksize; + outblock.len = blocksize; + rawkey.data = outblock.data + blocksize; + rawkey.len = keybytes; + + /* initialize the input block */ + + if (in_constant->len == inblock.len) + memcpy(inblock.data, in_constant->data, inblock.len); + else + rfc3961_nfold(in_constant, &inblock); + + /* loop encrypting the blocks until enough key bytes are generated */ + n = 0; + while (n < rawkey.len) { + rfc3961_do_encrypt(cipher, NULL, &inblock, &outblock); + + if (keybytes - n <= outblock.len) { + memcpy(rawkey.data + n, outblock.data, keybytes - n); + break; + } + + memcpy(rawkey.data + n, outblock.data, outblock.len); + memcpy(inblock.data, outblock.data, outblock.len); + n += outblock.len; + } + + /* postprocess the key */ + if (!krb5->random_to_key) { + /* Identity random-to-key function. */ + memcpy(result->data, rawkey.data, rawkey.len); + ret = 0; + } else { + ret = krb5->random_to_key(krb5, &rawkey, result); + } + + kfree_sensitive(inblock.data); +err_free_cipher: + crypto_free_sync_skcipher(cipher); +err_return: + return ret; +} + +/* + * Calculate single encryption, E() + * + * E(Key, octets) + */ +static int rfc3961_calc_E(const struct krb5_enctype *krb5, + const struct krb5_buffer *key, + const struct krb5_buffer *in_data, + struct krb5_buffer *result, + gfp_t gfp) +{ + struct crypto_sync_skcipher *cipher; + int ret; + + cipher = crypto_alloc_sync_skcipher(krb5->encrypt_name, 0, 0); + if (IS_ERR(cipher)) { + ret = (PTR_ERR(cipher) == -ENOENT) ? -ENOPKG : PTR_ERR(cipher); + goto err; + } + + ret = crypto_sync_skcipher_setkey(cipher, key->data, key->len); + if (ret < 0) + goto err_free; + + ret = rfc3961_do_encrypt(cipher, NULL, in_data, result); + +err_free: + crypto_free_sync_skcipher(cipher); +err: + return ret; +} + +/* + * Calculate the pseudo-random function, PRF(). + * + * tmp1 = H(octet-string) + * tmp2 = truncate tmp1 to multiple of m + * PRF = E(DK(protocol-key, prfconstant), tmp2, initial-cipher-state) + * + * The "prfconstant" used in the PRF operation is the three-octet string + * "prf". + * [rfc3961 sec 5.3] + */ +static int rfc3961_calc_PRF(const struct krb5_enctype *krb5, + const struct krb5_buffer *protocol_key, + const struct krb5_buffer *octet_string, + struct krb5_buffer *result, + gfp_t gfp) +{ + static const struct krb5_buffer prfconstant = { 3, "prf" }; + struct krb5_buffer derived_key; + struct krb5_buffer tmp1, tmp2; + unsigned int m = krb5->block_len; + void *buffer; + int ret; + + if (result->len != krb5->prf_len) + return -EINVAL; + + tmp1.len = krb5->hash_len; + derived_key.len = krb5->key_bytes; + buffer = kzalloc(round16(tmp1.len) + round16(derived_key.len), gfp); + if (!buffer) + return -ENOMEM; + + tmp1.data = buffer; + derived_key.data = buffer + round16(tmp1.len); + + ret = rfc3961_calc_H(krb5, octet_string, &tmp1, gfp); + if (ret < 0) + goto err; + + tmp2.len = tmp1.len & ~(m - 1); + tmp2.data = tmp1.data; + + ret = rfc3961_calc_DK(krb5, protocol_key, &prfconstant, &derived_key, gfp); + if (ret < 0) + goto err; + + ret = rfc3961_calc_E(krb5, &derived_key, &tmp2, result, gfp); + +err: + kfree_sensitive(buffer); + return ret; +} + +/* + * Apply encryption and checksumming functions to part of a message. The + * caller is responsible for laying out the message and inserting a confounder. + * + * req->cryptlen indicates the size of the area in the source to be encrypted, + * and must include any metadata prior to the data area, such as the + * confounder. Space for post-data metadata, such as the checksum, only needs + * to exist in the destination. + */ +static int rfc3961_encrypt(struct aead_request *req) +{ + struct skcipher_request *ci; + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + struct krb5_enctype *krb5 = crypto_krb5_enctype(tfm); + struct scatterlist *src = req->src, *dst = req->dst; + struct shash_desc *desc; + struct krb5_ctx *ctx = crypto_krb5_ctx(tfm); + size_t bsize, enc_len = req->cryptlen; + size_t src_len, dst_len, done; + void *buffer; + int ret, nr_src, nr_dst; + u8 *cksum, *iv; + + src_len = sg_count(src, &nr_src); + dst_len = sg_count(dst, &nr_dst); + + if (WARN_ON(enc_len < krb5->conf_len) || + WARN_ON(src_len < enc_len) || + WARN_ON(dst_len < enc_len + krb5->cksum_len)) + return -EINVAL; + + /* The message is laid out thusly: + * + * Confounder||Data||Padding||Integrity + * + * The Padding may or may not be present, but the Integrity checksum + * must be right at the end of the message so that we can find it. The + * Confounder, Data and Padding are encrypted; the Integrity checksum + * is not. The Integrity checksum is over the plaintext. + */ + enc_len = req->cryptlen; + + bsize = krb5_shash_size(ctx->Ki) + + krb5_digest_size(ctx->Ki) + + krb5_sync_skcipher_size(ctx->Ke) + + krb5_sync_skcipher_ivsize(ctx->Ke); + bsize = umax(umax(bsize, krb5->conf_len), krb5->block_len); + buffer = kzalloc(bsize, GFP_NOFS); + if (!buffer) + return -ENOMEM; + + /* Calculate the checksum using key Ki */ + cksum = buffer + krb5_shash_size(ctx->Ki); + + desc = buffer; + desc->tfm = ctx->Ki; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + ret = crypto_shash_update_sg(desc, src, 0, enc_len); + if (ret < 0) + goto error; + ret = crypto_shash_final(desc, cksum); + if (ret < 0) + goto error; + + /* Append the checksum into the buffer. */ + ret = -EFAULT; + done = sg_pcopy_from_buffer(dst, nr_dst, cksum, krb5->cksum_len, enc_len); + if (done != krb5->cksum_len) + goto error; + + /* Encrypt the secure region with key Ke. */ + ci = buffer + + krb5_shash_size(ctx->Ki) + + krb5_digest_size(ctx->Ki); + iv = buffer + + krb5_shash_size(ctx->Ki) + + krb5_digest_size(ctx->Ki) + + krb5_sync_skcipher_size(ctx->Ke); + + skcipher_request_set_sync_tfm(ci, ctx->Ke); + skcipher_request_set_callback(ci, 0, NULL, NULL); + skcipher_request_set_crypt(ci, src, dst, enc_len, iv); + ret = crypto_skcipher_encrypt(ci); + if (ret < 0) + goto error; + + ret = 0; +error: + kfree_sensitive(buffer); + return ret; +} + +/* + * Apply decryption and checksumming functions to part of an skbuff. The + * offset and length are updated to reflect the actual content of the encrypted + * region. + * + * The associated data must contain a krb5_assoc_data struct. At the + * conclusion, the output associated data is updated with the size of the + * encrypted data. The associated data must be in its own scatterlist element + * in both chains so that we can skip over it. + */ +static int rfc3961_decrypt(struct aead_request *req) +{ + struct skcipher_request *ci; + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + struct krb5_enctype *krb5 = crypto_krb5_enctype(tfm); + struct scatterlist *src = req->src, *dst = req->dst; + struct shash_desc *desc; + struct krb5_ctx *ctx = crypto_krb5_ctx(tfm); + size_t bsize, src_len, dst_len, enc_len, msg_len = req->cryptlen, done; + void *buffer; + int ret, nr_src, nr_dst; + u8 *cksum, *cksum2, *iv; + + src_len = sg_count(src, &nr_src); + dst_len = sg_count(dst, &nr_dst); + + if (WARN_ON(msg_len < krb5->conf_len + krb5->cksum_len) || + WARN_ON(src_len < msg_len) || + WARN_ON(dst_len < msg_len - krb5->cksum_len)) + return -EINVAL; + + /* The integrity checksum is right up against the end. */ + enc_len = msg_len - krb5->cksum_len; + + bsize = krb5_shash_size(ctx->Ki) + + krb5_digest_size(ctx->Ki) * 2 + + krb5_sync_skcipher_size(ctx->Ke) + + krb5_sync_skcipher_ivsize(ctx->Ke); + buffer = kzalloc(bsize, GFP_NOFS); + if (!buffer) + return -ENOMEM; + + cksum = buffer + + krb5_shash_size(ctx->Ki); + cksum2 = buffer + + krb5_shash_size(ctx->Ki) + + krb5_digest_size(ctx->Ki); + ci = buffer + + krb5_shash_size(ctx->Ki) + + krb5_digest_size(ctx->Ki) * 2; + iv = buffer + + krb5_shash_size(ctx->Ki) + + krb5_digest_size(ctx->Ki) * 2 + + krb5_sync_skcipher_size(ctx->Ke); + + /* Decrypt the secure region with key Ke. */ + skcipher_request_set_sync_tfm(ci, ctx->Ke); + skcipher_request_set_callback(ci, 0, NULL, NULL); + skcipher_request_set_crypt(ci, src, dst, enc_len, iv); + ret = crypto_skcipher_decrypt(ci); + if (ret < 0) + goto error; + + /* Calculate the checksum using key Ki */ + desc = buffer; + desc->tfm = ctx->Ki; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + + ret = crypto_shash_update_sg(desc, src, 0, enc_len); + if (ret < 0) + goto error; + + ret = crypto_shash_final(desc, cksum); + if (ret < 0) + goto error; + + /* Get the checksum from the buffer. */ + ret = -EFAULT; + done = sg_pcopy_to_buffer(src, nr_src, cksum2, krb5->cksum_len, enc_len); + if (done != krb5->cksum_len) + goto error; + + ret = -EPROTO; + if (memcmp(cksum, cksum2, krb5->cksum_len) != 0) + goto error; + + ret = 0; +error: + kfree_sensitive(buffer); + return ret; +} + +/* + * Generate a checksum over some metadata and part of a message and insert the + * MIC into the message immediately prior to the data. + * + * Any metadata to be added to the hash must be in assoc data. + */ +int rfc3961_get_mic(struct aead_request *req) +{ + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + struct krb5_enctype *krb5 = crypto_krb5_enctype(tfm); + struct scatterlist *src = req->src, *dst = req->dst; + struct shash_desc *desc; + struct krb5_ctx *ctx = crypto_krb5_ctx(tfm); + size_t meta_len = req->assoclen, msg_len = req->cryptlen; + size_t src_len, dst_len, data_len, bsize, done; + void *buffer, *digest; + int ret, nr_src, nr_dst; + + src_len = sg_count(src, &nr_src); + dst_len = sg_count(dst, &nr_dst); + + if (WARN_ON(msg_len < krb5->cksum_len) || + WARN_ON(src_len < msg_len) || + WARN_ON(dst_len < src_len)) + return -EINVAL; + + /* The message is laid out thusly: + * + * Checksum||Data + * + * The Checksum must be right at the beginning of the message so that + * we can find it. + */ + data_len = msg_len - krb5->cksum_len; + + bsize = krb5_shash_size(ctx->Kc) + + krb5_digest_size(ctx->Kc); + buffer = kzalloc(bsize, GFP_NOFS); + if (!buffer) + return -ENOMEM; + + /* Calculate the MIC with key Kc and store it into the skb */ + desc = buffer; + desc->tfm = ctx->Kc; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + + if (meta_len) { + ret = crypto_shash_update_sg(desc, src, 0, meta_len); + if (ret < 0) + goto error; + } + + ret = crypto_shash_update_sg(desc, src, meta_len + krb5->cksum_len, data_len); + if (ret < 0) + goto error; + + digest = buffer + krb5_shash_size(ctx->Kc); + ret = crypto_shash_final(desc, digest); + if (ret < 0) + goto error; + + ret = -EFAULT; + done = sg_pcopy_from_buffer(dst, nr_dst, digest, krb5->cksum_len, meta_len); + if (done != krb5->cksum_len) + goto error; + + ret = 0; +error: + kfree_sensitive(buffer); + return ret; +} + +/* + * Check the MIC on a message. + * + * Any metadata to be added to the hash must be in assoc data. + * + * [!] NOTE: This produces nothing in the destination buffer. + */ +int rfc3961_verify_mic(struct aead_request *req) +{ + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + struct krb5_enctype *krb5 = crypto_krb5_enctype(tfm); + struct scatterlist *src = req->src, *dst = req->dst; + struct shash_desc *desc; + struct krb5_ctx *ctx = crypto_krb5_ctx(tfm); + size_t meta_len = req->assoclen, msg_len = req->cryptlen; + size_t src_len, dst_len, data_len, bsize, done; + void *buffer, *cksum, *cksum2; + int ret, nr_src, nr_dst; + + src_len = sg_count(src, &nr_src); + dst_len = sg_count(dst, &nr_dst); + + if (WARN_ON(msg_len < krb5->cksum_len) || + WARN_ON(src_len < msg_len) || + WARN_ON(dst_len < src_len)) + return -EINVAL; + + /* The message is laid out thusly: + * + * Checksum||Data + * + * The Checksum must be right at the beginning of the message so that + * we can find it. + */ + data_len = msg_len - krb5->cksum_len; + + bsize = krb5_shash_size(ctx->Kc) + + krb5_digest_size(ctx->Kc) * 2; + buffer = kzalloc(bsize, GFP_NOFS); + if (!buffer) + return -ENOMEM; + + cksum = buffer + + krb5_shash_size(ctx->Kc); + cksum2 = buffer + + krb5_shash_size(ctx->Kc) + + krb5_digest_size(ctx->Kc); + + /* Calculate the MIC */ + desc = buffer; + desc->tfm = ctx->Kc; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + + if (meta_len) { + ret = crypto_shash_update_sg(desc, src, 0, meta_len); + if (ret < 0) + goto error; + } + + ret = crypto_shash_update_sg(desc, src, meta_len + krb5->cksum_len, data_len); + if (ret < 0) + goto error; + + ret = crypto_shash_final(desc, cksum); + if (ret < 0) + goto error; + + ret = -EFAULT; + done = sg_pcopy_to_buffer(src, nr_src, cksum2, krb5->cksum_len, meta_len); + if (done != krb5->cksum_len) + goto error; + + if (memcmp(cksum, cksum2, krb5->cksum_len) != 0) { + ret = -EPROTO; + goto error; + } + + ret = 0; +error: + kfree_sensitive(buffer); + return ret; +} + +int rfc3961_aead_encrypt(struct aead_request *req) +{ + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + struct krb5_ctx *ctx = crypto_krb5_ctx(tfm); + + if (ctx->Ke) + return rfc3961_encrypt(req); + return rfc3961_get_mic(req); +} + +int rfc3961_aead_decrypt(struct aead_request *req) +{ + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + struct krb5_ctx *ctx = crypto_krb5_ctx(tfm); + + if (ctx->Ke) + return rfc3961_decrypt(req); + return rfc3961_verify_mic(req); +} + +const struct krb5_crypto_profile rfc3961_simplified_profile = { + .calc_PRF = rfc3961_calc_PRF, + .calc_Kc = rfc3961_calc_DK, + .calc_Ke = rfc3961_calc_DK, + .calc_Ki = rfc3961_calc_DK, +}; diff --git a/crypto/krb5/rfc6803_camellia.c b/crypto/krb5/rfc6803_camellia.c new file mode 100644 index 000000000000..206224ab05ec --- /dev/null +++ b/crypto/krb5/rfc6803_camellia.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* rfc6803 Camellia Encryption for Kerberos 5 + * + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include "internal.h" + +/* + * Calculate the key derivation function KDF-FEEDBACK_CMAC(key, constant) + * + * n = ceiling(k / 128) + * K(0) = zeros + * K(i) = CMAC(key, K(i-1) | i | constant | 0x00 | k) + * DR(key, constant) = k-truncate(K(1) | K(2) | ... | K(n)) + * KDF-FEEDBACK-CMAC(key, constant) = random-to-key(DR(key, constant)) + * + * [rfc6803 sec 3] + */ +static int rfc6803_calc_KDF_FEEDBACK_CMAC(const struct krb5_enctype *krb5, + const struct krb5_buffer *key, + const struct krb5_buffer *constant, + struct krb5_buffer *result, + gfp_t gfp) +{ + struct crypto_shash *shash; + struct krb5_buffer K, data; + struct shash_desc *desc; + __be32 tmp; + size_t bsize, offset, seg; + void *buffer; + u32 i = 0, k = result->len * 8; + u8 *p; + int ret = -ENOMEM; + + shash = crypto_alloc_shash(krb5->cksum_name, 0, 0); + if (IS_ERR(shash)) + return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash); + ret = crypto_shash_setkey(shash, key->data, key->len); + if (ret < 0) { + pr_err("setkey %s failed %d %u\n", krb5->cksum_name, ret, key->len); + goto error_shash; + } + + ret = -ENOMEM; + K.len = crypto_shash_digestsize(shash); + data.len = K.len + 4 + constant->len + 1 + 4; + bsize = krb5_shash_size(shash) + + krb5_digest_size(shash) + + crypto_roundup(K.len) + + crypto_roundup(data.len); + buffer = kzalloc(bsize, GFP_NOFS); + if (!buffer) + goto error_shash; + + desc = buffer; + desc->tfm = shash; + + K.data = buffer + + krb5_shash_size(shash) + + krb5_digest_size(shash); + data.data = buffer + + krb5_shash_size(shash) + + krb5_digest_size(shash) + + crypto_roundup(K.len); + + p = data.data + K.len + 4; + memcpy(p, constant->data, constant->len); + p += constant->len; + *p++ = 0x00; + tmp = htonl(k); + memcpy(p, &tmp, 4); + p += 4; + + ret = -EINVAL; + if (WARN_ON(p - (u8 *)data.data != data.len)) { + pr_err("len check\n"); + goto error; + } + + offset = 0; + do { + i++; + p = data.data; + memcpy(p, K.data, K.len); + p += K.len; + *(__be32 *)p = htonl(i); + + ret = crypto_shash_init(desc); + if (ret < 0) { + pr_err("shash_init\n"); + goto error; + } + ret = crypto_shash_finup(desc, data.data, data.len, K.data); + if (ret < 0) { + pr_err("shash_finup\n"); + goto error; + } + + seg = min_t(size_t, result->len - offset, K.len); + memcpy(result->data + offset, K.data, seg); + offset += seg; + } while (offset < result->len); + +error: + kfree_sensitive(buffer); +error_shash: + crypto_free_shash(shash); + return ret; +} + +/* + * Calculate the pseudo-random function, PRF(). + * + * Kp = KDF-FEEDBACK-CMAC(protocol-key, "prf") + * PRF = CMAC(Kp, octet-string) + * [rfc6803 sec 6] + */ +static int rfc6803_calc_PRF(const struct krb5_enctype *krb5, + const struct krb5_buffer *protocol_key, + const struct krb5_buffer *octet_string, + struct krb5_buffer *result, + gfp_t gfp) +{ + static const struct krb5_buffer prfconstant = { 3, "prf" }; + struct crypto_shash *shash; + struct krb5_buffer Kp; + struct shash_desc *desc; + size_t bsize; + void *buffer; + int ret; + + Kp.len = krb5->prf_len; + + shash = crypto_alloc_shash(krb5->cksum_name, 0, 0); + if (IS_ERR(shash)) + return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash); + + ret = -EINVAL; + if (result->len != crypto_shash_digestsize(shash)) + goto out_shash; + + ret = -ENOMEM; + bsize = krb5_shash_size(shash) + + krb5_digest_size(shash) + + crypto_roundup(Kp.len); + buffer = kzalloc(bsize, GFP_NOFS); + if (!buffer) + goto out_shash; + + Kp.data = buffer + + krb5_shash_size(shash) + + krb5_digest_size(shash); + + ret = rfc6803_calc_KDF_FEEDBACK_CMAC(krb5, protocol_key, &prfconstant, + &Kp, gfp); + if (ret < 0) + goto out; + + ret = crypto_shash_setkey(shash, Kp.data, Kp.len); + if (ret < 0) + goto out; + + desc = buffer; + desc->tfm = shash; + ret = crypto_shash_init(desc); + if (ret < 0) + goto out; + + ret = crypto_shash_finup(desc, octet_string->data, octet_string->len, result->data); + if (ret < 0) + goto out; + +out: + kfree_sensitive(buffer); +out_shash: + crypto_free_shash(shash); + return ret; +} + +const struct krb5_crypto_profile rfc6803_crypto_profile = { + .calc_PRF = rfc6803_calc_PRF, + .calc_Kc = rfc6803_calc_KDF_FEEDBACK_CMAC, + .calc_Ke = rfc6803_calc_KDF_FEEDBACK_CMAC, + .calc_Ki = rfc6803_calc_KDF_FEEDBACK_CMAC, +}; diff --git a/crypto/krb5/rfc8009_aes2.c b/crypto/krb5/rfc8009_aes2.c new file mode 100644 index 000000000000..60b43479e108 --- /dev/null +++ b/crypto/krb5/rfc8009_aes2.c @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* rfc8009 AES Encryption with HMAC-SHA2 for Kerberos 5 + * + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include "internal.h" + +static const struct krb5_buffer rfc8009_no_context = { .len = 0, .data = "" }; + +/* + * Calculate the key derivation function KDF-HMAC-SHA2(key, label, [context,] k) + * + * KDF-HMAC-SHA2(key, label, [context,] k) = k-truncate(K1) + * + * Using the appropriate one of: + * K1 = HMAC-SHA-256(key, 0x00000001 | label | 0x00 | k) + * K1 = HMAC-SHA-384(key, 0x00000001 | label | 0x00 | k) + * K1 = HMAC-SHA-256(key, 0x00000001 | label | 0x00 | context | k) + * K1 = HMAC-SHA-384(key, 0x00000001 | label | 0x00 | context | k) + * [rfc8009 sec 3] + */ +static int rfc8009_calc_KDF_HMAC_SHA2(const struct krb5_enctype *krb5, + const struct krb5_buffer *key, + const struct krb5_buffer *label, + const struct krb5_buffer *context, + unsigned int k, + struct krb5_buffer *result, + gfp_t gfp) +{ + struct crypto_shash *shash; + struct krb5_buffer K1, data; + struct shash_desc *desc; + __be32 tmp; + size_t bsize; + void *buffer; + u8 *p; + int ret = -ENOMEM; + + if (WARN_ON(result->len != k / 8)) + return -EINVAL; + + shash = crypto_alloc_shash(krb5->cksum_name, 0, 0); + if (IS_ERR(shash)) + return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash); + ret = crypto_shash_setkey(shash, key->data, key->len); + if (ret < 0) + goto error_shash; + + ret = -EINVAL; + if (WARN_ON(crypto_shash_digestsize(shash) * 8 < k)) + goto error_shash; + + ret = -ENOMEM; + data.len = 4 + label->len + 1 + context->len + 4; + bsize = krb5_shash_size(shash) + + krb5_digest_size(shash) + + crypto_roundup(data.len); + buffer = kzalloc(bsize, GFP_NOFS); + if (!buffer) + goto error_shash; + + desc = buffer; + desc->tfm = shash; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + + p = data.data = buffer + + krb5_shash_size(shash) + + krb5_digest_size(shash); + *(__be32 *)p = htonl(0x00000001); + p += 4; + memcpy(p, label->data, label->len); + p += label->len; + *p++ = 0; + memcpy(p, context->data, context->len); + p += context->len; + tmp = htonl(k); + memcpy(p, &tmp, 4); + p += 4; + + ret = -EINVAL; + if (WARN_ON(p - (u8 *)data.data != data.len)) + goto error; + + K1.len = crypto_shash_digestsize(shash); + K1.data = buffer + + krb5_shash_size(shash); + + ret = crypto_shash_finup(desc, data.data, data.len, K1.data); + if (ret < 0) + goto error; + + memcpy(result->data, K1.data, result->len); + +error: + kfree_sensitive(buffer); +error_shash: + crypto_free_shash(shash); + return ret; +} + +/* + * Calculate the pseudo-random function, PRF(). + * + * PRF = KDF-HMAC-SHA2(input-key, "prf", octet-string, 256) + * PRF = KDF-HMAC-SHA2(input-key, "prf", octet-string, 384) + * + * The "prfconstant" used in the PRF operation is the three-octet string + * "prf". + * [rfc8009 sec 5] + */ +static int rfc8009_calc_PRF(const struct krb5_enctype *krb5, + const struct krb5_buffer *input_key, + const struct krb5_buffer *octet_string, + struct krb5_buffer *result, + gfp_t gfp) +{ + static const struct krb5_buffer prfconstant = { 3, "prf" }; + + return rfc8009_calc_KDF_HMAC_SHA2(krb5, input_key, &prfconstant, + octet_string, krb5->prf_len * 8, + result, gfp); +} + +/* + * Derive Ke. + * Ke = KDF-HMAC-SHA2(base-key, usage | 0xAA, 128) + * Ke = KDF-HMAC-SHA2(base-key, usage | 0xAA, 256) + * [rfc8009 sec 5] + */ +static int rfc8009_calc_Ke(const struct krb5_enctype *krb5, + const struct krb5_buffer *base_key, + const struct krb5_buffer *usage_constant, + struct krb5_buffer *result, + gfp_t gfp) +{ + return rfc8009_calc_KDF_HMAC_SHA2(krb5, base_key, usage_constant, + &rfc8009_no_context, krb5->key_bytes * 8, + result, gfp); +} + +/* + * Derive Kc/Ki + * Kc = KDF-HMAC-SHA2(base-key, usage | 0x99, 128) + * Ki = KDF-HMAC-SHA2(base-key, usage | 0x55, 128) + * Kc = KDF-HMAC-SHA2(base-key, usage | 0x99, 192) + * Ki = KDF-HMAC-SHA2(base-key, usage | 0x55, 192) + * [rfc8009 sec 5] + */ +static int rfc8009_calc_Ki(const struct krb5_enctype *krb5, + const struct krb5_buffer *base_key, + const struct krb5_buffer *usage_constant, + struct krb5_buffer *result, + gfp_t gfp) +{ + return rfc8009_calc_KDF_HMAC_SHA2(krb5, base_key, usage_constant, + &rfc8009_no_context, krb5->cksum_len * 8, + result, gfp); +} + +/* + * Apply encryption and checksumming functions to part of a message. The + * caller is responsible for laying out the message and inserting a confounder. + * + * req->cryptlen indicates the size of the area in the source to be encrypted, + * and must include any metadata prior to the data area, such as the + * confounder. Space for post-data metadata, such as the checksum, only needs + * to exist in the destination. + */ +static int rfc8009_encrypt(struct aead_request *req) +{ + struct skcipher_request *ci; + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + struct krb5_enctype *krb5 = crypto_krb5_enctype(tfm); + struct scatterlist *src = req->src, *dst = req->dst; + struct shash_desc *desc; + struct krb5_ctx *ctx = crypto_krb5_ctx(tfm); + size_t bsize, enc_len = req->cryptlen, msg_len; + size_t src_len, dst_len, done; + void *buffer; + int ret, nr_src, nr_dst; + u8 *cksum, *iv; + + src_len = sg_count(src, &nr_src); + dst_len = sg_count(dst, &nr_dst); + + if (WARN_ON(enc_len < krb5->conf_len) || + WARN_ON(src_len < enc_len) || + WARN_ON(dst_len < enc_len + krb5->cksum_len)) + return -EINVAL; + + /* The message is laid out thusly: + * + * Confounder||Data||Padding||Integrity + * + * The Padding may or may not be present, but the Integrity checksum + * must be right at the end of the message so that we can find it. The + * Confounder, Data and Padding are encrypted; the Integrity checksum + * is not. The Integrity checksum is over the ciphertext. + */ + msg_len = src_len; + enc_len = msg_len - krb5->cksum_len; + + bsize = krb5_shash_size(ctx->Ki) + + krb5_digest_size(ctx->Ki) + + krb5_sync_skcipher_size(ctx->Ke) + + krb5_sync_skcipher_ivsize(ctx->Ke); + bsize = umax(umax(bsize, krb5->conf_len), krb5->block_len); + buffer = kzalloc(bsize, GFP_NOFS); + if (!buffer) + return -ENOMEM; + + /* Encrypt the secure region with key Ke. */ + ci = buffer + + krb5_shash_size(ctx->Ki) + + krb5_digest_size(ctx->Ki); + iv = buffer + + krb5_shash_size(ctx->Ki) + + krb5_digest_size(ctx->Ki) + + krb5_sync_skcipher_size(ctx->Ke); + + skcipher_request_set_sync_tfm(ci, ctx->Ke); + skcipher_request_set_callback(ci, 0, NULL, NULL); + skcipher_request_set_crypt(ci, src, dst, enc_len, iv); + ret = crypto_skcipher_encrypt(ci); + if (ret < 0) + goto error; + + /* Calculate the checksum using key Ki */ + cksum = buffer + krb5_shash_size(ctx->Ki); + + desc = buffer; + desc->tfm = ctx->Ki; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + + memset(iv, 0, crypto_sync_skcipher_ivsize(ctx->Ke)); + ret = crypto_shash_update(desc, iv, crypto_sync_skcipher_ivsize(ctx->Ke)); + if (ret < 0) + goto error; + + ret = crypto_shash_update_sg(desc, src, 0, enc_len); + if (ret < 0) + goto error; + + ret = crypto_shash_final(desc, cksum); + if (ret < 0) + goto error; + + /* Append the checksum into the buffer. */ + ret = -EFAULT; + sg_zero_buffer(dst, nr_dst, 3, enc_len); + done = sg_pcopy_from_buffer(dst, nr_dst, cksum, krb5->cksum_len, enc_len); + if (done != krb5->cksum_len) + goto error; + + ret = 0; +error: + kfree_sensitive(buffer); + return ret; +} + +/* + * Apply decryption and checksumming functions to part of an skbuff. The + * offset and length are updated to reflect the actual content of the encrypted + * region. + * + * The associated data must contain a krb5_assoc_data struct. At the + * conclusion, the output associated data is updated with the size of the + * encrypted data. The associated data must be in its own scatterlist element + * in both chains so that we can skip over it. + */ +static int rfc8009_decrypt(struct aead_request *req) +{ + struct skcipher_request *ci; + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + struct krb5_enctype *krb5 = crypto_krb5_enctype(tfm); + struct scatterlist *src = req->src, *dst = req->dst; + struct shash_desc *desc; + struct krb5_ctx *ctx = crypto_krb5_ctx(tfm); + size_t bsize, src_len, dst_len, enc_len, msg_len = req->cryptlen, done; + void *buffer; + int ret, nr_src, nr_dst; + u8 *cksum, *cksum2, *iv; + + src_len = sg_count(src, &nr_src); + dst_len = sg_count(dst, &nr_dst); + + if (WARN_ON(msg_len < krb5->conf_len + krb5->cksum_len) || + WARN_ON(src_len < msg_len) || + WARN_ON(dst_len < msg_len - krb5->cksum_len)) + return -EINVAL; + + /* The integrity checksum is right up against the end. */ + enc_len = msg_len - krb5->cksum_len; + + bsize = krb5_shash_size(ctx->Ki) + + krb5_digest_size(ctx->Ki) * 2 + + krb5_sync_skcipher_size(ctx->Ke) + + krb5_sync_skcipher_ivsize(ctx->Ke); + buffer = kzalloc(bsize, GFP_NOFS); + if (!buffer) + return -ENOMEM; + + cksum = buffer + + krb5_shash_size(ctx->Ki); + cksum2 = buffer + + krb5_shash_size(ctx->Ki) + + krb5_digest_size(ctx->Ki); + ci = buffer + + krb5_shash_size(ctx->Ki) + + krb5_digest_size(ctx->Ki) * 2; + iv = buffer + + krb5_shash_size(ctx->Ki) + + krb5_digest_size(ctx->Ki) * 2 + + krb5_sync_skcipher_size(ctx->Ke); + + /* Calculate the checksum using key Ki */ + desc = buffer; + desc->tfm = ctx->Ki; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + + ret = crypto_shash_update(desc, iv, crypto_sync_skcipher_ivsize(ctx->Ke)); + if (ret < 0) + goto error; + + ret = crypto_shash_update_sg(desc, src, 0, enc_len); + if (ret < 0) + goto error; + + ret = crypto_shash_final(desc, cksum); + if (ret < 0) + goto error; + + /* Get the checksum from the buffer. */ + ret = -EFAULT; + done = sg_pcopy_to_buffer(src, nr_src, cksum2, krb5->cksum_len, enc_len); + if (done != krb5->cksum_len) + goto error; + + ret = -EPROTO; + if (memcmp(cksum, cksum2, krb5->cksum_len) != 0) + goto error; + + /* Decrypt the secure region with key Ke. */ + skcipher_request_set_sync_tfm(ci, ctx->Ke); + skcipher_request_set_callback(ci, 0, NULL, NULL); + skcipher_request_set_crypt(ci, src, dst, enc_len, iv); + ret = crypto_skcipher_decrypt(ci); + if (ret < 0) + goto error; + + ret = 0; +error: + kfree_sensitive(buffer); + return ret; +} + +int rfc8009_aead_encrypt(struct aead_request *req) +{ + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + struct krb5_ctx *ctx = crypto_krb5_ctx(tfm); + + if (ctx->Ke) + return rfc8009_encrypt(req); + return rfc3961_get_mic(req); +} + +int rfc8009_aead_decrypt(struct aead_request *req) +{ + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + struct krb5_ctx *ctx = crypto_krb5_ctx(tfm); + + if (ctx->Ke) + return rfc8009_decrypt(req); + return rfc3961_verify_mic(req); +} + +const struct krb5_crypto_profile rfc8009_crypto_profile = { + .calc_PRF = rfc8009_calc_PRF, + .calc_Kc = rfc8009_calc_Ki, + .calc_Ke = rfc8009_calc_Ke, + .calc_Ki = rfc8009_calc_Ki, +}; diff --git a/crypto/krb5/selftest.c b/crypto/krb5/selftest.c new file mode 100644 index 000000000000..b22bd8d00d1c --- /dev/null +++ b/crypto/krb5/selftest.c @@ -0,0 +1,533 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Kerberos library self-testing + * + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include "internal.h" + +#define VALID(X) \ + ({ \ + bool __x = (X); \ + if (__x) { \ + pr_warn("!!! TESTINVAL %s:%u\n", __FILE__, __LINE__); \ + ret = -EBADMSG; \ + } \ + __x; \ + }) + +#define CHECK(X) \ + ({ \ + bool __x = (X); \ + if (__x) { \ + pr_warn("!!! TESTFAIL %s:%u\n", __FILE__, __LINE__); \ + ret = -EBADMSG; \ + } \ + __x; \ + }) + +enum which_key { + TEST_KC, TEST_KE, TEST_KI, +}; + +static int prep_buf(struct krb5_buffer *buf) +{ + buf->data = kmalloc(buf->len, GFP_KERNEL); + if (!buf->data) + return -ENOMEM; + return 0; +} + +#define PREP_BUF(BUF, LEN) \ + do { \ + (BUF)->len = (LEN); \ + ret = prep_buf((BUF)); \ + if (ret < 0) \ + goto out; \ + } while (0) + +static int load_buf(struct krb5_buffer *buf, const char *from) +{ + size_t len = strlen(from); + int ret; + + if (len > 1 && from[0] == '\'') { + PREP_BUF(buf, len - 1); + memcpy(buf->data, from + 1, len - 1); + ret = 0; + goto out; + } + + if (VALID(len & 1)) + return -EINVAL; + + PREP_BUF(buf, len / 2); + ret = hex2bin(buf->data, from, buf->len); + if (ret < 0) { + VALID(1); + goto out; + } +out: + return ret; +} + +#define LOAD_BUF(BUF, FROM) do { ret = load_buf(BUF, FROM); if (ret < 0) goto out; } while (0) + +static void clear_buf(struct krb5_buffer *buf) +{ + kfree(buf->data); + buf->len = 0; + buf->data = NULL; +} + +/* + * Perform a pseudo-random function check. + */ +static int krb5_test_one_prf(const struct krb5_prf_test *test) +{ + const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype); + struct krb5_buffer key = {}, octet = {}, result = {}, prf = {}; + int ret; + + if (!krb5) + return -EOPNOTSUPP; + + pr_notice("Running %s %s\n", krb5->name, test->name); + + LOAD_BUF(&key, test->key); + LOAD_BUF(&octet, test->octet); + LOAD_BUF(&prf, test->prf); + PREP_BUF(&result, krb5->prf_len); + + if (VALID(result.len != prf.len)) { + ret = -EINVAL; + goto out; + } + + ret = krb5->profile->calc_PRF(krb5, &key, &octet, &result, GFP_KERNEL); + if (ret < 0) { + CHECK(1); + pr_warn("PRF calculation failed %d\n", ret); + goto out; + } + + if (memcmp(result.data, prf.data, result.len) != 0) { + CHECK(1); + ret = -EKEYREJECTED; + goto out; + } + + ret = 0; + +out: + clear_buf(&result); + clear_buf(&octet); + clear_buf(&key); + return ret; +} + +/* + * Perform a key derivation check. + */ +static int krb5_test_key(const struct krb5_enctype *krb5, + const struct krb5_buffer *base_key, + const struct krb5_key_test_one *test, + enum which_key which) +{ + struct krb5_buffer key = {}, result = {}; + int ret; + + LOAD_BUF(&key, test->key); + PREP_BUF(&result, key.len); + + switch (which) { + case TEST_KC: + ret = krb5_derive_Kc(krb5, base_key, test->use, &result, GFP_KERNEL); + break; + case TEST_KE: + ret = krb5_derive_Ke(krb5, base_key, test->use, &result, GFP_KERNEL); + break; + case TEST_KI: + ret = krb5_derive_Ki(krb5, base_key, test->use, &result, GFP_KERNEL); + break; + default: + VALID(1); + ret = -EINVAL; + goto out; + } + + if (ret < 0) { + CHECK(1); + pr_warn("Key derivation failed %d\n", ret); + goto out; + } + + if (memcmp(result.data, key.data, result.len) != 0) { + CHECK(1); + ret = -EKEYREJECTED; + goto out; + } + +out: + clear_buf(&key); + clear_buf(&result); + return ret; +} + +static int krb5_test_one_key(const struct krb5_key_test *test) +{ + const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype); + struct krb5_buffer base_key = {}; + int ret; + + if (!krb5) + return -EOPNOTSUPP; + + pr_notice("Running %s %s\n", krb5->name, test->name); + + LOAD_BUF(&base_key, test->key); + + ret = krb5_test_key(krb5, &base_key, &test->Kc, TEST_KC); + if (ret < 0) + goto out; + ret = krb5_test_key(krb5, &base_key, &test->Ke, TEST_KE); + if (ret < 0) + goto out; + ret = krb5_test_key(krb5, &base_key, &test->Ki, TEST_KI); + if (ret < 0) + goto out; + +out: + clear_buf(&base_key); + return ret; +} + +/* + * Perform an encryption test. + */ +static int krb5_test_one_enc(const struct krb5_enc_test *test, void *buf) +{ + const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype); + struct aead_request *req = NULL; + struct crypto_aead *ci = NULL; + struct krb5_buffer key = {}, conf = {}, plain = {}, ct = {}; + struct scatterlist sg[1]; + size_t reqsize, data_len, data_offset, message_len; + int ret; + + if (!krb5) + return -EOPNOTSUPP; + + pr_notice("Running %s %s\n", krb5->name, test->name); + + /* Load the test data into binary buffers. */ + LOAD_BUF(&key, test->key); + LOAD_BUF(&conf, test->conf); + LOAD_BUF(&plain, test->plain); + LOAD_BUF(&ct, test->ct); + + if (VALID(conf.len != krb5->conf_len) || + VALID(ct.len != krb5->conf_len + plain.len + krb5->cksum_len)) + return ret; + + data_len = plain.len; + message_len = crypto_krb5_how_much_buffer(krb5, KRB5_ENCRYPT_MODE, + data_len, &data_offset); + + if (CHECK(message_len != ct.len)) { + pr_warn("Encrypted length mismatch %zu != %u\n", message_len, ct.len); + goto out; + } + + memcpy(buf + data_offset, plain.data, plain.len); + + sg_init_one(sg, buf, message_len); + ret = crypto_krb5_confound_buffer(krb5, sg, 1, conf.data, conf.len, 0); + if (ret < 0) { + pr_err("Couldn't confound buffer %s: %d\n", krb5->aead.base.cra_name, ret); + goto out; + } + + /* Allocate a crypto object and set its key. */ + ci = crypto_alloc_aead(krb5->aead.base.cra_name, 0, 0); + if (IS_ERR(ci)) { + ret = (PTR_ERR(ci) == -ENOENT) ? -ENOPKG : PTR_ERR(ci); + ci = NULL; + pr_err("Couldn't alloc AEAD %s: %d\n", krb5->aead.base.cra_name, ret); + goto out; + } + + ret = crypto_aead_setkey(ci, key.data, key.len); + if (ret < 0) { + pr_err("Couldn't set AEAD key %s: %d\n", krb5->aead.base.cra_name, ret); + goto out; + } + + /* Generate an encryption request. */ + reqsize = crypto_roundup(sizeof(*req) + crypto_aead_reqsize(ci)); + req = kzalloc(reqsize, GFP_KERNEL); + if (!req) + goto out; + + aead_request_set_tfm(req, ci); + + sg_init_one(sg, buf, message_len); + aead_request_set_crypt(req, sg, sg, data_offset + data_len, NULL); + + ret = crypto_aead_encrypt(req); + if (ret < 0) { + CHECK(1); + pr_warn("Encryption failed %d\n", ret); + goto out; + } + + if (memcmp(buf, ct.data, ct.len) != 0) { + CHECK(1); + pr_warn("Ciphertext mismatch\n"); + pr_warn("BUF %*phN\n", ct.len, buf); + pr_warn("CT %*phN\n", ct.len, ct.data); + ret = -EKEYREJECTED; + goto out; + } + + /* Generate a decryption request. */ + memset(req, 0, reqsize); + aead_request_set_tfm(req, ci); + + sg_init_one(sg, buf, message_len); + aead_request_set_crypt(req, sg, sg, message_len, NULL); + + ret = crypto_aead_decrypt(req); + if (ret < 0) { + CHECK(1); + pr_warn("Decryption failed %d\n", ret); + goto out; + } + + data_offset = 0; + data_len = message_len; + crypto_krb5_where_is_the_data(krb5, KRB5_ENCRYPT_MODE, + &data_offset, &data_len); + + if (CHECK(data_offset != conf.len) || + CHECK(data_len != plain.len)) + goto out; + + if (memcmp(buf, conf.data, conf.len) != 0) { + CHECK(1); + pr_warn("Confounder mismatch\n"); + pr_warn("ENC %*phN\n", conf.len, buf); + pr_warn("DEC %*phN\n", conf.len, conf.data); + ret = -EKEYREJECTED; + goto out; + } + + if (memcmp(buf + conf.len, plain.data, plain.len) != 0) { + CHECK(1); + pr_warn("Plaintext mismatch\n"); + pr_warn("BUF %*phN\n", plain.len, buf + conf.len); + pr_warn("PT %*phN\n", plain.len, plain.data); + ret = -EKEYREJECTED; + goto out; + } + + ret = 0; + +out: + clear_buf(&ct); + clear_buf(&plain); + clear_buf(&conf); + clear_buf(&key); + aead_request_free(req); + if (ci) + crypto_free_aead(ci); + return ret; +} + +/* + * Perform a checksum test. + */ +static int krb5_test_one_mic(const struct krb5_mic_test *test, void *buf) +{ + const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype); + struct aead_request *req = NULL; + struct crypto_aead *ci = NULL; + struct krb5_buffer key = {}, plain = {}, mic = {}; + struct scatterlist sg[2]; + size_t reqsize, data_len, data_offset, message_len; + int ret; + + if (!krb5) + return -EOPNOTSUPP; + + pr_notice("Running %s %s\n", krb5->name, test->name); + + /* Load the test data into binary buffers. */ + LOAD_BUF(&key, test->key); + LOAD_BUF(&plain, test->plain); + LOAD_BUF(&mic, test->mic); + + if (VALID(mic.len != krb5->cksum_len)) + return ret; + + data_len = plain.len; + message_len = crypto_krb5_how_much_buffer(krb5, KRB5_CHECKSUM_MODE, + data_len, &data_offset); + + if (CHECK(message_len != mic.len + plain.len)) { + pr_warn("MIC length mismatch %zu != %u\n", + message_len, mic.len + plain.len); + goto out; + } + + memcpy(buf + data_offset, plain.data, plain.len); + + /* Allocate a crypto object and set its key. */ + ci = crypto_alloc_aead(krb5->aead.base.cra_name, 0, 0); + if (IS_ERR(ci)) { + ret = (PTR_ERR(ci) == -ENOENT) ? -ENOPKG : PTR_ERR(ci); + ci = NULL; + pr_err("Couldn't alloc AEAD %s: %d\n", krb5->aead.base.cra_name, ret); + goto out; + } + + ret = crypto_aead_setkey(ci, key.data, key.len); + if (ret < 0) { + pr_err("Couldn't set AEAD key %s: %d\n", krb5->aead.base.cra_name, ret); + goto out; + } + + /* Generate an encryption request. */ + reqsize = sizeof(*req) + crypto_aead_reqsize(ci); + req = kzalloc(reqsize, GFP_KERNEL); + if (!req) + goto out; + + aead_request_set_tfm(req, ci); + + sg_init_one(sg, buf, 1024); + aead_request_set_crypt(req, sg, sg, data_offset + data_len, NULL); + + ret = crypto_aead_encrypt(req); + if (ret < 0) { + CHECK(1); + pr_warn("Get MIC failed %d\n", ret); + goto out; + } + + if (memcmp(buf, mic.data, mic.len) != 0) { + CHECK(1); + pr_warn("MIC mismatch\n"); + pr_warn("BUF %*phN\n", mic.len, buf); + pr_warn("MIC %*phN\n", mic.len, mic.data); + ret = -EKEYREJECTED; + goto out; + } + + /* Generate a decryption request. */ + memset(req, 0, reqsize); + + aead_request_set_tfm(req, ci); + + sg_init_one(sg, buf, message_len); + aead_request_set_crypt(req, sg, sg, message_len, NULL); + + ret = crypto_aead_decrypt(req); + if (ret < 0) { + CHECK(1); + pr_warn("Verify MIC failed %d\n", ret); + goto out; + } + + data_offset = 0; + data_len = message_len; + crypto_krb5_where_is_the_data(krb5, KRB5_CHECKSUM_MODE, + &data_offset, &data_len); + + if (CHECK(data_offset != mic.len) || + CHECK(data_len != plain.len)) + goto out; + + if (memcmp(buf + data_offset, plain.data, plain.len) != 0) { + CHECK(1); + pr_warn("Plaintext mismatch\n"); + pr_warn("BUF %*phN\n", plain.len, buf + data_offset); + pr_warn("PT %*phN\n", plain.len, plain.data); + ret = -EKEYREJECTED; + goto out; + } + + ret = 0; + +out: + clear_buf(&mic); + clear_buf(&plain); + clear_buf(&key); + aead_request_free(req); + if (ci) + crypto_free_aead(ci); + return ret; +} + +int krb5_selftest(void) +{ + void *buf; + int ret = 0, i; + + buf = kmalloc(4096, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pr_notice("\n"); + pr_notice("Running selftests\n"); + + for (i = 0; krb5_prf_tests[i].name; i++) { + ret = krb5_test_one_prf(&krb5_prf_tests[i]); + if (ret < 0) { + if (ret != -EOPNOTSUPP) + goto out; + pr_notice("Skipping %s\n", krb5_prf_tests[i].name); + } + } + + for (i = 0; krb5_key_tests[i].name; i++) { + ret = krb5_test_one_key(&krb5_key_tests[i]); + if (ret < 0) { + if (ret != -EOPNOTSUPP) + goto out; + pr_notice("Skipping %s\n", krb5_key_tests[i].name); + } + } + + for (i = 0; krb5_enc_tests[i].name; i++) { + memset(buf, 0x5a, 4096); + ret = krb5_test_one_enc(&krb5_enc_tests[i], buf); + if (ret < 0) { + if (ret != -EOPNOTSUPP) + goto out; + pr_notice("Skipping %s\n", krb5_enc_tests[i].name); + } + } + + for (i = 0; krb5_mic_tests[i].name; i++) { + memset(buf, 0x5a, 4096); + ret = krb5_test_one_mic(&krb5_mic_tests[i], buf); + if (ret < 0) { + if (ret != -EOPNOTSUPP) + goto out; + pr_notice("Skipping %s\n", krb5_mic_tests[i].name); + } + } + + ret = 0; +out: + pr_notice("Selftests %s\n", ret == 0 ? "succeeded" : "failed"); + kfree(buf); + return ret; +} diff --git a/crypto/krb5/selftest_data.c b/crypto/krb5/selftest_data.c new file mode 100644 index 000000000000..7c35b1d7f4a2 --- /dev/null +++ b/crypto/krb5/selftest_data.c @@ -0,0 +1,370 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Data for Kerberos library self-testing + * + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "internal.h" + +/* + * Pseudo-random function tests. + */ +const struct krb5_prf_test krb5_prf_tests[] = { + /* rfc8009 Appendix A */ + { + .etype = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128, + .name = "prf", + .key = "3705D96080C17728A0E800EAB6E0D23C", + .octet = "74657374", + .prf = "9D188616F63852FE86915BB840B4A886FF3E6BB0F819B49B893393D393854295", + }, { + .etype = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192, + .name = "prf", + .key = "6D404D37FAF79F9DF0D33568D320669800EB4836472EA8A026D16B7182460C52", + .octet = "74657374", + .prf = + "9801F69A368C2BF675E59521E177D9A07F67EFE1CFDE8D3C8D6F6A0256E3B17D" + "B3C1B62AD1B8553360D17367EB1514D2", + }, + {/* END */} +}; + +/* + * Key derivation tests. + */ +const struct krb5_key_test krb5_key_tests[] = { + /* rfc8009 Appendix A */ + { + .etype = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128, + .name = "key", + .key = "3705D96080C17728A0E800EAB6E0D23C", + .Kc.use = 0x00000002, + .Kc.key = "B31A018A48F54776F403E9A396325DC3", + .Ke.use = 0x00000002, + .Ke.key = "9B197DD1E8C5609D6E67C3E37C62C72E", + .Ki.use = 0x00000002, + .Ki.key = "9FDA0E56AB2D85E1569A688696C26A6C", + }, { + .etype = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192, + .name = "key", + .key = "6D404D37FAF79F9DF0D33568D320669800EB4836472EA8A026D16B7182460C52", + .Kc.use = 0x00000002, + .Kc.key = "EF5718BE86CC84963D8BBB5031E9F5C4BA41F28FAF69E73D", + .Ke.use = 0x00000002, + .Ke.key = "56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49", + .Ki.use = 0x00000002, + .Ki.key = "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F", + }, + /* rfc6803 sec 10 */ + { + .etype = KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC, + .name = "key", + .key = "57D0297298FFD9D35DE5A47FB4BDE24B", + .Kc.use = 0x00000002, + .Kc.key = "D155775A209D05F02B38D42A389E5A56", + .Ke.use = 0x00000002, + .Ke.key = "64DF83F85A532F17577D8C37035796AB", + .Ki.use = 0x00000002, + .Ki.key = "3E4FBDF30FB8259C425CB6C96F1F4635", + }, + { + .etype = KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC, + .name = "key", + .key = "B9D6828B2056B7BE656D88A123B1FAC68214AC2B727ECF5F69AFE0C4DF2A6D2C", + .Kc.use = 0x00000002, + .Kc.key = "E467F9A9552BC7D3155A6220AF9C19220EEED4FF78B0D1E6A1544991461A9E50", + .Ke.use = 0x00000002, + .Ke.key = "412AEFC362A7285FC3966C6A5181E7605AE675235B6D549FBFC9AB6630A4C604", + .Ki.use = 0x00000002, + .Ki.key = "FA624FA0E523993FA388AEFDC67E67EBCD8C08E8A0246B1D73B0D1DD9FC582B0", + }, + {/* END */} +}; + +/* + * Encryption tests. + */ +const struct krb5_enc_test krb5_enc_tests[] = { + /* rfc8009 Appendix A */ + { + .etype = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128, + .name = "enc no plain", + .plain = "", + .conf = "7E5895EAF2672435BAD817F545A37148", + .key = + "00000003" // KRB5_ENCRYPT_MODE_KEKI + "00000000" // Usage + "9B197DD1E8C5609D6E67C3E37C62C72E" // Ke + "9FDA0E56AB2D85E1569A688696C26A6C", // Ki + .ct = + "EF85FB890BB8472F4DAB20394DCA781DAD877EDA39D50C870C0D5A0A8E48C718", + }, { + .etype = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128, + .name = "enc plain +#include + +/* + * Per Kerberos v5 protocol spec crypto types from the wire. These get mapped + * to linux kernel crypto routines. */ #define KRB5_ENCTYPE_NULL 0x0000 #define KRB5_ENCTYPE_DES_CBC_CRC 0x0001 /* DES cbc mode with CRC-32 */ @@ -23,8 +27,12 @@ #define KRB5_ENCTYPE_DES3_CBC_SHA1 0x0010 #define KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96 0x0011 #define KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96 0x0012 +#define KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128 0x0013 +#define KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192 0x0014 #define KRB5_ENCTYPE_ARCFOUR_HMAC 0x0017 #define KRB5_ENCTYPE_ARCFOUR_HMAC_EXP 0x0018 +#define KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC 0x0019 +#define KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC 0x001a #define KRB5_ENCTYPE_UNKNOWN 0x01ff #define KRB5_CKSUMTYPE_CRC32 0x0001 @@ -37,6 +45,10 @@ #define KRB5_CKSUMTYPE_HMAC_SHA1_DES3 0x000c #define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128 0x000f #define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256 0x0010 +#define KRB5_CKSUMTYPE_CMAC_CAMELLIA128 0x0011 +#define KRB5_CKSUMTYPE_CMAC_CAMELLIA256 0x0012 +#define KRB5_CKSUMTYPE_HMAC_SHA256_128_AES128 0x0013 +#define KRB5_CKSUMTYPE_HMAC_SHA384_192_AES256 0x0014 #define KRB5_CKSUMTYPE_HMAC_MD5_ARCFOUR -138 /* Microsoft md5 hmac cksumtype */ /* @@ -47,4 +59,89 @@ #define KEY_USAGE_SEED_ENCRYPTION (0xAA) #define KEY_USAGE_SEED_INTEGRITY (0x55) +/* + * Mode of operation. + */ +enum krb5_crypto_mode { + KRB5_CHECKSUM_MODE, /* Checksum only */ + KRB5_ENCRYPT_MODE, /* Fully encrypted, possibly with integrity checksum */ + KRB5_CHECKSUM_MODE_KC, /* Checksum only, keys Kc supplied directly */ + KRB5_ENCRYPT_MODE_KEKI, /* Fully encrypted, keys Ke and Ki supplied directly */ +}; + +struct krb5_buffer { + unsigned int len; + void *data; +}; + +/* + * Kerberos encoding type definition. + */ +struct krb5_enctype { + struct aead_alg aead; /* AEAD API */ + int etype; /* Encryption (key) type */ + int ctype; /* Checksum type */ + const char *name; /* "Friendly" name */ + const char *encrypt_name; /* Crypto encrypt name */ + const char *cksum_name; /* Crypto checksum name */ + const char *hash_name; /* Crypto hash name */ + u16 block_len; /* Length of encryption block */ + u16 conf_len; /* Length of confounder (normally == block_len) */ + u16 cksum_len; /* Length of checksum */ + u16 key_bytes; /* Length of raw key, in bytes */ + u16 key_len; /* Length of final key, in bytes */ + u16 hash_len; /* Length of hash in bytes */ + u16 prf_len; /* Length of PRF() result in bytes */ + u16 Kc_len; /* Length of Kc in bytes */ + u16 Ke_len; /* Length of Ke in bytes */ + u16 Ki_len; /* Length of Ki in bytes */ + bool keyed_cksum; /* T if a keyed cksum */ + + const struct krb5_crypto_profile *profile; + + int (*random_to_key)(const struct krb5_enctype *krb5, + const struct krb5_buffer *in, + struct krb5_buffer *out); /* complete key generation */ +}; + +/** + * crypto_krb5_enctype - Find the encoding type definition from the algorithm + * @tfm: The algorithm to query + */ +static inline struct krb5_enctype *crypto_krb5_enctype(const struct crypto_aead *tfm) +{ + struct aead_alg *alg = crypto_aead_alg((struct crypto_aead *)tfm); + + return container_of(alg, struct krb5_enctype, aead); +} + +/* + * krb5_aead.c + */ +const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype); + +size_t crypto_krb5_how_much_buffer(const struct krb5_enctype *krb5, + enum krb5_crypto_mode mode, + size_t data_size, size_t *_offset); +size_t crypto_krb5_how_much_data(const struct krb5_enctype *krb5, + enum krb5_crypto_mode mode, + size_t *_buffer_size, size_t *_offset); +void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, + enum krb5_crypto_mode mode, + size_t *_offset, size_t *_len); +int crypto_krb5_confound_buffer(const struct krb5_enctype *krb5, + struct scatterlist *sg, unsigned int nr_sg, + const u8 *confounder, size_t conf_len, + size_t msg_offset); + +/* + * kdf.c + */ +int crypto_krb5_calc_PRFplus(const struct krb5_enctype *krb5, + const struct krb5_buffer *K, + unsigned int L, + const struct krb5_buffer *S, + struct krb5_buffer *result, + gfp_t gfp); + #endif /* _CRYPTO_KRB5_H */ From patchwork Fri Jan 10 01:03:05 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 13933363 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A1B461E25EC for ; Fri, 10 Jan 2025 01:03:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736471034; cv=none; b=PlXHVBjSuAcpXwSMkoOw3KJ8UMa0O8rMN843BQMRyVe1BkrmQRVSvvE5Km0O5CXab7y23AXN+L1820qdyVotaG3QNdxIiClCX+YsQZwEWN26PyYeclFTQJFSTzKJrQJV/1w/K7dK2diJ8FS+p8PeTDZ4ANthtAmMTpnjR8TwJY8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736471034; c=relaxed/simple; bh=RyQRSs0XcRS5II/f+B+N88e2b+kw8Oildn+CZsT9+ow=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=oj5R+w6A+A4zwfIzKitEWDYtE+ZiC26RsiIOEp8POg0HksUwvVdfRfrvVZh5NZyV78RNnhMdFlCp0lA2FuKA26mveoUmpR/Z/S5ZMgAXuowaDr4ECWo62R8HItaCa24cwYqoSfBVX7LAJPvQuAYDIi0TD2IrZ7t/UlwGozMUwF4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=f8X4HPcW; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="f8X4HPcW" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1736471029; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Fa1OBjLx4mDzEeT5HD0qu7BJLifxbXY/Qt37I0Y8vQE=; b=f8X4HPcWl9M8t7i2gJMGwGbLDHxCEMUFWDo53MN0FoZ2wF23M0cA+3Obea8eibRq4m4wWR JlhR4H1knCnqy/e6qFYwpNeH+6fPJNlulmJLumIWGfKiq6J8mt1dkDD59CtXquzQjmvzXI VQ9CsSdrMXWgTVt7gecfI/sqdI3jHQE= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-7-mEdFelAqM-yIqWsb8HnyaA-1; Thu, 09 Jan 2025 20:03:44 -0500 X-MC-Unique: mEdFelAqM-yIqWsb8HnyaA-1 X-Mimecast-MFC-AGG-ID: mEdFelAqM-yIqWsb8HnyaA Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 720361956056; Fri, 10 Jan 2025 01:03:40 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.42.28.12]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 2814C1955BE3; Fri, 10 Jan 2025 01:03:35 +0000 (UTC) From: David Howells To: Herbert Xu , Chuck Lever Cc: David Howells , Trond Myklebust , "David S. Miller" , Marc Dionne , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 3/8] crypto/krb5: Test manager data Date: Fri, 10 Jan 2025 01:03:05 +0000 Message-ID: <20250110010313.1471063-4-dhowells@redhat.com> In-Reply-To: <20250110010313.1471063-1-dhowells@redhat.com> References: <20250110010313.1471063-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Add Kerberos crypto tests to the test manager database. This covers: camellia128-cts-cmac samples from RFC6803 camellia256-cts-cmac samples from RFC6803 aes128-cts-hmac-sha256-128 samples from RFC8009 aes256-cts-hmac-sha384-192 samples from RFC8009 but not: aes128-cts-hmac-sha1-96 aes256-cts-hmac-sha1-96 as the test samples in RFC3962 don't seem to be suitable. Note that the test manager makes some assumptions about AEAD algorithm type that would otherwise prevent testing the kerberos algorithms. The problem is that the test manager assumes that if there's a difference between the length of the plain text and the cipher text supplied in the test vector, then the extra data is the authentication tag - but these aren't applicable here. This is worked around by adding a flag in the AEAD algorithm definition that causes EINVAL be returned unconditionally if anyone tries to set the auth tag length. Signed-off-by: David Howells cc: Herbert Xu cc: "David S. Miller" cc: Chuck Lever cc: Marc Dionne cc: Eric Dumazet cc: Jakub Kicinski cc: Paolo Abeni cc: Simon Horman cc: linux-afs@lists.infradead.org cc: linux-nfs@vger.kernel.org cc: linux-crypto@vger.kernel.org cc: netdev@vger.kernel.org --- crypto/aead.c | 2 + crypto/krb5/krb5_aead.c | 8 +- crypto/testmgr.c | 24 +++ crypto/testmgr.h | 456 ++++++++++++++++++++++++++++++++++++++++ include/crypto/aead.h | 2 + 5 files changed, 491 insertions(+), 1 deletion(-) diff --git a/crypto/aead.c b/crypto/aead.c index cade532413bf..fb23557cd09a 100644 --- a/crypto/aead.c +++ b/crypto/aead.c @@ -65,6 +65,8 @@ int crypto_aead_setauthsize(struct crypto_aead *tfm, unsigned int authsize) { int err; + if (crypto_aead_alg(tfm)->no_authtags) + return -EINVAL; if ((!authsize && crypto_aead_maxauthsize(tfm)) || authsize > crypto_aead_maxauthsize(tfm)) return -EINVAL; diff --git a/crypto/krb5/krb5_aead.c b/crypto/krb5/krb5_aead.c index 2c8b3921e976..453b16a17ca9 100644 --- a/crypto/krb5/krb5_aead.c +++ b/crypto/krb5/krb5_aead.c @@ -201,6 +201,7 @@ static struct krb5_enctype krb5_enctypes[] = { .aead.ivsize = 0, .aead.maxauthsize = 0, .aead.chunksize = 16, + .aead.no_authtags = true, .aead.base.cra_flags = CRYPTO_ALG_ALLOCATES_MEMORY, .aead.base.cra_blocksize = 1, @@ -238,6 +239,7 @@ static struct krb5_enctype krb5_enctypes[] = { .aead.ivsize = 0, .aead.maxauthsize = 0, .aead.chunksize = 16, + .aead.no_authtags = true, .aead.base.cra_flags = CRYPTO_ALG_ALLOCATES_MEMORY, .aead.base.cra_blocksize = 1, @@ -275,6 +277,7 @@ static struct krb5_enctype krb5_enctypes[] = { .aead.ivsize = 0, .aead.maxauthsize = 0, .aead.chunksize = 16, + .aead.no_authtags = true, .aead.base.cra_flags = CRYPTO_ALG_ALLOCATES_MEMORY, .aead.base.cra_blocksize = 1, @@ -312,6 +315,7 @@ static struct krb5_enctype krb5_enctypes[] = { .aead.ivsize = 0, .aead.maxauthsize = 0, .aead.chunksize = 16, + .aead.no_authtags = true, .aead.base.cra_flags = CRYPTO_ALG_ALLOCATES_MEMORY, .aead.base.cra_blocksize = 1, @@ -349,6 +353,7 @@ static struct krb5_enctype krb5_enctypes[] = { .aead.ivsize = 0, .aead.maxauthsize = 0, .aead.chunksize = 16, + .aead.no_authtags = true, .aead.base.cra_flags = CRYPTO_ALG_ALLOCATES_MEMORY, .aead.base.cra_blocksize = 1, @@ -356,7 +361,7 @@ static struct krb5_enctype krb5_enctypes[] = { .aead.base.cra_alignmask = 0, .aead.base.cra_priority = 100, .aead.base.cra_name = "krb5-aes128-cts-hmac-sha256-128", - .aead.base.cra_driver_name = "krb5-aes128-cts-hmac-sha256-128generic", + .aead.base.cra_driver_name = "krb5-aes128-cts-hmac-sha256-128-generic", .aead.base.cra_module = THIS_MODULE, }, { .etype = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192, @@ -386,6 +391,7 @@ static struct krb5_enctype krb5_enctypes[] = { .aead.ivsize = 0, .aead.maxauthsize = 0, .aead.chunksize = 16, + .aead.no_authtags = true, .aead.base.cra_flags = CRYPTO_ALG_ALLOCATES_MEMORY, .aead.base.cra_blocksize = 1, diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 1f5f48ab18c7..6e4cf8427e4e 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -5408,6 +5408,30 @@ static const struct alg_test_desc alg_test_descs[] = { .alg = "jitterentropy_rng", .fips_allowed = 1, .test = alg_test_null, + }, { + .alg = "krb5-aes128-cts-hmac-sha256-128", + .test = alg_test_aead, + .suite = { + .aead = __VECS(krb5_test_aes128_cts_hmac_sha256_128) + } + }, { + .alg = "krb5-aes256-cts-hmac-sha384-192", + .test = alg_test_aead, + .suite = { + .aead = __VECS(krb5_test_aes256_cts_hmac_sha384_192) + } + }, { + .alg = "krb5-camellia128-cts-cmac", + .test = alg_test_aead, + .suite = { + .aead = __VECS(krb5_test_camellia128_cts_cmac) + } + }, { + .alg = "krb5-camellia256-cts-cmac", + .test = alg_test_aead, + .suite = { + .aead = __VECS(krb5_test_camellia256_cts_cmac) + } }, { .alg = "kw(aes)", .test = alg_test_skcipher, diff --git a/crypto/testmgr.h b/crypto/testmgr.h index 430d33d9ac13..12c550248a15 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -39086,4 +39086,460 @@ static const struct cipher_testvec aes_hctr2_tv_template[] = { }; +static const struct aead_testvec krb5_test_aes128_cts_hmac_sha256_128[] = { + /* rfc8009 Appendix A */ + { + /* "enc no plain" */ + .key = + "\x00\x00\x00\x03" // KRB5_ENCRYPT_MODE_KEKI + "\x00\x00\x00\x00" // Usage + "\x9B\x19\x7D\xD1\xE8\xC5\x60\x9D\x6E\x67\xC3\xE3\x7C\x62\xC7\x2E" // Ke + "\x9F\xDA\x0E\x56\xAB\x2D\x85\xE1\x56\x9A\x68\x86\x96\xC2\x6A\x6C", // Ki + .klen = 4 + 4 + 16 + 16, + .ptext = + "\x7E\x58\x95\xEA\xF2\x67\x24\x35\xBA\xD8\x17\xF5\x45\xA3\x71\x48" // Confounder + "", // Plain + .plen = 16 + 0, + .ctext = + "\xEF\x85\xFB\x89\x0B\xB8\x47\x2F\x4D\xAB\x20\x39\x4D\xCA\x78\x1D" + "\xAD\x87\x7E\xDA\x39\xD5\x0C\x87\x0C\x0D\x5A\x0A\x8E\x48\xC7\x18", + .clen = 16 + 0 + 16, + .setauthsize_error = -EINVAL, + }, { + /* "enc plainblock" */ + .key = + "\x00\x00\x00\x03" // KRB5_ENCRYPT_MODE_KEKI + "\x00\x00\x00\x00" // Usage + "\x9B\x19\x7D\xD1\xE8\xC5\x60\x9D\x6E\x67\xC3\xE3\x7C\x62\xC7\x2E" // Ke + "\x9F\xDA\x0E\x56\xAB\x2D\x85\xE1\x56\x9A\x68\x86\x96\xC2\x6A\x6C", // Ki + .klen = 4 + 4 + 16 + 16, + .ptext = + "\xA7\xA4\xE2\x9A\x47\x28\xCE\x10\x66\x4F\xB6\x4E\x49\xAD\x3F\xAC" // Confounder + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x10\x11\x12\x13\x14", // Plain + .plen = 16 + 21, + .ctext = + "\x72\x0F\x73\xB1\x8D\x98\x59\xCD\x6C\xCB\x43\x46\x11\x5C\xD3\x36" + "\xC7\x0F\x58\xED\xC0\xC4\x43\x7C\x55\x73\x54\x4C\x31\xC8\x13\xBC" + "\xE1\xE6\xD0\x72\xC1\x86\xB3\x9A\x41\x3C\x2F\x92\xCA\x9B\x83\x34" + "\xA2\x87\xFF\xCB\xFC", + .clen = 16 + 21 + 16, + .setauthsize_error = -EINVAL, + }, { + // "mic" + .key = + "\x00\x00\x00\x02" // KR5_ENCRYPT_MODE_KC + "\x00\x00\x00\x00" // Usage + "\xB3\x1A\x01\x8A\x48\xF5\x47\x76\xF4\x03\xE9\xA3\x96\x32\x5D\xC3", // Kc + .klen = 4 + 4 + 16, + .ptext = + "\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55" // MIC + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x10\x11\x12\x13\x14", + .plen = 16 + 21, + .ctext = + "\xD7\x83\x67\x18\x66\x43\xD6\x7B\x41\x1C\xBA\x91\x39\xFC\x1D\xEE" // MIC + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x10\x11\x12\x13\x14", + .clen = 16 + 21, + .setauthsize_error = -EINVAL, + } +}; + +static const struct aead_testvec krb5_test_aes256_cts_hmac_sha384_192[] = { + /* rfc8009 Appendix A */ + { + /* "enc no plain" */ + .key = + "\x00\x00\x00\x03" // KRB5_ENCRYPT_MODE_KEKI + "\x00\x00\x00\x00" // Usage + "\x56\xAB\x22\xBE\xE6\x3D\x82\xD7\xBC\x52\x27\xF6\x77\x3F\x8E\xA7" + "\xA5\xEB\x1C\x82\x51\x60\xC3\x83\x12\x98\x0C\x44\x2E\x5C\x7E\x49" // Ke + "\x69\xB1\x65\x14\xE3\xCD\x8E\x56\xB8\x20\x10\xD5\xC7\x30\x12\xB6" + "\x22\xC4\xD0\x0F\xFC\x23\xED\x1F", // Ki + .klen = 4 + 4 + 32 + 24, + .ptext = + "\xF7\x64\xE9\xFA\x15\xC2\x76\x47\x8B\x2C\x7D\x0C\x4E\x5F\x58\xE4" // Confounder + "", // Plain + .plen = 16 + 0, + .ctext = + "\x41\xF5\x3F\xA5\xBF\xE7\x02\x6D\x91\xFA\xF9\xBE\x95\x91\x95\xA0" + "\x58\x70\x72\x73\xA9\x6A\x40\xF0\xA0\x19\x60\x62\x1A\xC6\x12\x74" + "\x8B\x9B\xBF\xBE\x7E\xB4\xCE\x3C", + .clen = 16 + 0 + 24, + .setauthsize_error = -EINVAL, + }, { + /* "enc plainblock" */ + .key = + "\x00\x00\x00\x03" // KRB5_ENCRYPT_MODE_KEKI + "\x00\x00\x00\x00" // Usage + "\x56\xAB\x22\xBE\xE6\x3D\x82\xD7\xBC\x52\x27\xF6\x77\x3F\x8E\xA7" + "\xA5\xEB\x1C\x82\x51\x60\xC3\x83\x12\x98\x0C\x44\x2E\x5C\x7E\x49" // Ke + "\x69\xB1\x65\x14\xE3\xCD\x8E\x56\xB8\x20\x10\xD5\xC7\x30\x12\xB6" + "\x22\xC4\xD0\x0F\xFC\x23\xED\x1F", // Ki + .klen = 4 + 4 + 32 + 24, + .ptext = + "\x76\x3E\x65\x36\x7E\x86\x4F\x02\xF5\x51\x53\xC7\xE3\xB5\x8A\xF1" // Confounder + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x10\x11\x12\x13\x14", // Plain + .plen = 16 + 21, + .ctext = + "\x40\x01\x3E\x2D\xF5\x8E\x87\x51\x95\x7D\x28\x78\xBC\xD2\xD6\xFE" + "\x10\x1C\xCF\xD5\x56\xCB\x1E\xAE\x79\xDB\x3C\x3E\xE8\x64\x29\xF2" + "\xB2\xA6\x02\xAC\x86\xFE\xF6\xEC\xB6\x47\xD6\x29\x5F\xAE\x07\x7A" + "\x1F\xEB\x51\x75\x08\xD2\xC1\x6B\x41\x92\xE0\x1F\x62", + .clen = 16 + 21 + 24, + .setauthsize_error = -EINVAL, + }, { + // "mic" + .key = + "\x00\x00\x00\x02" // KR5_ENCRYPT_MODE_KC + "\x00\x00\x00\x00" // Usage + "\xEF\x57\x18\xBE\x86\xCC\x84\x96\x3D\x8B\xBB\x50\x31\xE9\xF5\xC4" + "\xBA\x41\xF2\x8F\xAF\x69\xE7\x3D", // Kc + .klen = 4 + 4 + 24, + .ptext = + "\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55" + "\xAA\x55\xAA\x55\xAA\x55\xAA\x55" // MIC + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x10\x11\x12\x13\x14", + .plen = 24 + 21, + .ctext = + "\x45\xEE\x79\x15\x67\xEE\xFC\xA3\x7F\x4A\xC1\xE0\x22\x2D\xE8\x0D" + "\x43\xC3\xBF\xA0\x66\x99\x67\x2A" // MIC + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x10\x11\x12\x13\x14", + .clen = 24 + 21, + .setauthsize_error = -EINVAL, + } +}; + +static const struct aead_testvec krb5_test_camellia128_cts_cmac[] = { + /* rfc6803 sec 10 */ + { + // "enc no plain" + .key = + "\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE + "\x00\x00\x00\x00" // Usage + "\x1D\xC4\x6A\x8D\x76\x3F\x4F\x93\x74\x2B\xCB\xA3\x38\x75\x76\xC3", // K0 + .klen = 4 + 4 + 16, + .ptext = + "\xB6\x98\x22\xA1\x9A\x6B\x09\xC0\xEB\xC8\x55\x7D\x1F\x1B\x6C\x0A" // Confounder + "", // Plain + .plen = 16 + 0, + .ctext = + "\xC4\x66\xF1\x87\x10\x69\x92\x1E\xDB\x7C\x6F\xDE\x24\x4A\x52\xDB" + "\x0B\xA1\x0E\xDC\x19\x7B\xDB\x80\x06\x65\x8C\xA3\xCC\xCE\x6E\xB8", + .clen = 16 + 0 + 16, + .setauthsize_error = -EINVAL, + }, { + // "enc 1 plain", + .key = + "\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE + "\x00\x00\x00\x01" // Usage + "\x50\x27\xBC\x23\x1D\x0F\x3A\x9D\x23\x33\x3F\x1C\xA6\xFD\xBE\x7C", // K0 + .klen = 4 + 4 + 16, + .ptext = + "\x6F\x2F\xC3\xC2\xA1\x66\xFD\x88\x98\x96\x7A\x83\xDE\x95\x96\xD9" // Confounder + "1", // Plain + .plen = 16 + 1, + .ctext = + "\x84\x2D\x21\xFD\x95\x03\x11\xC0\xDD\x46\x4A\x3F\x4B\xE8\xD6\xDA" + "\x88\xA5\x6D\x55\x9C\x9B\x47\xD3\xF9\xA8\x50\x67\xAF\x66\x15\x59" + "\xB8", + .clen = 16 + 1 + 16, + .setauthsize_error = -EINVAL, + }, { + // "enc 9 plain", + .key = + "\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE + "\x00\x00\x00\x02" // Usage + "\xA1\xBB\x61\xE8\x05\xF9\xBA\x6D\xDE\x8F\xDB\xDD\xC0\x5C\xDE\xA0", // K0 + .klen = 4 + 4 + 16, + .ptext = + "\xA5\xB4\xA7\x1E\x07\x7A\xEE\xF9\x3C\x87\x63\xC1\x8F\xDB\x1F\x10" // Confounder + "9 bytesss", // Plain + .plen = 16 + 9, + .ctext = + "\x61\x9F\xF0\x72\xE3\x62\x86\xFF\x0A\x28\xDE\xB3\xA3\x52\xEC\x0D" + "\x0E\xDF\x5C\x51\x60\xD6\x63\xC9\x01\x75\x8C\xCF\x9D\x1E\xD3\x3D" + "\x71\xDB\x8F\x23\xAA\xBF\x83\x48\xA0", + .clen = 16 + 9 + 16, + .setauthsize_error = -EINVAL, + }, { + // "enc 13 plain", + .key = + "\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE + "\x00\x00\x00\x03" // Usage + "\x2C\xA2\x7A\x5F\xAF\x55\x32\x24\x45\x06\x43\x4E\x1C\xEF\x66\x76", // K0 + .klen = 4 + 4 + 16, + .ptext = + "\x19\xFE\xE4\x0D\x81\x0C\x52\x4B\x5B\x22\xF0\x18\x74\xC6\x93\xDA" // Confounder + "13 bytes byte", // Plain + .plen = 16 + 13, + .ctext = + "\xB8\xEC\xA3\x16\x7A\xE6\x31\x55\x12\xE5\x9F\x98\xA7\xC5\x00\x20" + "\x5E\x5F\x63\xFF\x3B\xB3\x89\xAF\x1C\x41\xA2\x1D\x64\x0D\x86\x15" + "\xC9\xED\x3F\xBE\xB0\x5A\xB6\xAC\xB6\x76\x89\xB5\xEA", + .clen = 16 + 13 + 16, + .setauthsize_error = -EINVAL, + }, { + // "enc 30 plain", + .key = + "\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE + "\x00\x00\x00\x04" // Usage + "\x78\x24\xF8\xC1\x6F\x83\xFF\x35\x4C\x6B\xF7\x51\x5B\x97\x3F\x43", // K0 + .klen = 4 + 4 + 16, + .ptext = + "\xCA\x7A\x7A\xB4\xBE\x19\x2D\xAB\xD6\x03\x50\x6D\xB1\x9C\x39\xE2" // Confounder + "30 bytes bytes bytes bytes byt", // Plain + .plen = 16 + 30, + .ctext = + "\xA2\x6A\x39\x05\xA4\xFF\xD5\x81\x6B\x7B\x1E\x27\x38\x0D\x08\x09" + "\x0C\x8E\xC1\xF3\x04\x49\x6E\x1A\xBD\xCD\x2B\xDC\xD1\xDF\xFC\x66" + "\x09\x89\xE1\x17\xA7\x13\xDD\xBB\x57\xA4\x14\x6C\x15\x87\xCB\xA4" + "\x35\x66\x65\x59\x1D\x22\x40\x28\x2F\x58\x42\xB1\x05\xA5", + .clen = 16 + 30 + 16, + .setauthsize_error = -EINVAL, + }, { + // "mic abc" + .key = + "\x00\x00\x00\x00" // KR5_ENCRYPT_MODE + "\x00\x00\x00\x07" // Usage + "\x1D\xC4\x6A\x8D\x76\x3F\x4F\x93\x74\x2B\xCB\xA3\x38\x75\x76\xC3", // K0 + .klen = 4 + 4 + 16, + .ptext = + "\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55" // MIC + "abcdefghijk", // Plain + .plen = 16 + 10, + .ctext = + "\x11\x78\xE6\xC5\xC4\x7A\x8C\x1A\xE0\xC4\xB9\xC7\xD4\xEB\x7B\x6B" // MIC + "abcdefghijk", // Plain + .clen = 16 + 10, + .setauthsize_error = -EINVAL, + }, { + // "mic ABC" + .key = + "\x00\x00\x00\x00" // KR5_ENCRYPT_MODE + "\x00\x00\x00\x07" // Usage + "\x50\x27\xBC\x23\x1D\x0F\x3A\x9D\x23\x33\x3F\x1C\xA6\xFD\xBE\x7C", // K0 + .klen = 4 + 4 + 16, + .ptext = + "\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55" // MIC + "ABCDEFGHIJKLMNOPQRSTUVWXYZ", // Plain + .plen = 16 + 26, + .ctext = + "\xD1\xB3\x4F\x70\x04\xA7\x31\xF2\x3A\x0C\x00\xBF\x6C\x3F\x75\x3A" // MIC + "ABCDEFGHIJKLMNOPQRSTUVWXYZ", // Plain + .clen = 16 + 26, + .setauthsize_error = -EINVAL, + } +}; + +static const struct aead_testvec krb5_test_camellia256_cts_cmac[] = { + /* rfc6803 sec 10 */ + { + // "enc no plain", + .key = + "\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE + "\x00\x00\x00\x00" // Usage + "\xB6\x1C\x86\xCC\x4E\x5D\x27\x57\x54\x5A\xD4\x23\x39\x9F\xB7\x03" + "\x1E\xCA\xB9\x13\xCB\xB9\x00\xBD\x7A\x3C\x6D\xD8\xBF\x92\x01\x5B", // K0 + .klen = 4 + 4 + 32, + .ptext = + "\x3C\xBB\xD2\xB4\x59\x17\x94\x10\x67\xF9\x65\x99\xBB\x98\x92\x6C" // Confounder + "", // Plain + .plen = 16 + 1, + .ctext = + "\x03\x88\x6D\x03\x31\x0B\x47\xA6\xD8\xF0\x6D\x7B\x94\xD1\xDD\x83" + "\x7E\xCC\xE3\x15\xEF\x65\x2A\xFF\x62\x08\x59\xD9\x4A\x25\x92\x66", + .clen = 16 + 0 + 16, + .setauthsize_error = -EINVAL, + }, { + // "enc 1 plain", + .key = + "\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE + "\x00\x00\x00\x01" // Usage + "\x1B\x97\xFE\x0A\x19\x0E\x20\x21\xEB\x30\x75\x3E\x1B\x6E\x1E\x77" + "\xB0\x75\x4B\x1D\x68\x46\x10\x35\x58\x64\x10\x49\x63\x46\x38\x33", // K0 + .klen = 4 + 4 + 32, + .ptext = + "\xDE\xF4\x87\xFC\xEB\xE6\xDE\x63\x46\xD4\xDA\x45\x21\xBB\xA2\xD2" // Confounder + "1", // Plain + .plen = 16 + 1, + .ctext = + "\x2C\x9C\x15\x70\x13\x3C\x99\xBF\x6A\x34\xBC\x1B\x02\x12\x00\x2F" + "\xD1\x94\x33\x87\x49\xDB\x41\x35\x49\x7A\x34\x7C\xFC\xD9\xD1\x8A" + "\x12", + .clen = 16 + 1 + 16, + .setauthsize_error = -EINVAL, + }, { + // "enc 9 plain", + .key = + "\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE + "\x00\x00\x00\x02" // Usage + "\x32\x16\x4C\x5B\x43\x4D\x1D\x15\x38\xE4\xCF\xD9\xBE\x80\x40\xFE" + "\x8C\x4A\xC7\xAC\xC4\xB9\x3D\x33\x14\xD2\x13\x36\x68\x14\x7A\x05", // K0 + .klen = 4 + 4 + 32, + .ptext = + "\xAD\x4F\xF9\x04\xD3\x4E\x55\x53\x84\xB1\x41\x00\xFC\x46\x5F\x88" // Confounder + "9 bytesss", // Plain + .plen = 16 + 9, + .ctext = + "\x9C\x6D\xE7\x5F\x81\x2D\xE7\xED\x0D\x28\xB2\x96\x35\x57\xA1\x15" + "\x64\x09\x98\x27\x5B\x0A\xF5\x15\x27\x09\x91\x3F\xF5\x2A\x2A\x9C" + "\x8E\x63\xB8\x72\xF9\x2E\x64\xC8\x39", + .clen = 16 + 9 + 16, + .setauthsize_error = -EINVAL, + }, { + // "enc 13 plain", + .key = + "\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE + "\x00\x00\x00\x03" // Usage + "\xB0\x38\xB1\x32\xCD\x8E\x06\x61\x22\x67\xFA\xB7\x17\x00\x66\xD8" + "\x8A\xEC\xCB\xA0\xB7\x44\xBF\xC6\x0D\xC8\x9B\xCA\x18\x2D\x07\x15", // K0 + .klen = 4 + 4 + 32, + .ptext = + "\xCF\x9B\xCA\x6D\xF1\x14\x4E\x0C\x0A\xF9\xB8\xF3\x4C\x90\xD5\x14" // Confounder + "13 bytes byte", + .plen = 16 + 1, + .ctext = + "\xEE\xEC\x85\xA9\x81\x3C\xDC\x53\x67\x72\xAB\x9B\x42\xDE\xFC\x57" + "\x06\xF7\x26\xE9\x75\xDD\xE0\x5A\x87\xEB\x54\x06\xEA\x32\x4C\xA1" + "\x85\xC9\x98\x6B\x42\xAA\xBE\x79\x4B\x84\x82\x1B\xEE", + .clen = 16 + 0 + 16, + .setauthsize_error = -EINVAL, + }, { + // "enc 30 plain", + .key = + "\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE + "\x00\x00\x00\x04" // Usage + "\xCC\xFC\xD3\x49\xBF\x4C\x66\x77\xE8\x6E\x4B\x02\xB8\xEA\xB9\x24" + "\xA5\x46\xAC\x73\x1C\xF9\xBF\x69\x89\xB9\x96\xE7\xD6\xBF\xBB\xA7", // K0 + .klen = 4 + 4 + 32, + .ptext = + "\x64\x4D\xEF\x38\xDA\x35\x00\x72\x75\x87\x8D\x21\x68\x55\xE2\x28" // Confounder + "30 bytes bytes bytes bytes byt", // Plain + .plen = 16 + 30, + .ctext = + "\x0E\x44\x68\x09\x85\x85\x5F\x2D\x1F\x18\x12\x52\x9C\xA8\x3B\xFD" + "\x8E\x34\x9D\xE6\xFD\x9A\xDA\x0B\xAA\xA0\x48\xD6\x8E\x26\x5F\xEB" + "\xF3\x4A\xD1\x25\x5A\x34\x49\x99\xAD\x37\x14\x68\x87\xA6\xC6\x84" + "\x57\x31\xAC\x7F\x46\x37\x6A\x05\x04\xCD\x06\x57\x14\x74", + .clen = 16 + 30 + 16, + .setauthsize_error = -EINVAL, + }, { + // "mic 123" + .key = + "\x00\x00\x00\x00" // KR5_ENCRYPT_MODE + "\x00\x00\x00\x09" // Usage + "\xB6\x1C\x86\xCC\x4E\x5D\x27\x57\x54\x5A\xD4\x23\x39\x9F\xB7\x03" + "\x1E\xCA\xB9\x13\xCB\xB9\x00\xBD\x7A\x3C\x6D\xD8\xBF\x92\x01\x5B", // K0 + .klen = 4 + 4 + 32, + .ptext = + "\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55" // MIC + "123456789", // Plain + .plen = 16 + 9, + .ctext = + "\x87\xA1\x2C\xFD\x2B\x96\x21\x48\x10\xF0\x1C\x82\x6E\x77\x44\xB1" // MIC + "123456789", // Plain + .clen = 16 + 9, + .setauthsize_error = -EINVAL, + }, { + // "mic !@#" + .key = + "\x00\x00\x00\x00" // KR5_ENCRYPT_MODE + "\x00\x00\x00\x0a" // Usage + "\x32\x16\x4C\x5B\x43\x4D\x1D\x15\x38\xE4\xCF\xD9\xBE\x80\x40\xFE" + "\x8C\x4A\xC7\xAC\xC4\xB9\x3D\x33\x14\xD2\x13\x36\x68\x14\x7A\x05", // K0 + .klen = 4 + 4 + 32, + .ptext = + "\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55" // MIC + "!@#$%^&*()!@#$%^&*()!@#$%^&*()", // Plain + .plen = 16 + 30, + .ctext = + "\x3F\xA0\xB4\x23\x55\xE5\x2B\x18\x91\x87\x29\x4A\xA2\x52\xAB\x64" // MIC + "!@#$%^&*()!@#$%^&*()!@#$%^&*()", // Plain + .clen = 16 + 30, + .setauthsize_error = -EINVAL, + } +}; + #endif /* _CRYPTO_TESTMGR_H */ diff --git a/include/crypto/aead.h b/include/crypto/aead.h index 0e8a41638678..0b0826131883 100644 --- a/include/crypto/aead.h +++ b/include/crypto/aead.h @@ -121,6 +121,7 @@ struct aead_request { * @decrypt: see struct skcipher_alg * @ivsize: see struct skcipher_alg * @chunksize: see struct skcipher_alg + * @no_authtags: This algo doesn't use authentication tags. * @init: Initialize the cryptographic transformation object. This function * is used to initialize the cryptographic transformation object. * This function is called only once at the instantiation time, right @@ -148,6 +149,7 @@ struct aead_alg { unsigned int ivsize; unsigned int maxauthsize; unsigned int chunksize; + bool no_authtags; struct crypto_alg base; }; From patchwork Fri Jan 10 01:03:06 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 13933364 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CCFE81DA3D for ; Fri, 10 Jan 2025 01:03:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736471035; cv=none; b=eB+c5mAogTBjJt5AuQBtTygiZnPc9Ckg7VntyXAW3EgByMamvLXgjJZrfXMQNop/V8+vdytKoaRn8XpSG4oFbBEkGwgjVetgfh1zP/yJ+6FMeuY/DhEY8jRF4LKRhopk67qCcoKmnKxLU0PpuRwAA1zS/OYeSjzCQbLilmVA+00= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736471035; c=relaxed/simple; bh=eRGwWfvARVVB5IIybt5hsgG5iVKGIKGIytS7xR8nTXU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=tbf+gxzAbO8KGEM5VdhQwFNEYtjMSzaj8e/Xg2hbopNwcZ+2wejBEaBQqN+l2+Nxx3hATZadvKnZdjxY/05M0GIO9nQI0mo7rKwmOe78O3jdnmV55R1V2x7xVN7rNAf5hTIWMjHQRF5RRZiQCGMj6TAOOqKhSKt6dQ5dmkVBJ4I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=V2/rTJ9s; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="V2/rTJ9s" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1736471033; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=8Bmn3ia59/qmwq5mHOM25O+wtO2NUPM5vVCRn+4R/Mo=; b=V2/rTJ9sMp1Q6RcamjpjRGuoOhc0oD1PBIRxFPsc2vsdhIQqV6vNpEWCkWRJCgt4bMOCZm SNzICo+j1AlAh5Kjn16INJsDlzaO20KfdDcX4mT1JZNGMEhBsr/bTwthC4tk4dxPwhYA4L 7WINdyiLr5LahVQSA1wNHKqsKKRs70o= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-330-tiI1Cgi5N1SrS-he_zmxHQ-1; Thu, 09 Jan 2025 20:03:48 -0500 X-MC-Unique: tiI1Cgi5N1SrS-he_zmxHQ-1 X-Mimecast-MFC-AGG-ID: tiI1Cgi5N1SrS-he_zmxHQ Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 990311955DCC; Fri, 10 Jan 2025 01:03:46 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.42.28.12]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id E06941955BE3; Fri, 10 Jan 2025 01:03:41 +0000 (UTC) From: David Howells To: Herbert Xu , Chuck Lever Cc: David Howells , Trond Myklebust , "David S. Miller" , Marc Dionne , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 4/8] rxrpc: Add the security index for yfs-rxgk Date: Fri, 10 Jan 2025 01:03:06 +0000 Message-ID: <20250110010313.1471063-5-dhowells@redhat.com> In-Reply-To: <20250110010313.1471063-1-dhowells@redhat.com> References: <20250110010313.1471063-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Add the security index and abort codes for the YFS variant of rxgk. Signed-off-by: David Howells cc: Herbert Xu cc: "David S. Miller" cc: Chuck Lever cc: Marc Dionne cc: Eric Dumazet cc: Jakub Kicinski cc: Paolo Abeni cc: Simon Horman cc: linux-afs@lists.infradead.org cc: linux-nfs@vger.kernel.org cc: linux-crypto@vger.kernel.org cc: netdev@vger.kernel.org --- fs/afs/misc.c | 13 +++++++++++++ include/uapi/linux/rxrpc.h | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/fs/afs/misc.c b/fs/afs/misc.c index b8180bf2281f..57f779804d50 100644 --- a/fs/afs/misc.c +++ b/fs/afs/misc.c @@ -103,6 +103,19 @@ int afs_abort_to_error(u32 abort_code) case RXKADDATALEN: return -EKEYREJECTED; case RXKADILLEGALLEVEL: return -EKEYREJECTED; + case RXGK_INCONSISTENCY: return -EPROTO; + case RXGK_PACKETSHORT: return -EPROTO; + case RXGK_BADCHALLENGE: return -EPROTO; + case RXGK_BADETYPE: return -ENOPKG; + case RXGK_BADLEVEL: return -EKEYREJECTED; + case RXGK_BADKEYNO: return -EKEYREJECTED; + case RXGK_EXPIRED: return -EKEYEXPIRED; + case RXGK_NOTAUTH: return -EKEYREJECTED; + case RXGK_BAD_TOKEN: return -EKEYREJECTED; + case RXGK_SEALED_INCON: return -EKEYREJECTED; + case RXGK_DATA_LEN: return -EPROTO; + case RXGK_BAD_QOP: return -EKEYREJECTED; + case RXGEN_OPCODE: return -ENOTSUPP; default: return -EREMOTEIO; diff --git a/include/uapi/linux/rxrpc.h b/include/uapi/linux/rxrpc.h index 8f8dc7a937a4..0e296d219191 100644 --- a/include/uapi/linux/rxrpc.h +++ b/include/uapi/linux/rxrpc.h @@ -73,6 +73,7 @@ enum rxrpc_cmsg_type { #define RXRPC_SECURITY_RXKAD 2 /* kaserver or kerberos 4 */ #define RXRPC_SECURITY_RXGK 4 /* gssapi-based */ #define RXRPC_SECURITY_RXK5 5 /* kerberos 5 */ +#define RXRPC_SECURITY_YFS_RXGK 6 /* YFS gssapi-based */ /* * RxRPC-level abort codes @@ -118,4 +119,20 @@ enum rxrpc_cmsg_type { #define RXKADDATALEN 19270411 /* user data too long */ #define RXKADILLEGALLEVEL 19270412 /* caller not authorised to use encrypted conns */ +/* + * RxGK GSSAPI security abort codes. + */ +#define RXGK_INCONSISTENCY 1233242880 /* Security module structure inconsistent */ +#define RXGK_PACKETSHORT 1233242881 /* Packet too short for security challenge */ +#define RXGK_BADCHALLENGE 1233242882 /* Invalid security challenge */ +#define RXGK_BADETYPE 1233242883 /* Invalid or impermissible encryption type */ +#define RXGK_BADLEVEL 1233242884 /* Invalid or impermissible security level */ +#define RXGK_BADKEYNO 1233242885 /* Key version number not found */ +#define RXGK_EXPIRED 1233242886 /* Token has expired */ +#define RXGK_NOTAUTH 1233242887 /* Caller not authorized */ +#define RXGK_BAD_TOKEN 1233242888 /* Security object was passed a bad token */ +#define RXGK_SEALED_INCON 1233242889 /* Sealed data inconsistent */ +#define RXGK_DATA_LEN 1233242890 /* User data too long */ +#define RXGK_BAD_QOP 1233242891 /* Inadequate quality of protection available */ + #endif /* _UAPI_LINUX_RXRPC_H */ From patchwork Fri Jan 10 01:03:07 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 13933365 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 06F962063CF for ; Fri, 10 Jan 2025 01:03:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736471039; cv=none; b=e4QkTJ7osvIyxfIdXTfQxuIHFmv5ATnjOS3Ole30StctBPIn0JJKGNvKmgQPVL3Ej87hEMjF/b3OmcD8uOD12MCnU78IyYx7Lt+I2sV5bWzImBq1j/MPQSrKHf4Kbix5zPKal4zOBZn2Wd0bDCJ5D9ElS+8zC3CElm0doDGNDEc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736471039; c=relaxed/simple; bh=86Q8XCc2TtGaREZLnhbTpNObNB33rvrKvlO7lOewIBE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Gv+f1tPQ32aWUc1UevENyVJLT89bFWqHveeKls0ZGEFq4rEbtkzsJL7pnJrlc5CrM5DAA2LWqbtfbkwBgDZg+JNr7JQeDx9z/zTirj0G2WlI/EqONLXgiSF5xoP272PEBuflUSqKXsui/4dWj0abdRP7ZxtmmmqT2JoHs7QZa30= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Pc6cQ1T6; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Pc6cQ1T6" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1736471037; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=LYtu8/CPyj9Kmn4h8ogtOjqZGicDRGKeNSt+PeFsJW8=; b=Pc6cQ1T6Ok/T0gLEF559++pmDMBrqnEyUpNXy1w7i3UyR9IFHp8qxFzjiHE2qDUasjO7mn jooHXMCiZFyZdPQ3KkpM2rLBu/rwb9zDgVnA3ahmwwOXdMQOt8Y8/oiR/wIcfd7mC9B4Ct tmio4XMjJUJaS2pgj82yz0vp0GXzJrw= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-553-odNwFdzCMoutguAzn85JWw-1; Thu, 09 Jan 2025 20:03:54 -0500 X-MC-Unique: odNwFdzCMoutguAzn85JWw-1 X-Mimecast-MFC-AGG-ID: odNwFdzCMoutguAzn85JWw Received: from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.15]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id E172019560B4; Fri, 10 Jan 2025 01:03:51 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.42.28.12]) by mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 1EA6B195E3D9; Fri, 10 Jan 2025 01:03:47 +0000 (UTC) From: David Howells To: Herbert Xu , Chuck Lever Cc: David Howells , Trond Myklebust , "David S. Miller" , Marc Dionne , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 5/8] rxrpc: Add YFS RxGK (GSSAPI) security class Date: Fri, 10 Jan 2025 01:03:07 +0000 Message-ID: <20250110010313.1471063-6-dhowells@redhat.com> In-Reply-To: <20250110010313.1471063-1-dhowells@redhat.com> References: <20250110010313.1471063-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.15 Add support for the YFS-variant RxGK security class to support GSSAPI-derived authentication. This also allows the use of better crypto over the rxkad security class. The key payload is XDR encoded of the form: typedef int64_t opr_time; const AFSTOKEN_RK_TIX_MAX = 12000; /* Matches entry in rxkad.h */ struct token_rxkad { afs_int32 viceid; afs_int32 kvno; afs_int64 key; afs_int32 begintime; afs_int32 endtime; afs_int32 primary_flag; opaque ticket; }; struct token_rxgk { opr_time begintime; opr_time endtime; afs_int64 level; afs_int64 lifetime; afs_int64 bytelife; afs_int64 enctype; opaque key<>; opaque ticket<>; }; const AFSTOKEN_UNION_NOAUTH = 0; const AFSTOKEN_UNION_KAD = 2; const AFSTOKEN_UNION_YFSGK = 6; union ktc_tokenUnion switch (afs_int32 type) { case AFSTOKEN_UNION_KAD: token_rxkad kad; case AFSTOKEN_UNION_YFSGK: token_rxgk gk; }; const AFSTOKEN_LENGTH_MAX = 16384; typedef opaque token_opaque; const AFSTOKEN_MAX = 8; const AFSTOKEN_CELL_MAX = 64; struct ktc_setTokenData { afs_int32 flags; string cell; token_opaque tokens; }; The parser for the basic token struct is already present, as is the rxkad token type. This adds a parser for the rxgk token type. Signed-off-by: David Howells cc: Herbert Xu cc: "David S. Miller" cc: Chuck Lever cc: Marc Dionne cc: Eric Dumazet cc: Jakub Kicinski cc: Paolo Abeni cc: Simon Horman cc: linux-afs@lists.infradead.org cc: linux-nfs@vger.kernel.org cc: linux-crypto@vger.kernel.org cc: netdev@vger.kernel.org --- include/keys/rxrpc-type.h | 17 ++++ net/rxrpc/key.c | 183 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 200 insertions(+) diff --git a/include/keys/rxrpc-type.h b/include/keys/rxrpc-type.h index 333c0f49a9cd..0ddbe197a261 100644 --- a/include/keys/rxrpc-type.h +++ b/include/keys/rxrpc-type.h @@ -9,6 +9,7 @@ #define _KEYS_RXRPC_TYPE_H #include +#include /* * key type for AF_RXRPC keys @@ -31,6 +32,21 @@ struct rxkad_key { u8 ticket[]; /* the encrypted ticket */ }; +/* + * RxRPC key for YFS-RxGK (type-6 security) + */ +struct rxgk_key { + s64 begintime; /* Time at which the ticket starts */ + s64 endtime; /* Time at which the ticket ends */ + u64 lifetime; /* Maximum lifespan of a connection (seconds) */ + u64 bytelife; /* Maximum number of bytes on a connection */ + unsigned int enctype; /* Encoding type */ + s8 level; /* Negotiated security RXRPC_SECURITY_PLAIN/AUTH/ENCRYPT */ + struct krb5_buffer key; /* Master key, K0 */ + struct krb5_buffer ticket; /* Ticket to be passed to server */ + u8 _key[]; /* Key storage */ +}; + /* * list of tokens attached to an rxrpc key */ @@ -40,6 +56,7 @@ struct rxrpc_key_token { struct rxrpc_key_token *next; /* the next token in the list */ union { struct rxkad_key *kad; + struct rxgk_key *rxgk; }; }; diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c index 33e8302a79e3..2e42a0bbeac2 100644 --- a/net/rxrpc/key.c +++ b/net/rxrpc/key.c @@ -129,6 +129,158 @@ static int rxrpc_preparse_xdr_rxkad(struct key_preparsed_payload *prep, return 0; } +static u64 xdr_dec64(const __be32 *xdr) +{ + return (u64)ntohl(xdr[0]) << 32 | (u64)ntohl(xdr[1]); +} + +static time64_t rxrpc_s64_to_time64(s64 time_in_100ns) +{ + bool neg = false; + u64 tmp = time_in_100ns; + + if (time_in_100ns < 0) { + tmp = -time_in_100ns; + neg = true; + } + do_div(tmp, 10000000); + return neg ? -tmp : tmp; +} + +/* + * Parse a YFS-RxGK type XDR format token + * - the caller guarantees we have at least 4 words + * + * struct token_rxgk { + * opr_time begintime; + * opr_time endtime; + * afs_int64 level; + * afs_int64 lifetime; + * afs_int64 bytelife; + * afs_int64 enctype; + * opaque key<>; + * opaque ticket<>; + * }; + */ +static int rxrpc_preparse_xdr_yfs_rxgk(struct key_preparsed_payload *prep, + size_t datalen, + const __be32 *xdr, unsigned int toklen) +{ + struct rxrpc_key_token *token, **pptoken; + time64_t expiry; + size_t plen; + const __be32 *ticket, *key; + s64 tmp; + u32 tktlen, keylen; + + _enter(",{%x,%x,%x,%x},%x", + ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]), + toklen); + + if (6 * 2 + 2 > toklen / 4) + goto reject; + + key = xdr + (6 * 2 + 1); + keylen = ntohl(key[-1]); + _debug("keylen: %x", keylen); + keylen = round_up(keylen, 4); + if ((6 * 2 + 2) * 4 + keylen > toklen) + goto reject; + + ticket = xdr + (6 * 2 + 1 + (keylen / 4) + 1); + tktlen = ntohl(ticket[-1]); + _debug("tktlen: %x", tktlen); + tktlen = round_up(tktlen, 4); + if ((6 * 2 + 2) * 4 + keylen + tktlen != toklen) { + kleave(" = -EKEYREJECTED [%x!=%x, %x,%x]", + (6 * 2 + 2) * 4 + keylen + tktlen, toklen, + keylen, tktlen); + goto reject; + } + + plen = sizeof(*token) + sizeof(*token->rxgk) + tktlen + keylen; + prep->quotalen = datalen + plen; + + plen -= sizeof(*token); + token = kzalloc(sizeof(*token), GFP_KERNEL); + if (!token) + goto nomem; + + token->rxgk = kzalloc(sizeof(*token->rxgk) + keylen, GFP_KERNEL); + if (!token->rxgk) + goto nomem_token; + + token->security_index = RXRPC_SECURITY_YFS_RXGK; + token->rxgk->begintime = xdr_dec64(xdr + 0 * 2); + token->rxgk->endtime = xdr_dec64(xdr + 1 * 2); + token->rxgk->level = tmp = xdr_dec64(xdr + 2 * 2); + if (tmp < -1LL || tmp > RXRPC_SECURITY_ENCRYPT) + goto reject_token; + token->rxgk->lifetime = xdr_dec64(xdr + 3 * 2); + token->rxgk->bytelife = xdr_dec64(xdr + 4 * 2); + token->rxgk->enctype = tmp = xdr_dec64(xdr + 5 * 2); + if (tmp < 0 || tmp > UINT_MAX) + goto reject_token; + token->rxgk->key.len = ntohl(key[-1]); + token->rxgk->key.data = token->rxgk->_key; + token->rxgk->ticket.len = ntohl(ticket[-1]); + + expiry = rxrpc_s64_to_time64(token->rxgk->endtime); + if (expiry < 0) + goto expired; + if (expiry < prep->expiry) + prep->expiry = expiry; + + memcpy(token->rxgk->key.data, key, token->rxgk->key.len); + + /* Pad the ticket so that we can use it directly in XDR */ + token->rxgk->ticket.data = kzalloc(round_up(token->rxgk->ticket.len, 4), + GFP_KERNEL); + if (!token->rxgk->ticket.data) + goto nomem_yrxgk; + memcpy(token->rxgk->ticket.data, ticket, token->rxgk->ticket.len); + + _debug("SCIX: %u", token->security_index); + _debug("EXPY: %llx", token->rxgk->endtime); + _debug("LIFE: %llx", token->rxgk->lifetime); + _debug("BYTE: %llx", token->rxgk->bytelife); + _debug("ENC : %u", token->rxgk->enctype); + _debug("LEVL: %u", token->rxgk->level); + _debug("KLEN: %u", token->rxgk->key.len); + _debug("TLEN: %u", token->rxgk->ticket.len); + _debug("KEY0: %*phN", token->rxgk->key.len, token->rxgk->key.data); + _debug("TICK: %*phN", + min_t(u32, token->rxgk->ticket.len, 32), token->rxgk->ticket.data); + + /* count the number of tokens attached */ + prep->payload.data[1] = (void *)((unsigned long)prep->payload.data[1] + 1); + + /* attach the data */ + for (pptoken = (struct rxrpc_key_token **)&prep->payload.data[0]; + *pptoken; + pptoken = &(*pptoken)->next) + continue; + *pptoken = token; + + _leave(" = 0"); + return 0; + +nomem_yrxgk: + kfree(token->rxgk); +nomem_token: + kfree(token); +nomem: + return -ENOMEM; +reject_token: + kfree(token); +reject: + return -EKEYREJECTED; +expired: + kfree(token->rxgk); + kfree(token); + return -EKEYEXPIRED; +} + /* * attempt to parse the data as the XDR format * - the caller guarantees we have more than 7 words @@ -228,6 +380,9 @@ static int rxrpc_preparse_xdr(struct key_preparsed_payload *prep) case RXRPC_SECURITY_RXKAD: ret2 = rxrpc_preparse_xdr_rxkad(prep, datalen, token, toklen); break; + case RXRPC_SECURITY_YFS_RXGK: + ret2 = rxrpc_preparse_xdr_yfs_rxgk(prep, datalen, token, toklen); + break; default: ret2 = -EPROTONOSUPPORT; break; @@ -390,6 +545,10 @@ static void rxrpc_free_token_list(struct rxrpc_key_token *token) case RXRPC_SECURITY_RXKAD: kfree(token->kad); break; + case RXRPC_SECURITY_YFS_RXGK: + kfree(token->rxgk->ticket.data); + kfree(token->rxgk); + break; default: pr_err("Unknown token type %x on rxrpc key\n", token->security_index); @@ -433,6 +592,9 @@ static void rxrpc_describe(const struct key *key, struct seq_file *m) case RXRPC_SECURITY_RXKAD: seq_puts(m, "ka"); break; + case RXRPC_SECURITY_YFS_RXGK: + seq_puts(m, "ygk"); + break; default: /* we have a ticket we can't encode */ seq_printf(m, "%u", token->security_index); break; @@ -595,6 +757,13 @@ static long rxrpc_read(const struct key *key, toksize += RND(token->kad->ticket_len); break; + case RXRPC_SECURITY_YFS_RXGK: + toksize += 6 * 8 + 2 * 4; + if (!token->no_leak_key) + toksize += RND(token->rxgk->key.len); + toksize += RND(token->rxgk->ticket.len); + break; + default: /* we have a ticket we can't encode */ pr_err("Unsupported key token type (%u)\n", token->security_index); @@ -674,6 +843,20 @@ static long rxrpc_read(const struct key *key, ENCODE_DATA(token->kad->ticket_len, token->kad->ticket); break; + case RXRPC_SECURITY_YFS_RXGK: + ENCODE64(token->rxgk->begintime); + ENCODE64(token->rxgk->endtime); + ENCODE64(token->rxgk->level); + ENCODE64(token->rxgk->lifetime); + ENCODE64(token->rxgk->bytelife); + ENCODE64(token->rxgk->enctype); + if (token->no_leak_key) + ENCODE(0); + else + ENCODE_DATA(token->rxgk->key.len, token->rxgk->key.data); + ENCODE_DATA(token->rxgk->ticket.len, token->rxgk->ticket.data); + break; + default: pr_err("Unsupported key token type (%u)\n", token->security_index); From patchwork Fri Jan 10 01:03:08 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 13933386 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7DA452066DE for ; Fri, 10 Jan 2025 01:04:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736471047; cv=none; b=dAbJQVCiWmwiiok8/fEwm1hhBfm6TqtvMc+BBlXP8AbCdtjm4eHbZ/sSIHVVkJmjM/SDzs6FQHHs/jKGDVECAWqOC9pjDAxNSs56K8Co/9rbR0izUFvf/LG7eBfwE1kagp4zB1Ppc0QCreSi0J1YiQvwykdyYtCjfTbB6obUCSw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736471047; c=relaxed/simple; bh=NDgxNvCoCIk52Ob+AWZHEGqXOXRDfnYGvy3mr9trhnc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DGSJ3pG5hJ78VdV49TMIwj0DJoNQwoON3H7E3IkBWy/4SN5iSLGMZjt1pWkVPxD9VzVQEma6x2nnwQeryjQMEN+Abkl4JWsrOaL2mKmuTdg6q+JG+POdpTTbTktUL1zVd8eBUfeQljuo48W7LIUcOUUZAr9M9Wy3oE+IbzbR1L8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=TneRlzrg; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="TneRlzrg" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1736471044; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=5FybuO2e6jAfgFg1N7R8bOgxXmdIKnrYsg9YYFnBvIk=; b=TneRlzrgb0suorGw0n1t50T6IlDReS3I7d/ieZLjFruHDRkRK6tniWrIbXFVKfmuh8oU/G 1fN0oilDAunncBwm7hfs65JoO4Wzv7s8t3AFzpOnDl7B97NiE1mGk9A3pJG1EAN5S13C+S t89PjFcsA/vguuBxXJFR381WuvIRXWI= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-279-rdoeS7ANPFaGWqNUF-ROkQ-1; Thu, 09 Jan 2025 20:03:59 -0500 X-MC-Unique: rdoeS7ANPFaGWqNUF-ROkQ-1 X-Mimecast-MFC-AGG-ID: rdoeS7ANPFaGWqNUF-ROkQ Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 5045C19560B3; Fri, 10 Jan 2025 01:03:57 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.42.28.12]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 6019B30001BE; Fri, 10 Jan 2025 01:03:53 +0000 (UTC) From: David Howells To: Herbert Xu , Chuck Lever Cc: David Howells , Trond Myklebust , "David S. Miller" , Marc Dionne , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 6/8] rxrpc: rxgk: Provide infrastructure and key derivation Date: Fri, 10 Jan 2025 01:03:08 +0000 Message-ID: <20250110010313.1471063-7-dhowells@redhat.com> In-Reply-To: <20250110010313.1471063-1-dhowells@redhat.com> References: <20250110010313.1471063-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Provide some infrastructure for implementing the RxGK transport security class: (1) A definition of an encoding type, including: - Relevant crypto-layer names - Lengths of the crypto keys and checksums involved - Crypto functions specific to the encoding type - Crypto scheme used for that type (2) A definition of a crypto scheme, including: - Underlying crypto handlers - The pseudo-random function, PRF, used in base key derivation - Functions for deriving usage keys Kc, Ke and Ki - Functions for en/decrypting parts of an sk_buff (3) A key context, with the usage keys required for a derivative of a transport key for a specific key number. This includes keys for securing packets for transmission, extracting received packets and dealing with response packets. (3) A function to look up an encoding type by number. (4) A function to set up a key context and derive the keys. (5) A function to set up the keys required to extract the ticket obtained from the GSS negotiation in the server. (6) Miscellaneous functions for context handling. The keys and key derivation functions are described in: tools.ietf.org/html/draft-wilkinson-afs3-rxgk-11 Signed-off-by: David Howells cc: Herbert Xu cc: "David S. Miller" cc: Chuck Lever cc: Marc Dionne cc: Eric Dumazet cc: Jakub Kicinski cc: Paolo Abeni cc: Simon Horman cc: linux-afs@lists.infradead.org cc: linux-nfs@vger.kernel.org cc: linux-crypto@vger.kernel.org cc: netdev@vger.kernel.org --- net/rxrpc/Kconfig | 10 ++ net/rxrpc/Makefile | 3 +- net/rxrpc/ar-internal.h | 3 + net/rxrpc/rxgk_common.h | 44 +++++++ net/rxrpc/rxgk_kdf.c | 260 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 net/rxrpc/rxgk_common.h create mode 100644 net/rxrpc/rxgk_kdf.c diff --git a/net/rxrpc/Kconfig b/net/rxrpc/Kconfig index a20986806fea..0a2b38b9b94a 100644 --- a/net/rxrpc/Kconfig +++ b/net/rxrpc/Kconfig @@ -67,6 +67,16 @@ config RXKAD See Documentation/networking/rxrpc.rst. +config RXGK + bool "RxRPC GSSAPI security" + depends on AF_RXRPC + depends on CRYPTO_KRB5 + help + Provide the GSSAPI-based RxGK security class for AFS. Keys are added + with add_key(). + + See Documentation/networking/rxrpc.rst. + config RXPERF tristate "RxRPC test service" help diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile index 210b75e3179e..9c8eb1471054 100644 --- a/net/rxrpc/Makefile +++ b/net/rxrpc/Makefile @@ -39,6 +39,7 @@ rxrpc-y := \ rxrpc-$(CONFIG_PROC_FS) += proc.o rxrpc-$(CONFIG_RXKAD) += rxkad.o rxrpc-$(CONFIG_SYSCTL) += sysctl.o - +rxrpc-$(CONFIG_RXGK) += \ + rxgk_kdf.o obj-$(CONFIG_RXPERF) += rxperf.o diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 718193df9d2e..2392f2e062c2 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -526,6 +526,9 @@ struct rxrpc_connection { struct rxrpc_crypt csum_iv; /* packet checksum base */ u32 nonce; /* response re-use preventer */ } rxkad; + struct { + u64 start_time; /* The start time for TK derivation */ + } rxgk; }; unsigned long flags; unsigned long events; diff --git a/net/rxrpc/rxgk_common.h b/net/rxrpc/rxgk_common.h new file mode 100644 index 000000000000..84e76fe8e324 --- /dev/null +++ b/net/rxrpc/rxgk_common.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Common bits for GSSAPI-based RxRPC security. + * + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include +#include +#include + +/* + * Per-key number context. This is replaced when the connection is rekeyed. + */ +struct rxgk_context { + refcount_t usage; + unsigned int key_number; /* Rekeying number (goes in the rx header) */ + unsigned long flags; +#define RXGK_TK_NEEDS_REKEY 0 /* Set if this needs rekeying */ + unsigned long expiry; /* Expiration time of this key */ + long long bytes_remaining; /* Remaining Tx lifetime of this key */ + const struct krb5_enctype *krb5; /* RxGK encryption type */ + const struct rxgk_key *key; + + /* We need up to 7 keys derived from the transport key, but we don't + * actually need the transport key. Each key is derived by + * DK(TK,constant). + */ + struct crypto_aead *tx_crypto; /* Transmission key */ + struct crypto_aead *rx_crypto; /* Reception key */ + struct crypto_aead *resp_crypto; /* Response key */ +}; + +/* + * rxgk_kdf.c + */ +void rxgk_put(struct rxgk_context *gk); +struct rxgk_context *rxgk_generate_transport_key(struct rxrpc_connection *conn, + const struct rxgk_key *key, + unsigned int key_number, + gfp_t gfp); +struct crypto_aead *rxgk_set_up_token_cipher(const struct krb5_buffer *server_key, + unsigned int enctype, + gfp_t gfp); diff --git a/net/rxrpc/rxgk_kdf.c b/net/rxrpc/rxgk_kdf.c new file mode 100644 index 000000000000..4257d3d0190b --- /dev/null +++ b/net/rxrpc/rxgk_kdf.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* RxGK transport key derivation. + * + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include "ar-internal.h" +#include "rxgk_common.h" + +/* + * Constants used to derive the keys and hmacs actually used for doing stuff. + */ +#define RXGK_CLIENT_ENC_PACKET 1026U // 0x402 +#define RXGK_CLIENT_MIC_PACKET 1027U // 0x403 +#define RXGK_SERVER_ENC_PACKET 1028U // 0x404 +#define RXGK_SERVER_MIC_PACKET 1029U // 0x405 +#define RXGK_CLIENT_ENC_RESPONSE 1030U // 0x406 +#define RXGK_SERVER_ENC_TOKEN 1036U // 0x40c + +#define round16(x) (((x) + 15) & ~15) + +static void rxgk_free(struct rxgk_context *gk) +{ + crypto_free_aead(gk->tx_crypto); + crypto_free_aead(gk->rx_crypto); + crypto_free_aead(gk->resp_crypto); + kfree(gk); +} + +void rxgk_put(struct rxgk_context *gk) +{ + if (gk && refcount_dec_and_test(&gk->usage)) + rxgk_free(gk); +} + +/* + * Transport key derivation function. + * + * TK = random-to-key(PRF+(K0, L, + * epoch || cid || start_time || key_number)) + * [tools.ietf.org/html/draft-wilkinson-afs3-rxgk-11 sec 8.3] + */ +static int rxgk_derive_transport_key(struct rxrpc_connection *conn, + struct rxgk_context *gk, + const struct rxgk_key *rxgk, + struct krb5_buffer *TK, + gfp_t gfp) +{ + const struct krb5_enctype *krb5 = gk->krb5; + struct krb5_buffer conn_info; + unsigned int L = krb5->key_bytes; + __be32 *info; + u8 *buffer; + int ret; + + _enter(""); + + conn_info.len = sizeof(__be32) * 5; + + buffer = kzalloc(round16(conn_info.len), gfp); + if (!buffer) + return -ENOMEM; + + conn_info.data = buffer; + + info = (__be32 *)conn_info.data; + info[0] = htonl(conn->proto.epoch); + info[1] = htonl(conn->proto.cid); + info[2] = htonl(conn->rxgk.start_time >> 32); + info[3] = htonl(conn->rxgk.start_time >> 0); + info[4] = htonl(gk->key_number); + + ret = crypto_krb5_calc_PRFplus(krb5, &rxgk->key, L, &conn_info, TK, gfp); + kfree_sensitive(buffer); + _leave(" = %d", ret); + return ret; +} + +/* + * Derive a cipher. + */ +static int rxgk_derive_cipher(const struct krb5_enctype *krb5, + u32 *params, size_t plen, struct crypto_aead **_ci) +{ + struct crypto_aead *ci; + int ret; + + ci = crypto_alloc_aead(krb5->aead.base.cra_name, 0, 0); + if (IS_ERR(ci)) + return (PTR_ERR(ci) == -ENOENT) ? -ENOPKG : PTR_ERR(ci); + + ret = crypto_aead_setkey(ci, (void *)params, plen); + if (ret < 0) { + crypto_free_aead(ci); + return ret; + } + + *_ci = ci; + return 0; +} + +/* + * Set up the ciphers for the usage keys. + */ +static int rxgk_set_up_ciphers(struct rxrpc_connection *conn, + struct rxgk_context *gk, + const struct rxgk_key *rxgk, + gfp_t gfp) +{ + const struct krb5_enctype *krb5 = gk->krb5; + struct krb5_buffer TK; + __be32 *params, tx_usage, rx_usage; + size_t plen = 2 * sizeof(__be32) + krb5->key_bytes; + int ret; + + params = kzalloc(plen, gfp); + if (!params) + return -ENOMEM; + + TK.len = krb5->key_bytes; + TK.data = (void *)¶ms[2]; + + ret = rxgk_derive_transport_key(conn, gk, rxgk, &TK, gfp); + if (ret < 0) + goto out; + + params[0] = htonl(KRB5_ENCRYPT_MODE); + params[1] = htonl(RXGK_CLIENT_ENC_RESPONSE); + ret = rxgk_derive_cipher(krb5, params, plen, &gk->resp_crypto); + if (ret < 0) + goto out; + + if (conn->security_level == RXRPC_SECURITY_AUTH) { + params[0] = htonl(KRB5_CHECKSUM_MODE); + tx_usage = htonl(RXGK_CLIENT_MIC_PACKET); + rx_usage = htonl(RXGK_SERVER_MIC_PACKET); + } else { + params[0] = htonl(KRB5_ENCRYPT_MODE); + tx_usage = htonl(RXGK_CLIENT_ENC_PACKET); + rx_usage = htonl(RXGK_SERVER_ENC_PACKET); + } + + if (rxrpc_conn_is_service(conn)) + swap(tx_usage, rx_usage); + + params[1] = tx_usage; + ret = rxgk_derive_cipher(krb5, params, plen, &gk->tx_crypto); + if (ret < 0) + goto out; + + params[1] = rx_usage; + ret = rxgk_derive_cipher(krb5, params, plen, &gk->rx_crypto); + if (ret < 0) + goto out; + + ret = 0; +out: + kfree_sensitive(params); + return ret; +} + +/* + * Derive a transport key for a connection and then derive a bunch of usage + * keys from it and set up ciphers using them. + */ +struct rxgk_context *rxgk_generate_transport_key(struct rxrpc_connection *conn, + const struct rxgk_key *key, + unsigned int key_number, + gfp_t gfp) +{ + struct rxgk_context *gk; + unsigned long lifetime; + int ret; + + _enter(""); + + gk = kzalloc(sizeof(*gk), GFP_KERNEL); + if (!gk) + return ERR_PTR(-ENOMEM); + refcount_set(&gk->usage, 1); + gk->key = key; + gk->key_number = key_number; + + gk->krb5 = crypto_krb5_find_enctype(key->enctype); + if (!gk->krb5) { + ret = -ENOPKG; + goto err_tk; + } + + ret = rxgk_set_up_ciphers(conn, gk, key, gfp); + if (ret) + goto err_tk; + + /* Set the remaining number of bytes encrypted with this key that may + * be transmitted before rekeying. Note that the spec has been + * interpreted differently on this point... + */ + switch (key->bytelife) { + case 0: + case 63: + gk->bytes_remaining = LLONG_MAX; + break; + case 1 ... 62: + gk->bytes_remaining = 1LL << key->bytelife; + break; + default: + gk->bytes_remaining = key->bytelife; + break; + } + + /* Set the time after which rekeying must occur */ + if (key->lifetime) { + lifetime = min_t(u64, key->lifetime, INT_MAX / HZ); + lifetime *= HZ; + } else { + lifetime = MAX_JIFFY_OFFSET; + } + gk->expiry = jiffies + lifetime; + return gk; + +err_tk: + rxgk_put(gk); + _leave(" = %d", ret); + return ERR_PTR(ret); +} + +/* + * Use the server secret key to set up the ciphers that will be used to extract + * the token from a response packet. + */ +struct crypto_aead *rxgk_set_up_token_cipher(const struct krb5_buffer *server_key, + unsigned int enctype, gfp_t gfp) +{ + const struct krb5_enctype *krb5; + struct crypto_aead *ci; + __be32 *params; + size_t plen = 2 * sizeof(__be32) + server_key->len; + int ret; + + krb5 = crypto_krb5_find_enctype(enctype); + if (!krb5) + return ERR_PTR(-ENOPKG); + + params = kzalloc(plen, gfp); + if (!params) + return ERR_PTR(-ENOMEM); + + params[0] = htonl(KRB5_ENCRYPT_MODE); + params[1] = htonl(RXGK_SERVER_ENC_TOKEN); + memcpy(¶ms[2], server_key->data, server_key->len); + ret = rxgk_derive_cipher(krb5, params, plen, &ci); + kfree_sensitive(params); + return ret < 0 ? ERR_PTR(ret) : ci; +} From patchwork Fri Jan 10 01:03:09 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 13933387 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CA3FF206F05 for ; Fri, 10 Jan 2025 01:04:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736471055; cv=none; b=SxKxFyihl/58F0ozMx11PlI71rRaaxa1Z2mFkspwAXlOXb6b+hQsiAlhPgkQITHqzyYoNmuDSQWGoGiDSxLl4FnxSslrX1JYUaW1JqxcHVF0vKmQGUco0LPVzLHBvWMI1O8XqZ+oY08OQ/sqDIcHIndqbrff8O75K7FVNORZXy8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736471055; c=relaxed/simple; bh=UaavGsW5yiRIBBCqyz06QxViJ2hOP5S/TYqSQMg8eqQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ecfDwIkDV9EiY3D3PGGxCSFjVMv1+E5cjj6QtI+qHlUuBWsmnP20FD+Mf/r/krkRPmEAg94n/C53WRy/Mlc5qDZNqyHxMFBnMpALz+6L95nQmmbWHc8mj2WfqKo9nJD//YwDvyqQsYaxOCFYGf+Q2Y0ylIz16rp5t1de4+LCfXg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=OpGMjVax; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="OpGMjVax" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1736471049; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=KvJ/Bg+m3XcXyt+WY521dQYQwMndCy6asqYJN3pCYcc=; b=OpGMjVaxOO+nD3j4HuiHiVrIFqp19ybWLzc8Imq9JrbyAYbD2Yn+7qN+PNo9ax8xx5rTLZ RAoPTrdOlBSbaCCfpyddpgoxWwhPPmc3G98maYGUSsJB3+eplkz5pJPUK7FBJGW8zI8sDE Et/3hxzxh8B7yUJiqqMt3DJ7JME4dPk= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-220-H8bdV8sbPoyNPO7iHhasfw-1; Thu, 09 Jan 2025 20:04:05 -0500 X-MC-Unique: H8bdV8sbPoyNPO7iHhasfw-1 X-Mimecast-MFC-AGG-ID: H8bdV8sbPoyNPO7iHhasfw Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 04D46195608F; Fri, 10 Jan 2025 01:04:03 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.42.28.12]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id A748219560AF; Fri, 10 Jan 2025 01:03:58 +0000 (UTC) From: David Howells To: Herbert Xu , Chuck Lever Cc: David Howells , Trond Myklebust , "David S. Miller" , Marc Dionne , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 7/8] rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI) Date: Fri, 10 Jan 2025 01:03:09 +0000 Message-ID: <20250110010313.1471063-8-dhowells@redhat.com> In-Reply-To: <20250110010313.1471063-1-dhowells@redhat.com> References: <20250110010313.1471063-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 Implement the basic parts of the yfs-rxgk security class (security index 6) to support GSSAPI-negotiated security. Signed-off-by: David Howells cc: Herbert Xu cc: "David S. Miller" cc: Chuck Lever cc: Marc Dionne cc: Eric Dumazet cc: Jakub Kicinski cc: Paolo Abeni cc: Simon Horman cc: linux-afs@lists.infradead.org cc: linux-nfs@vger.kernel.org cc: linux-crypto@vger.kernel.org cc: netdev@vger.kernel.org --- include/trace/events/rxrpc.h | 36 ++ net/rxrpc/Makefile | 2 + net/rxrpc/ar-internal.h | 16 + net/rxrpc/conn_event.c | 2 +- net/rxrpc/output.c | 2 +- net/rxrpc/protocol.h | 20 + net/rxrpc/rxgk.c | 1100 ++++++++++++++++++++++++++++++++++ net/rxrpc/rxgk_app.c | 318 ++++++++++ net/rxrpc/rxgk_common.h | 14 + net/rxrpc/rxkad.c | 6 +- net/rxrpc/security.c | 3 + 11 files changed, 1515 insertions(+), 4 deletions(-) create mode 100644 net/rxrpc/rxgk.c create mode 100644 net/rxrpc/rxgk_app.c diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 2f119d18a061..168f3ab7c0e4 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -68,6 +68,38 @@ EM(rxkad_abort_resp_tkt_sname, "rxkad-resp-tk-sname") \ EM(rxkad_abort_resp_unknown_tkt, "rxkad-resp-unknown-tkt") \ EM(rxkad_abort_resp_version, "rxkad-resp-version") \ + /* RxGK security errors */ \ + EM(rxgk_abort_1_verify_mic_eproto, "rxgk1-vfy-mic-eproto") \ + EM(rxgk_abort_2_decrypt_eproto, "rxgk2-dec-eproto") \ + EM(rxgk_abort_2_short_data, "rxgk2-short-data") \ + EM(rxgk_abort_2_short_encdata, "rxgk2-short-encdata") \ + EM(rxgk_abort_2_short_header, "rxgk2-short-hdr") \ + EM(rxgk_abort_bad_key_number, "rxgk-bad-key-num") \ + EM(rxgk_abort_chall_key_expired, "rxgk-chall-key-exp") \ + EM(rxgk_abort_chall_no_key, "rxgk-chall-nokey") \ + EM(rxgk_abort_chall_short, "rxgk-chall-short") \ + EM(rxgk_abort_resp_auth_dec, "rxgk-resp-auth-dec") \ + EM(rxgk_abort_resp_bad_callid, "rxgk-resp-bad-callid") \ + EM(rxgk_abort_resp_bad_nonce, "rxgk-resp-bad-nonce") \ + EM(rxgk_abort_resp_bad_param, "rxgk-resp-bad-param") \ + EM(rxgk_abort_resp_call_ctr, "rxgk-resp-call-ctr") \ + EM(rxgk_abort_resp_call_state, "rxgk-resp-call-state") \ + EM(rxgk_abort_resp_internal_error, "rxgk-resp-int-error") \ + EM(rxgk_abort_resp_nopkg, "rxgk-resp-nopkg") \ + EM(rxgk_abort_resp_short_applen, "rxgk-resp-short-applen") \ + EM(rxgk_abort_resp_short_auth, "rxgk-resp-short-auth") \ + EM(rxgk_abort_resp_short_call_list, "rxgk-resp-short-callls") \ + EM(rxgk_abort_resp_short_packet, "rxgk-resp-short-packet") \ + EM(rxgk_abort_resp_short_yfs_klen, "rxgk-resp-short-yfs-klen") \ + EM(rxgk_abort_resp_short_yfs_key, "rxgk-resp-short-yfs-key") \ + EM(rxgk_abort_resp_short_yfs_tkt, "rxgk-resp-short-yfs-tkt") \ + EM(rxgk_abort_resp_tok_dec, "rxgk-resp-tok-dec") \ + EM(rxgk_abort_resp_tok_internal_error, "rxgk-resp-tok-int-err") \ + EM(rxgk_abort_resp_tok_keyerr, "rxgk-resp-tok-keyerr") \ + EM(rxgk_abort_resp_tok_nokey, "rxgk-resp-tok-nokey") \ + EM(rxgk_abort_resp_tok_nopkg, "rxgk-resp-tok-nopkg") \ + EM(rxgk_abort_resp_tok_short, "rxgk-resp-tok-short") \ + EM(rxgk_abort_resp_xdr_align, "rxgk-resp-xdr-align") \ /* rxrpc errors */ \ EM(rxrpc_abort_call_improper_term, "call-improper-term") \ EM(rxrpc_abort_call_reset, "call-reset") \ @@ -454,6 +486,8 @@ EM(rxrpc_tx_point_call_final_resend, "CallFinalResend") \ EM(rxrpc_tx_point_conn_abort, "ConnAbort") \ EM(rxrpc_tx_point_reject, "Reject") \ + EM(rxrpc_tx_point_rxgk_challenge, "RxGKChall") \ + EM(rxrpc_tx_point_rxgk_response, "RxGKResp") \ EM(rxrpc_tx_point_rxkad_challenge, "RxkadChall") \ EM(rxrpc_tx_point_rxkad_response, "RxkadResp") \ EM(rxrpc_tx_point_version_keepalive, "VerKeepalive") \ @@ -472,6 +506,7 @@ #define rxrpc_txbuf_traces \ EM(rxrpc_txbuf_alloc_data, "ALLOC DATA ") \ + EM(rxrpc_txbuf_alloc_response, "ALLOC RESP ") \ EM(rxrpc_txbuf_free, "FREE ") \ EM(rxrpc_txbuf_get_buffer, "GET BUFFER ") \ EM(rxrpc_txbuf_get_trans, "GET TRANS ") \ @@ -479,6 +514,7 @@ EM(rxrpc_txbuf_put_cleaned, "PUT CLEANED") \ EM(rxrpc_txbuf_put_nomem, "PUT NOMEM ") \ EM(rxrpc_txbuf_put_rotated, "PUT ROTATED") \ + EM(rxrpc_txbuf_put_response_tx, "PUT RESP TX") \ EM(rxrpc_txbuf_put_send_aborted, "PUT SEND-X ") \ EM(rxrpc_txbuf_put_trans, "PUT TRANS ") \ EM(rxrpc_txbuf_see_lost, "SEE LOST ") \ diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile index 9c8eb1471054..2ef05701d6d1 100644 --- a/net/rxrpc/Makefile +++ b/net/rxrpc/Makefile @@ -40,6 +40,8 @@ rxrpc-$(CONFIG_PROC_FS) += proc.o rxrpc-$(CONFIG_RXKAD) += rxkad.o rxrpc-$(CONFIG_SYSCTL) += sysctl.o rxrpc-$(CONFIG_RXGK) += \ + rxgk.o \ + rxgk_app.o \ rxgk_kdf.o obj-$(CONFIG_RXPERF) += rxperf.o diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 2392f2e062c2..736dc6ea20ac 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -31,6 +31,7 @@ struct key_preparsed_payload; struct rxrpc_connection; struct rxrpc_txbuf; struct rxrpc_txqueue; +struct rxgk_context; /* * Mark applied to socket buffers in skb->mark. skb->priority is used @@ -280,6 +281,11 @@ struct rxrpc_security { /* clear connection security */ void (*clear)(struct rxrpc_connection *); + + /* Default ticket -> key decoder */ + int (*default_decode_ticket)(struct rxrpc_connection *conn, struct sk_buff *skb, + unsigned int ticket_offset, unsigned int ticket_len, + struct key **_key); }; /* @@ -527,7 +533,9 @@ struct rxrpc_connection { u32 nonce; /* response re-use preventer */ } rxkad; struct { + struct rxgk_context *keys[1]; u64 start_time; /* The start time for TK derivation */ + u8 nonce[20]; /* Response re-use preventer */ } rxgk; }; unsigned long flags; @@ -870,6 +878,8 @@ struct rxrpc_txbuf { unsigned short len; /* Amount of data in buffer */ unsigned short space; /* Remaining data space */ unsigned short offset; /* Offset of fill point */ + unsigned short crypto_header; /* Size of crypto header */ + unsigned short sec_header; /* Size of security header */ unsigned short pkt_len; /* Size of packet content */ unsigned short alloc_size; /* Amount of bufferage allocated */ unsigned int flags; @@ -1295,6 +1305,7 @@ static inline struct rxrpc_net *rxrpc_net(struct net *net) /* * output.c */ +ssize_t do_udp_sendmsg(struct socket *socket, struct msghdr *msg, size_t len); void rxrpc_send_ACK(struct rxrpc_call *call, u8 ack_reason, rxrpc_serial_t serial, enum rxrpc_propose_ack_trace why); void rxrpc_send_probe_for_pmtud(struct rxrpc_call *call); @@ -1366,6 +1377,11 @@ void rxrpc_call_add_rtt(struct rxrpc_call *call, enum rxrpc_rtt_rx_trace why, ktime_t rxrpc_get_rto_backoff(struct rxrpc_call *call, bool retrans); void rxrpc_call_init_rtt(struct rxrpc_call *call); +/* + * rxgk.c + */ +extern const struct rxrpc_security rxgk_yfs; + /* * rxkad.c */ diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index 713e04394ceb..3c1663318626 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -57,7 +57,7 @@ int rxrpc_abort_conn(struct rxrpc_connection *conn, struct sk_buff *skb, sp->hdr.seq, abort_code, err); rxrpc_poke_conn(conn, rxrpc_conn_get_poke_abort); } - return -EPROTO; + return err; } /* diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 6f7a125d6e90..6e2def0d8773 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -18,7 +18,7 @@ extern int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len); -static ssize_t do_udp_sendmsg(struct socket *socket, struct msghdr *msg, size_t len) +ssize_t do_udp_sendmsg(struct socket *socket, struct msghdr *msg, size_t len) { struct sockaddr *sa = msg->msg_name; struct sock *sk = socket->sk; diff --git a/net/rxrpc/protocol.h b/net/rxrpc/protocol.h index 42f70e4636f8..f8bfec12bc7e 100644 --- a/net/rxrpc/protocol.h +++ b/net/rxrpc/protocol.h @@ -181,4 +181,24 @@ struct rxkad_response { __be32 ticket_len; /* Kerberos ticket length */ } __packed; +/* + * GSSAPI security type-4 and type-6 data header. + */ +struct rxgk_header { + __be32 epoch; + __be32 cid; + __be32 call_number; + __be32 seq; + __be32 sec_index; + __be32 data_len; +} __packed; + +/* + * GSSAPI security type-4 and type-6 response packet header. + */ +struct rxgk_response { + __be64 start_time; + __be32 token_len; +} __packed; + #endif /* _LINUX_RXRPC_PACKET_H */ diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c new file mode 100644 index 000000000000..7344f19b8ae2 --- /dev/null +++ b/net/rxrpc/rxgk.c @@ -0,0 +1,1100 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* GSSAPI-based RxRPC security + * + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include "ar-internal.h" +#include "rxgk_common.h" + +/* + * Parse the information from a server key + */ +static int rxgk_preparse_server_key(struct key_preparsed_payload *prep) +{ + const struct krb5_enctype *krb5; + struct krb5_buffer *server_key = (void *)&prep->payload.data[2]; + unsigned int service, sec_class, kvno, enctype; + int n = 0; + + _enter("%zu", prep->datalen); + + if (sscanf(prep->orig_description, "%u:%u:%u:%u%n", + &service, &sec_class, &kvno, &enctype, &n) != 4) + return -EINVAL; + + if (prep->orig_description[n]) + return -EINVAL; + + krb5 = crypto_krb5_find_enctype(enctype); + if (!krb5) + return -ENOPKG; + + prep->payload.data[0] = (struct krb5_enctype *)krb5; + + if (prep->datalen != krb5->key_len) + return -EKEYREJECTED; + + server_key->len = prep->datalen; + server_key->data = kmemdup(prep->data, prep->datalen, GFP_KERNEL); + if (!server_key->data) + return -ENOMEM; + + _leave(" = 0"); + return 0; +} + +static void rxgk_free_server_key(union key_payload *payload) +{ + struct krb5_buffer *server_key = (void *)&payload->data[2]; + + kfree_sensitive(server_key->data); +} + +static void rxgk_free_preparse_server_key(struct key_preparsed_payload *prep) +{ + rxgk_free_server_key(&prep->payload); +} + +static void rxgk_destroy_server_key(struct key *key) +{ + rxgk_free_server_key(&key->payload); +} + +static void rxgk_describe_server_key(const struct key *key, struct seq_file *m) +{ + const struct krb5_enctype *krb5 = key->payload.data[0]; + + if (krb5) + seq_printf(m, ": %s", krb5->name); +} + +static struct rxgk_context *rxgk_get_key(struct rxrpc_connection *conn, + u16 *specific_key_number) +{ + refcount_inc(&conn->rxgk.keys[0]->usage); + return conn->rxgk.keys[0]; +} + +/* + * initialise connection security + */ +static int rxgk_init_connection_security(struct rxrpc_connection *conn, + struct rxrpc_key_token *token) +{ + struct rxgk_context *gk; + int ret; + + _enter("{%d},{%x}", conn->debug_id, key_serial(conn->key)); + + conn->security_ix = token->security_index; + conn->security_level = token->rxgk->level; + + if (rxrpc_conn_is_client(conn)) { + conn->rxgk.start_time = ktime_get(); + do_div(conn->rxgk.start_time, 100); + } + + gk = rxgk_generate_transport_key(conn, token->rxgk, 0, GFP_NOFS); + if (IS_ERR(gk)) + return PTR_ERR(gk); + conn->rxgk.keys[0] = gk; + + switch (conn->security_level) { + case RXRPC_SECURITY_PLAIN: + case RXRPC_SECURITY_AUTH: + case RXRPC_SECURITY_ENCRYPT: + break; + default: + ret = -EKEYREJECTED; + goto error; + } + + ret = 0; +error: + _leave(" = %d", ret); + return ret; +} + +/* + * Clean up the crypto on a call. + */ +static void rxgk_free_call_crypto(struct rxrpc_call *call) +{ +} + +/* + * Work out how much data we can put in a packet. + */ +static struct rxrpc_txbuf *rxgk_alloc_txbuf(struct rxrpc_call *call, size_t remain, gfp_t gfp) +{ + enum krb5_crypto_mode mode; + struct rxgk_context *gk; + struct rxrpc_txbuf *txb; + size_t shdr, alloc, limit, part, offset, gap; + + switch (call->conn->security_level) { + default: + alloc = umin(remain, RXRPC_JUMBO_DATALEN); + return rxrpc_alloc_data_txbuf(call, alloc, 1, gfp); + case RXRPC_SECURITY_AUTH: + shdr = 0; + mode = KRB5_CHECKSUM_MODE; + break; + case RXRPC_SECURITY_ENCRYPT: + shdr = sizeof(struct rxgk_header); + mode = KRB5_ENCRYPT_MODE; + break; + } + + gk = rxgk_get_key(call->conn, NULL); + if (IS_ERR(gk)) + return NULL; + + /* Work out the maximum amount of data that will fit. */ + alloc = RXRPC_JUMBO_DATALEN; + limit = crypto_krb5_how_much_data(gk->krb5, mode, &alloc, &offset); + + if (remain < limit - shdr) { + part = remain; + alloc = crypto_krb5_how_much_buffer(gk->krb5, mode, + shdr + part, &offset); + gap = 0; + } else { + part = limit - shdr; + gap = RXRPC_JUMBO_DATALEN - alloc; + alloc = RXRPC_JUMBO_DATALEN; + } + + rxgk_put(gk); + + txb = rxrpc_alloc_data_txbuf(call, alloc, 16, gfp); + if (!txb) + return NULL; + + txb->crypto_header = offset; + txb->sec_header = shdr; + txb->offset += offset + shdr; + txb->space = part; + + /* Clear excess space in the packet */ + if (gap) { + struct rxrpc_wire_header *whdr = txb->kvec[0].iov_base; + void *p = whdr + 1; + + memset(p + alloc - gap, 0, gap); + } + return txb; +} + +/* + * Integrity mode (sign a packet - level 1 security) + */ +static int rxgk_secure_packet_integrity(const struct rxrpc_call *call, + struct rxgk_context *gk, + struct rxrpc_txbuf *txb) +{ + struct rxrpc_wire_header *whdr = txb->kvec[0].iov_base; + struct aead_request *req; + struct rxgk_header *hdr; + struct scatterlist sg[2]; + size_t data_offset; + void *payload = whdr + 1; + int ret = -ENOMEM; + + _enter(""); + + req = kzalloc(sizeof(*req) + crypto_aead_reqsize(gk->tx_crypto) + + sizeof(*hdr), GFP_NOFS); + if (!req) + goto error_gk; + + txb->pkt_len = crypto_krb5_how_much_buffer(gk->krb5, KRB5_CHECKSUM_MODE, + txb->len, &data_offset); + + hdr = (void *)req + sizeof(*req) + crypto_aead_reqsize(gk->tx_crypto); + hdr->epoch = htonl(call->conn->proto.epoch); + hdr->cid = htonl(call->cid); + hdr->call_number = htonl(call->call_id); + hdr->seq = htonl(txb->seq); + hdr->sec_index = htonl(call->security_ix); + hdr->data_len = htonl(txb->len); + + sg_init_table(sg, 2); + sg_set_buf(&sg[0], hdr, sizeof(*hdr)); + sg_set_buf(&sg[1], payload, txb->pkt_len); + + aead_request_set_tfm(req, gk->tx_crypto); + aead_request_set_crypt(req, sg, sg, data_offset + txb->len, NULL); + aead_request_set_ad(req, sizeof(*hdr)); + + ret = crypto_aead_encrypt(req); + if (ret < 0) + goto error; + + if (txb->pkt_len == RXRPC_JUMBO_DATALEN) + txb->jumboable = true; + gk->bytes_remaining -= txb->pkt_len; + +error: + aead_request_free(req); +error_gk: + rxgk_put(gk); + _leave(" = %d", ret); + return ret; +} + +/* + * wholly encrypt a packet (level 2 security) + */ +static int rxgk_secure_packet_encrypted(const struct rxrpc_call *call, + struct rxgk_context *gk, + struct rxrpc_txbuf *txb) +{ + struct rxrpc_wire_header *whdr = txb->kvec[0].iov_base; + struct aead_request *req; + struct rxgk_header *hdr; + struct scatterlist sg[1]; + size_t data_offset, data_len; + void *payload = whdr + 1; + int ret; + + _enter("%x", txb->len); + + req = kzalloc(sizeof(*req) + crypto_aead_reqsize(gk->tx_crypto), + GFP_NOFS); + if (!req) + goto error_gk; + + data_len = sizeof(*hdr) + txb->len; + txb->pkt_len = crypto_krb5_how_much_buffer(gk->krb5, KRB5_ENCRYPT_MODE, + data_len, &data_offset); + + /* Insert the header into the buffer. */ + hdr = payload + txb->crypto_header; + hdr->epoch = htonl(call->conn->proto.epoch); + hdr->cid = htonl(call->cid); + hdr->call_number = htonl(call->call_id); + hdr->seq = htonl(txb->seq); + hdr->sec_index = htonl(call->security_ix); + hdr->data_len = htonl(txb->len); + + sg_init_one(&sg[0], payload, txb->pkt_len); + ret = crypto_krb5_confound_buffer(gk->krb5, sg, 1, NULL, 0, 0); + if (ret < 0) + goto error; + + aead_request_set_tfm(req, gk->tx_crypto); + aead_request_set_crypt(req, sg, sg, data_offset + data_len, NULL); + + ret = crypto_aead_encrypt(req); + if (ret < 0) + goto error; + + if (txb->pkt_len == RXRPC_JUMBO_DATALEN) + txb->jumboable = true; + gk->bytes_remaining -= txb->pkt_len; + +error: + aead_request_free(req); +error_gk: + rxgk_put(gk); + _leave(" = %d", ret); + return ret; +} + +/* + * checksum an RxRPC packet header + */ +static int rxgk_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) +{ + struct rxgk_context *gk; + int ret; + + _enter("{%d{%x}},{#%u},%u,", + call->debug_id, key_serial(call->conn->key), txb->seq, txb->len); + + gk = rxgk_get_key(call->conn, NULL); + if (IS_ERR(gk)) + return PTR_ERR(gk) == -ESTALE ? -EKEYREJECTED : PTR_ERR(gk); + + ret = key_validate(call->conn->key); + if (ret < 0) + return ret; + + txb->cksum = htons(gk->key_number); + + switch (call->conn->security_level) { + case RXRPC_SECURITY_PLAIN: + rxgk_put(gk); + txb->pkt_len = txb->len; + return 0; + case RXRPC_SECURITY_AUTH: + return rxgk_secure_packet_integrity(call, gk, txb); + case RXRPC_SECURITY_ENCRYPT: + return rxgk_secure_packet_encrypted(call, gk, txb); + default: + rxgk_put(gk); + return -EPERM; + } +} + +/* + * Integrity mode (check the signature on a packet - level 1 security) + */ +static int rxgk_verify_packet_integrity(struct rxrpc_call *call, + struct rxgk_context *gk, + struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct aead_request *req; + struct rxgk_header *hdr; + struct scatterlist sg[17]; + unsigned int offset = sp->offset, len = sp->len; + size_t data_offset, data_len = len; + int ret = -ENOMEM, nr_sg; + + _enter(""); + + data_offset = sp->offset; + data_len = len; + crypto_krb5_where_is_the_data(gk->krb5, KRB5_CHECKSUM_MODE, + &data_offset, &data_len); + sp->offset = data_offset; + sp->len = data_len; + + req = kzalloc(sizeof(*req) + crypto_aead_reqsize(gk->rx_crypto) + + sizeof(*hdr), GFP_NOFS); + if (!req) + goto error_gk; + + aead_request_set_tfm(req, gk->rx_crypto); + + hdr = (void *)req + sizeof(*req) + crypto_aead_reqsize(gk->rx_crypto); + hdr->epoch = htonl(call->conn->proto.epoch); + hdr->cid = htonl(call->cid); + hdr->call_number = htonl(call->call_id); + hdr->seq = htonl(sp->hdr.seq); + hdr->sec_index = htonl(call->security_ix); + hdr->data_len = htonl(data_len); + + sg_init_table(sg, ARRAY_SIZE(sg)); + sg_set_buf(&sg[0], hdr, sizeof(*hdr)); + nr_sg = skb_to_sgvec(skb, sg + 1, offset, len); + if (unlikely(nr_sg < 0)) { + ret = nr_sg; + goto error; + } + nr_sg++; + + aead_request_set_crypt(req, sg, sg, len, NULL); + aead_request_set_ad(req, sizeof(*hdr)); + + ret = crypto_aead_decrypt(req); + if (ret < 0) { + if (ret == -EPROTO) + rxrpc_abort_eproto(call, skb, RXGK_INCONSISTENCY, + rxgk_abort_1_verify_mic_eproto); + goto error; + } + + ret = 0; +error: + aead_request_free(req); +error_gk: + rxgk_put(gk); + _leave(" = %d", ret); + return ret; +} + +/* + * Decrypt an encrypted packet (level 2 security). + */ +static int rxgk_verify_packet_encrypted(struct rxrpc_call *call, + struct rxgk_context *gk, + struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct aead_request *req; + struct rxgk_header *hdr; + struct scatterlist sg[16]; + size_t offset = sp->offset, len = sp->len; + int ret; + + _enter(""); + + req = kzalloc(sizeof(*req) + crypto_aead_reqsize(gk->rx_crypto) + + sizeof(*hdr), GFP_NOFS); + if (!req) + goto error_gk; + + hdr = (void *)req + sizeof(*req) + crypto_aead_reqsize(gk->rx_crypto); + + sg_init_table(sg, ARRAY_SIZE(sg)); + ret = skb_to_sgvec(skb, sg, offset, len); + if (unlikely(ret < 0)) + goto error; + + aead_request_set_tfm(req, gk->rx_crypto); + aead_request_set_crypt(req, sg, sg, len, NULL); + + ret = crypto_aead_decrypt(req); + if (ret < 0) { + if (ret == -EPROTO) + rxrpc_abort_eproto(call, skb, RXGK_INCONSISTENCY, + rxgk_abort_2_decrypt_eproto); + goto error; + } + + crypto_krb5_where_is_the_data(gk->krb5, KRB5_ENCRYPT_MODE, + &offset, &len); + + if (len < sizeof(*hdr)) { + ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT, + rxgk_abort_2_short_header); + goto error; + } + + /* Extract the header from the skb */ + ret = skb_copy_bits(skb, offset, hdr, sizeof(*hdr)); + if (ret < 0) { + ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT, + rxgk_abort_2_short_encdata); + goto error; + } + offset += sizeof(*hdr); + len -= sizeof(*hdr); + + if (ntohl(hdr->epoch) != call->conn->proto.epoch || + ntohl(hdr->cid) != call->cid || + ntohl(hdr->call_number) != call->call_id || + ntohl(hdr->seq) != sp->hdr.seq || + ntohl(hdr->sec_index) != call->security_ix || + ntohl(hdr->data_len) > len) { + ret = rxrpc_abort_eproto(call, skb, RXGK_SEALED_INCON, + rxgk_abort_2_short_data); + goto error; + } + + sp->offset = offset; + sp->len = ntohl(hdr->data_len); + ret = 0; +error: + aead_request_free(req); +error_gk: + rxgk_put(gk); + _leave(" = %d", ret); + return ret; +} + +/* + * Verify the security on a received packet or subpacket (if part of a + * jumbo packet). + */ +static int rxgk_verify_packet(struct rxrpc_call *call, struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxgk_context *gk; + u16 key_number = sp->hdr.cksum; + + _enter("{%d{%x}},{#%u}", + call->debug_id, key_serial(call->conn->key), sp->hdr.seq); + + gk = rxgk_get_key(call->conn, &key_number); + if (IS_ERR(gk)) { + switch (PTR_ERR(gk)) { + case -ESTALE: + return rxrpc_abort_eproto(call, skb, RXGK_BADKEYNO, + rxgk_abort_bad_key_number); + default: + return PTR_ERR(gk); + } + } + + switch (call->conn->security_level) { + case RXRPC_SECURITY_PLAIN: + return 0; + case RXRPC_SECURITY_AUTH: + return rxgk_verify_packet_integrity(call, gk, skb); + case RXRPC_SECURITY_ENCRYPT: + return rxgk_verify_packet_encrypted(call, gk, skb); + default: + rxgk_put(gk); + return -ENOANO; + } +} + +/* + * Allocate memory to hold a challenge or a response packet. We're not running + * in the io_thread, so we can't use ->tx_alloc. + */ +static struct page *rxgk_alloc_packet(size_t total_len) +{ + gfp_t gfp = GFP_NOFS; + int order; + + order = get_order(total_len); + if (order > 0) + gfp |= __GFP_COMP; + return alloc_pages(gfp, order); +} + +/* + * Issue a challenge. + */ +static int rxgk_issue_challenge(struct rxrpc_connection *conn) +{ + struct rxrpc_wire_header *whdr; + struct bio_vec bvec[1]; + struct msghdr msg; + struct page *page; + size_t len = sizeof(*whdr) + sizeof(conn->rxgk.nonce); + u32 serial; + int ret; + + _enter("{%d}", conn->debug_id); + + get_random_bytes(&conn->rxgk.nonce, sizeof(conn->rxgk.nonce)); + + /* We can't use conn->tx_alloc without a lock */ + page = rxgk_alloc_packet(sizeof(*whdr) + sizeof(conn->rxgk.nonce)); + if (!page) + return -ENOMEM; + + bvec[0].bv_page = page; + bvec[0].bv_len = len; + iov_iter_bvec(&msg.msg_iter, WRITE, bvec, 1, len); + + msg.msg_name = &conn->peer->srx.transport; + msg.msg_namelen = conn->peer->srx.transport_len; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = MSG_SPLICE_PAGES; + + whdr = page_address(page); + whdr->epoch = htonl(conn->proto.epoch); + whdr->cid = htonl(conn->proto.cid); + whdr->callNumber = 0; + whdr->seq = 0; + whdr->type = RXRPC_PACKET_TYPE_CHALLENGE; + whdr->flags = conn->out_clientflag; + whdr->userStatus = 0; + whdr->securityIndex = conn->security_ix; + whdr->_rsvd = 0; + whdr->serviceId = htons(conn->service_id); + + memcpy(whdr + 1, conn->rxgk.nonce, sizeof(conn->rxgk.nonce)); + + serial = rxrpc_get_next_serials(conn, 1); + whdr->serial = htonl(serial); + + ret = do_udp_sendmsg(conn->local->socket, &msg, len); + if (ret > 0) + conn->peer->last_tx_at = ktime_get_seconds(); + __free_page(page); + + if (ret < 0) { + trace_rxrpc_tx_fail(conn->debug_id, serial, ret, + rxrpc_tx_point_rxgk_challenge); + return -EAGAIN; + } + + trace_rxrpc_tx_packet(conn->debug_id, whdr, + rxrpc_tx_point_rxgk_challenge); + _leave(" = 0"); + return 0; +} + +/* + * Send a response packet. + */ +static int rxgk_send_response(struct rxrpc_connection *conn, + struct page *page, size_t total_len) +{ + struct rxrpc_wire_header *whdr = page_address(page); + struct bio_vec bvec[1]; + struct msghdr msg; + u32 serial; + int ret; + + _enter(""); + + msg.msg_name = &conn->peer->srx.transport; + msg.msg_namelen = conn->peer->srx.transport_len; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = MSG_SPLICE_PAGES; + + bvec[0].bv_page = page; + bvec[0].bv_len = total_len; + iov_iter_bvec(&msg.msg_iter, WRITE, bvec, 1, total_len); + + serial = rxrpc_get_next_serials(conn, 1); + whdr->serial = htonl(serial); + + ret = do_udp_sendmsg(conn->local->socket, &msg, total_len); + if (ret < 0) { + trace_rxrpc_tx_fail(conn->debug_id, serial, ret, + rxrpc_tx_point_rxgk_response); + return -EAGAIN; + } + + conn->peer->last_tx_at = ktime_get_seconds(); + _leave(" = 0"); + return 0; +} + +/* + * Construct the authenticator to go in the response packet + * + * struct RXGK_Authenticator { + * opaque nonce[20]; + * opaque appdata<>; + * RXGK_Level level; + * unsigned int epoch; + * unsigned int cid; + * unsigned int call_numbers<>; + * }; + */ +static void rxgk_construct_authenticator(struct rxrpc_connection *conn, + const u8 *nonce, __be32 *xdr) +{ + memcpy(xdr, nonce, 20); + xdr += 5; + *xdr++ = htonl(0); /* appdata len */ + *xdr++ = htonl(conn->security_level); + *xdr++ = htonl(conn->proto.epoch); + *xdr++ = htonl(conn->proto.cid); + *xdr++ = htonl(4); /* # call_numbers */ + *xdr++ = htonl(conn->channels[0].call_counter); + *xdr++ = htonl(conn->channels[1].call_counter); + *xdr++ = htonl(conn->channels[2].call_counter); + *xdr = htonl(conn->channels[3].call_counter); +} + +/* + * Construct the response. + * + * struct RXGK_Response { + * rxgkTime start_time; + * RXGK_Data token; + * opaque authenticator + * }; + */ +static int rxgk_construct_response(struct rxrpc_connection *conn, + struct sk_buff *challenge, + const u8 *nonce) +{ + struct rxrpc_wire_header *whdr; + struct rxrpc_skb_priv *csp = rxrpc_skb(challenge); + struct aead_request *req; + struct rxgk_context *gk; + struct scatterlist sg[1]; + struct page *page; + size_t resp_len, auth_len, authx_len, auth_offset, authx_offset; + __be32 *xdr; + void *payload; + int ret; + + gk = rxgk_get_key(conn, NULL); + if (IS_ERR(gk)) + return PTR_ERR(gk); + + ret = -ENOMEM; + req = kzalloc(sizeof(*req) + crypto_aead_reqsize(gk->resp_crypto), GFP_NOFS); + if (!req) + goto error_gk; + + auth_len = 20 + 4 /* appdatalen */ + 12 + (1 + 4) * 4; + authx_len = crypto_krb5_how_much_buffer(gk->krb5, KRB5_ENCRYPT_MODE, + auth_len, &auth_offset); + + resp_len = 8; + resp_len += 4 + xdr_round_up(gk->key->ticket.len); + resp_len += 4 + xdr_round_up(authx_len); + + page = rxgk_alloc_packet(sizeof(*whdr) + resp_len); + if (!page) + goto error_req; + + whdr = page_address(page); + whdr->epoch = htonl(csp->hdr.epoch); + whdr->cid = htonl(csp->hdr.cid); + whdr->callNumber = 0; + whdr->serial = 0; + whdr->seq = 0; + whdr->type = RXRPC_PACKET_TYPE_RESPONSE; + whdr->flags = conn->out_clientflag; + whdr->userStatus = 0; + whdr->securityIndex = csp->hdr.securityIndex; + whdr->cksum = htons(gk->key_number); + whdr->serviceId = htons(csp->hdr.serviceId); + + payload = whdr + 1; + xdr = payload; + + *xdr++ = htonl(upper_32_bits(conn->rxgk.start_time)); + *xdr++ = htonl(lower_32_bits(conn->rxgk.start_time)); + *xdr++ = htonl(gk->key->ticket.len); + memcpy(xdr, gk->key->ticket.data, xdr_round_up(gk->key->ticket.len)); + xdr += xdr_round_up(gk->key->ticket.len) / sizeof(*xdr); + *xdr++ = htonl(authx_len); + + authx_offset = (void *)xdr - payload; + + xdr = (void *)payload + authx_offset + auth_offset; + rxgk_construct_authenticator(conn, nonce, xdr); + + sg_init_one(sg, payload + authx_offset, authx_len); + + aead_request_set_tfm(req, gk->resp_crypto); + aead_request_set_crypt(req, sg, sg, auth_offset + auth_len, NULL); + + ret = crypto_aead_encrypt(req); + if (ret < 0) + goto error; + + ret = rxgk_send_response(conn, page, sizeof(*whdr) + authx_offset + authx_len); +error: + __free_page(page); +error_req: + kfree_sensitive(req); +error_gk: + rxgk_put(gk); + _leave(" = %d", ret); + return ret; +} + +/* + * Respond to a challenge packet + */ +static int rxgk_respond_to_challenge(struct rxrpc_connection *conn, + struct sk_buff *skb) +{ + u8 nonce[20]; + + _enter("{%d,%x}", conn->debug_id, key_serial(conn->key)); + + if (!conn->key) + return rxrpc_abort_conn(conn, skb, RX_PROTOCOL_ERROR, -EPROTO, + rxgk_abort_chall_no_key); + + if (key_validate(conn->key) < 0) + return rxrpc_abort_conn(conn, skb, RXGK_EXPIRED, -EPROTO, + rxgk_abort_chall_key_expired); + + if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), + nonce, sizeof(nonce)) < 0) + return rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO, + rxgk_abort_chall_short); + + return rxgk_construct_response(conn, skb, nonce); +} + +/* + * Verify the authenticator. + * + * struct RXGK_Authenticator { + * opaque nonce[20]; + * opaque appdata<>; + * RXGK_Level level; + * unsigned int epoch; + * unsigned int cid; + * unsigned int call_numbers<>; + * }; + */ +static int rxgk_do_verify_authenticator(struct rxrpc_connection *conn, + const struct krb5_enctype *krb5, + struct sk_buff *skb, + __be32 *p, __be32 *end) +{ + u32 app_len, call_count, level, epoch, cid, i; + + _enter(""); + + if (memcmp(p, conn->rxgk.nonce, 20) != 0) + return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO, + rxgk_abort_resp_bad_nonce); + p += 20 / sizeof(__be32); + + app_len = ntohl(*p++); + if (app_len > (end - p) * sizeof(__be32)) + return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO, + rxgk_abort_resp_short_applen); + + p += xdr_round_up(app_len) / sizeof(__be32); + if (end - p < 4) + return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO, + rxgk_abort_resp_short_applen); + + level = ntohl(*p++); + epoch = ntohl(*p++); + cid = ntohl(*p++); + call_count = ntohl(*p++); + + if (level != conn->security_level || + epoch != conn->proto.epoch || + cid != conn->proto.cid || + call_count > 4) + return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO, + rxgk_abort_resp_bad_param); + + if (end - p < call_count) + return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO, + rxgk_abort_resp_short_call_list); + + for (i = 0; i < call_count; i++) { + u32 call_id = ntohl(*p++); + + if (call_id > INT_MAX) + return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO, + rxgk_abort_resp_bad_callid); + + if (call_id < conn->channels[i].call_counter) + return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO, + rxgk_abort_resp_call_ctr); + + if (call_id > conn->channels[i].call_counter) { + if (conn->channels[i].call) + return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO, + rxgk_abort_resp_call_state); + + conn->channels[i].call_counter = call_id; + } + } + + _leave(" = 0"); + return 0; +} + +/* + * Extract the authenticator and verify it. + */ +static int rxgk_verify_authenticator(struct rxrpc_connection *conn, + const struct krb5_enctype *krb5, + struct sk_buff *skb, + unsigned int auth_offset, unsigned int auth_len) +{ + void *auth; + __be32 *p; + int ret; + + auth = kmalloc(auth_len, GFP_NOFS); + if (!auth) + return -ENOMEM; + + ret = skb_copy_bits(skb, auth_offset, auth, auth_len); + if (ret < 0) { + ret = rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO, + rxgk_abort_resp_short_auth); + goto error; + } + + p = auth; + ret = rxgk_do_verify_authenticator(conn, krb5, skb, p, p + auth_len); +error: + kfree(auth); + return ret; +} + +/* + * Verify a response. + * + * struct RXGK_Response { + * rxgkTime start_time; + * RXGK_Data token; + * opaque authenticator + * }; + */ +static int rxgk_verify_response(struct rxrpc_connection *conn, + struct sk_buff *skb) +{ + const struct krb5_enctype *krb5; + struct rxrpc_key_token *token; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxgk_response rhdr; + struct rxgk_context *gk; + struct key *key = NULL; + unsigned int offset = sizeof(struct rxrpc_wire_header); + unsigned int len = skb->len - sizeof(struct rxrpc_wire_header); + unsigned int token_offset, token_len; + size_t auth_offset, auth_len; + __be32 xauth_len; + int ret; + + _enter("{%d}", conn->debug_id); + + /* Parse the RXGK_Response object */ + if (sizeof(rhdr) + sizeof(__be32) > len) + goto short_packet; + + if (skb_copy_bits(skb, offset, &rhdr, sizeof(rhdr)) < 0) + goto short_packet; + offset += sizeof(rhdr); + len -= sizeof(rhdr); + + token_offset = offset; + token_len = ntohl(rhdr.token_len); + if (xdr_round_up(token_len) + sizeof(__be32) > len) + goto short_packet; + + offset += xdr_round_up(token_len); + len -= xdr_round_up(token_len); + + if (skb_copy_bits(skb, offset, &xauth_len, sizeof(xauth_len)) < 0) + goto short_packet; + offset += sizeof(xauth_len); + len -= sizeof(xauth_len); + + auth_offset = offset; + auth_len = ntohl(xauth_len); + if (auth_len < len) + goto short_packet; + if (auth_len & 3) + goto inconsistent; + if (auth_len < 20 + 9 * 4) + goto auth_too_short; + + /* We need to extract and decrypt the token and instantiate a session + * key for it. This bit, however, is application-specific. If + * possible, we use a default parser, but we might end up bumping this + * to the app to deal with - which might mean a round trip to + * userspace. + */ + ret = rxgk_extract_token(conn, skb, token_offset, token_len, &key); + if (ret < 0) + goto out; + + /* We now have a key instantiated from the decrypted ticket. We can + * pass this to the application so that they can parse the ticket + * content and we can use the session key it contains to derive the + * keys we need. + * + * Note that we have to switch enctype at this point as the enctype of + * the ticket doesn't necessarily match that of the transport. + */ + token = key->payload.data[0]; + conn->security_level = token->rxgk->level; + conn->rxgk.start_time = __be64_to_cpu(rhdr.start_time); + + gk = rxgk_generate_transport_key(conn, token->rxgk, sp->hdr.cksum, GFP_NOFS); + if (IS_ERR(gk)) { + ret = PTR_ERR(gk); + goto cant_get_token; + } + + krb5 = gk->krb5; + + /* Decrypt, parse and verify the authenticator. */ + ret = rxgk_decrypt_skb(gk->resp_crypto, skb, + &auth_offset, &auth_len); + if (ret < 0) { + rxrpc_abort_conn(conn, skb, RXGK_SEALED_INCON, ret, + rxgk_abort_resp_auth_dec); + goto out; + } + + ret = rxgk_verify_authenticator(conn, krb5, skb, auth_offset, auth_len); + if (ret < 0) + goto out; + + conn->key = key; + key = NULL; + ret = 0; +out: + key_put(key); + _leave(" = %d", ret); + return ret; + +inconsistent: + ret = rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO, + rxgk_abort_resp_xdr_align); + goto out; +auth_too_short: + ret = rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO, + rxgk_abort_resp_short_auth); + goto out; +short_packet: + ret = rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO, + rxgk_abort_resp_short_packet); + goto out; + +cant_get_token: + switch (ret) { + case -ENOMEM: + goto temporary_error; + case -EINVAL: + ret = rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EKEYREJECTED, + rxgk_abort_resp_internal_error); + goto out; + case -ENOPKG: + ret = rxrpc_abort_conn(conn, skb, RXGK_BADETYPE, -EKEYREJECTED, + rxgk_abort_resp_nopkg); + goto out; + } + +temporary_error: + /* Ignore the response packet if we got a temporary error such as + * ENOMEM. We just want to send the challenge again. Note that we + * also come out this way if the ticket decryption fails. + */ + goto out; +} + +/* + * clear the connection security + */ +static void rxgk_clear(struct rxrpc_connection *conn) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(conn->rxgk.keys); i++) + rxgk_put(conn->rxgk.keys[i]); +} + +/* + * Initialise the RxGK security service. + */ +static int rxgk_init(void) +{ + return 0; +} + +/* + * Clean up the RxGK security service. + */ +static void rxgk_exit(void) +{ +} + +/* + * RxRPC YFS GSSAPI-based security + */ +const struct rxrpc_security rxgk_yfs = { + .name = "yfs-rxgk", + .security_index = RXRPC_SECURITY_YFS_RXGK, + .no_key_abort = RXGK_NOTAUTH, + .init = rxgk_init, + .exit = rxgk_exit, + .preparse_server_key = rxgk_preparse_server_key, + .free_preparse_server_key = rxgk_free_preparse_server_key, + .destroy_server_key = rxgk_destroy_server_key, + .describe_server_key = rxgk_describe_server_key, + .init_connection_security = rxgk_init_connection_security, + .alloc_txbuf = rxgk_alloc_txbuf, + .secure_packet = rxgk_secure_packet, + .verify_packet = rxgk_verify_packet, + .free_call_crypto = rxgk_free_call_crypto, + .issue_challenge = rxgk_issue_challenge, + .respond_to_challenge = rxgk_respond_to_challenge, + .verify_response = rxgk_verify_response, + .clear = rxgk_clear, + .default_decode_ticket = rxgk_yfs_decode_ticket, +}; diff --git a/net/rxrpc/rxgk_app.c b/net/rxrpc/rxgk_app.c new file mode 100644 index 000000000000..1b4e322d619e --- /dev/null +++ b/net/rxrpc/rxgk_app.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Application-specific bits for GSSAPI-based RxRPC security + * + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include "ar-internal.h" +#include "rxgk_common.h" + +/* + * Decode a default-style YFS ticket in a response and turn it into an + * rxrpc-type key. + * + * struct rxgk_key { + * afs_uint32 enctype; + * opaque key<>; + * }; + * + * struct RXGK_AuthName { + * afs_int32 kind; + * opaque data; + * opaque display; + * }; + * + * struct RXGK_Token { + * rxgk_key K0; + * RXGK_Level level; + * rxgkTime starttime; + * afs_int32 lifetime; + * afs_int32 bytelife; + * rxgkTime expirationtime; + * struct RXGK_AuthName identities<>; + * }; + */ +int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb, + unsigned int ticket_offset, unsigned int ticket_len, + struct key **_key) +{ + struct rxrpc_key_token *token; + const struct cred *cred = current_cred(); // TODO - use socket creds + struct key *key; + size_t pre_ticket_len, payload_len; + unsigned int klen, enctype; + void *payload, *ticket; + __be32 *t, *p, *q, tmp[2]; + int ret; + + _enter(""); + + /* Get the session key length */ + ret = skb_copy_bits(skb, ticket_offset, tmp, sizeof(tmp)); + if (ret < 0) + return rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO, + rxgk_abort_resp_short_yfs_klen); + enctype = ntohl(tmp[0]); + klen = ntohl(tmp[1]); + + if (klen > ticket_len - 10 * sizeof(__be32)) + return rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO, + rxgk_abort_resp_short_yfs_key); + + pre_ticket_len = ((5 + 14) * sizeof(__be32) + + xdr_round_up(klen) + + sizeof(__be32)); + payload_len = pre_ticket_len + xdr_round_up(ticket_len); + + payload = kzalloc(payload_len, GFP_NOFS); + if (!payload) + return -ENOMEM; + + /* We need to fill out the XDR form for a key payload that we can pass + * to add_key(). Start by copying in the ticket so that we can parse + * it. + */ + ticket = payload + pre_ticket_len; + ret = skb_copy_bits(skb, ticket_offset, ticket, ticket_len); + if (ret < 0) { + ret = rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO, + rxgk_abort_resp_short_yfs_tkt); + goto error; + } + + /* Fill out the form header. */ + p = payload; + p[0] = htonl(0); /* Flags */ + p[1] = htonl(1); /* len(cellname) */ + p[2] = htonl(0x20000000); /* Cellname " " */ + p[3] = htonl(1); /* #tokens */ + p[4] = htonl(15 * sizeof(__be32) + xdr_round_up(klen) + + xdr_round_up(ticket_len)); /* Token len */ + + /* Now fill in the body. Most of this we can just scrape directly from + * the ticket. + */ + t = ticket + sizeof(__be32) * 2 + xdr_round_up(klen); + q = payload + 5 * sizeof(__be32); + q[ 0] = htonl(RXRPC_SECURITY_YFS_RXGK); + q[ 1] = t[1]; /* begintime - msw */ + q[ 2] = t[2]; /* - lsw */ + q[ 3] = t[5]; /* endtime - msw */ + q[ 4] = t[6]; /* - lsw */ + q[ 5] = 0; /* level - msw */ + q[ 6] = t[0]; /* - lsw */ + q[ 7] = 0; /* lifetime - msw */ + q[ 8] = t[3]; /* - lsw */ + q[ 9] = 0; /* bytelife - msw */ + q[10] = t[4]; /* - lsw */ + q[11] = 0; /* enctype - msw */ + q[12] = htonl(enctype); /* - lsw */ + q[13] = htonl(klen); /* Key length */ + + q += 14; + + memcpy(q, ticket + sizeof(__be32) * 2, klen); + q += xdr_round_up(klen) / 4; + q[0] = htonl(ticket_len); + q++; + if (WARN_ON((unsigned long)q != (unsigned long)ticket)) { + ret = -EIO; + goto error; + } + + /* Ticket read in with skb_copy_bits above */ + q += xdr_round_up(ticket_len) / 4; + if (WARN_ON((unsigned long)q - (unsigned long)payload != payload_len)) { + ret = -EIO; + goto error; + } + + /* Now turn that into a key. */ + key = key_alloc(&key_type_rxrpc, "x", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0, // TODO: Use socket owner + KEY_ALLOC_NOT_IN_QUOTA, NULL); + if (IS_ERR(key)) { + _leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key)); + goto error; + } + + _debug("key %d", key_serial(key)); + + ret = key_instantiate_and_link(key, payload, payload_len, NULL, NULL); + if (ret < 0) + goto error_key; + + token = key->payload.data[0]; + token->no_leak_key = true; + *_key = key; + key = NULL; + ret = 0; + goto error; + +error_key: + key_put(key); +error: + kfree_sensitive(payload); + _leave(" = %d", ret); + return ret; +} + +/* + * Decrypt the token. + */ +int rxgk_decrypt_skb(struct crypto_aead *token_enc, struct sk_buff *skb, + size_t *_offset, size_t *_len) +{ + struct krb5_enctype *krb5 = crypto_krb5_enctype(token_enc); + struct aead_request *req; + struct scatterlist sg[16]; + size_t len = *_len; + int ret; + + req = kzalloc(sizeof(*req) + crypto_aead_reqsize(token_enc), GFP_NOFS); + if (!req) + return -ENOMEM; + + sg_init_table(sg, ARRAY_SIZE(sg)); + ret = skb_to_sgvec(skb, sg, *_offset, len); + if (unlikely(ret < 0)) + goto out; + + aead_request_set_tfm(req, token_enc); + aead_request_set_crypt(req, sg, sg, len, NULL); + + ret = crypto_aead_decrypt(req); + if (ret < 0) + goto out; + + crypto_krb5_where_is_the_data(krb5, KRB5_ENCRYPT_MODE, _offset, _len); + ret = 0; +out: + kfree_sensitive(req); + return ret; +} + +/* + * Extract the token and set up a session key from the details. + * + * struct RXGK_TokenContainer { + * afs_int32 kvno; + * afs_int32 enctype; + * opaque encrypted_token<>; + * }; + * + * [tools.ietf.org/html/draft-wilkinson-afs3-rxgk-afs-08 sec 6.1] + */ +int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb, + unsigned int token_offset, unsigned int token_len, + struct key **_key) +{ + const struct krb5_buffer *server_secret; + struct crypto_aead *token_enc = NULL; + struct key *server_key; + size_t ticket_offset, ticket_len; + u32 kvno, enctype; + int ret; + + struct { + __be32 kvno; + __be32 enctype; + __be32 token_len; + } container; + + /* Decode the RXGK_TokenContainer object. This tells us which server + * key we should be using. We can then fetch the key, get the secret + * and set up the crypto to extract the token. + */ + if (skb_copy_bits(skb, token_offset, &container, sizeof(container)) < 0) + return rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO, + rxgk_abort_resp_tok_short); + + kvno = ntohl(container.kvno); + enctype = ntohl(container.enctype); + ticket_len = ntohl(container.token_len); + ticket_offset = token_offset + sizeof(container); + + if (xdr_round_up(ticket_len) > token_len - 3 * 4) + return rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO, + rxgk_abort_resp_tok_short); + + _debug("KVNO %u", kvno); + _debug("ENC %u", enctype); + _debug("TLEN %zu", ticket_len); + + server_key = rxrpc_look_up_server_security(conn, skb, kvno, enctype); + if (IS_ERR(server_key)) + goto cant_get_server_key; + + down_read(&server_key->sem); + server_secret = (const void *)&server_key->payload.data[2]; + token_enc = rxgk_set_up_token_cipher(server_secret, enctype, GFP_NOFS); + up_read(&server_key->sem); + key_put(server_key); + if (IS_ERR(token_enc)) { + ret = PTR_ERR(token_enc); + goto cant_get_token; + } + + /* We can now decrypt and parse the token/ticket. This allows us to + * gain access to K0, from which we can derive the transport key and + * thence decode the authenticator. + */ + ret = rxgk_decrypt_skb(token_enc, skb, &ticket_offset, &ticket_len); + crypto_free_aead(token_enc); + if (ret < 0) + return rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, ret, + rxgk_abort_resp_tok_dec); + + ret = conn->security->default_decode_ticket(conn, skb, ticket_offset, + ticket_len, _key); + if (ret < 0) + goto cant_get_token; + + _leave(" = 0"); + return ret; + +cant_get_server_key: + ret = PTR_ERR(server_key); + switch (ret) { + case -ENOMEM: + goto temporary_error; + case -ENOKEY: + case -EKEYREJECTED: + case -EKEYEXPIRED: + case -EKEYREVOKED: + case -EPERM: + return rxrpc_abort_conn(conn, skb, RXGK_BADKEYNO, -EKEYREJECTED, + rxgk_abort_resp_tok_nokey); + default: + return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EKEYREJECTED, + rxgk_abort_resp_tok_keyerr); + } + +cant_get_token: + switch (ret) { + case -ENOMEM: + goto temporary_error; + case -EINVAL: + return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EKEYREJECTED, + rxgk_abort_resp_tok_internal_error); + case -ENOPKG: + return rxrpc_abort_conn(conn, skb, RXGK_BADETYPE, -EKEYREJECTED, + rxgk_abort_resp_tok_nopkg); + } + +temporary_error: + /* Ignore the response packet if we got a temporary error such as + * ENOMEM. We just want to send the challenge again. Note that we + * also come out this way if the ticket decryption fails. + */ + return ret; +} diff --git a/net/rxrpc/rxgk_common.h b/net/rxrpc/rxgk_common.h index 84e76fe8e324..9bff844d9557 100644 --- a/net/rxrpc/rxgk_common.h +++ b/net/rxrpc/rxgk_common.h @@ -31,6 +31,20 @@ struct rxgk_context { struct crypto_aead *resp_crypto; /* Response key */ }; +#define xdr_round_up(x) (round_up((x), sizeof(__be32))) + +/* + * rxgk_app.c + */ +int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb, + unsigned int ticket_offset, unsigned int ticket_len, + struct key **_key); +int rxgk_decrypt_skb(struct crypto_aead *token_enc, struct sk_buff *skb, + size_t *_offset, size_t *_len); +int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb, + unsigned int token_offset, unsigned int token_len, + struct key **_key); + /* * rxgk_kdf.c */ diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index 62b09d23ec08..773405b55392 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -177,8 +177,10 @@ static struct rxrpc_txbuf *rxkad_alloc_txbuf(struct rxrpc_call *call, size_t rem if (!txb) return NULL; - txb->offset += shdr; - txb->space = part; + txb->crypto_header = 0; + txb->sec_header = shdr; + txb->offset += shdr; + txb->space = part; return txb; } diff --git a/net/rxrpc/security.c b/net/rxrpc/security.c index 9784adc8f275..078d91a6b77f 100644 --- a/net/rxrpc/security.c +++ b/net/rxrpc/security.c @@ -20,6 +20,9 @@ static const struct rxrpc_security *rxrpc_security_types[] = { #ifdef CONFIG_RXKAD [RXRPC_SECURITY_RXKAD] = &rxkad, #endif +#ifdef CONFIG_RXGK + [RXRPC_SECURITY_YFS_RXGK] = &rxgk_yfs, +#endif }; int __init rxrpc_init_security(void) From patchwork Fri Jan 10 01:03:10 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 13933388 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 750F21E0DCF for ; Fri, 10 Jan 2025 01:04:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736471056; cv=none; b=QIe+gdDHGgGVl+TwLDRGW4i4Eq6tZB0xQaFDSgTQLSra1q9w2yyZOv2jLn53gknxrEeip+vAOQotFaT8VS+xcBW9twowrhdBx0SN3rOKBwgKHT2D5t2UEh3k3UN/dZa1fKN6x4C/UnuonYiA2VFPqma/xfz6FEVj4FU6LIOfGY8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736471056; c=relaxed/simple; bh=mB7CSBUc/zPyuAIvcNU1VoCNxE1QrCmcgQm776XUdwc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dQgdR2u8SxFAH80ciUJzeVlbu0yjtEE/nhIJ3QO2PI+ywT0WTuDJvWD/DlHSYsI1C0q3xvuXqZ41XKT1zoipJRlaMHrTd+8yOx6Tmrg1kj69QXKxEr2YncyrnawCcv4yfAj6AYRbGdgztmSMiaER30I1B1vzotP+MIWkRrxnGiA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=hqjfj624; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="hqjfj624" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1736471053; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=r+BHhLFoDCiqeH2JzrdvAP1GvlO2WIxVq9IEXsMrIfw=; b=hqjfj624dSTrhLGl45XpslUiGVJ43gDxqomzQQcHYbeQgD65yTaIURpMoa2j3AT4Qoq8gC lvQzHgcsHdboIt2i8yKSneLsFpAt2MUG/9ZtBeV33SdqCKvAsxaUAMq0F1KQK1qox71giF 6caim0ssWaUNg6Gc//vpmft5sTerjwo= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-111-4VHIN3ueNOC3foRXWS9Fuw-1; Thu, 09 Jan 2025 20:04:10 -0500 X-MC-Unique: 4VHIN3ueNOC3foRXWS9Fuw-1 X-Mimecast-MFC-AGG-ID: 4VHIN3ueNOC3foRXWS9Fuw Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 29B7419560BD; Fri, 10 Jan 2025 01:04:08 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.42.28.12]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 5A08F19560AB; Fri, 10 Jan 2025 01:04:04 +0000 (UTC) From: David Howells To: Herbert Xu , Chuck Lever Cc: David Howells , Trond Myklebust , "David S. Miller" , Marc Dionne , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 8/8] rxrpc: rxgk: Implement connection rekeying Date: Fri, 10 Jan 2025 01:03:10 +0000 Message-ID: <20250110010313.1471063-9-dhowells@redhat.com> In-Reply-To: <20250110010313.1471063-1-dhowells@redhat.com> References: <20250110010313.1471063-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 Implement rekeying of connections with the RxGK security class. This involves regenerating the keys with a different key number as part of the input data after a certain amount of time or a certain amount of bytes encrypted. Rekeying may be triggered by either end. The LSW of the key number is inserted into the security-specific field in the RX header, and we try and expand it to 32-bits to make it last longer. Signed-off-by: David Howells cc: Herbert Xu cc: "David S. Miller" cc: Chuck Lever cc: Marc Dionne cc: Eric Dumazet cc: Jakub Kicinski cc: Paolo Abeni cc: Simon Horman cc: linux-afs@lists.infradead.org cc: linux-nfs@vger.kernel.org cc: linux-crypto@vger.kernel.org cc: netdev@vger.kernel.org --- net/rxrpc/ar-internal.h | 5 +- net/rxrpc/conn_object.c | 1 + net/rxrpc/rxgk.c | 156 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 155 insertions(+), 7 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 736dc6ea20ac..e00f3b0edc98 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -533,11 +533,14 @@ struct rxrpc_connection { u32 nonce; /* response re-use preventer */ } rxkad; struct { - struct rxgk_context *keys[1]; + struct rxgk_context *keys[4]; /* (Re-)keying buffer */ u64 start_time; /* The start time for TK derivation */ u8 nonce[20]; /* Response re-use preventer */ + u32 key_number; /* Current key number */ } rxgk; }; + rwlock_t security_use_lock; /* Security use/modification lock */ + unsigned long flags; unsigned long events; unsigned long idle_timestamp; /* Time at which last became idle */ diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index 7eba4d7d9a38..56459b00266d 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -72,6 +72,7 @@ struct rxrpc_connection *rxrpc_alloc_connection(struct rxrpc_net *rxnet, skb_queue_head_init(&conn->rx_queue); conn->rxnet = rxnet; conn->security = &rxrpc_no_security; + rwlock_init(&conn->security_use_lock); spin_lock_init(&conn->state_lock); conn->debug_id = atomic_inc_return(&rxrpc_debug_id); conn->idle_timestamp = jiffies; diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c index 7344f19b8ae2..c278a242f855 100644 --- a/net/rxrpc/rxgk.c +++ b/net/rxrpc/rxgk.c @@ -76,11 +76,153 @@ static void rxgk_describe_server_key(const struct key *key, struct seq_file *m) seq_printf(m, ": %s", krb5->name); } +/* + * Handle rekeying the connection when we see our limits overrun or when the + * far side decided to rekey. + * + * Returns a ref on the context if successful or -ESTALE if the key is out of + * date. + */ +static struct rxgk_context *rxgk_rekey(struct rxrpc_connection *conn, + const u16 *specific_key_number) +{ + struct rxgk_context *gk, *dead = NULL; + unsigned int key_number, current_key, mask = ARRAY_SIZE(conn->rxgk.keys) - 1; + bool crank = false; + + _enter("%d", specific_key_number ? *specific_key_number : -1); + + mutex_lock(&conn->security_lock); + + current_key = conn->rxgk.key_number; + if (!specific_key_number) { + key_number = current_key; + } else { + if (*specific_key_number == (u16)current_key) + key_number = current_key; + else if (*specific_key_number == (u16)(current_key - 1)) + key_number = current_key - 1; + else if (*specific_key_number == (u16)(current_key + 1)) + goto crank_window; + else + goto bad_key; + } + + gk = conn->rxgk.keys[key_number & mask]; + if (!gk) + goto generate_key; + if (!specific_key_number && + test_bit(RXGK_TK_NEEDS_REKEY, &gk->flags)) + goto crank_window; + +grab: + refcount_inc(&gk->usage); + mutex_unlock(&conn->security_lock); + rxgk_put(dead); + return gk; + +crank_window: + if (current_key == UINT_MAX) + goto bad_key; + if (current_key + 1 == UINT_MAX) + set_bit(RXRPC_CONN_DONT_REUSE, &conn->flags); + + key_number = current_key + 1; + if (WARN_ON(conn->rxgk.keys[key_number & mask])) + goto bad_key; + crank = true; + +generate_key: + gk = conn->rxgk.keys[current_key & mask]; + gk = rxgk_generate_transport_key(conn, gk->key, key_number, GFP_NOFS); + if (IS_ERR(gk)) { + mutex_unlock(&conn->security_lock); + return gk; + } + + write_lock(&conn->security_use_lock); + if (crank) { + current_key++; + conn->rxgk.key_number = current_key; + dead = conn->rxgk.keys[(current_key - 2) & mask]; + conn->rxgk.keys[(current_key - 2) & mask] = NULL; + } + conn->rxgk.keys[current_key & mask] = gk; + write_unlock(&conn->security_use_lock); + goto grab; + +bad_key: + mutex_unlock(&conn->security_lock); + return ERR_PTR(-ESTALE); +} + +/* + * Get the specified keying context. + * + * Returns a ref on the context if successful or -ESTALE if the key is out of + * date. + */ static struct rxgk_context *rxgk_get_key(struct rxrpc_connection *conn, - u16 *specific_key_number) + const u16 *specific_key_number) { - refcount_inc(&conn->rxgk.keys[0]->usage); - return conn->rxgk.keys[0]; + struct rxgk_context *gk; + unsigned int key_number, current_key, mask = ARRAY_SIZE(conn->rxgk.keys) - 1; + + _enter("{%u},%d", + conn->rxgk.key_number, specific_key_number ? *specific_key_number : -1); + + read_lock(&conn->security_use_lock); + + current_key = conn->rxgk.key_number; + if (!specific_key_number) { + key_number = current_key; + } else { + /* Only the bottom 16 bits of the key number are exposed in the + * header, so we try and keep the upper 16 bits in step. The + * whole 32 bits are used to generate the TK. + */ + if (*specific_key_number == (u16)current_key) + key_number = current_key; + else if (*specific_key_number == (u16)(current_key - 1)) + key_number = current_key - 1; + else if (*specific_key_number == (u16)(current_key + 1)) + goto rekey; + else + goto bad_key; + } + + gk = conn->rxgk.keys[key_number & mask]; + if (!gk) + goto slow_path; + if (!specific_key_number && + key_number < UINT_MAX) { + if (time_after(jiffies, gk->expiry) || + gk->bytes_remaining < 0) { + set_bit(RXGK_TK_NEEDS_REKEY, &gk->flags); + goto slow_path; + } + + if (test_bit(RXGK_TK_NEEDS_REKEY, &gk->flags)) + goto slow_path; + } + + refcount_inc(&gk->usage); + read_unlock(&conn->security_use_lock); + return gk; + +rekey: + _debug("rekey"); + if (current_key == UINT_MAX) + goto bad_key; + gk = conn->rxgk.keys[current_key & mask]; + if (gk) + set_bit(RXGK_TK_NEEDS_REKEY, &gk->flags); +slow_path: + read_unlock(&conn->security_use_lock); + return rxgk_rekey(conn, specific_key_number); +bad_key: + read_unlock(&conn->security_use_lock); + return ERR_PTR(-ESTALE); } /* @@ -92,7 +234,8 @@ static int rxgk_init_connection_security(struct rxrpc_connection *conn, struct rxgk_context *gk; int ret; - _enter("{%d},{%x}", conn->debug_id, key_serial(conn->key)); + _enter("{%d,%u},{%x}", + conn->debug_id, conn->rxgk.key_number, key_serial(conn->key)); conn->security_ix = token->security_index; conn->security_level = token->rxgk->level; @@ -102,10 +245,11 @@ static int rxgk_init_connection_security(struct rxrpc_connection *conn, do_div(conn->rxgk.start_time, 100); } - gk = rxgk_generate_transport_key(conn, token->rxgk, 0, GFP_NOFS); + gk = rxgk_generate_transport_key(conn, token->rxgk, conn->rxgk.key_number, + GFP_NOFS); if (IS_ERR(gk)) return PTR_ERR(gk); - conn->rxgk.keys[0] = gk; + conn->rxgk.keys[gk->key_number & 3] = gk; switch (conn->security_level) { case RXRPC_SECURITY_PLAIN: