Skip to contentSkip to conversation

crypto: gcm - Provide minimal library implementation

PATCH Superseded Ard Biesheuvel 3 participants 6 messages Oct 7, 2022 – Oct 10, 2022

Subsystems (2)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore

Ard Biesheuvel

author maintainer
#0 lore raw

PATCH 1/1 crypto: gcm - Provide minimal library implementation

Implement a minimal library version of GCM based on the existing library implementations of AES and multiplication in GF(2^128). Using these primitives, GCM can be implemented in a straight-forward manner.

GCM has a couple of sharp edges, i.e., the amount of input data processed with the same initialization vector (IV) should be capped to protect the counter from 32-bit rollover (or carry), and the size of the authentication tag should be fixed for a given key. [0]

The former concern is addressed trivially, given that the function call API uses 32-bit signed types for the input lengths. It is still up to the caller to avoid IV reuse in general, but this is not something we can police at the implementation level.

As for the latter concern, let's make the authentication tag size part of the key schedule, and only permit it to be configured as part of the key expansion routine.

Note that table based AES implementations are susceptible to known plaintext timing attacks on the encryption key. The AES library already attempts to mitigate this to some extent, but given that the counter mode encryption used by GCM operates exclusively on known plaintext by construction (the IV and therefore the initial counter value are known to an attacker), let's take some extra care to mitigate this, by calling the AES library with interrupts disabled.

[0] https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38d.pdf

---
 include/crypto/gcm.h |  61 ++
 lib/crypto/Kconfig   |   6 +
 lib/crypto/Makefile  |   3 +
 lib/crypto/gcm.c     | 714 ++++++++++++++++++++
 4 files changed, 784 insertions(+)
include/crypto/gcm.h +61 -0
index 9d7eff04f224..ce2b7eb16364 100644
@@ -3,6 +3,9 @@
 
 #include <linux/errno.h>
 
+#include <crypto/aes.h>
+#include <crypto/gf128mul.h>
+
 #define GCM_AES_IV_SIZE 12
 #define GCM_RFC4106_IV_SIZE 8
 #define GCM_RFC4543_IV_SIZE 8
@@ -60,4 +63,62 @@ static inline int crypto_ipsec_check_assoclen(unsigned int assoclen)
 
 	return 0;
 }
+
+struct gcm_ctx {
+	be128			ghash_key;
+	struct crypto_aes_ctx	aes_ctx;
+	unsigned int		authsize;
+};
+
+/**
+ * gcm_expandkey - Expands the AES and GHASH keys for the GCM key schedule
+ * @ctx:	The data structure that will hold the GCM key schedule
+ * @key:	The AES encryption input key
+ * @keysize:	The length in bytes of the input key
+ * @authsize:	The size in bytes of the GCM authentication tag
+ *
+ * Returns 0 on success, or -EINVAL if @keysize or @authsize contain values
+ * that are not permitted by the GCM specification.
+ */
+int gcm_expandkey(struct gcm_ctx *ctx, const u8 *key,
+		  unsigned int keysize, unsigned int authsize);
+
+/**
+ * gcm_encrypt - Perform GCM encryption on a block of data
+ * @ctx:	The GCM key schedule
+ * @dst:	Pointer to the ciphertext output buffer
+ * @src:	Pointer the plaintext (may equal @dst for encryption in place)
+ * @crypt_len:	The size in bytes of the plaintext and ciphertext.
+ * @assoc:	Pointer to the associated data,
+ * @assoc_len:	The size in bytes of the associated data
+ * @iv:		The initialization vector (IV) to use for this block of data
+ *		(must be 12 bytes in size as per the GCM spec recommendation)
+ * @authtag:	The address of the buffer in memory where the authentication
+ *		tag should be stored. The buffer is assumed to have space for
+ *		@ctx->authsize bytes.
+ */
+void gcm_encrypt(const struct gcm_ctx *ctx, u8 *dst, const u8 *src,
+		 int crypt_len, const u8 *assoc, int assoc_len,
+		 const u8 iv[GCM_AES_IV_SIZE], u8 *authtag);
+
+/**
+ * gcm_decrypt - Perform GCM encryption on a block of data
+ * @ctx:	The GCM key schedule
+ * @dst:	Pointer to the plaintext output buffer
+ * @src:	Pointer the ciphertext (may equal @dst for decryption in place)
+ * @crypt_len:	The size in bytes of the plaintext and ciphertext.
+ * @assoc:	Pointer to the associated data,
+ * @assoc_len:	The size in bytes of the associated data
+ * @iv:		The initialization vector (IV) to use for this block of data
+ *		(must be 12 bytes in size as per the GCM spec recommendation)
+ * @authtag:	The address of the buffer in memory where the authentication
+ *		tag is stored.
+ *
+ * Returns 0 on success, or -EBADMSG if the ciphertext failed authentication.
+ * On failure, no plaintext will be returned.
+ */
+int __must_check gcm_decrypt(const struct gcm_ctx *ctx, u8 *dst, const u8 *src,
+			     int crypt_len, const u8 *assoc, int assoc_len,
+			     const u8 iv[GCM_AES_IV_SIZE], const u8 *authtag);
+
 #endif
