Skip to contentSkip to conversation

Add sigv3 support for IMA and EVM and all key types

PATCH New Stefan Berger 1 participant 9 messages Mar 24 Keys-trusted

lore
Share & tools
lore
#0 lore raw

cover Add sigv3 support for IMA and EVM and all key types

This series of patches adds sigv3 support for IMA and EVM for all currently supported key types (RSA, ECDSA, ECRDSA, SM2). evmctl gets a --v3 option for creating the sigv3 signatures and also a --v2 option for creating the old sigv2 signatures. --v2 is still the default.

Some test cases are duplicated to test with --v3.

Regards,
Stefan

Stefan Berger (8):
Implement imaevm_create_sigv3 for creating V3 signatures Implement support for IMA signatures V3 signing scheme Implement support for EVM signatures V3 signing scheme Support v3 IMA and EVM file signatures with --v3 option. Use imaevm_create_sigv3 for fsverity signature creation
tests: Add new --v3 option to sign_verify tests Allow verification of EVM_XATTR_PORTABLE_DIGSIG with sigv3 Allow hashing for sigv3 on EVM_XATTR_PORTABLE_DIGSIG

 README                 |   6 +-
 src/evmctl.c           | 170 ++++++++++++++++++++++++++++-------------
 src/imaevm.h           |   7 ++
 src/libimaevm.c        |  69 ++++++++++++++++-
 tests/sign_verify.test |  31 ++++++--
 5 files changed, 221 insertions(+), 62 deletions(-)
Signature · 1 line
2.53.0
#1 lore raw

PATCH 1/8 Implement imaevm_create_sigv3 for creating V3 signatures

Implement imaevm_create_sigv3 that creates v3 signatures. This function will now also allocate a buffer if the caller did not provide one. Further, it will write the full signature into the signature buffer, including the leading xattr type byte.

---
 src/imaevm.h    |  7 +++++
 src/libimaevm.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 72 insertions(+), 3 deletions(-)
src/imaevm.h +7 -0
index 3b720d5..5a8441b 100644
@@ -282,4 +282,11 @@ int imaevm_hash_algo_from_sig(unsigned char *sig);
 const char *imaevm_hash_algo_by_id(int algo);
 int calc_hash_sigv3(enum evm_ima_xattr_type type, const char *algo, const unsigned char *in_hash, unsigned char *out_hash);
 
+int imaevm_create_sigv3(const char *hash_algo, const unsigned char *hash, int size,
+			const char *keyfile, const char *keypass,
+			unsigned char **sig, size_t siglen, long sigflags,
+			enum evm_ima_xattr_type xattr_type,
+			const struct imaevm_ossl_access *access_info,
+			uint32_t keyid);
+
 #endif
src/libimaevm.c +65 -3

86 lines not on this page · raw message

Signature · 1 line
2.53.0
#2 lore raw

PATCH 2/8 Implement support for IMA signatures V3 signing scheme

In reply to Stefan Berger

Add support for creating IMA signatures with the V3 signing scheme. Introduce a global variable that states which signing scheme to use and for now set it to SIGNATURE_V2. Implement the SIGNATURE_V3 case where necessary for IMA.

---
 src/evmctl.c | 87 ++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 67 insertions(+), 20 deletions(-)
src/evmctl.c +67 -20
index 7c940fa..8b44ee0 100644
@@ -123,6 +123,13 @@ static bool hwtpm;
 static char *g_hash_algo = DEFAULT_HASH_ALGO;
 static char *g_keypass;
 
+enum signature_version {
+	SIGNATURE_V2 = 2,
+	SIGNATURE_V3,
+};
+
+static enum signature_version g_signature_version = SIGNATURE_V2;
+
 #define HMAC_FLAG_NO_UUID	0x0001
 #define HMAC_FLAG_CAPS_SET	0x0002
 
