static int tls_push_record(struct sock *sk, int flags, unsigned char record_type) { struct tls_context *tls_ctx = tls_get_ctx(sk); struct tls_sw_context *ctx = tls_sw_ctx(tls_ctx); int rc; sg_mark_end(ctx->sg_plaintext_data + ctx->sg_plaintext_num_elem - 1); sg_mark_end(ctx->sg_encrypted_data + ctx->sg_encrypted_num_elem - 1); tls_make_aad(ctx->aad_space, ctx->sg_plaintext_size, tls_ctx->rec_seq, tls_ctx->rec_seq_size, record_type); tls_fill_prepend(tls_ctx, page_address(sg_page(&ctx->sg_encrypted_data[0])) + ctx->sg_encrypted_data[0].offset, ctx->sg_plaintext_size, record_type); tls_ctx->pending_open_record_frags = 0; set_bit(TLS_PENDING_CLOSED_RECORD, &tls_ctx->flags); rc = tls_do_encryption(tls_ctx, ctx, ctx->sg_plaintext_size, sk->sk_allocation); if (rc < 0) { /* If we are called from write_space and * we fail, we need to set this SOCK_NOSPACE * to trigger another write_space in the future. */ set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); return rc; } free_sg(sk, ctx->sg_plaintext_data, &ctx->sg_plaintext_num_elem, &ctx->sg_plaintext_size); ctx->sg_encrypted_num_elem = 0; ctx->sg_encrypted_size = 0; /* Only pass through MSG_DONTWAIT and MSG_NOSIGNAL flags */ rc = tls_push_sg(sk, tls_ctx, ctx->sg_encrypted_data, 0, flags); if (rc < 0 && rc != -EAGAIN) tls_err_abort(sk); tls_advance_record_sn(sk, tls_ctx); return rc; }
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) { unsigned char buf[TLS_HEADER_SIZE + TLS_CIPHER_AES_GCM_128_IV_SIZE]; struct scatterlist sg_in[3]; struct scatterlist sg_out[3]; u16 len; int rc; len = min_t(int, *in_len, ARRAY_SIZE(buf)); scatterwalk_copychunks(buf, in, len, 0); scatterwalk_copychunks(buf, out, len, 1); *in_len -= len; if (!*in_len) return 0; scatterwalk_pagedone(in, 0, 1); scatterwalk_pagedone(out, 1, 1); len = buf[4] | (buf[3] << 8); 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]); memcpy(iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE, buf + TLS_HEADER_SIZE, TLS_CIPHER_AES_GCM_128_IV_SIZE); sg_init_table(sg_in, ARRAY_SIZE(sg_in)); sg_init_table(sg_out, ARRAY_SIZE(sg_out)); sg_set_buf(sg_in, aad, TLS_AAD_SPACE_SIZE); sg_set_buf(sg_out, aad, TLS_AAD_SPACE_SIZE); chain_to_walk(sg_in + 1, in); chain_to_walk(sg_out + 1, out); *in_len -= len; if (*in_len < 0) { *in_len += TLS_CIPHER_AES_GCM_128_TAG_SIZE; /* the input buffer doesn't contain the entire record. * trim len accordingly. The resulting authentication tag * will contain garbage, but we don't care, so we won't * include any of it in the output skb * Note that we assume the output buffer length * is larger then input buffer length + tag size */ if (*in_len < 0) len += *in_len; *in_len = 0; } if (*in_len) { scatterwalk_copychunks(NULL, in, len, 2); scatterwalk_pagedone(in, 0, 1); scatterwalk_copychunks(NULL, out, len, 2); scatterwalk_pagedone(out, 1, 1); } len -= TLS_CIPHER_AES_GCM_128_TAG_SIZE; aead_request_set_crypt(aead_req, sg_in, sg_out, len, iv); rc = crypto_aead_encrypt(aead_req); return rc; }