lib/crypto/Kconfig +6 -0
index 7e9683e9f5c6..327a48980f2e 100644
@@ -8,6 +8,12 @@ config CRYPTO_LIB_UTILS
 config CRYPTO_LIB_AES
 	tristate
 
+config CRYPTO_LIB_GCM
+	tristate
+	select CRYPTO_GF128MUL
+	select CRYPTO_LIB_AES
+	select CRYPTO_LIB_UTILS
+
 config CRYPTO_LIB_ARC4
 	tristate
 
lib/crypto/Makefile +3 -0
index c852f067ab06..c37b1230b572 100644
@@ -10,6 +10,9 @@ obj-$(CONFIG_CRYPTO_LIB_CHACHA_GENERIC)		+= libchacha.o
 obj-$(CONFIG_CRYPTO_LIB_AES)			+= libaes.o
 libaes-y					:= aes.o
 
+obj-$(CONFIG_CRYPTO_LIB_GCM)			+= libgcm.o
+libgcm-y					:= gcm.o
+
 obj-$(CONFIG_CRYPTO_LIB_ARC4)			+= libarc4.o
 libarc4-y					:= arc4.o
 
lib/crypto/gcm.c new file +714 -0

714 lines not on this page · raw message

Signature · 1 line
2.35.1
#1 lore raw

I haven't looked at this in depth yet, but one thing jumped out:

include/crypto/gcm.h static inline int crypto_ipsec_check_assoclen(unsigned int assoclen) · line 102

Quoted 6 lines · Ard Biesheuvel

Implement a minimal library version of GCM based on the existing library

 include/crypto/gcm.h |  61 ++
 lib/crypto/gcm.c     | 714 ++++++++++++++++++++
include/crypto/gcm.h +3 -0
+void gcm_encrypt(const struct gcm_ctx *ctx, u8 *dst, const u8 *src,
+		 int crypt_len, const u8 *assoc, int assoc_len,
+		 const u8 iv[GCM_AES_IV_SIZE], u8 *authtag);

This should probably all be aes_gcm, aes-gcm.[ch] LIB_AES_GCM and so forth, since GCM is something that works with different block ciphers.

Jason

#2 lore raw
In reply to Ard Biesheuvel

Hi Ard,

Quoted 52 lines · Ard Biesheuvel

Implement a minimal library version of GCM based on the existing library implementations of AES and multiplication in GF(2^128). Using these primitives, GCM can be implemented in a straight-forward manner.

GCM has a couple of sharp edges, i.e., the amount of input data processed with the same initialization vector (IV) should be capped to protect the counter from 32-bit rollover (or carry), and the size of the authentication tag should be fixed for a given key. [0]