@@ -652,6 +659,7 @@ static int sign_ima(const char *file, char *hash_algo, const char *key)
 {
 	unsigned char hash[MAX_DIGEST_SIZE];
 	unsigned char sig[MAX_SIGNATURE_SIZE];
+	unsigned char *psig;
 	size_t len;
 	int err;
 
@@ -661,16 +669,35 @@ static int sign_ima(const char *file, char *hash_algo, const char *key)
 	len = (size_t)err;
 	assert(len <= sizeof(hash));
 
-	err = imaevm_signhash(hash_algo, hash, len, key, g_keypass,
-			      sig + 1, sigflags, &access_info, imaevm_keyid);
-	if (err <= 1)
-		return err;
-	len = (size_t)err;
-	assert(len < sizeof(sig));
-
-	/* add header */
-	len++;
-	sig[0] = EVM_IMA_XATTR_DIGSIG;
+	switch (g_signature_version) {
+	case SIGNATURE_V3:
+		psig = sig;
+		err = imaevm_create_sigv3(hash_algo, hash, len, key, g_keypass,
+					  &psig, sizeof(sig), sigflags,
+					  EVM_IMA_XATTR_DIGSIG, &access_info,
+					  imaevm_keyid);
+		if (err <= 1)
+			return err;
+		len = (size_t)err;
+		assert(len <= sizeof(sig));
+		break;
+	case SIGNATURE_V2:
+		err = imaevm_signhash(hash_algo, hash, len, key, g_keypass,
+				      sig + 1, sigflags, &access_info,
+				      imaevm_keyid);
+		if (err <= 1)
+			return err;
+		len = (size_t)err;
+		assert(len < sizeof(sig));
+		/* add header */
+		len++;
+		sig[0] = EVM_IMA_XATTR_DIGSIG;
+		break;
+	default:
+		log_err("Internal error: Unsupported signature version: %d\n",
+			g_signature_version);
+		return -1;
+	}
 
 	if (sigdump || imaevm_params.verbose >= LOG_INFO)
 		imaevm_hexdump(sig, len);
@@ -791,6 +818,7 @@ static int cmd_sign_hash(struct command *cmd)
 	size_t hashlen = 0;
 	int siglen;
 	char *line = NULL, *token, *hashp;
+	unsigned char *psig;
 	size_t line_len = 0;
 	const char *key;
 	char algo[7];	/* Current maximum fsverity hash algo name length */
@@ -863,20 +891,39 @@ static int cmd_sign_hash(struct command *cmd)
 			assert(hashlen / 2 <= sizeof(hash));
 			hex2bin(hash, line, hashlen / 2);
 
-			siglen = imaevm_signhash(g_hash_algo, hash,
-						 hashlen / 2, key, g_keypass,
-						 sig + 1, sigflags,
-						 &access_info, imaevm_keyid);
-			sig[0] = EVM_IMA_XATTR_DIGSIG;
+			switch (g_signature_version) {
+			case SIGNATURE_V3:
+				psig = sig;
+				siglen = imaevm_create_sigv3(g_hash_algo, hash,
+							     hashlen / 2, key, g_keypass,
+							     &psig, sizeof(sig), sigflags,
+							     EVM_IMA_XATTR_DIGSIG,
+							     &access_info, imaevm_keyid);
+				if (siglen <= 1)
+					return siglen;
+				assert(siglen <= (int)sizeof(sig));
+				break;
+			case SIGNATURE_V2:
+				siglen = imaevm_signhash(g_hash_algo, hash,
+							 hashlen / 2, key, g_keypass,
+							 sig + 1, sigflags,
+							 &access_info, imaevm_keyid);
+				if (siglen <= 1)
+					return siglen;
+				assert(siglen < (int)sizeof(sig));
+				siglen++;
+				sig[0] = EVM_IMA_XATTR_DIGSIG;
+				break;
+			default:
+				log_err("Internal error: Unsupported signature version: %d\n",
+					g_signature_version);
+				return -1;
+			}
 		}
 
-		if (siglen <= 1)
-			return siglen;
-		assert(siglen < (int)sizeof(sig));
-
 		fwrite(line, len, 1, stdout);
 		fprintf(stdout, " ");
-		bin2hex(sig, siglen + 1, stdout);
+		bin2hex(sig, siglen, stdout);
 		fprintf(stdout, "\n");
 	}
 
Signature · 1 line
2.53.0
#3 lore raw

PATCH 3/8 Implement support for EVM signatures V3 signing scheme

In reply to Stefan Berger

Add support for creating EVM signatures with the V3 signing scheme. Implement the SIGNATURE_v3 case where necessary for EVM.

---
 src/evmctl.c | 45 +++++++++++++++++++++++++++++++--------------
 1 file changed, 31 insertions(+), 14 deletions(-)
