1
0
mirror of https://github.com/moparisthebest/curl synced 2024-12-26 10:08:48 -05:00
curl/lib/vquic/ngtcp2-crypto.c

521 lines
13 KiB
C
Raw Normal View History

/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
#include "curl_setup.h"
#ifdef USE_NGTCP2
#include <ngtcp2/ngtcp2.h>
#include <openssl/ssl.h>
#include <openssl/evp.h>
#include <openssl/kdf.h>
#include "ngtcp2-crypto.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
void Curl_qc_prf_sha256(struct Context *ctx)
{
ctx->prf = EVP_sha256();
}
void Curl_qc_aead_aes_128_gcm(struct Context *ctx)
{
ctx->aead = EVP_aes_128_gcm();
ctx->hp = EVP_aes_128_ctr();
}
size_t Curl_qc_aead_nonce_length(const struct Context *ctx)
{
return EVP_CIPHER_iv_length(ctx->aead);
}
int Curl_qc_negotiated_prf(struct Context *ctx, SSL *ssl)
{
switch(SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
case 0x03001301u: /* TLS_AES_128_GCM_SHA256 */
case 0x03001303u: /* TLS_CHACHA20_POLY1305_SHA256 */
ctx->prf = EVP_sha256();
return 0;
case 0x03001302u: /* TLS_AES_256_GCM_SHA384 */
ctx->prf = EVP_sha384();
return 0;
default:
return -1;
}
}
int Curl_qc_negotiated_aead(struct Context *ctx, SSL *ssl)
{
switch(SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
case 0x03001301u: /* TLS_AES_128_GCM_SHA256 */
ctx->aead = EVP_aes_128_gcm();
ctx->hp = EVP_aes_128_ctr();
return 0;
case 0x03001302u: /* TLS_AES_256_GCM_SHA384 */
ctx->aead = EVP_aes_256_gcm();
ctx->hp = EVP_aes_256_ctr();
return 0;
case 0x03001303u: /* TLS_CHACHA20_POLY1305_SHA256 */
ctx->aead = EVP_chacha20_poly1305();
ctx->hp = EVP_chacha20();
return 0;
default:
return -1;
}
}
ssize_t Curl_qc_encrypt_pn(uint8_t *dest, size_t destlen,
const uint8_t *plaintext, size_t plaintextlen,
const struct Context *ctx,
const uint8_t *key, size_t keylen,
const uint8_t *nonce, size_t noncelen)
{
EVP_CIPHER_CTX *actx = EVP_CIPHER_CTX_new();
size_t outlen = 0;
int len;
(void)destlen;
(void)keylen;
(void)noncelen;
if(!actx)
return -1;
if(EVP_EncryptInit_ex(actx, ctx->hp, NULL, key, nonce) != 1)
goto error;
if(EVP_EncryptUpdate(actx, dest, &len, plaintext, (int)plaintextlen) != 1)
goto error;
assert(len > 0);
outlen = len;
if(EVP_EncryptFinal_ex(actx, dest + outlen, &len) != 1)
goto error;
assert(len == 0);
/* outlen += len; */
EVP_CIPHER_CTX_free(actx);
return outlen;
error:
EVP_CIPHER_CTX_free(actx);
return -1;
}
static int hkdf_expand(uint8_t *dest, size_t destlen, const uint8_t *secret,
size_t secretlen, const uint8_t *info, size_t infolen,
const struct Context *ctx)
{
void *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
if(!pctx)
return -1;
if(EVP_PKEY_derive_init(pctx) != 1)
goto err;
if(EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) != 1)
goto err;
if(EVP_PKEY_CTX_set_hkdf_md(pctx, ctx->prf) != 1)
goto err;
if(EVP_PKEY_CTX_set1_hkdf_salt(pctx, "", 0) != 1)
goto err;
if(EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1)
goto err;
if(EVP_PKEY_CTX_add1_hkdf_info(pctx, info, (int)infolen) != 1)
goto err;
if(EVP_PKEY_derive(pctx, dest, &destlen) != 1)
goto err;
return 0;
err:
EVP_PKEY_CTX_free(pctx);
return -1;
}
static int hkdf_extract(uint8_t *dest, size_t destlen,
const uint8_t *secret, size_t secretlen,
const uint8_t *salt, size_t saltlen,
const struct Context *ctx)
{
void *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
if(!pctx)
return -1;
if(EVP_PKEY_derive_init(pctx) != 1)
goto err;
if(EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) != 1) {
goto err;
}
if(EVP_PKEY_CTX_set_hkdf_md(pctx, ctx->prf) != 1) {
goto err;
}
if(EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, (int)saltlen) != 1) {
goto err;
}
if(EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1) {
goto err;
}
if(EVP_PKEY_derive(pctx, dest, &destlen) != 1) {
goto err;
}
EVP_PKEY_CTX_free(pctx);
return 0;
err:
EVP_PKEY_CTX_free(pctx);
return -1;
}
static int qhkdf_expand(uint8_t *dest, size_t destlen,
const uint8_t *secret, size_t secretlen,
const uint8_t *qlabel, size_t qlabellen,
const struct Context *ctx)
{
uint8_t info[256];
static const char LABEL[] = "quic ";
uint8_t *p = &info[0];
*p++ = (destlen / 256) & 0xff;
*p++ = destlen % 256;
*p++ = (strlen(LABEL) + qlabellen) & 0xff;
memcpy(p, LABEL, strlen(LABEL));
p += strlen(LABEL);
memcpy(p, qlabel, qlabellen);
p += qlabellen;
*p++ = 0;
return hkdf_expand(dest, destlen, secret, secretlen, &info[0],
p - &info[0], ctx);
}
static size_t aead_key_length(const struct Context *ctx)
{
return EVP_CIPHER_key_length(ctx->aead);
}
static size_t aead_tag_length(const struct Context *ctx)
{
if(ctx->aead == EVP_aes_128_gcm() || ctx->aead == EVP_aes_256_gcm()) {
return EVP_GCM_TLS_TAG_LEN;
}
if(ctx->aead == EVP_chacha20_poly1305()) {
return EVP_CHACHAPOLY_TLS_TAG_LEN;
}
assert(0);
}
size_t Curl_qc_aead_max_overhead(const struct Context *ctx)
{
return aead_tag_length(ctx);
}
ssize_t Curl_qc_encrypt(uint8_t *dest, size_t destlen,
const uint8_t *plaintext, size_t plaintextlen,
const struct Context *ctx,
const uint8_t *key, size_t keylen,
const uint8_t *nonce, size_t noncelen,
const uint8_t *ad, size_t adlen)
{
size_t taglen = aead_tag_length(ctx);
EVP_CIPHER_CTX *actx;
size_t outlen = 0;
int len;
(void)keylen;
if(destlen < plaintextlen + taglen) {
return -1;
}
actx = EVP_CIPHER_CTX_new();
if(!actx)
return -1;
if(EVP_EncryptInit_ex(actx, ctx->aead, NULL, NULL, NULL) != 1)
goto error;
if(EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN,
(int)noncelen, NULL) != 1)
goto error;
if(EVP_EncryptInit_ex(actx, NULL, NULL, key, nonce) != 1)
goto error;
if(EVP_EncryptUpdate(actx, NULL, &len, ad, (int)adlen) != 1)
goto error;
if(EVP_EncryptUpdate(actx, dest, &len, plaintext, (int)plaintextlen) != 1)
goto error;
outlen = len;
if(EVP_EncryptFinal_ex(actx, dest + outlen, &len) != 1)
goto error;
outlen += len;
assert(outlen + taglen <= destlen);
if(EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_GET_TAG,
(int)taglen, dest + outlen) != 1)
goto error;
outlen += taglen;
EVP_CIPHER_CTX_free(actx);
return outlen;
error:
EVP_CIPHER_CTX_free(actx);
return -1;
}
ssize_t Curl_qc_decrypt(uint8_t *dest, size_t destlen,
const uint8_t *ciphertext, size_t ciphertextlen,
const struct Context *ctx,
const uint8_t *key, size_t keylen,
const uint8_t *nonce, size_t noncelen,
const uint8_t *ad, size_t adlen)
{
size_t taglen = aead_tag_length(ctx);
const uint8_t *tag;
EVP_CIPHER_CTX *actx;
size_t outlen;
int len;
(void)keylen;
if(taglen > ciphertextlen || destlen + taglen < ciphertextlen) {
return -1;
}
ciphertextlen -= taglen;
tag = ciphertext + ciphertextlen;
actx = EVP_CIPHER_CTX_new();
if(!actx)
return -1;
if(EVP_DecryptInit_ex(actx, ctx->aead, NULL, NULL, NULL) != 1)
goto error;
if(EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen, NULL) !=
1)
goto error;
if(EVP_DecryptInit_ex(actx, NULL, NULL, key, nonce) != 1)
goto error;
if(EVP_DecryptUpdate(actx, NULL, &len, ad, (int)adlen) != 1)
goto error;
if(EVP_DecryptUpdate(actx, dest, &len, ciphertext, (int)ciphertextlen) != 1)
goto error;
outlen = len;
if(EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG,
(int)taglen, (char *)tag) != 1)
goto error;
if(EVP_DecryptFinal_ex(actx, dest + outlen, &len) != 1)
goto error;
outlen += len;
EVP_CIPHER_CTX_free(actx);
return outlen;
error:
EVP_CIPHER_CTX_free(actx);
return -1;
}
int Curl_qc_derive_initial_secret(uint8_t *dest, size_t destlen,
const ngtcp2_cid *secret,
const uint8_t *salt,
size_t saltlen)
{
struct Context ctx;
Curl_qc_prf_sha256(&ctx);
return hkdf_extract(dest, destlen, secret->data, secret->datalen, salt,
saltlen, &ctx);
}
int Curl_qc_derive_client_initial_secret(uint8_t *dest,
size_t destlen,
const uint8_t *secret,
size_t secretlen)
{
static uint8_t LABEL[] = "client in";
struct Context ctx;
Curl_qc_prf_sha256(&ctx);
return qhkdf_expand(dest, destlen, secret, secretlen, LABEL,
strlen((char *)LABEL), &ctx);
}
ssize_t Curl_qc_derive_packet_protection_key(uint8_t *dest, size_t destlen,
const uint8_t *secret,
size_t secretlen,
const struct Context *ctx)
{
int rv;
static uint8_t LABEL_KEY[] = "key";
size_t keylen = aead_key_length(ctx);
if(keylen > destlen) {
return -1;
}
rv = qhkdf_expand(dest, keylen, secret, secretlen, LABEL_KEY,
strlen((char *)LABEL_KEY), ctx);
if(rv) {
return -1;
}
return keylen;
}
ssize_t Curl_qc_derive_packet_protection_iv(uint8_t *dest, size_t destlen,
const uint8_t *secret,
size_t secretlen,
const struct Context *ctx)
{
int rv;
static uint8_t LABEL_IV[] = "iv";
size_t ivlen = CURLMAX(8, Curl_qc_aead_nonce_length(ctx));
if(ivlen > destlen) {
return -1;
}
rv = qhkdf_expand(dest, ivlen, secret, secretlen, LABEL_IV,
strlen((char *)LABEL_IV), ctx);
if(rv) {
return -1;
}
return ivlen;
}
int Curl_qc_derive_server_initial_secret(uint8_t *dest, size_t destlen,
const uint8_t *secret,
size_t secretlen)
{
static uint8_t LABEL[] = "server in";
struct Context ctx;
Curl_qc_prf_sha256(&ctx);
return qhkdf_expand(dest, destlen, secret, secretlen, LABEL,
strlen((char *)LABEL), &ctx);
}
static int
hkdf_expand_label(uint8_t *dest, size_t destlen, const uint8_t *secret,
size_t secretlen, const uint8_t *label, size_t labellen,
const struct Context *ctx)
{
uint8_t info[256];
static const uint8_t LABEL[] = "tls13 ";
uint8_t *p = &info[0];
*p++ = (destlen / 256)&0xff;
*p++ = destlen % 256;
*p++ = (strlen((char *)LABEL) + labellen)&0xff;
memcpy(p, LABEL, strlen((char *)LABEL));
p += strlen((char *)LABEL);
memcpy(p, label, labellen);
p += labellen;
*p++ = 0;
return hkdf_expand(dest, destlen, secret, secretlen, &info[0],
p - &info[0], ctx);
}
ssize_t
Curl_qc_derive_header_protection_key(uint8_t *dest, size_t destlen,
const uint8_t *secret, size_t secretlen,
const struct Context *ctx)
{
int rv;
static uint8_t LABEL[] = "quic hp";
size_t keylen = aead_key_length(ctx);
if(keylen > destlen)
return -1;
rv = hkdf_expand_label(dest, keylen, secret, secretlen, LABEL,
strlen((char *)LABEL), ctx);
if(rv)
return -1;
return keylen;
}
ssize_t Curl_qc_hp_mask(uint8_t *dest, size_t destlen,
const struct Context *ctx,
const uint8_t *key, size_t keylen,
const uint8_t *sample, size_t samplelen)
{
static uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
EVP_CIPHER_CTX *actx;
size_t outlen = 0;
int len;
(void)destlen; /* TODO: make use of these! */
(void)keylen;
(void)samplelen;
actx = EVP_CIPHER_CTX_new();
if(!actx)
return -1;
if(EVP_EncryptInit_ex(actx, ctx->hp, NULL, key, sample) != 1)
goto error;
if(EVP_EncryptUpdate(actx, dest, &len, PLAINTEXT,
(int)strlen((char *)PLAINTEXT)) != 1)
goto error;
DEBUGASSERT(len == 5);
outlen = len;
if(EVP_EncryptFinal_ex(actx, dest + outlen, &len) != 1)
goto error;
DEBUGASSERT(len == 0);
return outlen;
error:
EVP_CIPHER_CTX_free(actx);
return -1;
}
#endif