Commit fb3158ea authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'add-chacha20-poly1305-cipher-to-kernel-tls'

Vadim Fedorenko says:

====================
Add CHACHA20-POLY1305 cipher to Kernel TLS

RFC 7905 defines usage of ChaCha20-Poly1305 in TLS connections. This
cipher is widely used nowadays and it's good to have a support for it
in TLS connections in kernel.
====================

Link: https://lore.kernel.org/r/1606231490-653-1-git-send-email-vfedorenko@novek.ru


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 594e31bc 4f336e88
Loading
Loading
Loading
Loading
+17 −15
Original line number Diff line number Diff line
@@ -211,6 +211,7 @@ union tls_crypto_context {
	union {
		struct tls12_crypto_info_aes_gcm_128 aes_gcm_128;
		struct tls12_crypto_info_aes_gcm_256 aes_gcm_256;
		struct tls12_crypto_info_chacha20_poly1305 chacha20_poly1305;
	};
};

@@ -501,32 +502,33 @@ static inline void tls_advance_record_sn(struct sock *sk,
	if (tls_bigint_increment(ctx->rec_seq, prot->rec_seq_size))
		tls_err_abort(sk, EBADMSG);

	if (prot->version != TLS_1_3_VERSION)
		tls_bigint_increment(ctx->iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE,
	if (prot->version != TLS_1_3_VERSION &&
	    prot->cipher_type != TLS_CIPHER_CHACHA20_POLY1305)
		tls_bigint_increment(ctx->iv + prot->salt_size,
				     prot->iv_size);
}

static inline void tls_fill_prepend(struct tls_context *ctx,
			     char *buf,
			     size_t plaintext_len,
			     unsigned char record_type,
			     int version)
			     unsigned char record_type)
{
	struct tls_prot_info *prot = &ctx->prot_info;
	size_t pkt_len, iv_size = prot->iv_size;

	pkt_len = plaintext_len + prot->tag_size;
	if (version != TLS_1_3_VERSION) {
	if (prot->version != TLS_1_3_VERSION &&
	    prot->cipher_type != TLS_CIPHER_CHACHA20_POLY1305) {
		pkt_len += iv_size;

		memcpy(buf + TLS_NONCE_OFFSET,
		       ctx->tx.iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE, iv_size);
		       ctx->tx.iv + prot->salt_size, iv_size);
	}

	/* we cover nonce explicit here as well, so buf should be of
	 * size KTLS_DTLS_HEADER_SIZE + KTLS_DTLS_NONCE_EXPLICIT_SIZE
	 */
	buf[0] = version == TLS_1_3_VERSION ?
	buf[0] = prot->version == TLS_1_3_VERSION ?
		   TLS_RECORD_TYPE_DATA : record_type;
	/* Note that VERSION must be TLS_1_2 for both TLS1.2 and TLS1.3 */
	buf[1] = TLS_1_2_VERSION_MINOR;
@@ -539,18 +541,17 @@ static inline void tls_fill_prepend(struct tls_context *ctx,
static inline void tls_make_aad(char *buf,
				size_t size,
				char *record_sequence,
				int record_sequence_size,
				unsigned char record_type,
				int version)
				struct tls_prot_info *prot)
{
	if (version != TLS_1_3_VERSION) {
		memcpy(buf, record_sequence, record_sequence_size);
	if (prot->version != TLS_1_3_VERSION) {
		memcpy(buf, record_sequence, prot->rec_seq_size);
		buf += 8;
	} else {
		size += TLS_CIPHER_AES_GCM_128_TAG_SIZE;
		size += prot->tag_size;
	}

	buf[0] = version == TLS_1_3_VERSION ?
	buf[0] = prot->version == TLS_1_3_VERSION ?
		  TLS_RECORD_TYPE_DATA : record_type;
	buf[1] = TLS_1_2_VERSION_MAJOR;
	buf[2] = TLS_1_2_VERSION_MINOR;
@@ -558,11 +559,12 @@ static inline void tls_make_aad(char *buf,
	buf[4] = size & 0xFF;
}

static inline void xor_iv_with_seq(int version, char *iv, char *seq)
static inline void xor_iv_with_seq(struct tls_prot_info *prot, char *iv, char *seq)
{
	int i;

	if (version == TLS_1_3_VERSION) {
	if (prot->version == TLS_1_3_VERSION ||
	    prot->cipher_type == TLS_CIPHER_CHACHA20_POLY1305) {
		for (i = 0; i < 8; i++)
			iv[i + 4] ^= seq[i];
	}
+15 −0
Original line number Diff line number Diff line
@@ -77,6 +77,13 @@
#define TLS_CIPHER_AES_CCM_128_TAG_SIZE		16
#define TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE		8

#define TLS_CIPHER_CHACHA20_POLY1305			54
#define TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE		12
#define TLS_CIPHER_CHACHA20_POLY1305_KEY_SIZE	32
#define TLS_CIPHER_CHACHA20_POLY1305_SALT_SIZE		0
#define TLS_CIPHER_CHACHA20_POLY1305_TAG_SIZE	16
#define TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE	8

#define TLS_SET_RECORD_TYPE	1
#define TLS_GET_RECORD_TYPE	2

@@ -109,6 +116,14 @@ struct tls12_crypto_info_aes_ccm_128 {
	unsigned char rec_seq[TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE];
};

struct tls12_crypto_info_chacha20_poly1305 {
	struct tls_crypto_info info;
	unsigned char iv[TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE];
	unsigned char key[TLS_CIPHER_CHACHA20_POLY1305_KEY_SIZE];
	unsigned char salt[TLS_CIPHER_CHACHA20_POLY1305_SALT_SIZE];
	unsigned char rec_seq[TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE];
};

enum {
	TLS_INFO_UNSPEC,
	TLS_INFO_VERSION,
+1 −1
Original line number Diff line number Diff line
@@ -327,7 +327,7 @@ static int tls_device_record_close(struct sock *sk,
	/* fill prepend */
	tls_fill_prepend(ctx, skb_frag_address(&record->frags[0]),
			 record->len - prot->overhead_size,
			 record_type, prot->version);
			 record_type);
	return ret;
}

+7 −6
Original line number Diff line number Diff line
@@ -49,7 +49,8 @@ static int tls_enc_record(struct aead_request *aead_req,
			  struct crypto_aead *aead, char *aad,
			  char *iv, __be64 rcd_sn,
			  struct scatter_walk *in,
			  struct scatter_walk *out, int *in_len)
			  struct scatter_walk *out, int *in_len,
			  struct tls_prot_info *prot)
{
	unsigned char buf[TLS_HEADER_SIZE + TLS_CIPHER_AES_GCM_128_IV_SIZE];
	struct scatterlist sg_in[3];
@@ -73,8 +74,7 @@ static int tls_enc_record(struct aead_request *aead_req,
	len -= TLS_CIPHER_AES_GCM_128_IV_SIZE;

	tls_make_aad(aad, len - TLS_CIPHER_AES_GCM_128_TAG_SIZE,
		(char *)&rcd_sn, sizeof(rcd_sn), buf[0],
		TLS_1_2_VERSION);
		(char *)&rcd_sn, buf[0], prot);

	memcpy(iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE, buf + TLS_HEADER_SIZE,
	       TLS_CIPHER_AES_GCM_128_IV_SIZE);
@@ -140,7 +140,7 @@ static struct aead_request *tls_alloc_aead_request(struct crypto_aead *aead,
static int tls_enc_records(struct aead_request *aead_req,
			   struct crypto_aead *aead, struct scatterlist *sg_in,
			   struct scatterlist *sg_out, char *aad, char *iv,
			   u64 rcd_sn, int len)
			   u64 rcd_sn, int len, struct tls_prot_info *prot)
{
	struct scatter_walk out, in;
	int rc;
@@ -150,7 +150,7 @@ static int tls_enc_records(struct aead_request *aead_req,

	do {
		rc = tls_enc_record(aead_req, aead, aad, iv,
				    cpu_to_be64(rcd_sn), &in, &out, &len);
				    cpu_to_be64(rcd_sn), &in, &out, &len, prot);
		rcd_sn++;

	} while (rc == 0 && len);
@@ -348,7 +348,8 @@ static struct sk_buff *tls_enc_skb(struct tls_context *tls_ctx,
		    payload_len, sync_size, dummy_buf);

	if (tls_enc_records(aead_req, ctx->aead_send, sg_in, sg_out, aad, iv,
			    rcd_sn, sync_size + payload_len) < 0)
			    rcd_sn, sync_size + payload_len,
			    &tls_ctx->prot_info) < 0)
		goto free_nskb;

	complete_skb(nskb, skb, tcp_payload_offset);
+3 −0
Original line number Diff line number Diff line
@@ -521,6 +521,9 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval,
	case TLS_CIPHER_AES_CCM_128:
		optsize = sizeof(struct tls12_crypto_info_aes_ccm_128);
		break;
	case TLS_CIPHER_CHACHA20_POLY1305:
		optsize = sizeof(struct tls12_crypto_info_chacha20_poly1305);
		break;
	default:
		rc = -EINVAL;
		goto err_crypto_info;
Loading