src/evmctl.c +31 -14
index 8b44ee0..aab5af9 100644
@@ -564,8 +564,10 @@ out:
 
 static int sign_evm(const char *file, char *hash_algo, const char *key)
 {
-	unsigned char hash[MAX_DIGEST_SIZE];
 	unsigned char sig[MAX_SIGNATURE_SIZE];
+	unsigned char hash[MAX_DIGEST_SIZE];
+	enum evm_ima_xattr_type xattr_type;
+	unsigned char *psig;
 	size_t len;
 	int err;
 
@@ -575,22 +577,37 @@ static int sign_evm(const char *file, char *hash_algo, const char *key)
 	len = (size_t)err;
 	assert(len <= sizeof(hash));
 
-	err = imaevm_signhash(hash_algo, hash, len, key, g_keypass,
-			      sig + 1, sigflags, &access_info, imaevm_keyid);
-	if (err <= 1)
-		return err;
-	len = (size_t)err;
-	assert(len <= sizeof(sig));
-
-	/* add header */
-	len++;
 	if (evm_portable)
-		sig[0] = EVM_XATTR_PORTABLE_DIGSIG;
+		xattr_type = EVM_XATTR_PORTABLE_DIGSIG;
 	else
-		sig[0] = EVM_IMA_XATTR_DIGSIG;
+		xattr_type = EVM_IMA_XATTR_DIGSIG;
 
-	if (evm_immutable)
-		sig[1] = 3; /* immutable signature version */
+	switch (g_signature_version) {
+	case SIGNATURE_V3:
+		psig = sig;
+		err = imaevm_create_sigv3(hash_algo, hash, len, key, g_keypass,
+					  &psig, sizeof(sig), sigflags,
+					  xattr_type, &access_info,
+					  imaevm_keyid);
+		if (err <= 1)
+			return err;
+		len = (size_t)err;
+		assert(len <= sizeof(sig));
+		break;
+	case SIGNATURE_V2:
+		err = imaevm_signhash(hash_algo, hash, len, key, g_keypass,
+				      sig + 1, sigflags, &access_info, imaevm_keyid);
+		if (err <= 1)
+			return err;
+		len = (size_t)err;
+		assert(len <= sizeof(sig));
+		/* add header */
+		len++;
+		sig[0] = xattr_type;
+		if (evm_immutable)
+			sig[1] = 3; /* immutable signature version */
+		break;
+	}
 
 	if (sigdump || imaevm_params.verbose >= LOG_INFO)
 		imaevm_hexdump(sig, len);
Signature · 1 line
2.53.0
#4 lore raw

PATCH 4/8 Support v3 IMA and EVM file signatures with --v3 option.

In reply to Stefan Berger

Enable both IMA and EVM file signatures with a new --v3 option that sets the previously introduced global variable that states which signature version to use.

Similarly, introduce a --v2 option for users to (already) choose old V2 type of signatures.

Update the README with the dump of the evmctl help screen and mention v3 signature format that is expected for Linux 7.1.

---
 README       |  6 ++++--
 src/evmctl.c | 10 ++++++++++
 2 files changed, 14 insertions(+), 2 deletions(-)
README +4 -2
index 815b555..34dfddf 100644
@@ -84,6 +84,8 @@ OPTIONS
       --provider p   preload OpenSSL provider (such as: pkcs11)
       --ignore-violations ignore ToMToU measurement violations
       --hmackey      path to symmetric key (default: /etc/keys/evm-key-plain)
+      --v2           create V2 signatures; this is the default
+      --v3           create V3 signatures; this requires Linux 7.1 or later
   -v                 increase verbosity level
   -h, --help         display this help and exit
 
@@ -139,7 +141,7 @@ evmctl '--smack' options enables that.
 Key and signature formats
 -------------------------
 
-Linux integrity subsystem supports two type of signature and respectively two
+Linux integrity subsystem supports three types of signature and respectively two
 key formats.
 
 First key format (v1) is pure RSA key encoded in PEM a format and uses own signature
@@ -149,7 +151,7 @@ for signing and importing the key.
 Second key format uses X509 DER encoded public key certificates and uses asymmetric key support
 in the kernel (since kernel 3.9). CONFIG_INTEGRITY_ASYMMETRIC_KEYS must be enabled (default).
 
-For v2 signatures x509 certificate (containing the public key) could be appended to the
+For v2 and v3 signatures x509 certificate (containing the public key) could be appended to the
 private key (they both are in PEM format) to automatically extract keyid from its Subject
 Key Identifier (SKID).
 
src/evmctl.c +10 -0
index aab5af9..2843ff8 100644
@@ -3017,6 +3017,8 @@ static void usage(void)
 #ifdef DEBUG
 		"      --hmackey      path to symmetric key (default: /etc/keys/evm-key-plain)\n"
 #endif
+		"      --v2           create V2 signatures; this is the default\n"
+		"      --v3           create V3 signatures; this requires Linux 7.1 or later\n"
 		"  -v                 increase verbosity level\n"
 		"  -h, --help         display this help and exit\n"
 		"\n"
@@ -3092,6 +3094,8 @@ static struct option opts[] = {
 #if CONFIG_IMA_EVM_PROVIDER
 	{"provider", 1, 0, 149},
 #endif
+	{"v2", 0, 0, 150},
+	{"v3", 0, 0, 151},
 	{}
 
 };
@@ -3370,6 +3374,12 @@ int main(int argc, char *argv[])
 			access_info.type = IMAEVM_OSSL_ACCESS_TYPE_PROVIDER;
 			break;
 #endif
+		case 150: /* --v2 */
+			g_signature_version = SIGNATURE_V2;
+			break;
+		case 151: /* --v3 */
+			g_signature_version = SIGNATURE_V3;
+			break;
 		case '?':
 			exit(1);
 			break;
Signature · 1 line
2.53.0
#5 lore raw

PATCH 5/8 Use imaevm_create_sigv3 for fsverity signature creation

In reply to Stefan Berger

Convert the code that built the fsverity signature with V3 signing scheme to use the new imaevm_create_sigv3 function.

---
 src/evmctl.c | 24 +++++++++---------------
 1 file changed, 9 insertions(+), 15 deletions(-)
src/evmctl.c +9 -15
index 2843ff8..0a38aa7 100644
@@ -828,7 +828,6 @@ static int cmd_sign_ima(struct command *cmd)
  */
 static int cmd_sign_hash(struct command *cmd)
 {
-	unsigned char sigv3_hash[MAX_DIGEST_SIZE];
 	unsigned char sig[MAX_SIGNATURE_SIZE];
 	unsigned char hash[MAX_DIGEST_SIZE];
 	size_t algolen = 0;
@@ -840,7 +839,6 @@ static int cmd_sign_hash(struct command *cmd)
 	const char *key;
 	char algo[7];	/* Current maximum fsverity hash algo name length */
 	ssize_t len;
-	int ret;
 
 	key = imaevm_params.keyfile ? : "/etc/keys/privkey_evm.pem";
 
@@ -888,19 +886,15 @@ static int cmd_sign_hash(struct command *cmd)
 			assert(hashlen / 2 <= sizeof(hash));
 			hex2bin(hash, hashp, hashlen / 2);
 
-			ret = calc_hash_sigv3(IMA_VERITY_DIGSIG, algo, hash,
-					      sigv3_hash);
-			if (ret < 0 || ret == 1) {
-				log_info("Failure to calculate fs-verity hash\n");
-				continue;
-			}
-
-			siglen = imaevm_signhash(algo, sigv3_hash, hashlen / 2,
-						 key, g_keypass, sig + 1, sigflags,
-						 &access_info, imaevm_keyid);
-
-			sig[0] = IMA_VERITY_DIGSIG;
-			sig[1] = DIGSIG_VERSION_3;	/* sigv3 */
+			psig = sig;
+			siglen = imaevm_create_sigv3(algo, hash,
+						     hashlen / 2, key, g_keypass,
+						     &psig, sizeof(sig), sigflags,
+						     IMA_VERITY_DIGSIG,
+						     &access_info, imaevm_keyid);
+			if (siglen <= 1)
+				return siglen;
+			assert(siglen <= (int)sizeof(sig));
 		} else {
 			/* Parse the shaXsum output */
 			token = strpbrk(line, " \t");
Signature · 1 line
2.53.0
#6 lore raw

PATCH 6/8 tests: Add new --v3 option to sign_verify tests

In reply to Stefan Berger

Add the new --v3 option to the sign_verify test cases.

For --v3, adjust openssl signature verification to build an ima_file_id structure in a file that is then used for signature verification rather than the plain file (as before).

---
 tests/sign_verify.test | 31 +++++++++++++++++++++++++------
 1 file changed, 25 insertions(+), 6 deletions(-)
tests/sign_verify.test +25 -6
index c94de24..9319123 100755
@@ -128,7 +128,7 @@ check_sign() {
   # OPTS (additional options for evmctl),
   # FILE (working file to sign).
   local "$@"
-  local key verifykey
+  local key verifykey sigver
   local FILE=${FILE:-$ALG.txt}
 
   # Normalize key filename if it's not a pkcs11 URI
@@ -213,18 +213,30 @@ check_sign() {
       verifykey=${key}
   fi
 
-  cmd="openssl dgst $OPENSSL_ENGINE $OPENSSL_KEYFORM -$ALG -verify ${verifykey} \
-	-signature $FILE.sig2 $FILE"
+  if [[ "$OPTS" =~ "--v3" ]]; then
+    # In case of v3 signatures we need to create ima_file_id now.
+    # All data for it can be found in PREFIX and by hashing $FILE.
+    echo -en "\x${PREFIX:2:2}\x${PREFIX:6:2}" > "$FILE.tmp"
+    # shellcheck disable=SC2086
+    openssl dgst $OPENSSL_ENGINE $OPENSSL_KEYFORM -"$ALG" -binary "$FILE" >> "$FILE.tmp"
+    cmd="openssl dgst $OPENSSL_ENGINE $OPENSSL_KEYFORM -$ALG -verify ${verifykey} \
+	  -signature $FILE.sig2 $FILE.tmp"
+    sigver=3
+  else
+    cmd="openssl dgst $OPENSSL_ENGINE $OPENSSL_KEYFORM -$ALG -verify ${verifykey} \
+	  -signature $FILE.sig2 $FILE"
+    sigver=2
+  fi
   echo - "$cmd"
   if ! $cmd; then
     color_red_on_failure
-    echo "Signature v2 verification with openssl is failed."
+    echo "Signature v${sigver} verification with openssl is failed."
     color_restore
-    rm "$FILE.sig2"
+    rm "$FILE.sig2" "$FILE.tmp"
     return "$FAIL"
   fi
 
-  rm "$FILE.sig2"
+  rm "$FILE.sig2" "$FILE.tmp"
   return "$OK"
 }
 
@@ -390,6 +402,9 @@ sign_verify  rsa1024  sha384  0x030205:K:0080
 sign_verify  rsa1024  sha512  0x030206:K:0080
 sign_verify  rsa1024  rmd160  0x030203:K:0080
 
+sign_verify rsa1024  sha384  0x030305:K:0080 --v3
+sign_verify rsa1024  sha512  0x030306:K:0080 --v3
+
 # Test v2 signatures with ECDSA
 # Signature length is typically 0x34-0x38 bytes long, very rarely 0x33
 sign_verify  prime192v1 sha1   0x030202:K:003[345678]
@@ -405,6 +420,10 @@ sign_verify  prime256v1 sha256 0x030204:K:004[345678]
 sign_verify  prime256v1 sha384 0x030205:K:004[345678]
 sign_verify  prime256v1 sha512 0x030206:K:004[345678]
 
+sign_verify  prime256v1 sha256   0x030304:K:004[345678] --v3
+sign_verify  prime256v1 sha384   0x030305:K:004[345678] --v3
+sign_verify  prime256v1 sha512   0x030306:K:004[345678] --v3
+
 # If openssl 3.0 is installed, test the SM2/3 algorithm combination
 ssl_major_version=$(openssl version | sed -n 's/^OpenSSL \([^\.]\).*/\1/p')
 if [ "${ssl_major_version}" = 3 ]; then
Signature · 1 line
2.53.0
#7 lore raw

PATCH 7/8 Allow verification of EVM_XATTR_PORTABLE_DIGSIG with sigv3

In reply to Stefan Berger

To enable sigv3 signature verification for EVM portable signatures, allow signature verification on EVM_XATTR_PORTABLE_DIGSIG type of xattrs with sigv3.

---
 src/evmctl.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
src/evmctl.c +1 -1
index 0a38aa7..de67178 100644
@@ -995,7 +995,7 @@ static int verify_evm(struct public_key_entry *public_keys, const char *file)
 	}
 
 	if (sig[0] == EVM_XATTR_PORTABLE_DIGSIG) {
-		if (sig[1] != DIGSIG_VERSION_2) {
+		if (sig[1] != DIGSIG_VERSION_2 && sig[1] != DIGSIG_VERSION_3) {
 			log_err("Portable sig: invalid type\n");
 			return -1;
 		}
Signature · 1 line
2.53.0
#8 lore raw

PATCH 8/8 Allow hashing for sigv3 on EVM_XATTR_PORTABLE_DIGSIG

In reply to Stefan Berger

To enable sigv3 for EVM portable signatures, enable hashing for sigv3 for EVM_XATTR_PORTABLE_DIGSIG.

---
 src/libimaevm.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
src/libimaevm.c +2 -1
index 7c78432..49bfb62 100644
@@ -623,7 +623,8 @@ int calc_hash_sigv3(enum evm_ima_xattr_type type, const char *algo,
 	unsigned int unused;
 
 	if (type != IMA_VERITY_DIGSIG &&
-	    type != EVM_IMA_XATTR_DIGSIG) {
+	    type != EVM_IMA_XATTR_DIGSIG &&
+	    type != EVM_XATTR_PORTABLE_DIGSIG) {
 		log_err("Only fsverity and IMA/EVM support signature format v3 (sigv3)\n");
 		return -EINVAL;
 	}
Signature · 1 line
2.53.0