The former concern is addressed trivially, given that the function call API uses 32-bit signed types for the input lengths. It is still up to the caller to avoid IV reuse in general, but this is not something we can police at the implementation level.

As for the latter concern, let's make the authentication tag size part of the key schedule, and only permit it to be configured as part of the key expansion routine.

Note that table based AES implementations are susceptible to known plaintext timing attacks on the encryption key. The AES library already attempts to mitigate this to some extent, but given that the counter mode encryption used by GCM operates exclusively on known plaintext by construction (the IV and therefore the initial counter value are known to an attacker), let's take some extra care to mitigate this, by calling the AES library with interrupts disabled.

[0] https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38d.pdf

---

lib/crypto/gcm.c new file +20 -0
+/**
+ * gcm_decrypt - Perform GCM decryption on a block of data
+ * @ctx:	The GCM key schedule
+ * @dst:	Pointer to the plaintext output buffer
+ * @src:	Pointer the ciphertext (may equal @dst for decryption in place)
+ * @crypt_len:	The size in bytes of the plaintext and ciphertext.
+ * @assoc:	Pointer to the associated data,
+ * @assoc_len:	The size in bytes of the associated data
+ * @iv:		The initialization vector (IV) to use for this block of data
+ *		(must be 12 bytes in size as per the GCM spec recommendation)
+ * @authtag:	The address of the buffer in memory where the authentication
+ *		tag is stored.
+ *
+ * Returns 0 on success, or -EBADMSG if the ciphertext failed authentication.
+ * On failure, no plaintext will be returned.
+ */
+int __must_check gcm_decrypt(const struct gcm_ctx *ctx, u8 *dst, const u8 *src,
+			     int crypt_len, const u8 *assoc, int assoc_len,
+			     const u8 iv[GCM_AES_IV_SIZE], const u8 *authtag)
+{

lib/crypto/gcm.c line 166

Quoted 10 lines
lib/crypto/gcm.c new file +10 -0
+	u8 tagbuf[AES_BLOCK_SIZE];
+	__be32 ctr[4];
+
+	memcpy(ctr, iv, GCM_AES_IV_SIZE);
+
+	gcm_mac(ctx, src, crypt_len, assoc, assoc_len, ctr, tagbuf);
+	if (crypto_memneq(authtag, tagbuf, ctx->authsize)) {
+		memzero_explicit(tagbuf, sizeof(tagbuf));
+		return -EBADMSG;
+	}

The gcm_mac computation seems to be broken in this version. When I receive the encrypted packet back from the security processor the authtag does not match. Will debug further and report back.

lib/crypto/gcm.c line 174

Quoted 8 lines
lib/crypto/gcm.c new file +8 -0
+	gcm_crypt(ctx, dst, src, crypt_len, ctr);
+	return 0;
+}
+EXPORT_SYMBOL(gcm_decrypt);
+
+MODULE_DESCRIPTION("Generic GCM library");
+MODULE_AUTHOR("Ard Biesheuvel <[email protected]>");
+MODULE_LICENSE("GPL");

Regards
Nikunj

author maintainer
#3 lore raw
Quoted 62 lines · Nikunj A. Dadhania

Hi Ard,

Quoted 58 lines · Ard Biesheuvel

Implement a minimal library version of GCM based on the existing library implementations of AES and multiplication in GF(2^128). Using these primitives, GCM can be implemented in a straight-forward manner.

GCM has a couple of sharp edges, i.e., the amount of input data processed with the same initialization vector (IV) should be capped to protect the counter from 32-bit rollover (or carry), and the size of the authentication tag should be fixed for a given key. [0]

The former concern is addressed trivially, given that the function call API uses 32-bit signed types for the input lengths. It is still up to the caller to avoid IV reuse in general, but this is not something we can police at the implementation level.

As for the latter concern, let's make the authentication tag size part of the key schedule, and only permit it to be configured as part of the key expansion routine.

Note that table based AES implementations are susceptible to known plaintext timing attacks on the encryption key. The AES library already attempts to mitigate this to some extent, but given that the counter mode encryption used by GCM operates exclusively on known plaintext by construction (the IV and therefore the initial counter value are known to an attacker), let's take some extra care to mitigate this, by calling the AES library with interrupts disabled.

[0] https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38d.pdf

---

lib/crypto/gcm.c new file +26 -0
+/**
+ * gcm_decrypt - Perform GCM decryption on a block of data
+ * @ctx:     The GCM key schedule
+ * @dst:     Pointer to the plaintext output buffer
+ * @src:     Pointer the ciphertext (may equal @dst for decryption in place)
+ * @crypt_len:       The size in bytes of the plaintext and ciphertext.
+ * @assoc:   Pointer to the associated data,
+ * @assoc_len:       The size in bytes of the associated data
+ * @iv:              The initialization vector (IV) to use for this block of data
+ *           (must be 12 bytes in size as per the GCM spec recommendation)
+ * @authtag: The address of the buffer in memory where the authentication
+ *           tag is stored.
+ *
+ * Returns 0 on success, or -EBADMSG if the ciphertext failed authentication.
+ * On failure, no plaintext will be returned.
+ */
+int __must_check gcm_decrypt(const struct gcm_ctx *ctx, u8 *dst, const u8 *src,
+                          int crypt_len, const u8 *assoc, int assoc_len,
+                          const u8 iv[GCM_AES_IV_SIZE], const u8 *authtag)
+{
+     u8 tagbuf[AES_BLOCK_SIZE];
+     __be32 ctr[4];
+
+     memcpy(ctr, iv, GCM_AES_IV_SIZE);
+
+     gcm_mac(ctx, src, crypt_len, assoc, assoc_len, ctr, tagbuf);
Quoted 9 lines

lib/crypto/gcm.c line 166

Quoted 4 lines
lib/crypto/gcm.c new file +4 -0
+     if (crypto_memneq(authtag, tagbuf, ctx->authsize)) {
+             memzero_explicit(tagbuf, sizeof(tagbuf));
+             return -EBADMSG;
+     }

The gcm_mac computation seems to be broken in this version. When I receive the encrypted packet back from the security processor the authtag does not match. Will debug further and report back.

Sorry to hear that. If you find out what's wrong, can you please provide a test vector that reproduces it so we can add it to the list?

#4 lore raw
Quoted 73 lines · Ard Biesheuvel
Quoted 62 lines · Nikunj A. Dadhania

Hi Ard,

Quoted 58 lines · Ard Biesheuvel

Implement a minimal library version of GCM based on the existing library implementations of AES and multiplication in GF(2^128). Using these primitives, GCM can be implemented in a straight-forward manner.

GCM has a couple of sharp edges, i.e., the amount of input data processed with the same initialization vector (IV) should be capped to protect the counter from 32-bit rollover (or carry), and the size of the authentication tag should be fixed for a given key. [0]

The former concern is addressed trivially, given that the function call API uses 32-bit signed types for the input lengths. It is still up to the caller to avoid IV reuse in general, but this is not something we can police at the implementation level.

As for the latter concern, let's make the authentication tag size part of the key schedule, and only permit it to be configured as part of the key expansion routine.

Note that table based AES implementations are susceptible to known plaintext timing attacks on the encryption key. The AES library already attempts to mitigate this to some extent, but given that the counter mode encryption used by GCM operates exclusively on known plaintext by construction (the IV and therefore the initial counter value are known to an attacker), let's take some extra care to mitigate this, by calling the AES library with interrupts disabled.

[0] https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fnvlpubs.nist.gov%2Fnistpubs%2Flegacy%2Fsp%2Fnistspecialpublication800-38d.pdf&amp;data=05%7C01%7Cnikunj.dadhania%40amd.com%7C912c5b73be0146ca63db08daaa97ade4%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C638009869061457627%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=sNHrpE3d7w%2BpT8dzAPiFbmvYLLIu9zsCnJ%2BFFxjzp%2Bk%3D&amp;reserved=0

---

lib/crypto/gcm.c new file +26 -0
+/**
+ * gcm_decrypt - Perform GCM decryption on a block of data
+ * @ctx:     The GCM key schedule
+ * @dst:     Pointer to the plaintext output buffer
+ * @src:     Pointer the ciphertext (may equal @dst for decryption in place)
+ * @crypt_len:       The size in bytes of the plaintext and ciphertext.
+ * @assoc:   Pointer to the associated data,
+ * @assoc_len:       The size in bytes of the associated data
+ * @iv:              The initialization vector (IV) to use for this block of data
+ *           (must be 12 bytes in size as per the GCM spec recommendation)
+ * @authtag: The address of the buffer in memory where the authentication
+ *           tag is stored.
+ *
+ * Returns 0 on success, or -EBADMSG if the ciphertext failed authentication.
+ * On failure, no plaintext will be returned.
+ */
+int __must_check gcm_decrypt(const struct gcm_ctx *ctx, u8 *dst, const u8 *src,
+                          int crypt_len, const u8 *assoc, int assoc_len,
+                          const u8 iv[GCM_AES_IV_SIZE], const u8 *authtag)
+{
+     u8 tagbuf[AES_BLOCK_SIZE];
+     __be32 ctr[4];
+
+     memcpy(ctr, iv, GCM_AES_IV_SIZE);
+
+     gcm_mac(ctx, src, crypt_len, assoc, assoc_len, ctr, tagbuf);
Quoted 9 lines

lib/crypto/gcm.c line 166

Quoted 4 lines
lib/crypto/gcm.c new file +4 -0
+     if (crypto_memneq(authtag, tagbuf, ctx->authsize)) {
+             memzero_explicit(tagbuf, sizeof(tagbuf));
+             return -EBADMSG;
+     }

The gcm_mac computation seems to be broken in this version. When I receive the encrypted packet back from the security processor the authtag does not match. Will debug further and report back.

Quoted 2 lines

Sorry to hear that. If you find out what's wrong, can you please provide a test vector that reproduces it so we can add it to the list?

My bad, it was wrong crypt_len that I was sending. Working fine now.

author maintainer
#5 lore raw
Quoted 39 lines · Nikunj A. Dadhania
Quoted 35 lines · Ard Biesheuvel
Quoted 34 lines · Nikunj A. Dadhania

Hi Ard,

Quoted 30 lines · Ard Biesheuvel

Implement a minimal library version of GCM based on the existing library implementations of AES and multiplication in GF(2^128). Using these primitives, GCM can be implemented in a straight-forward manner.

GCM has a couple of sharp edges, i.e., the amount of input data processed with the same initialization vector (IV) should be capped to protect the counter from 32-bit rollover (or carry), and the size of the authentication tag should be fixed for a given key. [0]

The former concern is addressed trivially, given that the function call API uses 32-bit signed types for the input lengths. It is still up to the caller to avoid IV reuse in general, but this is not something we can police at the implementation level.

As for the latter concern, let's make the authentication tag size part of the key schedule, and only permit it to be configured as part of the key expansion routine.

Note that table based AES implementations are susceptible to known plaintext timing attacks on the encryption key. The AES library already attempts to mitigate this to some extent, but given that the counter mode encryption used by GCM operates exclusively on known plaintext by construction (the IV and therefore the initial counter value are known to an attacker), let's take some extra care to mitigate this, by calling the AES library with interrupts disabled.

Cc "Nikunj A. Dadhania" <[email protected]>

---

...

Quoted 11 lines
Quoted 7 lines
Quoted 4 lines

The gcm_mac computation seems to be broken in this version. When I receive the encrypted packet back from the security processor the authtag does not match. Will debug further and report back.

Sorry to hear that. If you find out what's wrong, can you please provide a test vector that reproduces it so we can add it to the list?

My bad, it was wrong crypt_len that I was sending. Working fine now.

OK, good to know - thanks.