Added support for client certificates (both client-side and server-side, but still missing an API for extracting the client identity from the certificate).
This commit is contained in:
parent
e9ce2f4e8c
commit
e61ad42191
27 changed files with 4441 additions and 1482 deletions
150
src/ssl/ssl_ccert_single_ec.c
Normal file
150
src/ssl/ssl_ccert_single_ec.c
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "inner.h"
|
||||
|
||||
static void
|
||||
cc_none0(const br_ssl_client_certificate_class **pctx)
|
||||
{
|
||||
(void)pctx;
|
||||
}
|
||||
|
||||
static void
|
||||
cc_none1(const br_ssl_client_certificate_class **pctx, size_t len)
|
||||
{
|
||||
(void)pctx;
|
||||
(void)len;
|
||||
}
|
||||
|
||||
static void
|
||||
cc_none2(const br_ssl_client_certificate_class **pctx,
|
||||
const unsigned char *data, size_t len)
|
||||
{
|
||||
(void)pctx;
|
||||
(void)data;
|
||||
(void)len;
|
||||
}
|
||||
|
||||
static void
|
||||
cc_choose(const br_ssl_client_certificate_class **pctx,
|
||||
const br_ssl_client_context *cc, uint32_t auth_types,
|
||||
br_ssl_client_certificate *choices)
|
||||
{
|
||||
br_ssl_client_certificate_ec_context *zc;
|
||||
int x;
|
||||
int scurve;
|
||||
|
||||
zc = (br_ssl_client_certificate_ec_context *)pctx;
|
||||
scurve = br_ssl_client_get_server_curve(cc);
|
||||
|
||||
if ((zc->allowed_usages & BR_KEYTYPE_KEYX) != 0
|
||||
&& scurve == zc->sk->curve)
|
||||
{
|
||||
int x;
|
||||
|
||||
x = (zc->issuer_key_type == BR_KEYTYPE_RSA) ? 16 : 17;
|
||||
if (((auth_types >> x) & 1) != 0) {
|
||||
choices->auth_type = BR_AUTH_ECDH;
|
||||
choices->hash_id = -1;
|
||||
choices->chain = zc->chain;
|
||||
choices->chain_len = zc->chain_len;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For ECDSA authentication, we must choose an appropriate
|
||||
* hash function.
|
||||
*/
|
||||
x = br_ssl_choose_hash((unsigned)(auth_types >> 8));
|
||||
if (x == 0 || (zc->allowed_usages & BR_KEYTYPE_SIGN) == 0) {
|
||||
memset(choices, 0, sizeof *choices);
|
||||
return;
|
||||
}
|
||||
choices->auth_type = BR_AUTH_ECDSA;
|
||||
choices->hash_id = x;
|
||||
choices->chain = zc->chain;
|
||||
choices->chain_len = zc->chain_len;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
cc_do_keyx(const br_ssl_client_certificate_class **pctx,
|
||||
unsigned char *data, size_t len)
|
||||
{
|
||||
br_ssl_client_certificate_ec_context *zc;
|
||||
|
||||
zc = (br_ssl_client_certificate_ec_context *)pctx;
|
||||
return zc->iec->mul(data, len, zc->sk->x, zc->sk->xlen, zc->sk->curve);
|
||||
}
|
||||
|
||||
static size_t
|
||||
cc_do_sign(const br_ssl_client_certificate_class **pctx,
|
||||
int hash_id, size_t hv_len, unsigned char *data, size_t len)
|
||||
{
|
||||
br_ssl_client_certificate_ec_context *zc;
|
||||
unsigned char hv[64];
|
||||
const br_hash_class *hc;
|
||||
|
||||
zc = (br_ssl_client_certificate_ec_context *)pctx;
|
||||
memcpy(hv, data, hv_len);
|
||||
hc = br_multihash_getimpl(zc->mhash, hash_id);
|
||||
if (hc == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (len < 139) {
|
||||
return 0;
|
||||
}
|
||||
return zc->iecdsa(zc->iec, hc, hv, zc->sk, data);
|
||||
}
|
||||
|
||||
static const br_ssl_client_certificate_class ccert_vtable = {
|
||||
sizeof(br_ssl_client_certificate_ec_context),
|
||||
cc_none0, /* start_name_list */
|
||||
cc_none1, /* start_name */
|
||||
cc_none2, /* append_name */
|
||||
cc_none0, /* end_name */
|
||||
cc_none0, /* end_name_list */
|
||||
cc_choose,
|
||||
cc_do_keyx,
|
||||
cc_do_sign
|
||||
};
|
||||
|
||||
/* see bearssl_ssl.h */
|
||||
void
|
||||
br_ssl_client_set_single_ec(br_ssl_client_context *cc,
|
||||
const br_x509_certificate *chain, size_t chain_len,
|
||||
const br_ec_private_key *sk, unsigned allowed_usages,
|
||||
unsigned cert_issuer_key_type,
|
||||
const br_ec_impl *iec, br_ecdsa_sign iecdsa)
|
||||
{
|
||||
cc->client_auth.single_ec.vtable = &ccert_vtable;
|
||||
cc->client_auth.single_ec.chain = chain;
|
||||
cc->client_auth.single_ec.chain_len = chain_len;
|
||||
cc->client_auth.single_ec.sk = sk;
|
||||
cc->client_auth.single_ec.allowed_usages = allowed_usages;
|
||||
cc->client_auth.single_ec.issuer_key_type = cert_issuer_key_type;
|
||||
cc->client_auth.single_ec.mhash = &cc->eng.mhash;
|
||||
cc->client_auth.single_ec.iec = iec;
|
||||
cc->client_auth.single_ec.iecdsa = iecdsa;
|
||||
cc->client_auth_vtable = &cc->client_auth.single_ec.vtable;
|
||||
}
|
||||
149
src/ssl/ssl_ccert_single_rsa.c
Normal file
149
src/ssl/ssl_ccert_single_rsa.c
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "inner.h"
|
||||
|
||||
static void
|
||||
cc_none0(const br_ssl_client_certificate_class **pctx)
|
||||
{
|
||||
(void)pctx;
|
||||
}
|
||||
|
||||
static void
|
||||
cc_none1(const br_ssl_client_certificate_class **pctx, size_t len)
|
||||
{
|
||||
(void)pctx;
|
||||
(void)len;
|
||||
}
|
||||
|
||||
static void
|
||||
cc_none2(const br_ssl_client_certificate_class **pctx,
|
||||
const unsigned char *data, size_t len)
|
||||
{
|
||||
(void)pctx;
|
||||
(void)data;
|
||||
(void)len;
|
||||
}
|
||||
|
||||
static void
|
||||
cc_choose(const br_ssl_client_certificate_class **pctx,
|
||||
const br_ssl_client_context *cc, uint32_t auth_types,
|
||||
br_ssl_client_certificate *choices)
|
||||
{
|
||||
br_ssl_client_certificate_rsa_context *zc;
|
||||
int x;
|
||||
|
||||
(void)cc;
|
||||
zc = (br_ssl_client_certificate_rsa_context *)pctx;
|
||||
x = br_ssl_choose_hash((unsigned)auth_types);
|
||||
if (x == 0 && (auth_types & 1) == 0) {
|
||||
memset(choices, 0, sizeof *choices);
|
||||
}
|
||||
choices->auth_type = BR_AUTH_RSA;
|
||||
choices->hash_id = x;
|
||||
choices->chain = zc->chain;
|
||||
choices->chain_len = zc->chain_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* OID for hash functions in RSA signatures.
|
||||
*/
|
||||
static const unsigned char HASH_OID_SHA1[] = {
|
||||
0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A
|
||||
};
|
||||
|
||||
static const unsigned char HASH_OID_SHA224[] = {
|
||||
0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04
|
||||
};
|
||||
|
||||
static const unsigned char HASH_OID_SHA256[] = {
|
||||
0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
|
||||
};
|
||||
|
||||
static const unsigned char HASH_OID_SHA384[] = {
|
||||
0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02
|
||||
};
|
||||
|
||||
static const unsigned char HASH_OID_SHA512[] = {
|
||||
0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03
|
||||
};
|
||||
|
||||
static const unsigned char *HASH_OID[] = {
|
||||
HASH_OID_SHA1,
|
||||
HASH_OID_SHA224,
|
||||
HASH_OID_SHA256,
|
||||
HASH_OID_SHA384,
|
||||
HASH_OID_SHA512
|
||||
};
|
||||
|
||||
static size_t
|
||||
cc_do_sign(const br_ssl_client_certificate_class **pctx,
|
||||
int hash_id, size_t hv_len, unsigned char *data, size_t len)
|
||||
{
|
||||
br_ssl_client_certificate_rsa_context *zc;
|
||||
unsigned char hv[64];
|
||||
const unsigned char *hash_oid;
|
||||
size_t sig_len;
|
||||
|
||||
zc = (br_ssl_client_certificate_rsa_context *)pctx;
|
||||
memcpy(hv, data, hv_len);
|
||||
if (hash_id == 0) {
|
||||
hash_oid = NULL;
|
||||
} else if (hash_id >= 2 && hash_id <= 6) {
|
||||
hash_oid = HASH_OID[hash_id - 2];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
sig_len = (zc->sk->n_bitlen + 7) >> 3;
|
||||
if (len < sig_len) {
|
||||
return 0;
|
||||
}
|
||||
return zc->irsasign(hash_oid, hv, hv_len, zc->sk, data) ? sig_len : 0;
|
||||
}
|
||||
|
||||
static const br_ssl_client_certificate_class ccert_vtable = {
|
||||
sizeof(br_ssl_client_certificate_rsa_context),
|
||||
cc_none0, /* start_name_list */
|
||||
cc_none1, /* start_name */
|
||||
cc_none2, /* append_name */
|
||||
cc_none0, /* end_name */
|
||||
cc_none0, /* end_name_list */
|
||||
cc_choose,
|
||||
0,
|
||||
cc_do_sign
|
||||
};
|
||||
|
||||
/* see bearssl_ssl.h */
|
||||
void
|
||||
br_ssl_client_set_single_rsa(br_ssl_client_context *cc,
|
||||
const br_x509_certificate *chain, size_t chain_len,
|
||||
const br_rsa_private_key *sk, br_rsa_pkcs1_sign irsasign)
|
||||
{
|
||||
cc->client_auth.single_rsa.vtable = &ccert_vtable;
|
||||
cc->client_auth.single_rsa.chain = chain;
|
||||
cc->client_auth.single_rsa.chain_len = chain_len;
|
||||
cc->client_auth.single_rsa.sk = sk;
|
||||
cc->client_auth.single_rsa.irsasign = irsasign;
|
||||
cc->client_auth_vtable = &cc->client_auth.single_rsa.vtable;
|
||||
}
|
||||
|
|
@ -123,9 +123,9 @@ br_ssl_client_init_full(br_ssl_client_context *cc,
|
|||
br_ssl_engine_set_suites(&cc->eng, suites,
|
||||
(sizeof suites) / (sizeof suites[0]));
|
||||
br_ssl_client_set_rsapub(cc, &br_rsa_i31_public);
|
||||
br_ssl_client_set_rsavrfy(cc, &br_rsa_i31_pkcs1_vrfy);
|
||||
br_ssl_engine_set_rsavrfy(&cc->eng, &br_rsa_i31_pkcs1_vrfy);
|
||||
br_ssl_engine_set_ec(&cc->eng, &br_ec_prime_i31);
|
||||
br_ssl_client_set_ecdsa(cc, &br_ecdsa_i31_vrfy_asn1);
|
||||
br_ssl_engine_set_ecdsa(&cc->eng, &br_ecdsa_i31_vrfy_asn1);
|
||||
br_x509_minimal_set_rsa(xc, &br_rsa_i31_pkcs1_vrfy);
|
||||
br_x509_minimal_set_ecdsa(xc,
|
||||
&br_ec_prime_i31, &br_ecdsa_i31_vrfy_asn1);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -55,7 +55,7 @@ make_pms_rsa(br_ssl_client_context *ctx, int prf_id)
|
|||
size_t nlen, u;
|
||||
|
||||
xc = ctx->eng.x509ctx;
|
||||
pk = (*xc)->get_pkey(xc);
|
||||
pk = (*xc)->get_pkey(xc, NULL);
|
||||
|
||||
/*
|
||||
* Compute actual RSA key length, in case there are leading zeros.
|
||||
|
|
@ -145,9 +145,11 @@ static const unsigned char *HASH_OID[] = {
|
|||
|
||||
/*
|
||||
* Check the RSA signature on the ServerKeyExchange message.
|
||||
*
|
||||
* hash hash function ID (2 to 6), or 0 for MD5+SHA-1 (with RSA only)
|
||||
* use_rsa non-zero for RSA signature, zero for ECDSA
|
||||
* sig_len signature length (in bytes); signature value is in the pad
|
||||
*
|
||||
* Returned value is 0 on success, or an error code.
|
||||
*/
|
||||
static int
|
||||
|
|
@ -161,7 +163,7 @@ verify_SKE_sig(br_ssl_client_context *ctx,
|
|||
size_t hv_len;
|
||||
|
||||
xc = ctx->eng.x509ctx;
|
||||
pk = (*xc)->get_pkey(xc);
|
||||
pk = (*xc)->get_pkey(xc, NULL);
|
||||
br_multihash_zero(&mhc);
|
||||
br_multihash_copyimpl(&mhc, &ctx->eng.mhash);
|
||||
br_multihash_init(&mhc);
|
||||
|
|
@ -198,14 +200,14 @@ verify_SKE_sig(br_ssl_client_context *ctx,
|
|||
} else {
|
||||
hash_oid = NULL;
|
||||
}
|
||||
if (!ctx->irsavrfy(ctx->eng.pad, sig_len,
|
||||
if (!ctx->eng.irsavrfy(ctx->eng.pad, sig_len,
|
||||
hash_oid, hv_len, &pk->key.rsa, tmp)
|
||||
|| memcmp(tmp, hv, hv_len) != 0)
|
||||
{
|
||||
return BR_ERR_BAD_SIGNATURE;
|
||||
}
|
||||
} else {
|
||||
if (!ctx->iecdsa(ctx->eng.iec, hv, hv_len, &pk->key.ec,
|
||||
if (!ctx->eng.iecdsa(ctx->eng.iec, hv, hv_len, &pk->key.ec,
|
||||
ctx->eng.pad, sig_len))
|
||||
{
|
||||
return BR_ERR_BAD_SIGNATURE;
|
||||
|
|
@ -215,7 +217,7 @@ verify_SKE_sig(br_ssl_client_context *ctx,
|
|||
}
|
||||
|
||||
/*
|
||||
* Perform client-size ECDH (or ECDHE). The point that should be sent to
|
||||
* Perform client-side ECDH (or ECDHE). The point that should be sent to
|
||||
* the server is written in the pad; returned value is either the point
|
||||
* length (in bytes), or -x on error, with 'x' being an error code.
|
||||
*
|
||||
|
|
@ -241,7 +243,7 @@ make_pms_ecdh(br_ssl_client_context *ctx, unsigned ecdhe, int prf_id)
|
|||
const br_x509_pkey *pk;
|
||||
|
||||
xc = ctx->eng.x509ctx;
|
||||
pk = (*xc)->get_pkey(xc);
|
||||
pk = (*xc)->get_pkey(xc, NULL);
|
||||
curve = pk->key.ec.curve;
|
||||
point_src = pk->key.ec.q;
|
||||
point_len = pk->key.ec.qlen;
|
||||
|
|
@ -292,6 +294,73 @@ make_pms_ecdh(br_ssl_client_context *ctx, unsigned ecdhe, int prf_id)
|
|||
return (int)glen;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform full static ECDH. This occurs only in the context of client
|
||||
* authentication with certificates: the server uses an EC public key,
|
||||
* the cipher suite is of type ECDH (not ECDHE), the server requested a
|
||||
* client certificate and accepts static ECDH, the client has a
|
||||
* certificate with an EC public key in the same curve, and accepts
|
||||
* static ECDH as well.
|
||||
*
|
||||
* Returned value is 0 on success, -1 on error.
|
||||
*/
|
||||
static int
|
||||
make_pms_static_ecdh(br_ssl_client_context *ctx, int prf_id)
|
||||
{
|
||||
unsigned char point[133];
|
||||
size_t point_len;
|
||||
const br_x509_class **xc;
|
||||
const br_x509_pkey *pk;
|
||||
|
||||
xc = ctx->eng.x509ctx;
|
||||
pk = (*xc)->get_pkey(xc, NULL);
|
||||
point_len = pk->key.ec.qlen;
|
||||
if (point_len > sizeof point) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(point, pk->key.ec.q, point_len);
|
||||
if (!(*ctx->client_auth_vtable)->do_keyx(
|
||||
ctx->client_auth_vtable, point, point_len))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
br_ssl_engine_compute_master(&ctx->eng,
|
||||
prf_id, point + 1, point_len >> 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the client-side signature. This is invoked only when a
|
||||
* signature-based client authentication was selected. The computed
|
||||
* signature is in the pad; its length (in bytes) is returned. On
|
||||
* error, 0 is returned.
|
||||
*/
|
||||
static size_t
|
||||
make_client_sign(br_ssl_client_context *ctx)
|
||||
{
|
||||
size_t hv_len;
|
||||
|
||||
/*
|
||||
* Compute hash of handshake messages so far. This "cannot" fail
|
||||
* because the list of supported hash functions provided to the
|
||||
* client certificate handler was trimmed to include only the
|
||||
* hash functions that the multi-hasher supports.
|
||||
*/
|
||||
if (ctx->hash_id) {
|
||||
hv_len = br_multihash_out(&ctx->eng.mhash,
|
||||
ctx->hash_id, ctx->eng.pad);
|
||||
} else {
|
||||
br_multihash_out(&ctx->eng.mhash,
|
||||
br_md5_ID, ctx->eng.pad);
|
||||
br_multihash_out(&ctx->eng.mhash,
|
||||
br_sha1_ID, ctx->eng.pad + 16);
|
||||
hv_len = 36;
|
||||
}
|
||||
return (*ctx->client_auth_vtable)->do_sign(
|
||||
ctx->client_auth_vtable, ctx->hash_id, hv_len,
|
||||
ctx->eng.pad, sizeof ctx->eng.pad);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
\ =======================================================================
|
||||
|
|
@ -303,6 +372,9 @@ make_pms_ecdh(br_ssl_client_context *ctx, unsigned ecdhe, int prf_id)
|
|||
postpone literal postpone ; ;
|
||||
|
||||
addr-ctx: min_clienthello_len
|
||||
addr-ctx: hashes
|
||||
addr-ctx: auth_type
|
||||
addr-ctx: hash_id
|
||||
|
||||
\ Length of the Secure Renegotiation extension. This is 5 for the
|
||||
\ first handshake, 17 for a renegotiation (if the server supports the
|
||||
|
|
@ -319,16 +391,6 @@ addr-ctx: min_clienthello_len
|
|||
: ext-frag-length ( -- len )
|
||||
addr-log_max_frag_len get8 14 = if 0 else 5 then ;
|
||||
|
||||
\ Test support for RSA signatures.
|
||||
cc: supports-rsa-sign? ( -- bool ) {
|
||||
T0_PUSHi(-(CTX->irsavrfy != 0));
|
||||
}
|
||||
|
||||
\ Test support for ECDSA signatures.
|
||||
cc: supports-ecdsa? ( -- bool ) {
|
||||
T0_PUSHi(-(CTX->iecdsa != 0));
|
||||
}
|
||||
|
||||
\ Length of Signatures extension.
|
||||
: ext-signatures-length ( -- len )
|
||||
supported-hash-functions { x } drop
|
||||
|
|
@ -709,80 +771,26 @@ cc: DEBUG-BLOB ( addr len -- ) {
|
|||
resume
|
||||
;
|
||||
|
||||
cc: x509-start-chain ( expected-key-type -- ) {
|
||||
cc: set-server-curve ( -- ) {
|
||||
const br_x509_class *xc;
|
||||
const br_x509_pkey *pk;
|
||||
|
||||
xc = *(ENG->x509ctx);
|
||||
xc->start_chain(ENG->x509ctx, T0_POP(), ENG->server_name);
|
||||
pk = xc->get_pkey(ENG->x509ctx, NULL);
|
||||
CTX->server_curve =
|
||||
(pk->key_type == BR_KEYTYPE_EC) ? pk->key.ec.curve : 0;
|
||||
}
|
||||
|
||||
cc: x509-start-cert ( length -- ) {
|
||||
const br_x509_class *xc;
|
||||
|
||||
xc = *(ENG->x509ctx);
|
||||
xc->start_cert(ENG->x509ctx, T0_POP());
|
||||
}
|
||||
|
||||
cc: x509-append ( length -- ) {
|
||||
const br_x509_class *xc;
|
||||
size_t len;
|
||||
|
||||
xc = *(ENG->x509ctx);
|
||||
len = T0_POP();
|
||||
xc->append(ENG->x509ctx, ENG->pad, len);
|
||||
}
|
||||
|
||||
cc: x509-end-cert ( -- ) {
|
||||
const br_x509_class *xc;
|
||||
|
||||
xc = *(ENG->x509ctx);
|
||||
xc->end_cert(ENG->x509ctx);
|
||||
}
|
||||
|
||||
cc: x509-end-chain ( -- err ) {
|
||||
const br_x509_class *xc;
|
||||
|
||||
xc = *(ENG->x509ctx);
|
||||
T0_PUSH(xc->end_chain(ENG->x509ctx));
|
||||
}
|
||||
|
||||
\ Parse Certificate
|
||||
: read-Certificate ( -- )
|
||||
\ Get header, and check message type.
|
||||
read-handshake-header 11 = ifnot ERR_UNEXPECTED fail then
|
||||
|
||||
\ Start processing the chain through the X.509 engine.
|
||||
\ Read Certificate message from server.
|
||||
: read-Certificate-from-server ( -- )
|
||||
addr-cipher_suite get16 expected-key-type
|
||||
x509-start-chain
|
||||
-1 read-Certificate
|
||||
dup 0< if neg fail then
|
||||
dup ifnot ERR_UNEXPECTED fail then
|
||||
over and <> if ERR_WRONG_KEY_USAGE fail then
|
||||
|
||||
\ Total chain length is a 24-bit integer.
|
||||
read24 open-elt
|
||||
begin
|
||||
dup while
|
||||
read24 open-elt
|
||||
dup x509-start-cert
|
||||
|
||||
\ We read the certificate by chunks through the pad, so
|
||||
\ as to use the existing reading function (read-blob)
|
||||
\ that also ensures proper hashing.
|
||||
begin
|
||||
dup while
|
||||
dup 256 > if 256 else dup then { len }
|
||||
addr-pad len read-blob
|
||||
len x509-append
|
||||
repeat
|
||||
close-elt
|
||||
x509-end-cert
|
||||
repeat
|
||||
|
||||
\ We must close the chain AND the handshake message.
|
||||
close-elt
|
||||
close-elt
|
||||
|
||||
\ Chain processing is finished; get the error code.
|
||||
x509-end-chain
|
||||
dup if fail then drop
|
||||
;
|
||||
\ Set server curve (used for static ECDH).
|
||||
set-server-curve ;
|
||||
|
||||
\ Verify signature on ECDHE point sent by the server.
|
||||
\ 'hash' is the hash function to use (1 to 6, or 0 for RSA with MD5+SHA-1)
|
||||
|
|
@ -851,15 +859,166 @@ cc: verify-SKE-sig ( hash use-rsa sig-len -- err ) {
|
|||
|
||||
close-elt ;
|
||||
|
||||
\ Client certificate: start processing of anchor names.
|
||||
cc: anchor-dn-start-name-list ( -- ) {
|
||||
if (CTX->client_auth_vtable != NULL) {
|
||||
(*CTX->client_auth_vtable)->start_name_list(
|
||||
CTX->client_auth_vtable);
|
||||
}
|
||||
}
|
||||
|
||||
\ Client certificate: start a new anchor DN (length is 16-bit).
|
||||
cc: anchor-dn-start-name ( length -- ) {
|
||||
size_t len;
|
||||
|
||||
len = T0_POP();
|
||||
if (CTX->client_auth_vtable != NULL) {
|
||||
(*CTX->client_auth_vtable)->start_name(
|
||||
CTX->client_auth_vtable, len);
|
||||
}
|
||||
}
|
||||
|
||||
\ Client certificate: push some data for current anchor DN.
|
||||
cc: anchor-dn-append-name ( length -- ) {
|
||||
size_t len;
|
||||
|
||||
len = T0_POP();
|
||||
if (CTX->client_auth_vtable != NULL) {
|
||||
(*CTX->client_auth_vtable)->append_name(
|
||||
CTX->client_auth_vtable, ENG->pad, len);
|
||||
}
|
||||
}
|
||||
|
||||
\ Client certificate: end current anchor DN.
|
||||
cc: anchor-dn-end-name ( -- ) {
|
||||
if (CTX->client_auth_vtable != NULL) {
|
||||
(*CTX->client_auth_vtable)->end_name(
|
||||
CTX->client_auth_vtable);
|
||||
}
|
||||
}
|
||||
|
||||
\ Client certificate: end list of anchor DN.
|
||||
cc: anchor-dn-end-name-list ( -- ) {
|
||||
if (CTX->client_auth_vtable != NULL) {
|
||||
(*CTX->client_auth_vtable)->end_name_list(
|
||||
CTX->client_auth_vtable);
|
||||
}
|
||||
}
|
||||
|
||||
\ Client certificate: obtain the client certificate chain.
|
||||
cc: get-client-chain ( auth_types -- ) {
|
||||
uint32_t auth_types;
|
||||
|
||||
auth_types = T0_POP();
|
||||
if (CTX->client_auth_vtable != NULL) {
|
||||
br_ssl_client_certificate ux;
|
||||
|
||||
(*CTX->client_auth_vtable)->choose(CTX->client_auth_vtable,
|
||||
CTX, auth_types, &ux);
|
||||
CTX->auth_type = (unsigned char)ux.auth_type;
|
||||
CTX->hash_id = (unsigned char)ux.hash_id;
|
||||
ENG->chain = ux.chain;
|
||||
ENG->chain_len = ux.chain_len;
|
||||
} else {
|
||||
CTX->hash_id = 0;
|
||||
ENG->chain_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
\ Parse CertificateRequest. Header has already been read.
|
||||
: read-contents-CertificateRequest ( lim -- )
|
||||
\ TODO: implement client certificates. Right now, we simply
|
||||
\ drop the complete message.
|
||||
begin dup while read8 drop repeat drop ;
|
||||
\ Read supported client authentification types. We keep only
|
||||
\ RSA, ECDSA, and ECDH.
|
||||
0 { auth_types }
|
||||
read8 open-elt
|
||||
begin dup while
|
||||
read8 case
|
||||
1 of 0x0000FF endof
|
||||
64 of 0x00FF00 endof
|
||||
65 of 0x010000 endof
|
||||
66 of 0x020000 endof
|
||||
0 swap
|
||||
endcase
|
||||
auth_types or >auth_types
|
||||
repeat
|
||||
close-elt
|
||||
|
||||
\ Full static ECDH is allowed only if the cipher suite is ECDH
|
||||
\ (not ECDHE). It would be theoretically feasible to use static
|
||||
\ ECDH on the client side with an ephemeral key pair from the
|
||||
\ server, but RFC 4492 (section 3) forbids it because ECDHE suites
|
||||
\ are supposed to provide forward secrecy, and static ECDH would
|
||||
\ negate that property.
|
||||
addr-cipher_suite get16 use-ecdh? ifnot
|
||||
auth_types 0xFFFF and >auth_types
|
||||
then
|
||||
|
||||
\ Note: if the cipher suite is ECDH, then the X.509 validation
|
||||
\ engine was invoked with the BR_KEYTYPE_EC | BR_KEYTYPE_KEYX
|
||||
\ combination, so the server's public key has already been
|
||||
\ checked to be fit for a key exchange.
|
||||
|
||||
\ With TLS 1.2:
|
||||
\ - rsa_fixed_ecdh and ecdsa_fixed_ecdh are synoymous.
|
||||
\ - There is an explicit list of supported sign+hash.
|
||||
\ With TLS 1.0,
|
||||
addr-version get16 0x0303 >= if
|
||||
\ With TLS 1.2:
|
||||
\ - There is an explicit list of supported sign+hash.
|
||||
\ - The ECDH flags must be adjusted for RSA/ECDSA
|
||||
\ support.
|
||||
read-list-sign-algos dup addr-hashes set16
|
||||
|
||||
\ Trim down the list depending on what hash functions
|
||||
\ we support (since the hashing itself is done by the SSL
|
||||
\ engine, not by the certificate handler).
|
||||
supported-hash-functions drop dup 8 << or 0x030000 or and
|
||||
|
||||
auth_types and
|
||||
auth_types 0x030000 and if
|
||||
dup 0x0000FF and if 0x010000 or then
|
||||
dup 0x00FF00 and if 0x020000 or then
|
||||
then
|
||||
>auth_types
|
||||
else
|
||||
\ TLS 1.0 or 1.1. The hash function is fixed for signatures
|
||||
\ (MD5+SHA-1 for RSA, SHA-1 for ECDSA).
|
||||
auth_types 0x030401 and >auth_types
|
||||
then
|
||||
|
||||
\ Parse list of anchor DN.
|
||||
anchor-dn-start-name-list
|
||||
read16 open-elt
|
||||
begin dup while
|
||||
read16 open-elt
|
||||
dup anchor-dn-start-name
|
||||
|
||||
\ We read the DN by chunks through the pad, so
|
||||
\ as to use the existing reading function (read-blob)
|
||||
\ that also ensures proper hashing.
|
||||
begin
|
||||
dup while
|
||||
dup 256 > if 256 else dup then { len }
|
||||
addr-pad len read-blob
|
||||
len anchor-dn-append-name
|
||||
repeat
|
||||
close-elt
|
||||
anchor-dn-end-name
|
||||
repeat
|
||||
close-elt
|
||||
anchor-dn-end-name-list
|
||||
|
||||
\ We should have reached the message end.
|
||||
close-elt
|
||||
|
||||
\ Obtain the client chain.
|
||||
auth_types get-client-chain
|
||||
;
|
||||
|
||||
\ (obsolete)
|
||||
\ Write an empty Certificate message.
|
||||
: write-empty-Certificate ( -- )
|
||||
11 write8 3 write24 0 write24 ;
|
||||
\ : write-empty-Certificate ( -- )
|
||||
\ 11 write8 3 write24 0 write24 ;
|
||||
|
||||
cc: do-rsa-encrypt ( prf_id -- nlen ) {
|
||||
int x;
|
||||
|
|
@ -887,7 +1046,27 @@ cc: do-ecdh ( echde prf_id -- ulen ) {
|
|||
}
|
||||
}
|
||||
|
||||
\ Write ClientKeyExchange
|
||||
cc: do-static-ecdh ( prf-id -- ) {
|
||||
unsigned prf_id = T0_POP();
|
||||
|
||||
if (make_pms_static_ecdh(CTX, prf_id) < 0) {
|
||||
br_ssl_engine_fail(ENG, BR_ERR_INVALID_ALGORITHM);
|
||||
T0_CO();
|
||||
}
|
||||
}
|
||||
|
||||
cc: do-client-sign ( -- sig_len ) {
|
||||
size_t sig_len;
|
||||
|
||||
sig_len = make_client_sign(CTX);
|
||||
if (sig_len == 0) {
|
||||
br_ssl_engine_fail(ENG, BR_ERR_INVALID_ALGORITHM);
|
||||
T0_CO();
|
||||
}
|
||||
T0_PUSH(sig_len);
|
||||
}
|
||||
|
||||
\ Write ClientKeyExchange.
|
||||
: write-ClientKeyExchange ( -- )
|
||||
16 write8
|
||||
addr-cipher_suite get16
|
||||
|
|
@ -903,6 +1082,20 @@ cc: do-ecdh ( echde prf_id -- ulen ) {
|
|||
addr-pad swap write-blob
|
||||
then ;
|
||||
|
||||
\ Write CertificateVerify. This is invoked only if a client certificate
|
||||
\ was requested and sent, and the authentication is not full static ECDH.
|
||||
: write-CertificateVerify ( -- )
|
||||
do-client-sign
|
||||
15 write8 dup
|
||||
addr-version get16 0x0303 >= if
|
||||
4 + write24
|
||||
addr-hash_id get8 write8
|
||||
addr-auth_type get8 write8
|
||||
else
|
||||
2+ write24
|
||||
then
|
||||
dup write16 addr-pad swap write-blob ;
|
||||
|
||||
\ =======================================================================
|
||||
|
||||
\ Perform a handshake.
|
||||
|
|
@ -924,7 +1117,9 @@ cc: do-ecdh ( echde prf_id -- ulen ) {
|
|||
|
||||
\ Not a session resumption.
|
||||
|
||||
read-Certificate
|
||||
\ Read certificate; then check key type and usages against
|
||||
\ cipher suite.
|
||||
read-Certificate-from-server
|
||||
|
||||
\ Depending on cipher suite, we may now expect a
|
||||
\ ServerKeyExchange.
|
||||
|
|
@ -956,12 +1151,27 @@ cc: do-ecdh ( echde prf_id -- ulen ) {
|
|||
more-incoming-bytes? if ERR_UNEXPECTED fail then
|
||||
|
||||
seen-CR if
|
||||
\ TODO: client certificate support.
|
||||
write-empty-Certificate
|
||||
then
|
||||
write-ClientKeyExchange
|
||||
\ If the server requested a client certificate, then
|
||||
\ we must write a Certificate message (it may be
|
||||
\ empty).
|
||||
write-Certificate
|
||||
|
||||
\ TODO: CertificateVerify
|
||||
\ If using static ECDH, then the ClientKeyExchange
|
||||
\ is empty, and there is no CertificateVerify.
|
||||
\ Otherwise, there is a ClientKeyExchange; there
|
||||
\ will then be a CertificateVerify if a client chain
|
||||
\ was indeed sent.
|
||||
addr-hash_id get8 0xFF = if
|
||||
drop
|
||||
16 write8 0 write24
|
||||
addr-cipher_suite get16 prf-id do-static-ecdh
|
||||
else
|
||||
write-ClientKeyExchange
|
||||
if write-CertificateVerify then
|
||||
then
|
||||
else
|
||||
write-ClientKeyExchange
|
||||
then
|
||||
|
||||
-1 write-CCS-Finished
|
||||
-1 read-CCS-Finished
|
||||
|
|
|
|||
|
|
@ -145,12 +145,8 @@ addr-eng: version_max
|
|||
addr-eng: suites_buf
|
||||
addr-eng: suites_num
|
||||
addr-eng: server_name
|
||||
\ addr-eng: version
|
||||
\ addr-eng: cipher_suite
|
||||
addr-eng: client_random
|
||||
addr-eng: server_random
|
||||
\ addr-eng: session_id_len
|
||||
\ addr-eng: session_id
|
||||
addr-eng: ecdhe_curve
|
||||
addr-eng: ecdhe_point
|
||||
addr-eng: ecdhe_point_len
|
||||
|
|
@ -212,6 +208,9 @@ err: ERR_LIMIT_EXCEEDED
|
|||
err: ERR_BAD_FINISHED
|
||||
err: ERR_RESUME_MISMATCH
|
||||
err: ERR_INVALID_ALGORITHM
|
||||
err: ERR_BAD_SIGNATURE
|
||||
err: ERR_WRONG_KEY_USAGE
|
||||
err: ERR_NO_CLIENT_AUTH
|
||||
|
||||
\ Get supported curves (bit mask).
|
||||
cc: supported-curves ( -- x ) {
|
||||
|
|
@ -220,6 +219,7 @@ cc: supported-curves ( -- x ) {
|
|||
}
|
||||
|
||||
\ Get supported hash functions (bit mask and number).
|
||||
\ Note: this (on purpose) skips MD5.
|
||||
cc: supported-hash-functions ( -- x num ) {
|
||||
int i;
|
||||
unsigned x, num;
|
||||
|
|
@ -236,6 +236,16 @@ cc: supported-hash-functions ( -- x num ) {
|
|||
T0_PUSH(num);
|
||||
}
|
||||
|
||||
\ Test support for RSA signatures.
|
||||
cc: supports-rsa-sign? ( -- bool ) {
|
||||
T0_PUSHi(-(ENG->irsavrfy != 0));
|
||||
}
|
||||
|
||||
\ Test support for ECDSA signatures.
|
||||
cc: supports-ecdsa? ( -- bool ) {
|
||||
T0_PUSHi(-(ENG->iecdsa != 0));
|
||||
}
|
||||
|
||||
\ (Re)initialise the multihasher.
|
||||
cc: multihash-init ( -- ) {
|
||||
br_multihash_init(&ENG->mhash);
|
||||
|
|
@ -1020,3 +1030,186 @@ cc: compute-Finished-inner ( from_client prf_id -- ) {
|
|||
22 wait-rectype-out
|
||||
write-Finished
|
||||
flush-record ;
|
||||
|
||||
\ Read and parse a list of supported signature algorithms (with hash
|
||||
\ functions). The resulting bit field is returned.
|
||||
: read-list-sign-algos ( lim -- lim value )
|
||||
0 { hashes }
|
||||
read16 open-elt
|
||||
begin dup while
|
||||
read8 { hash } read8 { sign }
|
||||
\ We keep the value if the signature is either 1 (RSA)
|
||||
\ or 3 (ECDSA), and the hash is one of the SHA-* functions
|
||||
\ (2 to 6, from SHA-1 to SHA-512); we reject MD5.
|
||||
hash 2 >= hash 6 <= and
|
||||
sign 1 = sign 3 = or
|
||||
and if
|
||||
hashes 1 sign 1- 2 << hash + << or >hashes
|
||||
then
|
||||
repeat
|
||||
close-elt
|
||||
hashes ;
|
||||
|
||||
\ =======================================================================
|
||||
|
||||
\ Compute total chain length. This includes the individual certificate
|
||||
\ headers, but not the total chain header. This also sets the cert_cur,
|
||||
\ cert_len and chain_len context fields.
|
||||
cc: total-chain-length ( -- len ) {
|
||||
size_t u;
|
||||
uint32_t total;
|
||||
|
||||
total = 0;
|
||||
for (u = 0; u < ENG->chain_len; u ++) {
|
||||
total += 3 + (uint32_t)ENG->chain[u].data_len;
|
||||
}
|
||||
T0_PUSH(total);
|
||||
}
|
||||
|
||||
\ Get length for current certificate in the chain; if the chain end was
|
||||
\ reached, then this returns -1.
|
||||
cc: begin-cert ( -- len ) {
|
||||
if (ENG->chain_len == 0) {
|
||||
T0_PUSHi(-1);
|
||||
} else {
|
||||
ENG->cert_cur = ENG->chain->data;
|
||||
ENG->cert_len = ENG->chain->data_len;
|
||||
ENG->chain ++;
|
||||
ENG->chain_len --;
|
||||
T0_PUSH(ENG->cert_len);
|
||||
}
|
||||
}
|
||||
|
||||
\ Copy a chunk of certificate data into the pad. Returned value is the
|
||||
\ chunk length, or 0 if the certificate end is reached.
|
||||
cc: copy-cert-chunk ( -- len ) {
|
||||
size_t clen;
|
||||
|
||||
clen = ENG->cert_len;
|
||||
if (clen > sizeof ENG->pad) {
|
||||
clen = sizeof ENG->pad;
|
||||
}
|
||||
memcpy(ENG->pad, ENG->cert_cur, clen);
|
||||
ENG->cert_cur += clen;
|
||||
ENG->cert_len -= clen;
|
||||
T0_PUSH(clen);
|
||||
}
|
||||
|
||||
\ Write a Certificate message. Total chain length (excluding the 3-byte
|
||||
\ header) is returned; it is 0 if the chain is empty.
|
||||
: write-Certificate ( -- total_chain_len )
|
||||
11 write8
|
||||
total-chain-length dup
|
||||
dup 3 + write24 write24
|
||||
begin
|
||||
begin-cert
|
||||
dup 0< if drop ret then write24
|
||||
begin copy-cert-chunk dup while
|
||||
addr-pad swap write-blob
|
||||
repeat
|
||||
drop
|
||||
again ;
|
||||
|
||||
cc: x509-start-chain ( by_client -- ) {
|
||||
const br_x509_class *xc;
|
||||
uint32_t bc;
|
||||
|
||||
bc = T0_POP();
|
||||
xc = *(ENG->x509ctx);
|
||||
xc->start_chain(ENG->x509ctx, bc ? ENG->server_name : NULL);
|
||||
}
|
||||
|
||||
cc: x509-start-cert ( length -- ) {
|
||||
const br_x509_class *xc;
|
||||
|
||||
xc = *(ENG->x509ctx);
|
||||
xc->start_cert(ENG->x509ctx, T0_POP());
|
||||
}
|
||||
|
||||
cc: x509-append ( length -- ) {
|
||||
const br_x509_class *xc;
|
||||
size_t len;
|
||||
|
||||
xc = *(ENG->x509ctx);
|
||||
len = T0_POP();
|
||||
xc->append(ENG->x509ctx, ENG->pad, len);
|
||||
}
|
||||
|
||||
cc: x509-end-cert ( -- ) {
|
||||
const br_x509_class *xc;
|
||||
|
||||
xc = *(ENG->x509ctx);
|
||||
xc->end_cert(ENG->x509ctx);
|
||||
}
|
||||
|
||||
cc: x509-end-chain ( -- err ) {
|
||||
const br_x509_class *xc;
|
||||
|
||||
xc = *(ENG->x509ctx);
|
||||
T0_PUSH(xc->end_chain(ENG->x509ctx));
|
||||
}
|
||||
|
||||
cc: get-key-type-usages ( -- key-type-usages ) {
|
||||
const br_x509_class *xc;
|
||||
const br_x509_pkey *pk;
|
||||
unsigned usages;
|
||||
|
||||
xc = *(ENG->x509ctx);
|
||||
pk = xc->get_pkey(ENG->x509ctx, &usages);
|
||||
if (pk == NULL) {
|
||||
T0_PUSH(0);
|
||||
} else {
|
||||
T0_PUSH(pk->key_type | usages);
|
||||
}
|
||||
}
|
||||
|
||||
\ Read a Certificate message.
|
||||
\ Parameter: non-zero if this is a read by the client of a certificate
|
||||
\ sent by the server; zero otherwise.
|
||||
\ Returned value:
|
||||
\ - Empty: 0
|
||||
\ - Valid: combination of key type and allowed key usages.
|
||||
\ - Invalid: negative (-x for error code x)
|
||||
: read-Certificate ( by_client -- key-type-usages )
|
||||
\ Get header, and check message type.
|
||||
read-handshake-header 11 = ifnot ERR_UNEXPECTED fail then
|
||||
|
||||
\ If the chain is empty, do some special processing.
|
||||
dup 3 = if
|
||||
read24 if ERR_BAD_PARAM fail then
|
||||
swap drop ret
|
||||
then
|
||||
|
||||
\ Start processing the chain through the X.509 engine.
|
||||
swap x509-start-chain
|
||||
|
||||
\ Total chain length is a 24-bit integer.
|
||||
read24 open-elt
|
||||
begin
|
||||
dup while
|
||||
read24 open-elt
|
||||
dup x509-start-cert
|
||||
|
||||
\ We read the certificate by chunks through the pad, so
|
||||
\ as to use the existing reading function (read-blob)
|
||||
\ that also ensures proper hashing.
|
||||
begin
|
||||
dup while
|
||||
dup 256 > if 256 else dup then { len }
|
||||
addr-pad len read-blob
|
||||
len x509-append
|
||||
repeat
|
||||
close-elt
|
||||
x509-end-cert
|
||||
repeat
|
||||
|
||||
\ We must close the chain AND the handshake message.
|
||||
close-elt
|
||||
close-elt
|
||||
|
||||
\ Chain processing is finished; get the error code.
|
||||
x509-end-chain
|
||||
dup if neg ret then drop
|
||||
|
||||
\ Return key type and usages.
|
||||
get-key-type-usages ;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -140,6 +140,35 @@ do_ecdh(br_ssl_server_context *ctx, int prf_id,
|
|||
ecdh_common(ctx, prf_id, cpoint, cpoint_len, x);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the full static ECDH key exchange. When this function is called,
|
||||
* it has already been verified that the cipher suite uses ECDH (not ECDHE),
|
||||
* and the client's public key (from its certificate) has type EC and is
|
||||
* apt for key exchange.
|
||||
*/
|
||||
static void
|
||||
do_static_ecdh(br_ssl_server_context *ctx, int prf_id)
|
||||
{
|
||||
unsigned char cpoint[133];
|
||||
size_t cpoint_len;
|
||||
const br_x509_class **xc;
|
||||
const br_x509_pkey *pk;
|
||||
|
||||
xc = ctx->eng.x509ctx;
|
||||
pk = (*xc)->get_pkey(xc, NULL);
|
||||
cpoint_len = pk->key.ec.qlen;
|
||||
if (cpoint_len > sizeof cpoint) {
|
||||
/*
|
||||
* If the point is larger than our buffer then we need to
|
||||
* restrict it. Length 2 is not a valid point length, so
|
||||
* the ECDH will fail.
|
||||
*/
|
||||
cpoint_len = 2;
|
||||
}
|
||||
memcpy(cpoint, pk->key.ec.q, cpoint_len);
|
||||
do_ecdh(ctx, prf_id, cpoint, cpoint_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the ECDHE key exchange (part 1: generation of transient key, and
|
||||
* computing of the point to send to the client). Returned value is the
|
||||
|
|
@ -258,6 +287,94 @@ do_ecdhe_part2(br_ssl_server_context *ctx, int prf_id,
|
|||
memset(ctx->ecdhe_key, 0, ctx->ecdhe_key_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Offset for hash value within the pad (when obtaining all hash values,
|
||||
* in preparation for verification of the CertificateVerify message).
|
||||
* Order is MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512; last value
|
||||
* is used to get the total length.
|
||||
*/
|
||||
static const unsigned char HASH_PAD_OFF[] = { 0, 16, 36, 64, 96, 144, 208 };
|
||||
|
||||
/*
|
||||
* OID for hash functions in RSA signatures.
|
||||
*/
|
||||
static const unsigned char HASH_OID_SHA1[] = {
|
||||
0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A
|
||||
};
|
||||
|
||||
static const unsigned char HASH_OID_SHA224[] = {
|
||||
0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04
|
||||
};
|
||||
|
||||
static const unsigned char HASH_OID_SHA256[] = {
|
||||
0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
|
||||
};
|
||||
|
||||
static const unsigned char HASH_OID_SHA384[] = {
|
||||
0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02
|
||||
};
|
||||
|
||||
static const unsigned char HASH_OID_SHA512[] = {
|
||||
0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03
|
||||
};
|
||||
|
||||
static const unsigned char *HASH_OID[] = {
|
||||
HASH_OID_SHA1,
|
||||
HASH_OID_SHA224,
|
||||
HASH_OID_SHA256,
|
||||
HASH_OID_SHA384,
|
||||
HASH_OID_SHA512
|
||||
};
|
||||
|
||||
/*
|
||||
* Verify the signature in CertificateVerify. Returned value is 0 on
|
||||
* success, or a non-zero error code. Lack of implementation of the
|
||||
* designated signature algorithm is reported as a "bad signature"
|
||||
* error (because it means that the peer did not honour our advertised
|
||||
* set of supported signature algorithms).
|
||||
*/
|
||||
static int
|
||||
verify_CV_sig(br_ssl_server_context *ctx, size_t sig_len)
|
||||
{
|
||||
const br_x509_class **xc;
|
||||
const br_x509_pkey *pk;
|
||||
int id;
|
||||
|
||||
id = ctx->hash_CV_id;
|
||||
xc = ctx->eng.x509ctx;
|
||||
pk = (*xc)->get_pkey(xc, NULL);
|
||||
if (pk->key_type == BR_KEYTYPE_RSA) {
|
||||
unsigned char tmp[64];
|
||||
const unsigned char *hash_oid;
|
||||
|
||||
if (id == 0) {
|
||||
hash_oid = NULL;
|
||||
} else {
|
||||
hash_oid = HASH_OID[id - 2];
|
||||
}
|
||||
if (ctx->eng.irsavrfy == 0) {
|
||||
return BR_ERR_BAD_SIGNATURE;
|
||||
}
|
||||
if (!ctx->eng.irsavrfy(ctx->eng.pad, sig_len,
|
||||
hash_oid, ctx->hash_CV_len, &pk->key.rsa, tmp)
|
||||
|| memcmp(tmp, ctx->hash_CV, ctx->hash_CV_len) != 0)
|
||||
{
|
||||
return BR_ERR_BAD_SIGNATURE;
|
||||
}
|
||||
} else {
|
||||
if (ctx->eng.iecdsa == 0) {
|
||||
return BR_ERR_BAD_SIGNATURE;
|
||||
}
|
||||
if (!ctx->eng.iecdsa(ctx->eng.iec,
|
||||
ctx->hash_CV, ctx->hash_CV_len,
|
||||
&pk->key.ec, ctx->eng.pad, sig_len))
|
||||
{
|
||||
return BR_ERR_BAD_SIGNATURE;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
\ =======================================================================
|
||||
|
|
@ -386,26 +503,7 @@ cc: set-max-frag-len ( len -- ) {
|
|||
\ Open extension value.
|
||||
read16 open-elt
|
||||
|
||||
\ Clear list of supported signature algorithms.
|
||||
0 addr-hashes set16
|
||||
|
||||
\ Get list of algorithms length.
|
||||
read16 open-elt
|
||||
begin dup while
|
||||
read8 { hash } read8 { sign }
|
||||
\ We keep the value if the signature is either 1 (RSA) or
|
||||
\ 3 (ECDSA), and the hash is one of the SHA-* functions
|
||||
\ (2 to 6, from SHA-1 to SHA-512). Note that we reject
|
||||
\ any use of MD5. Also, we do not keep track of the client
|
||||
\ preferences.
|
||||
hash 2 >= hash 6 <= and
|
||||
sign 1 = sign 3 = or
|
||||
and if
|
||||
addr-hashes get16
|
||||
1 sign 1- 2 << hash + << or addr-hashes set16
|
||||
then
|
||||
repeat
|
||||
close-elt
|
||||
read-list-sign-algos addr-hashes set16
|
||||
|
||||
\ Close extension value.
|
||||
close-elt ;
|
||||
|
|
@ -440,8 +538,8 @@ cc: call-policy-handler ( -- bool ) {
|
|||
CTX->policy_vtable, CTX, &choices);
|
||||
ENG->session.cipher_suite = choices.cipher_suite;
|
||||
CTX->sign_hash_id = choices.hash_id;
|
||||
CTX->chain = choices.chain;
|
||||
CTX->chain_len = choices.chain_len;
|
||||
ENG->chain = choices.chain;
|
||||
ENG->chain_len = choices.chain_len;
|
||||
T0_PUSHi(-(x != 0));
|
||||
}
|
||||
|
||||
|
|
@ -667,11 +765,14 @@ cc: save-session ( -- ) {
|
|||
ok-compression ifnot 40 fail-alert then
|
||||
|
||||
\ Filter hash function support by what the server also supports.
|
||||
\ If no common hash function remains, then ECDHE suites are not
|
||||
\ possible.
|
||||
\ If no common hash function remains with RSA and/or ECDSA, then
|
||||
\ the corresponding ECDHE suites are not possible.
|
||||
supported-hash-functions drop 257 *
|
||||
addr-hashes get16 and dup addr-hashes set16
|
||||
0<> { can-ecdhe }
|
||||
\ In 'can-ecdhe', bit 12 is set if ECDHE_RSA is possible, bit 13 is
|
||||
\ set if ECDHE_ECDSA is possible.
|
||||
dup 0xFF and 0<> neg
|
||||
swap 8 >> 0<> 2 and or { can-ecdhe }
|
||||
|
||||
\ Filter supported curves. If there is no common curve between
|
||||
\ client and us, then ECDHE suites cannot be used. Note that we
|
||||
|
|
@ -684,6 +785,11 @@ cc: save-session ( -- ) {
|
|||
resume if -1 ret then
|
||||
|
||||
\ We are not resuming, so a new session ID should be generated.
|
||||
\ We don't check that the new ID is distinct from the one sent
|
||||
\ by the client because probability of such an event is 2^(-256),
|
||||
\ i.e. much (much) lower than that of an undetected transmission
|
||||
\ error or hardware miscomputation, and with similar consequences
|
||||
\ (handshake simply fails).
|
||||
addr-session_id 32 mkrand
|
||||
32 addr-session_id_len set8
|
||||
|
||||
|
|
@ -692,14 +798,14 @@ cc: save-session ( -- ) {
|
|||
\ server preference order is enforced, and also in case some
|
||||
\ suites are filtered out. In particular:
|
||||
\ -- ECDHE suites are removed if there is no common hash function
|
||||
\ (for signatures) or no common curve.
|
||||
\ (for the relevant signature algorithm) or no common curve.
|
||||
\ -- TLS-1.2-only suites are removed if the negociated version is
|
||||
\ TLS-1.1 or lower.
|
||||
addr-client_suites dup >css-off
|
||||
begin dup css-max < while
|
||||
dup get16 dup cipher-suite-to-elements
|
||||
can-ecdhe ifnot
|
||||
dup 12 >> dup 1 = swap 2 = or if
|
||||
dup 12 >> dup 1 = swap 2 = or if
|
||||
dup can-ecdhe and ifnot
|
||||
2drop 0 dup
|
||||
then
|
||||
then
|
||||
|
|
@ -791,63 +897,6 @@ cc: save-session ( -- ) {
|
|||
drop
|
||||
then ;
|
||||
|
||||
\ Compute total chain length. This includes the individual certificate
|
||||
\ headers, but not the total chain header. This also sets the cert_cur,
|
||||
\ cert_len and chain_len context fields.
|
||||
cc: total-chain-length ( -- len ) {
|
||||
size_t u;
|
||||
uint32_t total;
|
||||
|
||||
total = 0;
|
||||
for (u = 0; u < CTX->chain_len; u ++) {
|
||||
total += 3 + (uint32_t)CTX->chain[u].data_len;
|
||||
}
|
||||
T0_PUSH(total);
|
||||
}
|
||||
|
||||
\ Get length for current certificate in the chain; if the chain end was
|
||||
\ reached, then this returns -1.
|
||||
cc: begin-cert ( -- len ) {
|
||||
if (CTX->chain_len == 0) {
|
||||
T0_PUSHi(-1);
|
||||
} else {
|
||||
CTX->cert_cur = CTX->chain->data;
|
||||
CTX->cert_len = CTX->chain->data_len;
|
||||
CTX->chain ++;
|
||||
CTX->chain_len --;
|
||||
T0_PUSH(CTX->cert_len);
|
||||
}
|
||||
}
|
||||
|
||||
\ Copy a chunk of certificate data into the pad. Returned value is the
|
||||
\ chunk length, or 0 if the certificate end is reached.
|
||||
cc: copy-cert-chunk ( -- len ) {
|
||||
size_t clen;
|
||||
|
||||
clen = CTX->cert_len;
|
||||
if (clen > sizeof ENG->pad) {
|
||||
clen = sizeof ENG->pad;
|
||||
}
|
||||
memcpy(ENG->pad, CTX->cert_cur, clen);
|
||||
CTX->cert_cur += clen;
|
||||
CTX->cert_len -= clen;
|
||||
T0_PUSH(clen);
|
||||
}
|
||||
|
||||
\ Write the server Certificate.
|
||||
: write-Certificate ( -- )
|
||||
11 write8
|
||||
total-chain-length
|
||||
dup 3 + write24 write24
|
||||
begin
|
||||
begin-cert
|
||||
dup 0< if drop ret then write24
|
||||
begin copy-cert-chunk dup while
|
||||
addr-pad swap write-blob
|
||||
repeat
|
||||
drop
|
||||
again ;
|
||||
|
||||
\ Do the first part of ECDHE. Returned value is the computed signature
|
||||
\ length, or a negative error code on error.
|
||||
cc: do-ecdhe-part1 ( curve -- len ) {
|
||||
|
|
@ -900,6 +949,166 @@ cc: do-ecdhe-part1 ( curve -- len ) {
|
|||
sig-len write16
|
||||
addr-pad sig-len write-blob ;
|
||||
|
||||
\ Get length of the list of anchor names to send to the client. The length
|
||||
\ includes the per-name 2-byte header, but _not_ the 2-byte header for
|
||||
\ the list itself. If no client certificate is requested, then this
|
||||
\ returns 0.
|
||||
cc: ta-names-total-length ( -- len ) {
|
||||
size_t u, len;
|
||||
|
||||
len = 0;
|
||||
if (CTX->ta_names != NULL) {
|
||||
for (u = 0; u < CTX->num_tas; u ++) {
|
||||
len += CTX->ta_names[u].len + 2;
|
||||
}
|
||||
} else if (CTX->tas != NULL) {
|
||||
for (u = 0; u < CTX->num_tas; u ++) {
|
||||
len += CTX->tas[u].dn.len + 2;
|
||||
}
|
||||
}
|
||||
T0_PUSH(len);
|
||||
}
|
||||
|
||||
\ Compute length and optionally write the contents of the list of
|
||||
\ supported client authentication methods.
|
||||
: write-list-auth ( do_write -- len )
|
||||
0
|
||||
addr-cipher_suite get16 use-ecdh? if
|
||||
2+ over if 65 write8 66 write8 then
|
||||
then
|
||||
supports-rsa-sign? if 1+ over if 1 write8 then then
|
||||
supports-ecdsa? if 1+ over if 64 write8 then then
|
||||
swap drop ;
|
||||
|
||||
: write-signhash-inner2 ( dow algo hashes len id -- dow algo hashes len )
|
||||
{ id }
|
||||
over 1 id << and ifnot ret then
|
||||
2+
|
||||
3 pick if id write8 2 pick write8 then ;
|
||||
|
||||
: write-signhash-inner1 ( dow algo hashes -- dow len )
|
||||
0
|
||||
4 write-signhash-inner2
|
||||
5 write-signhash-inner2
|
||||
6 write-signhash-inner2
|
||||
3 write-signhash-inner2
|
||||
2 write-signhash-inner2
|
||||
-rot 2drop ;
|
||||
|
||||
\ Compute length and optionally write the contents of the list of
|
||||
\ supported sign+hash algorithms.
|
||||
: write-list-signhash ( do_write -- len )
|
||||
0 { len }
|
||||
\ If supporting neither RSA nor ECDSA in the engine, then we
|
||||
\ will do only static ECDH, and thus we claim support for
|
||||
\ everything (for the X.509 validator).
|
||||
supports-rsa-sign? supports-ecdsa? or ifnot
|
||||
1 0x7C write-signhash-inner1 >len
|
||||
3 0x7C write-signhash-inner1 len +
|
||||
swap drop ret
|
||||
then
|
||||
supports-rsa-sign? if
|
||||
1 supported-hash-functions drop
|
||||
write-signhash-inner1 >len
|
||||
then
|
||||
supports-ecdsa? if
|
||||
3 supported-hash-functions drop
|
||||
write-signhash-inner1 len + >len
|
||||
then
|
||||
drop len ;
|
||||
|
||||
\ Initialise index for sending the list of anchor DN.
|
||||
cc: begin-ta-name-list ( -- ) {
|
||||
CTX->cur_dn_index = 0;
|
||||
}
|
||||
|
||||
\ Switch to next DN in the list. Returned value is the DN length, or -1
|
||||
\ if the end of the list was reached.
|
||||
cc: begin-ta-name ( -- len ) {
|
||||
const br_x500_name *dn;
|
||||
if (CTX->cur_dn_index >= CTX->num_tas) {
|
||||
T0_PUSHi(-1);
|
||||
} else {
|
||||
if (CTX->ta_names == NULL) {
|
||||
dn = &CTX->tas[CTX->cur_dn_index].dn;
|
||||
} else {
|
||||
dn = &CTX->ta_names[CTX->cur_dn_index];
|
||||
}
|
||||
CTX->cur_dn_index ++;
|
||||
CTX->cur_dn = dn->data;
|
||||
CTX->cur_dn_len = dn->len;
|
||||
T0_PUSH(CTX->cur_dn_len);
|
||||
}
|
||||
}
|
||||
|
||||
\ Copy a chunk of the current DN into the pad. Returned value is the
|
||||
\ chunk length; this is 0 when the end of the current DN is reached.
|
||||
cc: copy-dn-chunk ( -- len ) {
|
||||
size_t clen;
|
||||
|
||||
clen = CTX->cur_dn_len;
|
||||
if (clen > sizeof ENG->pad) {
|
||||
clen = sizeof ENG->pad;
|
||||
}
|
||||
memcpy(ENG->pad, CTX->cur_dn, clen);
|
||||
CTX->cur_dn += clen;
|
||||
CTX->cur_dn_len -= clen;
|
||||
T0_PUSH(clen);
|
||||
}
|
||||
|
||||
\ Write a CertificateRequest message.
|
||||
: write-CertificateRequest ( -- )
|
||||
\ The list of client authentication types includes:
|
||||
\ rsa_sign (1)
|
||||
\ ecdsa_sign (64)
|
||||
\ rsa_fixed_ecdh (65)
|
||||
\ ecdsa_fixed_ecdh (66)
|
||||
\ rsa_sign and ecdsa_sign require, respectively, RSA and ECDSA
|
||||
\ support. Static ECDH requires that the cipher suite is ECDH.
|
||||
\ When we ask for static ECDH, we always send both rsa_fixed_ecdh
|
||||
\ and ecdsa_fixed_ecdh because what matters there is what the
|
||||
\ X.509 engine may support, and we do not control that.
|
||||
\
|
||||
\ With TLS 1.2, we must also send a list of supported signature
|
||||
\ and hash algorithms. That list is supposed to qualify both
|
||||
\ the engine itself, and the X.509 validator, which are separate
|
||||
\ in BearSSL. There again, we use the engine capabilities in that
|
||||
\ list, and resort to a generic all-support list if only
|
||||
\ static ECDH is accepted.
|
||||
\
|
||||
\ (In practice, client implementations tend to have at most one
|
||||
\ or two certificates, and send the chain regardless of what
|
||||
\ algorithms are used in it.)
|
||||
|
||||
0 write-list-auth
|
||||
addr-version get16 0x0303 >= if
|
||||
2+ 0 write-list-signhash +
|
||||
then
|
||||
ta-names-total-length + 3 +
|
||||
|
||||
\ Message header
|
||||
13 write8 write24
|
||||
|
||||
\ List of authentication methods
|
||||
0 write-list-auth write8 1 write-list-auth drop
|
||||
|
||||
\ For TLS 1.2+, list of sign+hash
|
||||
addr-version get16 0x0303 >= if
|
||||
0 write-list-signhash write16 1 write-list-signhash drop
|
||||
then
|
||||
|
||||
\ Trust anchor names
|
||||
ta-names-total-length write16
|
||||
begin-ta-name-list
|
||||
begin
|
||||
begin-ta-name
|
||||
dup 0< if drop ret then write16
|
||||
begin copy-dn-chunk dup while
|
||||
addr-pad swap write-blob
|
||||
repeat
|
||||
drop
|
||||
again ;
|
||||
|
||||
\ Write the Server Hello Done message.
|
||||
: write-ServerHelloDone ( -- )
|
||||
14 write8 0 write24 ;
|
||||
|
|
@ -927,11 +1136,18 @@ cc: do-ecdhe-part2 ( len prf_id -- ) {
|
|||
do_ecdhe_part2(CTX, prf_id, ENG->pad, len);
|
||||
}
|
||||
|
||||
\ Read the Client Key Exchange.
|
||||
: read-ClientKeyExchange ( -- )
|
||||
\ Get header, and check message type.
|
||||
read-handshake-header 16 = ifnot ERR_UNEXPECTED fail then
|
||||
\ Perform static ECDH. The point from the client is the public key
|
||||
\ extracted from its certificate.
|
||||
cc: do-static-ecdh ( prf_id -- ) {
|
||||
do_static_ecdh(CTX, T0_POP());
|
||||
}
|
||||
|
||||
\ Read a ClientKeyExchange header.
|
||||
: read-ClientKeyExchange-header ( -- len )
|
||||
read-handshake-header 16 = ifnot ERR_UNEXPECTED fail then ;
|
||||
|
||||
\ Read the Client Key Exchange contents (non-empty case).
|
||||
: read-ClientKeyExchange-contents ( lim -- )
|
||||
\ What we should get depends on the cipher suite.
|
||||
addr-cipher_suite get16 use-rsa-keyx? if
|
||||
\ RSA key exchange: we expect a RSA-encrypted value.
|
||||
|
|
@ -951,6 +1167,116 @@ cc: do-ecdhe-part2 ( len prf_id -- ) {
|
|||
then
|
||||
close-elt ;
|
||||
|
||||
\ Read the Client Key Exchange (normal case).
|
||||
: read-ClientKeyExchange ( -- )
|
||||
read-ClientKeyExchange-header
|
||||
read-ClientKeyExchange-contents ;
|
||||
|
||||
\ Obtain all possible hash values for handshake messages so far. This
|
||||
\ is done because we need the hash value for the CertificateVerify
|
||||
\ _before_ knowing which hash function will actually be used, as this
|
||||
\ information is obtained from decoding the message header itself.
|
||||
\ All hash values are stored in the pad (208 bytes in total).
|
||||
cc: compute-hash-CV ( -- ) {
|
||||
int i;
|
||||
|
||||
for (i = 1; i <= 6; i ++) {
|
||||
br_multihash_out(&ENG->mhash, i,
|
||||
ENG->pad + HASH_PAD_OFF[i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
\ Copy the proper hash value from the pad into the dedicated buffer.
|
||||
\ Returned value is true (-1) on success, false (0) on error (error
|
||||
\ being an unimplemented hash function). The id has already been verified
|
||||
\ to be either 0 (for MD5+SHA-1) or one of the SHA-* functions.
|
||||
cc: copy-hash-CV ( hash_id -- bool ) {
|
||||
int id = T0_POP();
|
||||
size_t off, len;
|
||||
|
||||
if (id == 0) {
|
||||
off = 0;
|
||||
len = 36;
|
||||
} else {
|
||||
if (br_multihash_getimpl(&ENG->mhash, id) == 0) {
|
||||
T0_PUSH(0);
|
||||
T0_RET();
|
||||
}
|
||||
off = HASH_PAD_OFF[id - 1];
|
||||
len = HASH_PAD_OFF[id] - off;
|
||||
}
|
||||
memcpy(CTX->hash_CV, ENG->pad + off, len);
|
||||
CTX->hash_CV_len = len;
|
||||
CTX->hash_CV_id = id;
|
||||
T0_PUSHi(-1);
|
||||
}
|
||||
|
||||
\ Verify signature in CertificateVerify. Output is 0 on success, or a
|
||||
\ non-zero error code.
|
||||
cc: verify-CV-sig ( sig-len -- err ) {
|
||||
int err;
|
||||
|
||||
err = verify_CV_sig(CTX, T0_POP());
|
||||
T0_PUSHi(err);
|
||||
}
|
||||
|
||||
\ Process static ECDH.
|
||||
: process-static-ECDH ( ktu -- )
|
||||
\ Static ECDH is allowed only if the cipher suite uses ECDH, and
|
||||
\ the client's public key has type EC and allows key exchange.
|
||||
\ BR_KEYTYPE_KEYX is 0x10, and BR_KEYTYPE_EC is 2.
|
||||
0x1F and 0x12 = ifnot ERR_WRONG_KEY_USAGE fail then
|
||||
addr-cipher_suite get16
|
||||
dup use-ecdh? ifnot ERR_UNEXPECTED fail then
|
||||
prf-id
|
||||
do-static-ecdh ;
|
||||
|
||||
\ Read CertificateVerify header.
|
||||
: read-CertificateVerify-header ( -- lim )
|
||||
compute-hash-CV
|
||||
read-handshake-header 15 = ifnot ERR_UNEXPECTED fail then ;
|
||||
|
||||
\ Read CertificateVerify. The client key type + usage is expected on the
|
||||
\ stack.
|
||||
: read-CertificateVerify ( ktu -- )
|
||||
\ Check that the key allows for signatures.
|
||||
dup 0x20 and ifnot ERR_WRONG_KEY_USAGE fail then
|
||||
0x0F and { key-type }
|
||||
|
||||
\ Get header.
|
||||
read-CertificateVerify-header
|
||||
|
||||
\ With TLS 1.2+, there is an explicit hash + signature indication,
|
||||
\ which must be compatible with the key type.
|
||||
addr-version get16 0x0303 >= if
|
||||
\ Get hash function, then signature algorithm. The
|
||||
\ signature algorithm is 1 (RSA) or 3 (ECDSA) while our
|
||||
\ symbolic constants for key types are 1 (RSA) or 2 (EC).
|
||||
read16
|
||||
dup 0xFF and 1+ 1 >> key-type = ifnot
|
||||
ERR_BAD_SIGNATURE fail
|
||||
then
|
||||
8 >>
|
||||
|
||||
\ We support only SHA-1, SHA-224, SHA-256, SHA-384
|
||||
\ and SHA-512. We explicitly reject MD5.
|
||||
dup 2 < over 6 > or if ERR_INVALID_ALGORITHM fail then
|
||||
else
|
||||
\ With TLS 1.0 and 1.1, hash is MD5+SHA-1 (0) for RSA,
|
||||
\ SHA-1 (2) for ECDSA.
|
||||
key-type 0x01 = if 0 else 2 then
|
||||
then
|
||||
copy-hash-CV ifnot ERR_INVALID_ALGORITHM fail then
|
||||
|
||||
\ Read signature.
|
||||
read16 dup { sig-len }
|
||||
dup 512 > if ERR_LIMIT_EXCEEDED fail then
|
||||
addr-pad swap read-blob
|
||||
sig-len verify-CV-sig
|
||||
dup if fail then drop
|
||||
|
||||
close-elt ;
|
||||
|
||||
\ Send a HelloRequest.
|
||||
: send-HelloRequest ( -- )
|
||||
flush-record
|
||||
|
|
@ -974,11 +1300,54 @@ cc: do-ecdhe-part2 ( len prf_id -- ) {
|
|||
else
|
||||
\ Not a session resumption
|
||||
write-ServerHello
|
||||
write-Certificate
|
||||
write-Certificate drop
|
||||
write-ServerKeyExchange
|
||||
ta-names-total-length if
|
||||
write-CertificateRequest
|
||||
then
|
||||
write-ServerHelloDone
|
||||
flush-record
|
||||
read-ClientKeyExchange
|
||||
|
||||
\ If we sent a CertificateRequest then we expect a
|
||||
\ Certificate message.
|
||||
ta-names-total-length if
|
||||
\ Read client certificate.
|
||||
0 read-Certificate
|
||||
|
||||
choice
|
||||
dup 0< uf
|
||||
\ Client certificate validation failed.
|
||||
2 flag? ifnot neg fail then
|
||||
drop
|
||||
read-ClientKeyExchange
|
||||
read-CertificateVerify-header
|
||||
dup skip-blob drop
|
||||
enduf
|
||||
dup 0= uf
|
||||
\ Client sent no certificate at all.
|
||||
drop
|
||||
2 flag? ifnot
|
||||
ERR_NO_CLIENT_AUTH fail
|
||||
then
|
||||
read-ClientKeyExchange
|
||||
enduf
|
||||
|
||||
\ Client certificate was validated.
|
||||
read-ClientKeyExchange-header
|
||||
dup ifnot
|
||||
\ Empty ClientKeyExchange.
|
||||
drop
|
||||
process-static-ECDH
|
||||
else
|
||||
read-ClientKeyExchange-contents
|
||||
read-CertificateVerify
|
||||
then
|
||||
endchoice
|
||||
else
|
||||
\ No client certificate request, we just expect
|
||||
\ a non-empty ClientKeyExchange.
|
||||
read-ClientKeyExchange
|
||||
then
|
||||
0 read-CCS-Finished
|
||||
0 write-CCS-Finished
|
||||
save-session
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ se_choose(const br_ssl_server_policy_class **pctx,
|
|||
|
||||
pc = (br_ssl_server_policy_ec_context *)pctx;
|
||||
st = br_ssl_server_get_client_suites(cc, &st_num);
|
||||
hash_id = br_ssl_choose_hash(br_ssl_server_get_client_hashes(cc));
|
||||
hash_id = br_ssl_choose_hash(br_ssl_server_get_client_hashes(cc) >> 8);
|
||||
if (cc->eng.session.version < BR_TLS12) {
|
||||
hash_id = br_sha1_ID;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue