All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 01/13] pem: Wipe more memory in PKCS#1 private key decoding
@ 2020-12-10 19:31 Andrew Zaborowski
  2020-12-10 19:31 ` [PATCH 02/13] pem: Factor out the PKCS#8 decoding code Andrew Zaborowski
                   ` (8 more replies)
  0 siblings, 9 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-12-10 19:31 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 1360 bytes --]

bzero some more potentially sensitive memory that we weren't bzeroing in
the recently added openssl legacy PKCS#1 private key format decoding.
---
 ell/pem.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/ell/pem.c b/ell/pem.c
index 790f2c2..70d1f62 100644
--- a/ell/pem.c
+++ b/ell/pem.c
@@ -581,18 +581,24 @@ static struct l_cipher *cipher_from_dek_info(const char *algid, const char *para
 
 	l_checksum_free(md5);
 
-	if (!ok)
-		return NULL;
+	if (!ok) {
+		cipher = NULL;
+		goto cleanup;
+	}
 
 	cipher  = l_cipher_new(type, key, key_len);
 	if (!cipher)
-		return NULL;
+		goto cleanup;
 
 	if (l_cipher_set_iv(cipher, iv, iv_len))
-		return cipher;
+		goto cleanup;
 
 	l_cipher_free(cipher);
-	return NULL;
+	cipher = NULL;
+
+cleanup:
+	explicit_bzero(key, sizeof(key));
+	return cipher;
 }
 
 static struct l_key *pem_load_private_key(uint8_t *content,
@@ -821,8 +827,10 @@ static struct l_key *pem_load_private_key(uint8_t *content,
 		ptr += sizeof(pkcs1_rsa_encryption);
 		memcpy(ptr, private_key, private_key_len);
 		ptr += private_key_len;
+		explicit_bzero(private_key, private_key_len);
 		l_free(private_key);
 
+		explicit_bzero(content, len);
 		l_free(content);
 		content = one_asymmetric_key;
 		len = ptr - one_asymmetric_key;
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 02/13] pem: Factor out the PKCS#8 decoding code
  2020-12-10 19:31 [PATCH 01/13] pem: Wipe more memory in PKCS#1 private key decoding Andrew Zaborowski
@ 2020-12-10 19:31 ` Andrew Zaborowski
  2020-12-10 19:31 ` [PATCH 03/13] cipher: Add an RC2 implementation Andrew Zaborowski
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-12-10 19:31 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 6979 bytes --]

---
 ell/pem-private.h |   7 ++
 ell/pem.c         | 167 ++++++++++++++++++++++++----------------------
 2 files changed, 96 insertions(+), 78 deletions(-)

diff --git a/ell/pem-private.h b/ell/pem-private.h
index 1cf7ee5..6e9458d 100644
--- a/ell/pem-private.h
+++ b/ell/pem-private.h
@@ -30,3 +30,10 @@
 const char *pem_next(const void *buf, size_t buf_len, char **type_label,
 				size_t *base64_len,
 				const char **endp, bool strict);
+
+struct l_key *pem_key_from_pkcs8_private_key_info(const uint8_t *der,
+							size_t der_len);
+
+struct l_key *pem_key_from_pkcs8_encrypted_private_key_info(const uint8_t *der,
+							size_t der_len,
+							const char *passphrase);
diff --git a/ell/pem.c b/ell/pem.c
index 70d1f62..42c7975 100644
--- a/ell/pem.c
+++ b/ell/pem.c
@@ -601,6 +601,79 @@ cleanup:
 	return cipher;
 }
 
+struct l_key *pem_key_from_pkcs8_private_key_info(const uint8_t *der,
+							size_t der_len)
+{
+	return l_key_new(L_KEY_RSA, der, der_len);
+}
+
+struct l_key *pem_key_from_pkcs8_encrypted_private_key_info(const uint8_t *der,
+							size_t der_len,
+							const char *passphrase)
+{
+	const uint8_t *key_info, *alg_id, *data;
+	uint8_t tag;
+	size_t key_info_len, alg_id_len, data_len, tmp_len;
+	struct l_cipher *alg;
+	uint8_t *decrypted;
+	int i;
+	struct l_key *pkey;
+	bool r;
+
+	/* Technically this is BER, not limited to DER */
+	key_info = asn1_der_find_elem(der, der_len, 0, &tag, &key_info_len);
+	if (!key_info || tag != ASN1_ID_SEQUENCE)
+		return NULL;
+
+	alg_id = asn1_der_find_elem(key_info, key_info_len, 0, &tag,
+					&alg_id_len);
+	if (!alg_id || tag != ASN1_ID_SEQUENCE)
+		return NULL;
+
+	data = asn1_der_find_elem(key_info, key_info_len, 1, &tag, &data_len);
+	if (!data || tag != ASN1_ID_OCTET_STRING || data_len < 8 ||
+			(data_len & 7) != 0)
+		return NULL;
+
+	if (asn1_der_find_elem(der, der_len, 2, &tag, &tmp_len))
+		return NULL;
+
+	alg = pkcs5_cipher_from_alg_id(alg_id, alg_id_len, passphrase);
+	if (!alg)
+		return NULL;
+
+	decrypted = l_malloc(data_len);
+
+	r = l_cipher_decrypt(alg, data, decrypted, data_len);
+	l_cipher_free(alg);
+
+	if (!r) {
+		l_free(decrypted);
+		return NULL;
+	}
+
+	/*
+	 * Strip padding as defined in RFC8018 (for PKCS#5 v1) or
+	 * RFC1423 / RFC5652 (for v2).
+	 */
+	pkey = NULL;
+	if (decrypted[data_len - 1] >= data_len ||
+			decrypted[data_len - 1] > 16)
+		goto cleanup;
+
+	for (i = 1; i < decrypted[data_len - 1]; i++)
+		if (decrypted[data_len - 1 - i] != decrypted[data_len - 1])
+			goto cleanup;
+
+	pkey = pem_key_from_pkcs8_private_key_info(decrypted,
+					data_len - decrypted[data_len - 1]);
+
+cleanup:
+	explicit_bzero(decrypted, data_len);
+	l_free(decrypted);
+	return pkey;
+}
+
 static struct l_key *pem_load_private_key(uint8_t *content,
 						size_t len,
 						char *label,
@@ -608,7 +681,7 @@ static struct l_key *pem_load_private_key(uint8_t *content,
 						char *headers,
 						bool *encrypted)
 {
-	struct l_key *pkey = NULL;
+	struct l_key *pkey;
 
 	/*
 	 * RFC7468 Section 10-compatible unencrypted private key label
@@ -621,6 +694,7 @@ static struct l_key *pem_load_private_key(uint8_t *content,
 		if (headers)
 			goto err;
 
+		pkey = pem_key_from_pkcs8_private_key_info(content, len);
 		goto done;
 	}
 
@@ -632,13 +706,6 @@ static struct l_key *pem_load_private_key(uint8_t *content,
 	 * pkcs8-key-parser module.
 	 */
 	if (!strcmp(label, "ENCRYPTED PRIVATE KEY")) {
-		const uint8_t *key_info, *alg_id, *data;
-		uint8_t tag;
-		size_t key_info_len, alg_id_len, data_len, tmp_len;
-		struct l_cipher *alg;
-		uint8_t *decrypted;
-		int i;
-
 		if (encrypted)
 			*encrypted = true;
 
@@ -649,59 +716,9 @@ static struct l_key *pem_load_private_key(uint8_t *content,
 		if (headers)
 			goto err;
 
-		/* Technically this is BER, not limited to DER */
-		key_info = asn1_der_find_elem(content, len, 0, &tag,
-						&key_info_len);
-		if (!key_info || tag != ASN1_ID_SEQUENCE)
-			goto err;
-
-		alg_id = asn1_der_find_elem(key_info, key_info_len, 0, &tag,
-						&alg_id_len);
-		if (!alg_id || tag != ASN1_ID_SEQUENCE)
-			goto err;
-
-		data = asn1_der_find_elem(key_info, key_info_len, 1, &tag,
-						&data_len);
-		if (!data || tag != ASN1_ID_OCTET_STRING || data_len < 8 ||
-				(data_len & 7) != 0)
-			goto err;
-
-		if (asn1_der_find_elem(content, len, 2, &tag, &tmp_len))
-			goto err;
-
-		alg = pkcs5_cipher_from_alg_id(alg_id, alg_id_len, passphrase);
-		if (!alg)
-			goto err;
-
-		decrypted = l_malloc(data_len);
-
-		if (!l_cipher_decrypt(alg, data, decrypted, data_len)) {
-			l_cipher_free(alg);
-			l_free(decrypted);
-			goto err;
-		}
-
-		l_cipher_free(alg);
-		explicit_bzero(content, len);
-		l_free(content);
-		content = decrypted;
-		len = data_len;
-
-		/*
-		 * Strip padding as defined in RFC8018 (for PKCS#5 v1) or
-		 * RFC1423 / RFC5652 (for v2).
-		 */
-
-		if (content[data_len - 1] >= data_len ||
-				content[data_len - 1] > 16)
-			goto err;
-
-		for (i = 1; i < content[data_len - 1]; i++)
-			if (content[data_len - 1 - i] != content[data_len - 1])
-				goto err;
-
-		len = data_len - content[data_len - 1];
-
+		pkey = pem_key_from_pkcs8_encrypted_private_key_info(content,
+								len,
+								passphrase);
 		goto done;
 	}
 
@@ -791,19 +808,19 @@ static struct l_key *pem_load_private_key(uint8_t *content,
 		key_data = asn1_der_find_elem(content, len, 0, &tag,
 						&key_data_len);
 		if (!key_data || tag != ASN1_ID_SEQUENCE)
-			goto done;
+			goto err;
 
 		data = asn1_der_find_elem(key_data, key_data_len, 0, &tag,
 						&data_len);
 		if (!data || tag != ASN1_ID_INTEGER || data_len != 1 ||
 				(data[0] != 0x00 && data[0] != 0x01))
-			goto done;
+			goto err;
 
 		for (i = 1; i < 9; i++) {
 			data = asn1_der_find_elem(key_data, key_data_len,
 							i, &tag, &data_len);
 			if (!data || tag != ASN1_ID_INTEGER || data_len < 1)
-				goto done;
+				goto err;
 		}
 
 		private_key = l_malloc(10 + len);
@@ -830,25 +847,19 @@ static struct l_key *pem_load_private_key(uint8_t *content,
 		explicit_bzero(private_key, private_key_len);
 		l_free(private_key);
 
-		explicit_bzero(content, len);
-		l_free(content);
-		content = one_asymmetric_key;
-		len = ptr - one_asymmetric_key;
+		pkey = pem_key_from_pkcs8_private_key_info(one_asymmetric_key,
+						ptr - one_asymmetric_key);
+		explicit_bzero(one_asymmetric_key, ptr - one_asymmetric_key);
+		l_free(one_asymmetric_key);
 		goto done;
 	}
 
 	/* Label not known */
-	goto err;
-
-done:
-	pkey = l_key_new(L_KEY_RSA, content, len);
-
 err:
-	if (content) {
-		explicit_bzero(content, len);
-		l_free(content);
-	}
-
+	pkey = NULL;
+done:
+	explicit_bzero(content, len);
+	l_free(content);
 	l_free(label);
 	l_free(headers);
 	return pkey;
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 03/13] cipher: Add an RC2 implementation
  2020-12-10 19:31 [PATCH 01/13] pem: Wipe more memory in PKCS#1 private key decoding Andrew Zaborowski
  2020-12-10 19:31 ` [PATCH 02/13] pem: Factor out the PKCS#8 decoding code Andrew Zaborowski
@ 2020-12-10 19:31 ` Andrew Zaborowski
  2020-12-10 19:31 ` [PATCH 04/13] cipher: Add ARC4 implementation Andrew Zaborowski
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-12-10 19:31 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 14425 bytes --]

RC2 is still used by default by tools creating PKCS#12 files (.pfx /
.p12).  Since it's not available from the kernel crypto API, add a local
implementation.  This is currently the only cipher implemented locally
so checks are needed in all our l_cipher ops to see whether a cipher is
handled locally or by the kernel.

Adding this cipher is not a recommendation of using it, it's discouraged
where it can be avoided.
---
 ell/cipher.c | 365 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 ell/cipher.h |   1 +
 2 files changed, 363 insertions(+), 3 deletions(-)

diff --git a/ell/cipher.c b/ell/cipher.c
index 6aec42f..a2db608 100644
--- a/ell/cipher.c
+++ b/ell/cipher.c
@@ -86,14 +86,18 @@ struct af_alg_iv {
 #define ALG_SET_AEAD_AUTHSIZE	5
 #endif
 
-#define is_valid_type(type)  ((type) <= L_CIPHER_DES3_EDE_CBC)
+#define is_valid_type(type)  ((type) <= L_CIPHER_RC2_CBC)
 
 static uint32_t supported_ciphers;
 static uint32_t supported_aead_ciphers;
 
 struct l_cipher {
 	int type;
-	int sk;
+	const struct local_impl *local;
+	union {
+		int sk;
+		void *local_data;
+	};
 };
 
 struct l_aead_cipher {
@@ -101,6 +105,16 @@ struct l_aead_cipher {
 	int sk;
 };
 
+struct local_impl {
+	void *(*cipher_new)(enum l_cipher_type,
+				const void *key, size_t key_length);
+	void (*cipher_free)(void *data);
+	bool (*set_iv)(void *data, const uint8_t *iv, size_t iv_length);
+	ssize_t (*operate)(void *data, __u32 operation,
+				const struct iovec *in, size_t in_cnt,
+				const struct iovec *out, size_t out_cnt);
+};
+
 static int create_alg(const char *alg_type, const char *alg_name,
 			const void *key, size_t key_length, size_t tag_length)
 {
@@ -154,11 +168,23 @@ static const char *cipher_type_to_name(enum l_cipher_type type)
 		return "cbc(des)";
 	case L_CIPHER_DES3_EDE_CBC:
 		return "cbc(des3_ede)";
+	case L_CIPHER_RC2_CBC:
+		return NULL;
 	}
 
 	return NULL;
 }
 
+static const struct local_impl local_rc2_cbc;
+
+static const struct local_impl *local_impl_ciphers[] = {
+	[L_CIPHER_RC2_CBC] = &local_rc2_cbc,
+};
+
+#define HAVE_LOCAL_IMPLEMENTATION(type)			\
+	((type) < L_ARRAY_SIZE(local_impl_ciphers) &&	\
+	 local_impl_ciphers[(type)])
+
 LIB_EXPORT struct l_cipher *l_cipher_new(enum l_cipher_type type,
 						const void *key,
 						size_t key_length)
@@ -176,6 +202,17 @@ LIB_EXPORT struct l_cipher *l_cipher_new(enum l_cipher_type type,
 	cipher->type = type;
 	alg_name = cipher_type_to_name(type);
 
+	if (HAVE_LOCAL_IMPLEMENTATION(type)) {
+		cipher->local = local_impl_ciphers[type];
+		cipher->local_data = cipher->local->cipher_new(type,
+							key, key_length);
+
+		if (!cipher->local_data)
+			goto error_free;
+
+		return cipher;
+	}
+
 	cipher->sk = create_alg("skcipher", alg_name, key, key_length, 0);
 	if (cipher->sk < 0)
 		goto error_free;
@@ -230,7 +267,10 @@ LIB_EXPORT void l_cipher_free(struct l_cipher *cipher)
 	if (unlikely(!cipher))
 		return;
 
-	close(cipher->sk);
+	if (cipher->local)
+		cipher->local->cipher_free(cipher->local_data);
+	else
+		close(cipher->sk);
 
 	l_free(cipher);
 }
@@ -403,6 +443,15 @@ LIB_EXPORT bool l_cipher_encrypt(struct l_cipher *cipher,
 	if (unlikely(!in) || unlikely(!out))
 		return false;
 
+	if (cipher->local) {
+		struct iovec in_iov = { (void *) in, len };
+		struct iovec out_iov = { out, len };
+
+		return cipher->local->operate(cipher->local_data,
+						ALG_OP_ENCRYPT,
+						&in_iov, 1, &out_iov, 1) >= 0;
+	}
+
 	return operate_cipher(cipher->sk, ALG_OP_ENCRYPT, in, len,
 				NULL, 0, NULL, 0, out, len) >= 0;
 }
@@ -417,6 +466,11 @@ LIB_EXPORT bool l_cipher_encryptv(struct l_cipher *cipher,
 	if (unlikely(!in) || unlikely(!out))
 		return false;
 
+	if (cipher->local)
+		return cipher->local->operate(cipher->local_data,
+						ALG_OP_ENCRYPT,
+						in, in_cnt, out, out_cnt) >= 0;
+
 	return operate_cipherv(cipher->sk, ALG_OP_ENCRYPT, in, in_cnt,
 				out, out_cnt) >= 0;
 }
@@ -430,6 +484,15 @@ LIB_EXPORT bool l_cipher_decrypt(struct l_cipher *cipher,
 	if (unlikely(!in) || unlikely(!out))
 		return false;
 
+	if (cipher->local) {
+		struct iovec in_iov = { (void *) in, len };
+		struct iovec out_iov = { out, len };
+
+		return cipher->local->operate(cipher->local_data,
+						ALG_OP_DECRYPT,
+						&in_iov, 1, &out_iov, 1) >= 0;
+	}
+
 	return operate_cipher(cipher->sk, ALG_OP_DECRYPT, in, len,
 				NULL, 0, NULL, 0, out, len) >= 0;
 }
@@ -444,6 +507,11 @@ LIB_EXPORT bool l_cipher_decryptv(struct l_cipher *cipher,
 	if (unlikely(!in) || unlikely(!out))
 		return false;
 
+	if (cipher->local)
+		return cipher->local->operate(cipher->local_data,
+						ALG_OP_DECRYPT,
+						in, in_cnt, out, out_cnt) >= 0;
+
 	return operate_cipherv(cipher->sk, ALG_OP_DECRYPT, in, in_cnt,
 				out, out_cnt) >= 0;
 }
@@ -459,6 +527,13 @@ LIB_EXPORT bool l_cipher_set_iv(struct l_cipher *cipher, const uint8_t *iv,
 	if (unlikely(!cipher))
 		return false;
 
+	if (cipher->local) {
+		if (!cipher->local->set_iv)
+			return false;
+
+		return cipher->local->set_iv(cipher->local_data, iv, iv_length);
+	}
+
 	memset(&c_msg_buf, 0, sizeof(c_msg_buf));
 	memset(&msg, 0, sizeof(struct msghdr));
 
@@ -616,6 +691,10 @@ static void init_supported()
 		supported_ciphers |= 1 << c;
 	}
 
+	for (c = 0; c < L_ARRAY_SIZE(local_impl_ciphers); c++)
+		if (HAVE_LOCAL_IMPLEMENTATION(c))
+			supported_ciphers |= 1 << c;
+
 	strcpy((char *) salg.salg_type, "aead");
 
 	for (a = L_AEAD_CIPHER_AES_CCM; a <= L_AEAD_CIPHER_AES_GCM; a++) {
@@ -649,3 +728,283 @@ LIB_EXPORT bool l_aead_cipher_is_supported(enum l_aead_cipher_type type)
 
 	return supported_aead_ciphers & (1 << type);
 }
+
+struct rc2_state {
+	union {
+		uint16_t xkey[64];
+		uint8_t xkey8[128];
+	};
+	struct rc2_state_ctx {
+		union {
+			uint16_t x[4];
+			uint64_t x64;
+		};
+	} ctx[2];
+};
+
+/* Simplified from the 1996 public-domain implementation */
+static void rc2_keyschedule(struct rc2_state *s,
+				const uint8_t *key, size_t key_len,
+				size_t bits)
+{
+	static const uint8_t permute[256] = {
+		217,120,249,196, 25,221,181,237, 40,233,253,121, 74,160,216,157,
+		198,126, 55,131, 43,118, 83,142, 98, 76,100,136, 68,139,251,162,
+		 23,154, 89,245,135,179, 79, 19, 97, 69,109,141,  9,129,125, 50,
+		189,143, 64,235,134,183,123, 11,240,149, 33, 34, 92,107, 78,130,
+		 84,214,101,147,206, 96,178, 28,115, 86,192, 20,167,140,241,220,
+		 18,117,202, 31, 59,190,228,209, 66, 61,212, 48,163, 60,182, 38,
+		111,191, 14,218, 70,105,  7, 87, 39,242, 29,155,188,148, 67,  3,
+		248, 17,199,246,144,239, 62,231,  6,195,213, 47,200,102, 30,215,
+		  8,232,234,222,128, 82,238,247,132,170,114,172, 53, 77,106, 42,
+		150, 26,210,113, 90, 21, 73,116, 75,159,208, 94,  4, 24,164,236,
+		194,224, 65,110, 15, 81,203,204, 36,145,175, 80,161,244,112, 57,
+		153,124, 58,133, 35,184,180,122,252,  2, 54, 91, 37, 85,151, 49,
+		 45, 93,250,152,227,138,146,174,  5,223, 41, 16,103,108,186,201,
+		211,  0,230,207,225,158,168, 44, 99, 22,  1, 63, 88,226,137,169,
+		 13, 56, 52, 27,171, 51,255,176,187, 72, 12, 95,185,177,205, 46,
+		197,243,219, 71,229,165,156,119, 10,166, 32,104,254,127,193,173
+	};
+	uint8_t x;
+	unsigned int i;
+
+	memcpy(&s->xkey8, key, key_len);
+
+	/* Step 1: expand input key to 128 bytes */
+	x = s->xkey8[key_len - 1];
+
+	for (i = 0; key_len < 128; key_len++, i++)
+		s->xkey8[key_len] = x = permute[(x + s->xkey8[i]) & 255];
+
+	/* Step 2: reduce effective key size to "bits" */
+	key_len = (bits + 7) >> 3;
+	i = 128 - key_len;
+	s->xkey8[i] = x = permute[s->xkey8[i] & (255 >> (7 & -bits))];
+
+	while (i--)
+		s->xkey8[i] = x = permute[x ^ s->xkey8[i + key_len]];
+
+	/* Step 3: copy to xkey in little-endian order */
+	for (i = 0; i < 64; i++)
+		s->xkey[i] = L_CPU_TO_LE16(s->xkey[i]);
+}
+
+static uint64_t rc2_operate(struct rc2_state *s, uint64_t in, __u32 operation)
+{
+	int i;
+	union {
+		uint16_t x16[4];
+		uint64_t x64;
+	} x;
+
+	x.x64 = in;
+
+	if (operation == ALG_OP_ENCRYPT) {
+		const uint16_t *xkey = s->xkey;
+
+		for (i = 0; i < 16; i++) {
+			x.x16[0] += (x.x16[1] & ~x.x16[3]) +
+				(x.x16[2] & x.x16[3]) + *xkey++;
+			x.x16[0] = (x.x16[0] << 1) | (x.x16[0] >> 15);
+			x.x16[1] += (x.x16[2] & ~x.x16[0]) +
+				(x.x16[3] & x.x16[0]) + *xkey++;
+			x.x16[1] = (x.x16[1] << 2) | (x.x16[1] >> 14);
+			x.x16[2] += (x.x16[3] & ~x.x16[1]) +
+				(x.x16[0] & x.x16[1]) + *xkey++;
+			x.x16[2] = (x.x16[2] << 3) | (x.x16[2] >> 13);
+			x.x16[3] += (x.x16[0] & ~x.x16[2]) +
+				(x.x16[1] & x.x16[2]) + *xkey++;
+			x.x16[3] = (x.x16[3] << 5) | (x.x16[3] >> 11);
+
+			if (i == 4 || i == 10) {
+				x.x16[0] += s->xkey[x.x16[3] & 63];
+				x.x16[1] += s->xkey[x.x16[0] & 63];
+				x.x16[2] += s->xkey[x.x16[1] & 63];
+				x.x16[3] += s->xkey[x.x16[2] & 63];
+			}
+		}
+	} else {
+		const uint16_t *xkey = s->xkey + 63;
+
+		for (i = 0; i < 16; i++) {
+			x.x16[3] = (x.x16[3] << 11) | (x.x16[3] >> 5);
+			x.x16[3] -= (x.x16[0] & ~x.x16[2]) +
+				(x.x16[1] & x.x16[2]) + *xkey--;
+			x.x16[2] = (x.x16[2] << 13) | (x.x16[2] >> 3);
+			x.x16[2] -= (x.x16[3] & ~x.x16[1]) +
+				(x.x16[0] & x.x16[1]) + *xkey--;
+			x.x16[1] = (x.x16[1] << 14) | (x.x16[1] >> 2);
+			x.x16[1] -= (x.x16[2] & ~x.x16[0]) +
+				(x.x16[3] & x.x16[0]) + *xkey--;
+			x.x16[0] = (x.x16[0] << 15) | (x.x16[0] >> 1);
+			x.x16[0] -= (x.x16[1] & ~x.x16[3]) +
+				(x.x16[2] & x.x16[3]) + *xkey--;
+
+			if (i == 4 || i == 10) {
+				x.x16[3] -= s->xkey[x.x16[2] & 63];
+				x.x16[2] -= s->xkey[x.x16[1] & 63];
+				x.x16[1] -= s->xkey[x.x16[0] & 63];
+				x.x16[0] -= s->xkey[x.x16[3] & 63];
+			}
+		}
+	}
+
+	return x.x64;
+}
+
+static void *local_rc2_cbc_new(enum l_cipher_type type,
+				const void *key, size_t key_length)
+{
+	struct rc2_state *s;
+
+	if (unlikely(key_length == 0 || key_length > 128))
+		return NULL;
+
+	/*
+	 * The key length and the effective "strength" bits are separate
+	 * parameters but they match in our current use cases.
+	 */
+	s = l_new(struct rc2_state, 1);
+	rc2_keyschedule(s, key, key_length, key_length * 8);
+	return s;
+}
+
+static void local_rc2_cbc_free(void *data)
+{
+	explicit_bzero(data, sizeof(struct rc2_state));
+	l_free(data);
+}
+
+static bool local_rc2_cbc_set_iv(void *data,
+				const uint8_t *iv, size_t iv_length)
+{
+	struct rc2_state *s = data;
+
+	if (unlikely(iv_length != 8))
+		return false;
+
+	s->ctx[0].x[0] = l_get_le16(iv + 0);
+	s->ctx[0].x[1] = l_get_le16(iv + 2);
+	s->ctx[0].x[2] = l_get_le16(iv + 4);
+	s->ctx[0].x[3] = l_get_le16(iv + 6);
+	s->ctx[1].x64 = s->ctx[0].x64;
+	return true;
+}
+
+static ssize_t local_rc2_cbc_operate(void *data, __u32 operation,
+					const struct iovec *in, size_t in_cnt,
+					const struct iovec *out, size_t out_cnt)
+{
+	struct rc2_state *s = data;
+	struct iovec cur_in = {};
+	struct iovec cur_out = {};
+	struct rc2_state_ctx *ctx =
+		&s->ctx[operation == ALG_OP_ENCRYPT ? 1 : 0];
+
+#define CONSUME_IN(bytes, eof_ok)		\
+	cur_in.iov_len -= (bytes);		\
+	while (!cur_in.iov_len) {		\
+		if (!in_cnt) {			\
+			if (eof_ok)		\
+				break;		\
+			else			\
+				return -1;	\
+		}				\
+						\
+		cur_in = *in++;			\
+		in_cnt--;			\
+	}
+
+#define CONSUME_OUT(bytes)			\
+	cur_out.iov_len -= (bytes);		\
+	while (!cur_out.iov_len) {		\
+		if (!out_cnt)			\
+			return 0;		\
+						\
+		cur_out = *out++;		\
+		out_cnt--;			\
+	}
+
+	CONSUME_IN(0, true)
+	CONSUME_OUT(0)
+
+	while (cur_in.iov_len) {
+		union {
+			uint16_t x16[4];
+			uint64_t x64;
+		} inblk;
+
+		if (cur_in.iov_len >= 8) {
+#define CUR_IN16 (*(uint16_t **) &cur_in.iov_base)
+			inblk.x16[0] = l_get_le16(CUR_IN16++);
+			inblk.x16[1] = l_get_le16(CUR_IN16++);
+			inblk.x16[2] = l_get_le16(CUR_IN16++);
+			inblk.x16[3] = l_get_le16(CUR_IN16++);
+			CONSUME_IN(8, true)
+		} else {
+			inblk.x16[0] = *(uint8_t *) cur_in.iov_base++;
+			CONSUME_IN(1, false)
+			inblk.x16[0] |= (*(uint8_t *) cur_in.iov_base++) << 8;
+			CONSUME_IN(1, false)
+			inblk.x16[1] = *(uint8_t *) cur_in.iov_base++;
+			CONSUME_IN(1, false)
+			inblk.x16[1] |= (*(uint8_t *) cur_in.iov_base++) << 8;
+			CONSUME_IN(1, false)
+			inblk.x16[2] = *(uint8_t *) cur_in.iov_base++;
+			CONSUME_IN(1, false)
+			inblk.x16[2] |= (*(uint8_t *) cur_in.iov_base++) << 8;
+			CONSUME_IN(1, false)
+			inblk.x16[3] = *(uint8_t *) cur_in.iov_base++;
+			CONSUME_IN(1, false)
+			inblk.x16[3] |= (*(uint8_t *) cur_in.iov_base++) << 8;
+			CONSUME_IN(1, true)
+		}
+
+		if (operation == ALG_OP_ENCRYPT)
+			ctx->x64 = rc2_operate(s, inblk.x64 ^ ctx->x64,
+						operation);
+		else
+			ctx->x64 ^= rc2_operate(s, inblk.x64, operation);
+
+		if (cur_out.iov_len >= 8) {
+#define CUR_OUT16 (*(uint16_t **) &cur_out.iov_base)
+			l_put_le16(ctx->x[0], CUR_OUT16++);
+			l_put_le16(ctx->x[1], CUR_OUT16++);
+			l_put_le16(ctx->x[2], CUR_OUT16++);
+			l_put_le16(ctx->x[3], CUR_OUT16++);
+			CONSUME_OUT(8)
+		} else {
+			*(uint8_t *) cur_out.iov_base++ = ctx->x[0];
+			CONSUME_OUT(1)
+			*(uint8_t *) cur_out.iov_base++ = ctx->x[0] >> 8;
+			CONSUME_OUT(1)
+			*(uint8_t *) cur_out.iov_base++ = ctx->x[1];
+			CONSUME_OUT(1)
+			*(uint8_t *) cur_out.iov_base++ = ctx->x[1] >> 8;
+			CONSUME_OUT(1)
+			*(uint8_t *) cur_out.iov_base++ = ctx->x[2];
+			CONSUME_OUT(1)
+			*(uint8_t *) cur_out.iov_base++ = ctx->x[2] >> 8;
+			CONSUME_OUT(1)
+			*(uint8_t *) cur_out.iov_base++ = ctx->x[3];
+			CONSUME_OUT(1)
+			*(uint8_t *) cur_out.iov_base++ = ctx->x[3] >> 8;
+			CONSUME_OUT(1)
+		}
+
+		/* Save ciphertext as IV for next CBC block */
+		if (operation == ALG_OP_DECRYPT)
+			ctx->x64 = inblk.x64;
+
+		inblk.x64 = 0;
+	}
+
+	return 0;
+}
+
+static const struct local_impl local_rc2_cbc = {
+	local_rc2_cbc_new,
+	local_rc2_cbc_free,
+	local_rc2_cbc_set_iv,
+	local_rc2_cbc_operate,
+};
diff --git a/ell/cipher.h b/ell/cipher.h
index e1a4bda..ed277a6 100644
--- a/ell/cipher.h
+++ b/ell/cipher.h
@@ -37,6 +37,7 @@ enum l_cipher_type {
 	L_CIPHER_DES = 4,
 	L_CIPHER_DES_CBC,
 	L_CIPHER_DES3_EDE_CBC,
+	L_CIPHER_RC2_CBC,
 };
 
 struct l_cipher *l_cipher_new(enum l_cipher_type type,
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 04/13] cipher: Add ARC4 implementation
  2020-12-10 19:31 [PATCH 01/13] pem: Wipe more memory in PKCS#1 private key decoding Andrew Zaborowski
  2020-12-10 19:31 ` [PATCH 02/13] pem: Factor out the PKCS#8 decoding code Andrew Zaborowski
  2020-12-10 19:31 ` [PATCH 03/13] cipher: Add an RC2 implementation Andrew Zaborowski
@ 2020-12-10 19:31 ` Andrew Zaborowski
  2020-12-10 22:46   ` Denis Kenzior
  2020-12-10 19:31 ` [PATCH 05/13] pem: Don't expect padding with stream ciphers Andrew Zaborowski
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 15+ messages in thread
From: Andrew Zaborowski @ 2020-12-10 19:31 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 3695 bytes --]

Re-add the L_CIPHER_ARC4 stream cipher for use in decoding PKCS#12
files.  Like RC2 it's implemented completely in cipher.c because the
kernel API doesn't support it.  This cipher is also discouraged where it
can be avoided.
---
 ell/cipher.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 ell/cipher.h |  4 +--
 2 files changed, 100 insertions(+), 2 deletions(-)

diff --git a/ell/cipher.c b/ell/cipher.c
index a2db608..5d67d7a 100644
--- a/ell/cipher.c
+++ b/ell/cipher.c
@@ -162,6 +162,8 @@ static const char *cipher_type_to_name(enum l_cipher_type type)
 		return "cbc(aes)";
 	case L_CIPHER_AES_CTR:
 		return "ctr(aes)";
+	case L_CIPHER_ARC4:
+		return NULL;
 	case L_CIPHER_DES:
 		return "ecb(des)";
 	case L_CIPHER_DES_CBC:
@@ -175,9 +177,11 @@ static const char *cipher_type_to_name(enum l_cipher_type type)
 	return NULL;
 }
 
+static const struct local_impl local_arc4;
 static const struct local_impl local_rc2_cbc;
 
 static const struct local_impl *local_impl_ciphers[] = {
+	[L_CIPHER_ARC4] = &local_arc4,
 	[L_CIPHER_RC2_CBC] = &local_rc2_cbc,
 };
 
@@ -729,6 +733,100 @@ LIB_EXPORT bool l_aead_cipher_is_supported(enum l_aead_cipher_type type)
 	return supported_aead_ciphers & (1 << type);
 }
 
+/* ARC4 implementation copyright (c) 2001 Niels Möller */
+
+#define SWAP(a, b) do { uint8_t _t = a; a = b; b = _t; } while (0)
+
+static void arc4_set_key(uint8_t *S, const uint8_t *key, size_t key_length)
+{
+	unsigned int i;
+	uint8_t j;
+
+	for (i = 0; i < 256; i++)
+		S[i] = i;
+
+	for (i = j = 0; i < 256; i++) {
+		j += S[i] + key[i % key_length];
+		SWAP(S[i], S[j]);
+	}
+}
+
+struct arc4_state {
+	struct arc4_state_ctx {
+		uint8_t S[256];
+		uint8_t i;
+		uint8_t j;
+	} ctx[2];
+};
+
+static void *local_arc4_new(enum l_cipher_type type,
+				const void *key, size_t key_length)
+{
+	struct arc4_state *s;
+
+	if (unlikely(key_length == 0 || key_length > 256))
+		return NULL;
+
+	s = l_new(struct arc4_state, 1);
+	arc4_set_key(s->ctx[0].S, key, key_length);
+	s->ctx[1] = s->ctx[0];
+	return s;
+}
+
+static void local_arc4_free(void *data)
+{
+	explicit_bzero(data, sizeof(struct arc4_state));
+	l_free(data);
+}
+
+static ssize_t local_arc4_operate(void *data, __u32 operation,
+					const struct iovec *in, size_t in_cnt,
+					const struct iovec *out, size_t out_cnt)
+{
+	struct arc4_state *s = data;
+	struct iovec cur_in;
+	struct iovec cur_out;
+	struct arc4_state_ctx *ctx =
+		&s->ctx[operation == ALG_OP_ENCRYPT ? 1 : 0];
+
+	if (!in_cnt || !out_cnt)
+		return 0;
+
+	cur_in = *in;
+	cur_out = *out;
+
+	while (1) {
+		while (!cur_in.iov_len) {
+			cur_in = *in++;
+
+			if (!--in_cnt)
+				return 0;
+		}
+
+		while (!cur_out.iov_len) {
+			cur_out = *out++;
+
+			if (!--out_cnt)
+				return 0;
+		}
+
+		ctx->j += ctx->S[++ctx->i];
+		SWAP(ctx->S[ctx->i], ctx->S[ctx->j]);
+		*(uint8_t *) cur_out.iov_base++ =
+			*(uint8_t *) cur_in.iov_base++ ^
+			ctx->S[(ctx->S[ctx->i] + ctx->S[ctx->j]) & 0xff];
+		cur_in.iov_len--;
+		cur_out.iov_len--;
+	}
+}
+
+static const struct local_impl local_arc4 = {
+	local_arc4_new,
+	local_arc4_free,
+	NULL,
+	local_arc4_operate,
+};
+
 struct rc2_state {
 	union {
 		uint16_t xkey[64];
diff --git a/ell/cipher.h b/ell/cipher.h
index ed277a6..f4c31fa 100644
--- a/ell/cipher.h
+++ b/ell/cipher.h
@@ -33,8 +33,8 @@ enum l_cipher_type {
 	L_CIPHER_AES = 0,
 	L_CIPHER_AES_CBC,
 	L_CIPHER_AES_CTR,
-	/* L_CIPHER_ARC4, */
-	L_CIPHER_DES = 4,
+	L_CIPHER_ARC4,
+	L_CIPHER_DES,
 	L_CIPHER_DES_CBC,
 	L_CIPHER_DES3_EDE_CBC,
 	L_CIPHER_RC2_CBC,
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 05/13] pem: Don't expect padding with stream ciphers
  2020-12-10 19:31 [PATCH 01/13] pem: Wipe more memory in PKCS#1 private key decoding Andrew Zaborowski
                   ` (2 preceding siblings ...)
  2020-12-10 19:31 ` [PATCH 04/13] cipher: Add ARC4 implementation Andrew Zaborowski
@ 2020-12-10 19:31 ` Andrew Zaborowski
  2020-12-10 19:31 ` [PATCH 06/13] pkcs5: Add the PKCS#12 KDF Andrew Zaborowski
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-12-10 19:31 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 3911 bytes --]

When decoding PKCS#8 encrypted certificates don't try to strip padding
when the encryption uses a stream cipher (RC4).  Standalone PKCS#8 files
encrypted with PKCS#12 ciphers are not common (that I know of..) but
they can be found embedded in PKCS#12 files.
---
 ell/pem.c           | 36 +++++++++++++++++++++++-------------
 ell/pkcs5-private.h |  3 ++-
 ell/pkcs5.c         | 13 +++++++++++--
 3 files changed, 36 insertions(+), 16 deletions(-)

diff --git a/ell/pem.c b/ell/pem.c
index 42c7975..1b995d5 100644
--- a/ell/pem.c
+++ b/ell/pem.c
@@ -619,6 +619,8 @@ struct l_key *pem_key_from_pkcs8_encrypted_private_key_info(const uint8_t *der,
 	int i;
 	struct l_key *pkey;
 	bool r;
+	bool is_block;
+	size_t decrypted_len;
 
 	/* Technically this is BER, not limited to DER */
 	key_info = asn1_der_find_elem(der, der_len, 0, &tag, &key_info_len);
@@ -638,7 +640,8 @@ struct l_key *pem_key_from_pkcs8_encrypted_private_key_info(const uint8_t *der,
 	if (asn1_der_find_elem(der, der_len, 2, &tag, &tmp_len))
 		return NULL;
 
-	alg = pkcs5_cipher_from_alg_id(alg_id, alg_id_len, passphrase);
+	alg = pkcs5_cipher_from_alg_id(alg_id, alg_id_len, passphrase,
+					&is_block);
 	if (!alg)
 		return NULL;
 
@@ -652,21 +655,28 @@ struct l_key *pem_key_from_pkcs8_encrypted_private_key_info(const uint8_t *der,
 		return NULL;
 	}
 
-	/*
-	 * Strip padding as defined in RFC8018 (for PKCS#5 v1) or
-	 * RFC1423 / RFC5652 (for v2).
-	 */
-	pkey = NULL;
-	if (decrypted[data_len - 1] >= data_len ||
-			decrypted[data_len - 1] > 16)
-		goto cleanup;
+	decrypted_len = data_len;
+
+	if (is_block) {
+		/*
+		 * For block ciphers strip padding as defined in RFC8018
+		 * (for PKCS#5 v1) or RFC1423 / RFC5652 (for v2).
+		 */
+		pkey = NULL;
 
-	for (i = 1; i < decrypted[data_len - 1]; i++)
-		if (decrypted[data_len - 1 - i] != decrypted[data_len - 1])
+		if (decrypted[data_len - 1] >= data_len ||
+				decrypted[data_len - 1] > 16)
 			goto cleanup;
 
-	pkey = pem_key_from_pkcs8_private_key_info(decrypted,
-					data_len - decrypted[data_len - 1]);
+		for (i = 1; i < decrypted[data_len - 1]; i++)
+			if (decrypted[data_len - 1 - i] !=
+					decrypted[data_len - 1])
+				goto cleanup;
+
+		decrypted_len -= decrypted[data_len - 1];
+	}
+
+	pkey = pem_key_from_pkcs8_private_key_info(decrypted, decrypted_len);
 
 cleanup:
 	explicit_bzero(decrypted, data_len);
diff --git a/ell/pkcs5-private.h b/ell/pkcs5-private.h
index f4f41f5..27ff41c 100644
--- a/ell/pkcs5-private.h
+++ b/ell/pkcs5-private.h
@@ -22,4 +22,5 @@
 
 struct l_cipher *pkcs5_cipher_from_alg_id(const uint8_t *id_asn1,
 						size_t id_asn1_len,
-						const char *password);
+						const char *password,
+						bool *out_is_block);
diff --git a/ell/pkcs5.c b/ell/pkcs5.c
index f225c71..21fda2b 100644
--- a/ell/pkcs5.c
+++ b/ell/pkcs5.c
@@ -415,7 +415,8 @@ static struct l_cipher *pkcs5_cipher_from_pbes2_params(
 
 struct l_cipher *pkcs5_cipher_from_alg_id(const uint8_t *id_asn1,
 						size_t id_asn1_len,
-						const char *password)
+						const char *password,
+						bool *out_is_block)
 {
 	uint8_t tag;
 	const uint8_t *oid, *params, *salt, *iter_count_buf;
@@ -436,9 +437,13 @@ struct l_cipher *pkcs5_cipher_from_alg_id(const uint8_t *id_asn1,
 	if (asn1_der_find_elem(id_asn1, id_asn1_len, 2, &tag, &tmp_len))
 		return NULL;
 
-	if (asn1_oid_eq(&pkcs5_pbes2_oid, oid_len, oid))
+	if (asn1_oid_eq(&pkcs5_pbes2_oid, oid_len, oid)) {
+		if (out_is_block)
+			*out_is_block = true;
+
 		return pkcs5_cipher_from_pbes2_params(params, params_len,
 							password);
+	}
 
 	/* RFC8018 section A.3 */
 
@@ -484,5 +489,9 @@ struct l_cipher *pkcs5_cipher_from_alg_id(const uint8_t *id_asn1,
 	}
 
 	explicit_bzero(derived_key, 16);
+
+	if (out_is_block)
+		*out_is_block = true;
+
 	return cipher;
 }
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 06/13] pkcs5: Add the PKCS#12 KDF
  2020-12-10 19:31 [PATCH 01/13] pem: Wipe more memory in PKCS#1 private key decoding Andrew Zaborowski
                   ` (3 preceding siblings ...)
  2020-12-10 19:31 ` [PATCH 05/13] pem: Don't expect padding with stream ciphers Andrew Zaborowski
@ 2020-12-10 19:31 ` Andrew Zaborowski
  2020-12-10 22:51   ` Denis Kenzior
  2020-12-10 19:32 ` [PATCH 07/13] pkcs5: Add PKCS#12 algorithms in pkcs5_cipher_from_alg_id Andrew Zaborowski
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 15+ messages in thread
From: Andrew Zaborowski @ 2020-12-10 19:31 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 5862 bytes --]

Add the key derivation algorithm used with PKCS#12 to pkcs5.c so that it
can be found together with the two PKCS#5 KDFs, and so that it can also
be used when parsing of the PKCS#12 AlgorithmIdentifiers in the next
commit.  This KDF is not recommended for new uses.
---
 ell/pkcs5-private.h |  12 ++++
 ell/pkcs5.c         | 146 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 158 insertions(+)

diff --git a/ell/pkcs5-private.h b/ell/pkcs5-private.h
index 27ff41c..9b85fdd 100644
--- a/ell/pkcs5-private.h
+++ b/ell/pkcs5-private.h
@@ -20,6 +20,18 @@
  *
  */
 
+struct pkcs12_hash {
+	enum l_checksum_type alg;
+	unsigned int len;
+	unsigned int u;
+	unsigned int v;
+	struct asn1_oid oid;
+};
+
+uint8_t *pkcs12_pbkdf(const char *password, const struct pkcs12_hash *hash,
+			const uint8_t *salt, size_t salt_len,
+			unsigned int iterations, uint8_t id, size_t key_len);
+
 struct l_cipher *pkcs5_cipher_from_alg_id(const uint8_t *id_asn1,
 						size_t id_asn1_len,
 						const char *password,
diff --git a/ell/pkcs5.c b/ell/pkcs5.c
index 21fda2b..bb02c33 100644
--- a/ell/pkcs5.c
+++ b/ell/pkcs5.c
@@ -33,6 +33,7 @@
 #include "checksum.h"
 #include "cipher.h"
 #include "util.h"
+#include "utf8.h"
 #include "asn1-private.h"
 #include "pkcs5.h"
 #include "pkcs5-private.h"
@@ -182,14 +183,159 @@ LIB_EXPORT bool l_pkcs5_pbkdf2(enum l_checksum_type type, const char *password,
 	return !dk_len;
 }
 
+static bool utf8_to_bmpstring(const char *utf, uint8_t *out_buf,
+				unsigned int *out_len)
+{
+	unsigned int n;
+	int utflen;
+	wchar_t cp;
+	uint16_t *ptr = (uint16_t *) out_buf;
+
+	for (n = strlen(utf); n; n -= utflen, utf += utflen) {
+		if ((utflen = l_utf8_get_codepoint(utf, n, &cp)) <= 0 ||
+				cp > 0xffff)
+			return false;
+
+		*ptr++ = L_CPU_TO_BE16(cp);
+	}
+
+	*ptr++ = 0;
+	*out_len = (uint8_t *) ptr - out_buf;
+	return true;
+}
+
+/* RFC7292 Appendix B */
+uint8_t *pkcs12_pbkdf(const char *password, const struct pkcs12_hash *hash,
+			const uint8_t *salt, size_t salt_len,
+			unsigned int iterations, uint8_t id, size_t key_len)
+{
+	/* All lengths in bytes instead of bits */
+	unsigned int passwd_len = password ? 2 * strlen(password) + 2 : 0;
+	uint8_t bmpstring[passwd_len];
+	/* Documented as v(ceiling(s/v)), usually will just equal v */
+	unsigned int s_len = (salt_len + hash->v - 1) & ~(hash->v - 1);
+	/* Documented as p(ceiling(s/p)), usually will just equal v */
+	unsigned int p_len = (passwd_len + hash->v - 1) & ~(hash->v - 1);
+	uint8_t di[hash->v + s_len + p_len];
+	uint8_t *ptr;
+	unsigned int j;
+	uint8_t *key;
+	unsigned int bytes;
+	struct l_checksum *h;
+
+	/*
+	 * If non-ASCII characters are used the BMPString enoding can end
+	 * up being shorter than 2 * strlen(password) + 2 but not longer
+	 * after this call.  Update p_len afterwards just in case.
+	 * utf8_to_bmpstring doesn't need the string to be UTF-8-validated.
+	 */
+	if (password && !utf8_to_bmpstring(password, bmpstring, &passwd_len))
+		return NULL;
+
+	p_len = (passwd_len + hash->v - 1) & ~(hash->v - 1);
+
+	if (!(h = l_checksum_new(hash->alg))) {
+		explicit_bzero(bmpstring, sizeof(bmpstring));
+		return NULL;
+	}
+
+	memset(di, id, hash->v);
+	ptr = di + hash->v;
+
+	for (j = salt_len; j < s_len; j += salt_len, ptr += salt_len)
+		memcpy(ptr, salt, salt_len);
+
+	if (s_len) {
+		memcpy(ptr, salt, s_len + salt_len - j);
+		ptr += s_len + salt_len - j;
+	}
+
+	for (j = passwd_len; j < p_len; j += passwd_len, ptr += passwd_len)
+		memcpy(ptr, bmpstring, passwd_len);
+
+	if (p_len)
+		memcpy(ptr, bmpstring, p_len + passwd_len - j);
+
+	explicit_bzero(bmpstring, sizeof(bmpstring));
+	key = l_malloc(key_len + hash->len);
+
+	for (bytes = 0; bytes < key_len; bytes += hash->u) {
+		uint8_t b[hash->v];
+		uint8_t *input = di;
+		unsigned int input_len = hash->v + s_len + p_len;
+
+		for (j = 0; j < iterations; j++) {
+			if (!l_checksum_update(h, input, input_len) ||
+					l_checksum_get_digest(h,
+							key + bytes,
+							hash->len) <= 0) {
+				l_checksum_free(h);
+				l_free(key);
+				return NULL;
+			}
+
+			input = key + bytes;
+			input_len = hash->u;
+			l_checksum_reset(h);
+		}
+
+		if (bytes + hash->u >= key_len)
+			break;
+
+		for (j = 0; j < hash->v - hash->u; j += hash->u)
+			memcpy(b + j, input, hash->u);
+
+		memcpy(b + j, input, hash->v - j);
+
+		ptr = di + hash->v;
+		for (j = 0; j < s_len + p_len; j += hash->v, ptr += hash->v) {
+			unsigned int k;
+			uint16_t carry = 1;
+
+			/*
+			 * Not specified in the RFC7292 but implementations
+			 * sum these octet strings as big-endian integers.
+			 * We could use 64-bit additions here but the benefit
+			 * may not compensate the cost of the byteswapping.
+			 */
+			for (k = hash->v - 1; k > 0; k--) {
+				carry = ptr[k] + b[k] + carry;
+				ptr[k] = carry;
+				carry >>= 8;
+			}
+
+			ptr[k] += b[k] + carry;
+			explicit_bzero(&carry, sizeof(carry));
+		}
+
+		explicit_bzero(b, sizeof(b));
+	}
+
+	explicit_bzero(di, sizeof(di));
+	l_checksum_free(h);
+	return key;
+}
+
+/* RFC7292 Appendix A */
+static const struct pkcs12_hash pkcs12_sha1_hash = {
+	.alg = L_CHECKSUM_SHA1,
+	.len = 20,
+	.u   = 20,
+	.v   = 64,
+	.oid = { 5, { 0x2b, 0x0e, 0x03, 0x02, 0x1a } },
+};
+
+/* RFC8018 Section A.2 */
 static struct asn1_oid pkcs5_pbkdf2_oid = {
 	9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0c }
 };
 
+/* RFC8018 Section A.4 */
 static struct asn1_oid pkcs5_pbes2_oid = {
 	9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0d }
 };
 
+/* RFC8018 Section A.3 */
 static const struct pkcs5_pbes1_encryption_oid {
 	enum l_checksum_type checksum_type;
 	enum l_cipher_type cipher_type;
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 07/13] pkcs5: Add PKCS#12 algorithms in pkcs5_cipher_from_alg_id
  2020-12-10 19:31 [PATCH 01/13] pem: Wipe more memory in PKCS#1 private key decoding Andrew Zaborowski
                   ` (4 preceding siblings ...)
  2020-12-10 19:31 ` [PATCH 06/13] pkcs5: Add the PKCS#12 KDF Andrew Zaborowski
@ 2020-12-10 19:32 ` Andrew Zaborowski
  2020-12-10 19:32 ` [PATCH 08/13] pem: Add l_pem_load_container_file Andrew Zaborowski
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-12-10 19:32 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 6377 bytes --]

Since pkcs5_cipher_from_alg_id() is used for decrypting PKCS#8 private
keys, teach it to also parse PKCS#12 AlgorithmIdentifier asn1 syntax
(almost identical to PKCS#5).  Ideally the pkcs5_* functions and pkcs5.c
should be renamed to not include the PKCS#5 string because the callers
are probably not interested in what cipher is used as long as they can
use it to decrypt data their input.  So ideally they want to call
one function that understands all the common algorithm OIDs.
---
 ell/pkcs5.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 169 insertions(+), 3 deletions(-)

diff --git a/ell/pkcs5.c b/ell/pkcs5.c
index bb02c33..ea0fcc5 100644
--- a/ell/pkcs5.c
+++ b/ell/pkcs5.c
@@ -352,6 +352,74 @@ static const struct pkcs5_pbes1_encryption_oid {
 	/* MD2- and RC2-based schemes 1, 4, 6 and 11 not supported */
 };
 
+/* RFC7292 Appendix C */
+static const struct pkcs12_encryption_oid {
+	enum l_cipher_type cipher_type;
+	unsigned int key_length;
+	unsigned int iv_length;
+	bool copy_k1;	/* Expand the 2-Key 3DES key for 3-Key 3DES */
+	bool is_block;
+	struct asn1_oid oid;
+} pkcs12_encryption_oids[] = {
+	{ /* pbeWithSHAAnd128BitRC4 */
+		.cipher_type = L_CIPHER_ARC4,
+		.key_length = 16,
+		.oid = { 10, {
+			0x2a, 0x86, 0x48, 0x86, 0xf7,
+			0x0d, 0x01, 0x0c, 0x01, 0x01,
+		} }
+	},
+	{ /* pbeWithSHAAnd40BitRC4 */
+		.cipher_type = L_CIPHER_ARC4,
+		.key_length = 5,
+		.oid = { 10, {
+			0x2a, 0x86, 0x48, 0x86, 0xf7,
+			0x0d, 0x01, 0x0c, 0x01, 0x02,
+		} }
+	},
+	{ /* pbeWithSHAAnd3-KeyTripleDES-CBC */
+		.cipher_type = L_CIPHER_DES3_EDE_CBC,
+		.key_length = 24,
+		.iv_length = 8,
+		.is_block = true,
+		.oid = { 10, {
+			0x2a, 0x86, 0x48, 0x86, 0xf7,
+			0x0d, 0x01, 0x0c, 0x01, 0x03,
+		} }
+	},
+	{ /* pbeWithSHAAnd2-KeyTripleDES-CBC */
+		.cipher_type = L_CIPHER_DES3_EDE_CBC,
+		.key_length = 16,
+		.iv_length = 8,
+		.copy_k1 = true,
+		.is_block = true,
+		.oid = { 10, {
+			0x2a, 0x86, 0x48, 0x86, 0xf7,
+			0x0d, 0x01, 0x0c, 0x01, 0x04,
+		} }
+	},
+	{ /* pbeWithSHAAnd128BitRC2-CBC */
+		.cipher_type = L_CIPHER_RC2_CBC,
+		.key_length = 16,
+		.iv_length = 8,
+		.is_block = true,
+		.oid = { 10, {
+			0x2a, 0x86, 0x48, 0x86, 0xf7,
+			0x0d, 0x01, 0x0c, 0x01, 0x05,
+		} }
+	},
+	{ /* pbeWithSHAAnd40BitRC2-CBC */
+		.cipher_type = L_CIPHER_RC2_CBC,
+		.key_length = 5,
+		.iv_length = 8,
+		.is_block = true,
+		.oid = { 10, {
+			0x2a, 0x86, 0x48, 0x86, 0xf7,
+			0x0d, 0x01, 0x0c, 0x01, 0x06,
+		} }
+	},
+};
+
 static const struct pkcs5_digest_alg_oid {
 	enum l_checksum_type type;
 	struct asn1_oid oid;
@@ -528,8 +596,8 @@ static struct l_cipher *pkcs5_cipher_from_pbes2_params(
 	/* RFC8018 section B.2 */
 
 	/*
-	 * Since we don't support RC2/RC5, all our PKCS#5 ciphers only
-	 * have an obligatory OCTET STRING IV parameter and a fixed key
+	 * Since we don't support the RC2/RC5 PBES2 ciphers, our parameters
+	 * only have an obligatory OCTET STRING IV parameter and a fixed key
 	 * length.
 	 */
 	if (tag != ASN1_ID_OCTET_STRING || params_len != enc_scheme->iv_size)
@@ -559,6 +627,94 @@ static struct l_cipher *pkcs5_cipher_from_pbes2_params(
 	return cipher;
 }
 
+static struct l_cipher *pkcs12_cipher_from_alg_id(
+				const struct pkcs12_encryption_oid *scheme,
+				const uint8_t *params, size_t params_len,
+				const char *password, bool *out_is_block)
+{
+	uint8_t tag;
+	const uint8_t *salt;
+	const uint8_t *iterations_data;
+	size_t salt_len;
+	size_t iterations_len;
+	unsigned int iterations;
+	uint8_t *key;
+	size_t key_len;
+	struct l_cipher *cipher;
+
+	/* Same parameters as in PKCS#5 */
+	salt = asn1_der_find_elem(params, params_len, 0, &tag, &salt_len);
+	if (!salt || tag != ASN1_ID_OCTET_STRING)
+		return NULL;
+
+	iterations_data = asn1_der_find_elem(params, params_len, 1,
+						&tag, &iterations_len);
+	if (!iterations_data || tag != ASN1_ID_INTEGER ||
+			iterations_len < 1 || iterations_len > 4)
+		return NULL;
+
+	for (iterations = 0; iterations_len; iterations_len--)
+		iterations = (iterations << 8) | *iterations_data++;
+
+	if (iterations < 1 || iterations > 8192)
+		return NULL;
+
+	if (iterations_data != params + params_len)
+		return NULL;
+
+	key_len = scheme->key_length;
+	key = pkcs12_pbkdf(password, &pkcs12_sha1_hash, salt, salt_len,
+				iterations, 1, key_len);
+	if (!key)
+		return NULL;
+
+	if (scheme->copy_k1) {
+		/*
+		 * 2-Key 3DES is like L_CIPHER_DES3_EDE_CBC except the last
+		 * of the 3 8-byte keys is not generated using a KDF and
+		 * instead is a copy of the first key.  In other words
+		 * the first half of the 16-byte key material is appended
+		 * at the end to produce the 24 bytes for DES3_EDE_CBC.
+		 */
+		uint8_t *key2 = l_malloc(24);
+
+		memcpy(key2, key, 16);
+		memcpy(key2 + 16, key, 8);
+		explicit_bzero(key, key_len);
+		l_free(key);
+		key = key2;
+		key_len = 24;
+	}
+
+	cipher = l_cipher_new(scheme->cipher_type, key, key_len);
+	explicit_bzero(key, key_len);
+	l_free(key);
+
+	if (!cipher)
+		return NULL;
+
+	if (scheme->iv_length) {
+		uint8_t *iv = pkcs12_pbkdf(password, &pkcs12_sha1_hash,
+						salt, salt_len, iterations, 2,
+						scheme->iv_length);
+
+		if (!iv || !l_cipher_set_iv(cipher, iv, scheme->iv_length)) {
+			l_cipher_free(cipher);
+			cipher = NULL;
+		}
+
+		if (iv)
+			explicit_bzero(iv, scheme->iv_length);
+
+		l_free(iv);
+	}
+
+	if (out_is_block)
+		*out_is_block = scheme->is_block;
+
+	return cipher;
+}
+
 struct l_cipher *pkcs5_cipher_from_alg_id(const uint8_t *id_asn1,
 						size_t id_asn1_len,
 						const char *password,
@@ -601,8 +757,18 @@ struct l_cipher *pkcs5_cipher_from_alg_id(const uint8_t *id_asn1,
 		}
 	}
 
-	if (!pbes1_scheme)
+	/* Check if this is a PKCS#12 OID */
+	if (!pbes1_scheme) {
+		for (i = 0; i < L_ARRAY_SIZE(pkcs12_encryption_oids); i++)
+			if (asn1_oid_eq(&pkcs12_encryption_oids[i].oid,
+					oid_len, oid))
+				return pkcs12_cipher_from_alg_id(
+						&pkcs12_encryption_oids[i],
+						params, params_len, password,
+						out_is_block);
+
 		return NULL;
+	}
 
 	salt = asn1_der_find_elem(params, params_len, 0, &tag, &tmp_len);
 	if (!salt || tag != ASN1_ID_OCTET_STRING || tmp_len != 8)
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 08/13] pem: Add l_pem_load_container_file
  2020-12-10 19:31 [PATCH 01/13] pem: Wipe more memory in PKCS#1 private key decoding Andrew Zaborowski
                   ` (5 preceding siblings ...)
  2020-12-10 19:32 ` [PATCH 07/13] pkcs5: Add PKCS#12 algorithms in pkcs5_cipher_from_alg_id Andrew Zaborowski
@ 2020-12-10 19:32 ` Andrew Zaborowski
  2020-12-10 19:32 ` [PATCH 09/13] pem: Add PKCS#12 parsing Andrew Zaborowski
  2020-12-10 22:47 ` [PATCH 01/13] pem: Wipe more memory in PKCS#1 private key decoding Denis Kenzior
  8 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-12-10 19:32 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 7631 bytes --]

Add a function that takes a file path and detects what X.509 certificate
and/or private key format it uses and parses it accordingly.  This is to
make it easier for clients to support multiple file formats, including
raw X.509 certificates -- without this they would have to load the
contents of the file and call l_cert_new_from_der().
l_pem_load_container can also be used instead of
l_pem_load_certificate_chain() and l_pem_load_private_key().

This function can now load binary certificate files which are not PEM
but there wasn't a better place for it than pem.c.  I guess the name
could also be improved to imply that it's for certificate and private
key container files, but I couldn't come up with a name that isn't too
long.

This function treats PEM files as containers that can have both private
keys and certificates at the same time, openssl can parse such files and
also produces such files in some situations and they may have some good
uses.
---
 ell/ell.sym |   1 +
 ell/pem.c   | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 ell/pem.h   |   5 ++
 3 files changed, 158 insertions(+), 5 deletions(-)

diff --git a/ell/ell.sym b/ell/ell.sym
index 49689d1..239ab65 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -415,6 +415,7 @@ global:
 	l_pem_load_file;
 	l_pem_load_private_key;
 	l_pem_load_private_key_from_data;
+	l_pem_load_container_file;
 	/* pkcs5 */
 	l_pkcs5_pbkdf1;
 	l_pkcs5_pbkdf2;
diff --git a/ell/pem.c b/ell/pem.c
index 1b995d5..90d06fb 100644
--- a/ell/pem.c
+++ b/ell/pem.c
@@ -195,7 +195,7 @@ const char *pem_next(const void *buf, size_t buf_len, char **type_label,
 
 static uint8_t *pem_load_buffer(const void *buf, size_t buf_len,
 				char **out_type_label, size_t *out_len,
-				char **out_headers)
+				char **out_headers, const char **out_endp)
 {
 	size_t base64_len;
 	const char *base64;
@@ -203,7 +203,7 @@ static uint8_t *pem_load_buffer(const void *buf, size_t buf_len,
 	uint8_t *ret;
 
 	base64 = pem_next(buf, buf_len, &label, &base64_len,
-				NULL, false);
+				out_endp, false);
 	if (!base64)
 		return NULL;
 
@@ -262,7 +262,7 @@ static uint8_t *pem_load_buffer(const void *buf, size_t buf_len,
 LIB_EXPORT uint8_t *l_pem_load_buffer(const void *buf, size_t buf_len,
 					char **type_label, size_t *out_len)
 {
-	return pem_load_buffer(buf, buf_len, type_label, out_len, NULL);
+	return pem_load_buffer(buf, buf_len, type_label, out_len, NULL, NULL);
 }
 
 struct pem_file_info {
@@ -315,7 +315,8 @@ static uint8_t *pem_load_file(const char *filename, char **out_type_label,
 		return NULL;
 
 	result = pem_load_buffer(file.data, file.st.st_size,
-					out_type_label, out_len, out_headers);
+					out_type_label, out_len, out_headers,
+					NULL);
 	pem_file_close(&file);
 	return result;
 }
@@ -888,7 +889,7 @@ LIB_EXPORT struct l_key *l_pem_load_private_key_from_data(const void *buf,
 	if (encrypted)
 		*encrypted = false;
 
-	content = pem_load_buffer(buf, buf_len, &label, &len, &headers);
+	content = pem_load_buffer(buf, buf_len, &label, &len, &headers, NULL);
 
 	if (!content)
 		return NULL;
@@ -933,3 +934,149 @@ LIB_EXPORT struct l_key *l_pem_load_private_key(const char *filename,
 	return pem_load_private_key(content, len, label, passphrase, headers,
 					encrypted);
 }
+
+/*
+ * Look at a file, try to detect which of the few X.509 certificate and/or
+ * private key container formats it uses and load any certificates in it as
+ * a certificate chain object, and load the first private key as an l_key
+ * object.
+ *
+ * Currently supported are:
+ *  PEM X.509 certificates
+ *  PEM PKCS#8 encrypted and unenecrypted private keys
+ *  PEM legacy PKCS#1 encrypted and unenecrypted private keys
+ *  Raw X.509 certificates (.cer, .der, .crt)
+ *
+ * The raw format contains exactly one certificate, PEM and PKCS#12 files
+ * can contain any combination of certificates and private keys.
+ */
+LIB_EXPORT void l_pem_load_container_file(const char *filename,
+					const char *password,
+					struct l_certchain **out_certchain,
+					struct l_key **out_privkey,
+					bool *out_encrypted)
+{
+	struct pem_file_info file;
+	const char *ptr;
+	size_t len;
+	bool error = false;
+
+	if (out_encrypted)
+		*out_encrypted = false;
+
+	if (out_certchain)
+		*out_certchain = NULL;
+
+	if (out_privkey)
+		*out_privkey = NULL;
+
+	if (unlikely(!filename))
+		return;
+
+	if (pem_file_open(&file, filename) < 0)
+		return;
+
+	if (file.st.st_size < 1)
+		goto close;
+
+	/* See if we have a DER sequence tag@the start */
+	if (file.data[0] == ASN1_ID_SEQUENCE) {
+		const uint8_t *seq_data;
+		const uint8_t *elem_data;
+		size_t elem_len;
+		uint8_t tag;
+
+		if (!(seq_data = asn1_der_find_elem(file.data, file.st.st_size,
+							0, &tag, &len)))
+			goto not_der_after_all;
+
+		/*
+		 * See if the first sub-element is another sequence, then, out
+		 * of the formats that we currently support this can only be a
+		 * raw certificate.  If integer, it's going to be PKCS#12.  If
+		 * we wish to add any more formats we'll probably need to start
+		 * guessing from the filename suffix.
+		 */
+		if (!(elem_data = asn1_der_find_elem(seq_data, len,
+							0, &tag, &elem_len)))
+			goto not_der_after_all;
+
+		if (tag == ASN1_ID_SEQUENCE) {
+			if (out_certchain) {
+				struct l_cert *cert;
+
+				if (!(cert = l_cert_new_from_der(file.data,
+							file.st.st_size)))
+					goto close;
+
+				*out_certchain = certchain_new_from_leaf(cert);
+			}
+
+			goto close;
+		}
+	}
+
+not_der_after_all:
+	/*
+	 * RFC 7486 allows whitespace and possibly other data before the
+	 * PEM "encapsulation boundary" so rather than check if the start
+	 * of the data looks like PEM, we fall back to this format if the
+	 * data didn't look like anything else we knew about.
+	 */
+	ptr = (const char *) file.data;
+	len = file.st.st_size;
+	while (!error && len) {
+		uint8_t *der;
+		size_t der_len;
+		char *type_label;
+		char *headers;
+		const char *endp;
+
+		if (!(der = pem_load_buffer(ptr, len, &type_label, &der_len,
+						&headers, &endp)))
+			break;
+
+		len -= endp - ptr;
+		ptr = endp;
+
+		if (!strcmp(type_label, "CERTIFICATE")) {
+			struct l_cert *cert;
+
+			if (!out_certchain)
+				goto next;
+
+			if (!(cert = l_cert_new_from_der(der, der_len))) {
+				l_certchain_free(*out_certchain);
+				*out_certchain = NULL;
+				error = true;
+				goto next;
+			}
+
+			if (!*out_certchain)
+				*out_certchain = certchain_new_from_leaf(cert);
+			else
+				certchain_link_issuer(*out_certchain, cert);
+
+			goto next;
+		}
+
+		/* Only use the first private key found */
+		if (out_privkey && !*out_privkey) {
+			*out_privkey = pem_load_private_key(der, der_len,
+								type_label,
+								password,
+								headers,
+								out_encrypted);
+			continue;
+		}
+
+next:
+		explicit_bzero(der, der_len);
+		l_free(der);
+		l_free(type_label);
+		l_free(headers);
+	}
+
+close:
+	pem_file_close(&file);
+}
diff --git a/ell/pem.h b/ell/pem.h
index 1c93e7a..ce5af04 100644
--- a/ell/pem.h
+++ b/ell/pem.h
@@ -50,6 +50,11 @@ struct l_key *l_pem_load_private_key_from_data(const void *buf, size_t len,
 						const char *passphrase,
 						bool *encrypted);
 
+void l_pem_load_container_file(const char *filename, const char *password,
+				struct l_certchain **out_certchain,
+				struct l_key **out_privkey,
+				bool *out_encrypted);
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 09/13] pem: Add PKCS#12 parsing
  2020-12-10 19:31 [PATCH 01/13] pem: Wipe more memory in PKCS#1 private key decoding Andrew Zaborowski
                   ` (6 preceding siblings ...)
  2020-12-10 19:32 ` [PATCH 08/13] pem: Add l_pem_load_container_file Andrew Zaborowski
@ 2020-12-10 19:32 ` Andrew Zaborowski
  2020-12-10 22:47 ` [PATCH 01/13] pem: Wipe more memory in PKCS#1 private key decoding Denis Kenzior
  8 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-12-10 19:32 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 19015 bytes --]

Add ability to load PKCS#12 files in l_pem_load_container_file().  This
has only been tested with the files produced by openssl so far.

Extend the static buffer in struct asn1_oid (asn1-private.h) so we can
handle some 11-byte OIDs used in PKCS#12 files.
---
 ell/asn1-private.h |   2 +-
 ell/pem.c          | 612 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 613 insertions(+), 1 deletion(-)

diff --git a/ell/asn1-private.h b/ell/asn1-private.h
index 2a31241..079570b 100644
--- a/ell/asn1-private.h
+++ b/ell/asn1-private.h
@@ -37,7 +37,7 @@
 
 struct asn1_oid {
 	uint8_t asn1_len;
-	uint8_t asn1[10];
+	uint8_t asn1[11];
 };
 
 #define asn1_oid_eq(oid1, oid2_len, oid2_string) \
diff --git a/ell/pem.c b/ell/pem.c
index 90d06fb..e9a4ae3 100644
--- a/ell/pem.c
+++ b/ell/pem.c
@@ -935,6 +935,582 @@ LIB_EXPORT struct l_key *l_pem_load_private_key(const char *filename,
 					encrypted);
 }
 
+static const uint8_t *pkcs7_unpack_content_info(const uint8_t *container,
+					size_t container_len, int pos,
+					const struct asn1_oid *expected_oid,
+					struct asn1_oid *out_oid,
+					uint8_t *out_tag, size_t *out_len)
+{
+	const uint8_t *content_info;
+	size_t content_info_len;
+	const uint8_t *type;
+	size_t type_len;
+	const uint8_t *ret;
+	uint8_t tag;
+
+	if (!(content_info = asn1_der_find_elem(container, container_len, pos,
+						&tag, &content_info_len)) ||
+			tag != ASN1_ID_SEQUENCE)
+		return NULL;
+
+	if (!(type = asn1_der_find_elem(content_info, content_info_len, 0,
+					&tag, &type_len)) ||
+			tag != ASN1_ID_OID ||
+			type_len > sizeof(out_oid->asn1))
+		return NULL;
+
+	if (expected_oid && !asn1_oid_eq(expected_oid, type_len, type))
+		return NULL;
+
+	if (!(ret = asn1_der_find_elem(content_info, content_info_len,
+					ASN1_CONTEXT_EXPLICIT(0),
+					out_tag, out_len)) ||
+			ret + *out_len != content_info + content_info_len)
+		return NULL;
+
+	if (out_oid) {
+		out_oid->asn1_len = type_len;
+		memcpy(out_oid->asn1, type, type_len);
+	}
+
+	return ret;
+}
+
+/* RFC5652 Section 8 */
+static uint8_t *pkcs7_decrypt_encrypted_data(const uint8_t *data, size_t data_len,
+						const char *password,
+						struct asn1_oid *out_oid,
+						size_t *out_len)
+{
+	const uint8_t *version;
+	size_t version_len;
+	const uint8_t *encrypted_info;
+	size_t encrypted_info_len;
+	const uint8_t *type;
+	size_t type_len;
+	const uint8_t *alg_id;
+	size_t alg_id_len;
+	const uint8_t *encrypted;
+	size_t encrypted_len;
+	uint8_t tag;
+	struct l_cipher *alg;
+	uint8_t *plaintext;
+	int i;
+	bool ok;
+	bool is_block;
+
+	if (!(version = asn1_der_find_elem(data, data_len, 0, &tag,
+						&version_len)) ||
+			tag != ASN1_ID_INTEGER || version_len != 1 ||
+			(version[0] != 0 && version[0] != 2))
+		return NULL;
+
+	if (!(encrypted_info = asn1_der_find_elem(data, data_len, 1, &tag,
+							&encrypted_info_len)) ||
+			tag != ASN1_ID_SEQUENCE)
+		return NULL;
+
+	if (!(type = asn1_der_find_elem(encrypted_info, encrypted_info_len, 0,
+					&tag, &type_len)) ||
+			tag != ASN1_ID_OID ||
+			type_len > sizeof(out_oid->asn1))
+		return NULL;
+
+	if (!(alg_id = asn1_der_find_elem(encrypted_info, encrypted_info_len, 1,
+					&tag, &alg_id_len)) ||
+			tag != ASN1_ID_SEQUENCE)
+		return NULL;
+
+	/* Not optional in our case, defined [0] IMPLICIT OCTET STRING */
+	if (!(encrypted = asn1_der_find_elem(encrypted_info, encrypted_info_len,
+						ASN1_CONTEXT_IMPLICIT(0),
+						&tag, &encrypted_len)) ||
+			tag != ASN1_ID(ASN1_CLASS_CONTEXT, 0, 0) ||
+			encrypted_len < 8)
+		return NULL;
+
+	if (!(alg = pkcs5_cipher_from_alg_id(alg_id, alg_id_len, password,
+						&is_block)))
+		return NULL;
+
+	plaintext = l_malloc(encrypted_len);
+	ok = l_cipher_decrypt(alg, encrypted, plaintext, encrypted_len);
+	l_cipher_free(alg);
+
+	if (!ok)
+		return NULL;
+
+	if (is_block) {
+		/* Also validate the padding */
+		if (encrypted_len < plaintext[encrypted_len - 1] ||
+				plaintext[encrypted_len - 1] > 16)
+			return NULL;
+
+		for (i = 1; i < plaintext[encrypted_len - 1]; i++)
+			if (plaintext[encrypted_len - 1 - i] !=
+					plaintext[encrypted_len - 1]) {
+				explicit_bzero(plaintext, encrypted_len);
+				l_free(plaintext);
+				return NULL;
+			}
+
+		encrypted_len -= plaintext[encrypted_len - 1];
+	}
+
+	if (out_oid) {
+		out_oid->asn1_len = type_len;
+		memcpy(out_oid->asn1, type, type_len);
+	}
+
+	*out_len = encrypted_len;
+	return plaintext;
+}
+
+/* RFC7292 Appendix A. */
+static struct pkcs12_hash pkcs12_mac_algs[] = {
+	{
+		L_CHECKSUM_MD5,    16, 16, 64,
+		{ 8, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0f, 0x02, 0x05 } }
+	},
+	{
+		L_CHECKSUM_SHA1,   20, 20, 64,
+		{ 5, { 0x2b, 0x0e, 0x03, 0x02, 0x1a } }
+	},
+	{
+		L_CHECKSUM_SHA224, 28, 28, 64,
+		{ 9, { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04 } }
+	},
+	{
+		L_CHECKSUM_SHA256, 32, 32, 64,
+		{ 9, { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 } }
+	},
+	{
+		L_CHECKSUM_SHA384, 48, 48, 128,
+		{ 9, { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 } }
+	},
+	{
+		L_CHECKSUM_SHA512, 64, 64, 128,
+		{ 9, { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 } }
+	},
+	{
+		L_CHECKSUM_SHA512, 64, 28, 128,
+		{ 9, { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x05 } }
+	},
+	{
+		L_CHECKSUM_SHA512, 64, 32, 128,
+		{ 9, { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x06 } }
+	},
+};
+
+static const struct asn1_oid pkcs12_key_bag_oid = {
+	11, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x01 }
+};
+
+static const struct asn1_oid pkcs12_pkcs8_shrouded_key_bag_oid = {
+	11, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02 }
+};
+
+static const struct asn1_oid pkcs12_cert_bag_oid = {
+	11, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x03 }
+};
+
+static const struct asn1_oid pkcs12_safe_contents_bag_oid = {
+	11, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x06 }
+};
+
+static const struct asn1_oid pkcs9_x509_certificate_oid = {
+	10, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x16, 0x01 }
+};
+
+/* RFC7292 Section 4.2.3 */
+static bool pkcs12_parse_cert_bag(const uint8_t *data, size_t data_len,
+					struct l_certchain **out_certchain)
+{
+	const uint8_t *cert_bag;
+	size_t cert_bag_len;
+	const uint8_t *cert_id;
+	size_t cert_id_len;
+	const uint8_t *cert_value;
+	size_t cert_value_len;
+	uint8_t tag;
+	struct l_cert *cert;
+
+	if (!(cert_bag = asn1_der_find_elem(data, data_len, 0,
+						&tag, &cert_bag_len)) ||
+			tag != ASN1_ID_SEQUENCE)
+		return false;
+
+	if (!(cert_id = asn1_der_find_elem(cert_bag, cert_bag_len, 0,
+						&tag, &cert_id_len)) ||
+			tag != ASN1_ID_OID)
+		return false;
+
+	if (!(cert_value = asn1_der_find_elem(cert_bag, cert_bag_len,
+						ASN1_CONTEXT_EXPLICIT(0),
+						&tag, &cert_value_len)) ||
+			tag != ASN1_ID_OCTET_STRING ||
+			cert_value + cert_value_len != data + data_len)
+		return false;
+
+	/* Skip unsupported certificate types */
+	if (!asn1_oid_eq(&pkcs9_x509_certificate_oid, cert_id_len, cert_id))
+		return true;
+
+	if (!(cert = l_cert_new_from_der(cert_value, cert_value_len)))
+		return false;
+
+	if (!*out_certchain)
+		*out_certchain = certchain_new_from_leaf(cert);
+	else
+		certchain_link_issuer(*out_certchain, cert);
+
+	return true;
+}
+
+static bool pkcs12_parse_safe_contents(const uint8_t *data,
+					size_t data_len, const char *password,
+					struct l_certchain **out_certchain,
+					struct l_key **out_privkey)
+{
+	const uint8_t *safe_contents;
+	size_t safe_contents_len;
+	uint8_t tag;
+
+	if (!(safe_contents = asn1_der_find_elem(data, data_len, 0, &tag,
+							&safe_contents_len)) ||
+			tag != ASN1_ID_SEQUENCE ||
+			data + data_len != safe_contents + safe_contents_len)
+		return false;
+
+	/* RFC7292 Section 4.2 */
+	while (safe_contents_len) {
+		const uint8_t *safe_bag;
+		size_t safe_bag_len;
+		const uint8_t *bag_id;
+		size_t bag_id_len;
+		const uint8_t *bag_value;
+		int bag_value_len;
+
+		/* RFC7292 Section 4.2 */
+		if (!(safe_bag = asn1_der_find_elem(safe_contents,
+							safe_contents_len, 0,
+							&tag, &safe_bag_len)) ||
+				tag != ASN1_ID_SEQUENCE)
+			return false;
+
+		if (!(bag_id = asn1_der_find_elem(safe_bag, safe_bag_len, 0,
+							&tag, &bag_id_len)) ||
+				tag != ASN1_ID_OID)
+			return false;
+
+		/*
+		 * The bagValue is EXPLICITly tagged but we don't want to
+		 * unpack the inner TLV yet so don't use asn1_der_find_elem.
+		 */
+		safe_bag_len -= bag_id + bag_id_len - safe_bag;
+		safe_bag = bag_id + bag_id_len;
+
+		if (safe_bag_len < 4)
+			return false;
+
+		tag = *safe_bag++;
+		safe_bag_len--;
+		bag_value_len = asn1_parse_definite_length(&safe_bag,
+								&safe_bag_len);
+		bag_value = safe_bag;
+
+		if (bag_value_len < 0 || bag_value_len > (int) safe_bag_len ||
+				tag != ASN1_ID(ASN1_CLASS_CONTEXT, 1, 0))
+			return false;
+
+		/* PKCS#9 attributes ignored */
+
+		safe_contents_len -= (safe_bag + safe_bag_len - safe_contents);
+		safe_contents = safe_bag + safe_bag_len;
+
+		if (asn1_oid_eq(&pkcs12_key_bag_oid, bag_id_len, bag_id)) {
+			if (!out_privkey || *out_privkey)
+				continue;
+
+			*out_privkey = pem_key_from_pkcs8_private_key_info(
+								bag_value,
+								bag_value_len);
+			if (!*out_privkey)
+				return false;
+		} else if (asn1_oid_eq(&pkcs12_pkcs8_shrouded_key_bag_oid,
+					bag_id_len, bag_id)) {
+			if (!out_privkey || *out_privkey)
+				continue;
+
+			*out_privkey =
+				pem_key_from_pkcs8_encrypted_private_key_info(
+								bag_value,
+								bag_value_len,
+								password);
+			if (!*out_privkey)
+				return false;
+		} else if (asn1_oid_eq(&pkcs12_cert_bag_oid,
+					bag_id_len, bag_id)) {
+			if (!out_certchain)
+				continue;
+
+			if (!pkcs12_parse_cert_bag(bag_value, bag_value_len,
+							out_certchain))
+				return false;
+		} else if (asn1_oid_eq(&pkcs12_safe_contents_bag_oid,
+					bag_id_len, bag_id)) {
+			/* TODO: depth check */
+			if (!(pkcs12_parse_safe_contents(bag_value,
+								bag_value_len,
+								password,
+								out_certchain,
+								out_privkey)))
+				return false;
+		}
+	}
+
+	return true;
+}
+
+/* RFC5652 Section 4 */
+static const struct asn1_oid pkcs7_data_oid = {
+	9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01 }
+};
+
+/* RFC5652 Section 8 */
+static const struct asn1_oid pkcs7_encrypted_data_oid = {
+	9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x06 }
+};
+
+static void pkcs12_parse_pfx(const uint8_t *ptr, size_t len,
+				const char *password,
+				struct l_certchain **out_certchain,
+				struct l_key **out_privkey)
+{
+	const uint8_t *version;
+	size_t version_len;
+	const uint8_t *auth_safe;
+	size_t auth_safe_len;
+	const uint8_t *auth_safe_seq;
+	size_t auth_safe_seq_len;
+	const uint8_t *mac_data;
+	size_t mac_data_len;
+	const uint8_t *mac;
+	size_t mac_len;
+	const uint8_t *mac_salt;
+	size_t mac_salt_len;
+	const uint8_t *iterations_data;
+	size_t iterations_len;
+	unsigned int iterations;
+	const uint8_t *digest_alg;
+	size_t digest_alg_len;
+	const uint8_t *digest;
+	size_t digest_len;
+	const uint8_t *alg_id;
+	size_t alg_id_len;
+	struct pkcs12_hash *mac_hash;
+	L_AUTO_FREE_VAR(uint8_t *, key) = NULL;
+	struct l_checksum *hmac;
+	uint8_t hmac_val[64];
+	uint8_t tag;
+	unsigned int i;
+	bool ok;
+
+	/* RFC7292 Section 4 */
+	if (!(version = asn1_der_find_elem(ptr, len, 0, &tag, &version_len)) ||
+			tag != ASN1_ID_INTEGER)
+		return;
+
+	if (version_len != 1 || version[0] != 3)
+		return;
+
+	/*
+	 * Since we only support the password-based integrity mode,  the
+	 * authSafe must be of PKCS#7 type "data" and not "signedData".
+	 */
+	if (!(auth_safe = pkcs7_unpack_content_info(ptr, len, 1,
+							&pkcs7_data_oid, NULL,
+							&tag,
+							&auth_safe_len)) ||
+			tag != ASN1_ID_OCTET_STRING)
+		return;
+
+	/*
+	 * openssl can generate PFX structures without macData not signed
+	 * with a public key so handle this case, otherwise the macData
+	 * would not be optional.
+	 */
+	if (auth_safe + auth_safe_len == ptr + len)
+		goto integrity_check_done;
+
+	if (!(mac_data = asn1_der_find_elem(ptr, len, 2, &tag,
+						&mac_data_len)) ||
+			tag != ASN1_ID_SEQUENCE)
+		return;
+
+	if (!(mac = asn1_der_find_elem(mac_data, mac_data_len, 0, &tag,
+					&mac_len)) ||
+			tag != ASN1_ID_SEQUENCE)
+		return;
+
+	if (!(mac_salt = asn1_der_find_elem(mac_data, mac_data_len, 1, &tag,
+						&mac_salt_len)) ||
+			tag != ASN1_ID_OCTET_STRING || mac_salt_len > 1024)
+		return;
+
+	if (!(iterations_data = asn1_der_find_elem(mac_data, mac_data_len, 2,
+							&tag,
+							&iterations_len)) ||
+			tag != ASN1_ID_INTEGER || iterations_len > 4)
+		return;
+
+	for (iterations = 0; iterations_len; iterations_len--)
+		iterations = (iterations << 8) | *iterations_data++;
+
+	if (iterations < 1 || iterations > 8192)
+		return;
+
+	/* RFC2315 Section 9.4 */
+	if (!(digest_alg = asn1_der_find_elem(mac, mac_len, 0, &tag,
+						&digest_alg_len)) ||
+			tag != ASN1_ID_SEQUENCE)
+		return;
+
+	if (!(digest = asn1_der_find_elem(mac, mac_len, 1, &tag,
+						&digest_len)) ||
+			tag != ASN1_ID_OCTET_STRING)
+		return;
+
+	if (!(alg_id = asn1_der_find_elem(digest_alg, digest_alg_len,
+						0, &tag, &alg_id_len)) ||
+			tag != ASN1_ID_OID)
+		return;
+
+	/* This is going to be used for both the MAC and its key derivation */
+	for (i = 0; i < L_ARRAY_SIZE(pkcs12_mac_algs); i++)
+		if (asn1_oid_eq(&pkcs12_mac_algs[i].oid, alg_id_len, alg_id)) {
+			mac_hash = &pkcs12_mac_algs[i];
+			break;
+		}
+
+	if (i == L_ARRAY_SIZE(pkcs12_mac_algs) || digest_len != mac_hash->u)
+		return;
+
+	if (!(key = pkcs12_pbkdf(password, mac_hash, mac_salt, mac_salt_len,
+					iterations, 3, mac_hash->u)))
+		return;
+
+	hmac = l_checksum_new_hmac(mac_hash->alg, key, mac_hash->u);
+	explicit_bzero(key, mac_hash->u);
+
+	if (!hmac)
+		return;
+
+	ok = l_checksum_update(hmac, auth_safe, auth_safe_len) &&
+		l_checksum_get_digest(hmac, hmac_val, mac_hash->len) > 0;
+	l_checksum_free(hmac);
+
+	if (!ok)
+		return;
+
+	/*
+	 * SHA-512/224 and SHA-512/256 are not supported.  We can truncate the
+	 * output for key derivation but we can't do this inside the HMAC
+	 * algorithms based on these hashes.  We skip the MAC verification
+	 * if one of these hashes is used (identified by .u != .len)
+	 */
+	if (mac_hash->u != mac_hash->len)
+		goto integrity_check_done;
+
+	if (memcmp(hmac_val, digest, digest_len))
+		return;
+
+integrity_check_done:
+	if (!(auth_safe_seq = asn1_der_find_elem(auth_safe, auth_safe_len, 0,
+						&tag, &auth_safe_seq_len)) ||
+			tag != ASN1_ID_SEQUENCE ||
+			auth_safe + auth_safe_len !=
+			auth_safe_seq + auth_safe_seq_len)
+		return;
+
+	i = 0;
+	while (1) {
+		struct asn1_oid data_oid;
+		const uint8_t *data;
+		size_t data_len;
+
+		if (!(data = pkcs7_unpack_content_info(auth_safe_seq,
+							auth_safe_seq_len, i++,
+							NULL, &data_oid, &tag,
+							&data_len)))
+			break;
+
+		if (asn1_oid_eq(&pkcs7_encrypted_data_oid,
+					data_oid.asn1_len, data_oid.asn1)) {
+			uint8_t *plaintext;
+			size_t plaintext_len;
+			struct asn1_oid oid;
+
+			if (tag != ASN1_ID_SEQUENCE)
+				goto error;
+
+			/*
+			 * This is same as PKCS#7 encryptedData but the ciphers
+			 * used are from PKCS#12 (broken but still the default
+			 * everywhere) and PKCS#5 (recommended).
+			 */
+			plaintext = pkcs7_decrypt_encrypted_data(data, data_len,
+								password, &oid,
+								&plaintext_len);
+			if (!plaintext)
+				goto error;
+
+			/*
+			 * Since we only support PKCS#7 data and encryptedData
+			 * types, and there's no point re-encrypting
+			 * encryptedData, the plaintext must be a PKCS#7
+			 * "data".
+			 */
+			ok = asn1_oid_eq(&pkcs7_data_oid,
+						oid.asn1_len, oid.asn1) &&
+				pkcs12_parse_safe_contents(plaintext,
+								plaintext_len,
+								password,
+								out_certchain,
+								out_privkey);
+			explicit_bzero(plaintext, plaintext_len);
+			l_free(plaintext);
+
+			if (!ok)
+				goto error;
+		} else if (asn1_oid_eq(&pkcs7_data_oid,
+					data_oid.asn1_len, data_oid.asn1)) {
+			if (tag != ASN1_ID_OCTET_STRING)
+				goto error;
+
+			if (!pkcs12_parse_safe_contents(data, data_len,
+							password,
+							out_certchain,
+							out_privkey))
+				goto error;
+		}
+		/* envelopedData support not needed */
+	}
+
+	return;
+
+error:
+	if (out_certchain && *out_certchain) {
+		l_certchain_free(*out_certchain);
+		*out_certchain = NULL;
+	}
+
+	if (out_privkey && *out_privkey) {
+		l_key_free(*out_privkey);
+		*out_privkey = NULL;
+	}
+}
+
 /*
  * Look at a file, try to detect which of the few X.509 certificate and/or
  * private key container formats it uses and load any certificates in it as
@@ -946,6 +1522,8 @@ LIB_EXPORT struct l_key *l_pem_load_private_key(const char *filename,
  *  PEM PKCS#8 encrypted and unenecrypted private keys
  *  PEM legacy PKCS#1 encrypted and unenecrypted private keys
  *  Raw X.509 certificates (.cer, .der, .crt)
+ *  PKCS#12 certificates
+ *  PKCS#12 encrypted and unencrypted private keys
  *
  * The raw format contains exactly one certificate, PEM and PKCS#12 files
  * can contain any combination of certificates and private keys.
@@ -1014,6 +1592,27 @@ LIB_EXPORT void l_pem_load_container_file(const char *filename,
 
 			goto close;
 		}
+
+		if (tag == ASN1_ID_INTEGER) {
+			/*
+			 * Since we don't support public key-protected PKCS#12
+			 * modes, we always require the password at least for
+			 * the integrity check.  Strictly speaking encryption
+			 * may not actually be in use.  We also don't support
+			 * files with different integrity and privacy
+			 * passwords, they must be identical if privacy is
+			 * enabled.
+			 */
+			if (out_encrypted)
+				*out_encrypted = true;
+
+			if (!password)
+				goto close;
+
+			pkcs12_parse_pfx(seq_data, len, password,
+						out_certchain, out_privkey);
+			goto close;
+		}
 	}
 
 not_der_after_all:
@@ -1060,6 +1659,19 @@ not_der_after_all:
 			goto next;
 		}
 
+		/* PEM-encoded PKCS12, probably very rare */
+		if (!strcmp(type_label, "PKCS12")) {
+			if (out_encrypted)
+				*out_encrypted = true;
+
+			if (!password)
+				goto next;
+
+			pkcs12_parse_pfx(der, der_len, password,
+						out_certchain, out_privkey);
+			goto next;
+		}
+
 		/* Only use the first private key found */
 		if (out_privkey && !*out_privkey) {
 			*out_privkey = pem_load_private_key(der, der_len,
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 04/13] cipher: Add ARC4 implementation
  2020-12-10 19:31 ` [PATCH 04/13] cipher: Add ARC4 implementation Andrew Zaborowski
@ 2020-12-10 22:46   ` Denis Kenzior
  0 siblings, 0 replies; 15+ messages in thread
From: Denis Kenzior @ 2020-12-10 22:46 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 572 bytes --]

Hi Andrew,

On 12/10/20 1:31 PM, Andrew Zaborowski wrote:
> Re-add the L_CIPHER_ARC4 stream cipher for use in decoding PKCS#12
> files.  Like RC2 it's implemented completely in cipher.c because the
> kernel API doesn't support it.  This cipher is also discouraged where it
> can be avoided.
> ---
>   ell/cipher.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>   ell/cipher.h |  4 +--
>   2 files changed, 100 insertions(+), 2 deletions(-)
> 

Looks like there's a funny character in this patch somewhere and git am complains.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 01/13] pem: Wipe more memory in PKCS#1 private key decoding
  2020-12-10 19:31 [PATCH 01/13] pem: Wipe more memory in PKCS#1 private key decoding Andrew Zaborowski
                   ` (7 preceding siblings ...)
  2020-12-10 19:32 ` [PATCH 09/13] pem: Add PKCS#12 parsing Andrew Zaborowski
@ 2020-12-10 22:47 ` Denis Kenzior
  8 siblings, 0 replies; 15+ messages in thread
From: Denis Kenzior @ 2020-12-10 22:47 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 377 bytes --]

Hi Andrew,

On 12/10/20 1:31 PM, Andrew Zaborowski wrote:
> bzero some more potentially sensitive memory that we weren't bzeroing in
> the recently added openssl legacy PKCS#1 private key format decoding.
> ---
>   ell/pem.c | 18 +++++++++++++-----
>   1 file changed, 13 insertions(+), 5 deletions(-)
> 

I went ahead and applied 1, 2, 3 and 5.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 06/13] pkcs5: Add the PKCS#12 KDF
  2020-12-10 19:31 ` [PATCH 06/13] pkcs5: Add the PKCS#12 KDF Andrew Zaborowski
@ 2020-12-10 22:51   ` Denis Kenzior
  2020-12-11 10:34     ` Andrew Zaborowski
  0 siblings, 1 reply; 15+ messages in thread
From: Denis Kenzior @ 2020-12-10 22:51 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 1143 bytes --]

Hi Andrew,

On 12/10/20 1:31 PM, Andrew Zaborowski wrote:
> Add the key derivation algorithm used with PKCS#12 to pkcs5.c so that it
> can be found together with the two PKCS#5 KDFs, and so that it can also
> be used when parsing of the PKCS#12 AlgorithmIdentifiers in the next
> commit.  This KDF is not recommended for new uses.
> ---
>   ell/pkcs5-private.h |  12 ++++
>   ell/pkcs5.c         | 146 ++++++++++++++++++++++++++++++++++++++++++++
>   2 files changed, 158 insertions(+)
> 

<snip>

> +static bool utf8_to_bmpstring(const char *utf, uint8_t *out_buf,
> +				unsigned int *out_len)
> +{
> +	unsigned int n;
> +	int utflen;
> +	wchar_t cp;
> +	uint16_t *ptr = (uint16_t *) out_buf;
> +
> +	for (n = strlen(utf); n; n -= utflen, utf += utflen) {
> +		if ((utflen = l_utf8_get_codepoint(utf, n, &cp)) <= 0 ||
> +				cp > 0xffff)
> +			return false;

So is this just a UCS2BE conversion?  If so, then using l_utf8_to_ucs2be might 
be clearer.

> +
> +		*ptr++ = L_CPU_TO_BE16(cp);
> +	}
> +
> +	*ptr++ = 0;
> +	*out_len = (uint8_t *) ptr - out_buf;
> +	return true;
> +}
> +

Regards,
-Denis

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 06/13] pkcs5: Add the PKCS#12 KDF
  2020-12-10 22:51   ` Denis Kenzior
@ 2020-12-11 10:34     ` Andrew Zaborowski
  2020-12-11 15:50       ` Denis Kenzior
  0 siblings, 1 reply; 15+ messages in thread
From: Andrew Zaborowski @ 2020-12-11 10:34 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 1611 bytes --]

Hi Denis,

On Thu, 10 Dec 2020 at 23:51, Denis Kenzior <denkenz@gmail.com> wrote:
> On 12/10/20 1:31 PM, Andrew Zaborowski wrote:
> > Add the key derivation algorithm used with PKCS#12 to pkcs5.c so that it
> > can be found together with the two PKCS#5 KDFs, and so that it can also
> > be used when parsing of the PKCS#12 AlgorithmIdentifiers in the next
> > commit.  This KDF is not recommended for new uses.
> > ---
> >   ell/pkcs5-private.h |  12 ++++
> >   ell/pkcs5.c         | 146 ++++++++++++++++++++++++++++++++++++++++++++
> >   2 files changed, 158 insertions(+)
> >
>
> <snip>
>
> > +static bool utf8_to_bmpstring(const char *utf, uint8_t *out_buf,
> > +                             unsigned int *out_len)
> > +{
> > +     unsigned int n;
> > +     int utflen;
> > +     wchar_t cp;
> > +     uint16_t *ptr = (uint16_t *) out_buf;
> > +
> > +     for (n = strlen(utf); n; n -= utflen, utf += utflen) {
> > +             if ((utflen = l_utf8_get_codepoint(utf, n, &cp)) <= 0 ||
> > +                             cp > 0xffff)
> > +                     return false;
>
> So is this just a UCS2BE conversion?  If so, then using l_utf8_to_ucs2be might
> be clearer.

Basically yes.  But with l_utf8_to_ucs2be this will look something like:

  uint8_t *usc2;

  if (!l_utf8_validate(utf))
    return false;

  ucs2 = l_utf8_to_ucs2be(utf, out_len);
  if (!str)
    return false;

  memcpy(out_buf, ucs2, *out_len);
  explicit_bzero(ucs2);
  l_free(ucs2);

I can do that but it seems we're doing less work without using
l_utf8_to_ucs2be here.

Best regards

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 06/13] pkcs5: Add the PKCS#12 KDF
  2020-12-11 10:34     ` Andrew Zaborowski
@ 2020-12-11 15:50       ` Denis Kenzior
  2020-12-12  0:06         ` Andrew Zaborowski
  0 siblings, 1 reply; 15+ messages in thread
From: Denis Kenzior @ 2020-12-11 15:50 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 988 bytes --]

Hi Andrew,

>> So is this just a UCS2BE conversion?  If so, then using l_utf8_to_ucs2be might
>> be clearer.
> 
> Basically yes.  But with l_utf8_to_ucs2be this will look something like:
> 
>    uint8_t *usc2;
> 
>    if (!l_utf8_validate(utf))
>      return false;

You'll potentially need this step in either version actually. 
utf8_get_codepoint isn't really enough to tell whether the entire string is 
valid utf8.  Where is the password coming from?

Also, I would think you'd pre-validate utf8 validity before ever getting to this 
point?

> 
>    ucs2 = l_utf8_to_ucs2be(utf, out_len);
>    if (!str)
>      return false;
> 
>    memcpy(out_buf, ucs2, *out_len);
>    explicit_bzero(ucs2);
>    l_free(ucs2);
> 
> I can do that but it seems we're doing less work without using
> l_utf8_to_ucs2be here.

You could add an own buffer version of utf8_to_ucs2be.  But even this memcpy 
version seems to be cleaner than open-coding it.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 06/13] pkcs5: Add the PKCS#12 KDF
  2020-12-11 15:50       ` Denis Kenzior
@ 2020-12-12  0:06         ` Andrew Zaborowski
  0 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-12-12  0:06 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 1551 bytes --]

Hi Denis,

On Fri, 11 Dec 2020 at 16:50, Denis Kenzior <denkenz@gmail.com> wrote:
> >> So is this just a UCS2BE conversion?  If so, then using l_utf8_to_ucs2be might
> >> be clearer.
> >
> > Basically yes.  But with l_utf8_to_ucs2be this will look something like:
> >
> >    uint8_t *usc2;
> >
> >    if (!l_utf8_validate(utf))
> >      return false;
>
> You'll potentially need this step in either version actually.
> utf8_get_codepoint isn't really enough to tell whether the entire string is
> valid utf8.

Right, but as long as we don't crash and we can decrypt the contents,
we don't care.  And since I was first using strlen() and then passing
the remaining byte count to l_utf8_get_codepoint() it shouldn't crash.

>  Where is the password coming from?

Currently from the l_settings or the agent, so on second thought we
can require that the password parameter to l_pem_load_container_file
is valid UTF-8 and skip l_utf8_validate().

>
> Also, I would think you'd pre-validate utf8 validity before ever getting to this
> point?
>
> >
> >    ucs2 = l_utf8_to_ucs2be(utf, out_len);
> >    if (!str)
> >      return false;
> >
> >    memcpy(out_buf, ucs2, *out_len);
> >    explicit_bzero(ucs2);
> >    l_free(ucs2);
> >
> > I can do that but it seems we're doing less work without using
> > l_utf8_to_ucs2be here.
>
> You could add an own buffer version of utf8_to_ucs2be.  But even this memcpy
> version seems to be cleaner than open-coding it.

Ok, let's use l_utf8_to_ucs2be then.

Best regards

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2020-12-12  0:06 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-10 19:31 [PATCH 01/13] pem: Wipe more memory in PKCS#1 private key decoding Andrew Zaborowski
2020-12-10 19:31 ` [PATCH 02/13] pem: Factor out the PKCS#8 decoding code Andrew Zaborowski
2020-12-10 19:31 ` [PATCH 03/13] cipher: Add an RC2 implementation Andrew Zaborowski
2020-12-10 19:31 ` [PATCH 04/13] cipher: Add ARC4 implementation Andrew Zaborowski
2020-12-10 22:46   ` Denis Kenzior
2020-12-10 19:31 ` [PATCH 05/13] pem: Don't expect padding with stream ciphers Andrew Zaborowski
2020-12-10 19:31 ` [PATCH 06/13] pkcs5: Add the PKCS#12 KDF Andrew Zaborowski
2020-12-10 22:51   ` Denis Kenzior
2020-12-11 10:34     ` Andrew Zaborowski
2020-12-11 15:50       ` Denis Kenzior
2020-12-12  0:06         ` Andrew Zaborowski
2020-12-10 19:32 ` [PATCH 07/13] pkcs5: Add PKCS#12 algorithms in pkcs5_cipher_from_alg_id Andrew Zaborowski
2020-12-10 19:32 ` [PATCH 08/13] pem: Add l_pem_load_container_file Andrew Zaborowski
2020-12-10 19:32 ` [PATCH 09/13] pem: Add PKCS#12 parsing Andrew Zaborowski
2020-12-10 22:47 ` [PATCH 01/13] pem: Wipe more memory in PKCS#1 private key decoding Denis Kenzior

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.