* specific name. It must be noted that since the engine context is the
* first field of the br_ssl_server_context structure ('eng'), then
* pointers values of both types are interchangeable, modulo an
- * appropriate cast. This also means that "adresses" computed as offsets
+ * appropriate cast. This also means that "addresses" computed as offsets
* within the structure work for both kinds of context.
*/
#define CTX ((br_ssl_server_context *)ENG)
/*
* Decrypt the PMS.
*/
- x = (*ctx->policy_vtable)->do_keyx(ctx->policy_vtable, epms, len);
+ x = (*ctx->policy_vtable)->do_keyx(ctx->policy_vtable, epms, &len);
/*
* Set the first two bytes to the maximum supported client
*/
static void
ecdh_common(br_ssl_server_context *ctx, int prf_id,
- unsigned char *cpoint, size_t cpoint_len, uint32_t ctl)
+ unsigned char *xcoor, size_t xcoor_len, uint32_t ctl)
{
unsigned char rpms[80];
- size_t pms_len;
- /*
- * The point length is supposed to be 1+2*Xlen, where Xlen is
- * the length (in bytes) of the X coordinate, i.e. the pre-master
- * secret. If the provided point is too large, then it is
- * obviously incorrect (i.e. everybody can see that it is
- * incorrect), so leaking that fact is not a problem.
- */
- pms_len = cpoint_len >> 1;
- if (pms_len > sizeof rpms) {
- pms_len = sizeof rpms;
+ if (xcoor_len > sizeof rpms) {
+ xcoor_len = sizeof rpms;
ctl = 0;
}
* decryption failed. Note that we use a constant-time conditional
* copy.
*/
- br_hmac_drbg_generate(&ctx->eng.rng, rpms, pms_len);
- br_ccopy(ctl ^ 1, cpoint + 1, rpms, pms_len);
+ br_hmac_drbg_generate(&ctx->eng.rng, rpms, xcoor_len);
+ br_ccopy(ctl ^ 1, xcoor, rpms, xcoor_len);
/*
* Compute master secret.
*/
- br_ssl_engine_compute_master(&ctx->eng, prf_id, cpoint + 1, pms_len);
+ br_ssl_engine_compute_master(&ctx->eng, prf_id, xcoor, xcoor_len);
/*
* Clear the pre-master secret from RAM: it is normally a buffer
* in the context, hence potentially long-lived.
*/
- memset(cpoint, 0, cpoint_len);
+ memset(xcoor, 0, xcoor_len);
}
/*
* Finalise the key exchange.
*/
x = (*ctx->policy_vtable)->do_keyx(ctx->policy_vtable,
- cpoint, cpoint_len);
+ cpoint, &cpoint_len);
ecdh_common(ctx, prf_id, cpoint, cpoint_len, x);
}
do_ecdh(ctx, prf_id, cpoint, cpoint_len);
}
+static size_t
+hash_data(br_ssl_server_context *ctx,
+ void *dst, int hash_id, const void *src, size_t len)
+{
+ const br_hash_class *hf;
+ br_hash_compat_context hc;
+
+ if (hash_id == 0) {
+ unsigned char tmp[36];
+
+ hf = br_multihash_getimpl(&ctx->eng.mhash, br_md5_ID);
+ if (hf == NULL) {
+ return 0;
+ }
+ hf->init(&hc.vtable);
+ hf->update(&hc.vtable, src, len);
+ hf->out(&hc.vtable, tmp);
+ hf = br_multihash_getimpl(&ctx->eng.mhash, br_sha1_ID);
+ if (hf == NULL) {
+ return 0;
+ }
+ hf->init(&hc.vtable);
+ hf->update(&hc.vtable, src, len);
+ hf->out(&hc.vtable, tmp + 16);
+ memcpy(dst, tmp, 36);
+ return 36;
+ } else {
+ hf = br_multihash_getimpl(&ctx->eng.mhash, hash_id);
+ if (hf == NULL) {
+ return 0;
+ }
+ hf->init(&hc.vtable);
+ hf->update(&hc.vtable, src, len);
+ hf->out(&hc.vtable, dst);
+ return (hf->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK;
+ }
+}
+
/*
* 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
static int
do_ecdhe_part1(br_ssl_server_context *ctx, int curve)
{
- int hash;
+ unsigned algo_id;
unsigned mask;
- const unsigned char *order, *generator;
+ const unsigned char *order;
size_t olen, glen;
- br_multihash_context mhc;
- unsigned char head[4];
size_t hv_len, sig_len;
if (!((ctx->eng.iec->supported_curves >> curve) & 1)) {
/*
* Compute our ECDH point.
*/
- generator = ctx->eng.iec->generator(curve, &glen);
- memcpy(ctx->eng.ecdhe_point, generator, glen);
+ glen = ctx->eng.iec->mulgen(ctx->eng.ecdhe_point,
+ ctx->ecdhe_key, olen, curve);
ctx->eng.ecdhe_point_len = glen;
- if (!ctx->eng.iec->mul(ctx->eng.ecdhe_point, glen,
- ctx->ecdhe_key, olen, curve))
- {
- return -BR_ERR_INVALID_ALGORITHM;
- }
/*
- * Compute the signature.
+ * Assemble the message to be signed, and possibly hash it.
*/
- br_multihash_zero(&mhc);
- br_multihash_copyimpl(&mhc, &ctx->eng.mhash);
- br_multihash_init(&mhc);
- br_multihash_update(&mhc,
- ctx->eng.client_random, sizeof ctx->eng.client_random);
- br_multihash_update(&mhc,
- ctx->eng.server_random, sizeof ctx->eng.server_random);
- head[0] = 3;
- head[1] = 0;
- head[2] = curve;
- head[3] = ctx->eng.ecdhe_point_len;
- br_multihash_update(&mhc, head, sizeof head);
- br_multihash_update(&mhc,
+ memcpy(ctx->eng.pad, ctx->eng.client_random, 32);
+ memcpy(ctx->eng.pad + 32, ctx->eng.server_random, 32);
+ ctx->eng.pad[64 + 0] = 0x03;
+ ctx->eng.pad[64 + 1] = 0x00;
+ ctx->eng.pad[64 + 2] = curve;
+ ctx->eng.pad[64 + 3] = ctx->eng.ecdhe_point_len;
+ memcpy(ctx->eng.pad + 64 + 4,
ctx->eng.ecdhe_point, ctx->eng.ecdhe_point_len);
- hash = ctx->sign_hash_id;
- if (hash) {
- hv_len = br_multihash_out(&mhc, hash, ctx->eng.pad);
+ hv_len = 64 + 4 + ctx->eng.ecdhe_point_len;
+ algo_id = ctx->sign_hash_id;
+ if (algo_id >= (unsigned)0xFF00) {
+ hv_len = hash_data(ctx, ctx->eng.pad, algo_id & 0xFF,
+ ctx->eng.pad, hv_len);
if (hv_len == 0) {
return -BR_ERR_INVALID_ALGORITHM;
}
- } else {
- if (!br_multihash_out(&mhc, br_md5_ID, ctx->eng.pad)
- || !br_multihash_out(&mhc,
- br_sha1_ID, ctx->eng.pad + 16))
- {
- return -BR_ERR_INVALID_ALGORITHM;
- }
- hv_len = 36;
}
+
sig_len = (*ctx->policy_vtable)->do_sign(ctx->policy_vtable,
- hash, hv_len, ctx->eng.pad, sizeof ctx->eng.pad);
+ algo_id, ctx->eng.pad, hv_len, sizeof ctx->eng.pad);
return sig_len ? (int)sig_len : -BR_ERR_INVALID_ALGORITHM;
}
unsigned char *cpoint, size_t cpoint_len)
{
int curve;
- uint32_t x;
+ uint32_t ctl;
+ size_t xoff, xlen;
curve = ctx->eng.ecdhe_curve;
/*
* Finalise the key exchange.
*/
- x = ctx->eng.iec->mul(cpoint, cpoint_len,
+ ctl = ctx->eng.iec->mul(cpoint, cpoint_len,
ctx->ecdhe_key, ctx->ecdhe_key_len, curve);
- ecdh_common(ctx, prf_id, cpoint, cpoint_len, x);
+ xoff = ctx->eng.iec->xoff(curve, &xlen);
+ ecdh_common(ctx, prf_id, cpoint + xoff, xlen, ctl);
/*
* Clear the ECDHE private key. Forward Secrecy is achieved insofar
\ Open extension value.
read16 open-elt
- read-list-sign-algos addr-hashes set16
+ read-list-sign-algos addr-hashes set32
\ Close extension value.
close-elt ;
x = (*CTX->policy_vtable)->choose(
CTX->policy_vtable, CTX, &choices);
ENG->session.cipher_suite = choices.cipher_suite;
- CTX->sign_hash_id = choices.hash_id;
+ CTX->sign_hash_id = choices.algo_id;
ENG->chain = choices.chain;
ENG->chain_len = choices.chain_len;
T0_PUSHi(-(x != 0));
}
}
+\ Read and drop ClientHello. This is used when a client-triggered
+\ renegotiation attempt is rejected.
+: skip-ClientHello ( -- )
+ read-handshake-header-core
+ 1 = ifnot ERR_UNEXPECTED fail then
+ dup skip-blob drop ;
+
\ Read ClientHello. If the session is resumed, then -1 is returned.
: read-ClientHello ( -- resume )
\ Get header, and check message type.
\ -- client is reputed to know RSA and ECDSA, both with SHA-1
\ -- the default elliptic curve is P-256 (secp256r1, id = 23)
0 addr-server_name set8
- 0x404 addr-hashes set16
+ 0x0404 addr-hashes set32
0x800000 addr-curves set32
\ Process extensions, if any.
\ we should mark the client as "supporting secure renegotiation".
reneg-scsv if 2 addr-reneg set8 then
+ \ If, at that point, the 'reneg' value is still 0, then the client
+ \ did not send the extension or the SCSV, so we have to assume
+ \ that secure renegotiation is not supported by that client.
+ addr-reneg get8 ifnot 1 addr-reneg set8 then
+
\ Check compression.
ok-compression ifnot 40 fail-alert then
\ Filter hash function support by what the server also supports.
\ 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
+ supported-hash-functions drop 257 * 0xFFFF0000 or
+ addr-hashes get32 and dup addr-hashes set32
\ 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
\ suites are filtered out. In particular:
\ -- ECDHE suites are removed if there is no common hash function
\ (for the relevant signature algorithm) or no common curve.
- \ -- TLS-1.2-only suites are removed if the negociated version is
+ \ -- TLS-1.2-only suites are removed if the negotiated version is
\ TLS-1.1 or lower.
addr-client_suites dup >css-off
begin dup css-max < while
T0_PUSHi(do_ecdhe_part1(CTX, curve));
}
+\ Get index of first bit set to 1 (in low to high order).
+: lowest-1 ( bits -- n )
+ dup ifnot drop -1 ret then
+ 0 begin dup2 >> 1 and 0= while 1+ repeat
+ swap drop ;
+
\ Write the Server Key Exchange message (if applicable).
: write-ServerKeyExchange ( -- )
addr-cipher_suite get16 use-ecdhe? ifnot ret then
\ We must select an appropriate curve among the curves that
- \ are supported both by us and the peer. Right now we use
- \ the one with the smallest ID, which in practice means P-256.
+ \ are supported both by us and the peer. Right now, we apply
+ \ a fixed preference order: Curve25519, P-256, P-384, P-521,
+ \ then the common curve with the lowest ID.
\ (TODO: add some option to make that behaviour configurable.)
\
\ This loop always terminates because previous processing made
\ sure that ECDHE suites are not selectable if there is no common
\ curve.
- addr-curves get32 0
- begin dup2 >> 1 and 0= while 1+ repeat
- { curve-id } drop
+ addr-curves get32
+ dup 0x20000000 and if
+ drop 29
+ else
+ dup 0x38000000 and dup if swap then
+ drop lowest-1
+ then
+ { curve-id }
\ Compute the signed curve point to send.
curve-id do-ecdhe-part1 dup 0< if neg fail then { sig-len }
\ If TLS-1.2+, write hash and signature identifiers.
tls1.2+ if
- \ Hash identifier is in the sign_hash_id field.
- addr-sign_hash_id get8 write8
- \ 'use-rsa-ecdhe?' returns -1 for RSA, 0 for ECDSA.
- \ The byte on the wire shall be 1 for RSA, 3 for ECDSA.
- addr-cipher_suite get16 use-rsa-ecdhe? 1 << 3 + write8
+ \ sign_hash_id contains either a hash identifier,
+ \ or the complete 16-bit value to write.
+ addr-sign_hash_id get16
+ dup 0xFF00 < if
+ write16
+ else
+ 0xFF and write8
+ \ 'use-rsa-ecdhe?' returns -1 for RSA, 0 for
+ \ ECDSA. The byte on the wire shall be 1 for RSA,
+ \ 3 for ECDSA.
+ addr-cipher_suite get16 use-rsa-ecdhe? 1 << 3 + write8
+ then
then
\ Signature.
\ The best we can do is ask for a
\ renegotiation, then wait for it
\ to happen.
+ 0 addr-application_data set8
send-HelloRequest
then
endof
\ "no renegotiation" flag is set.
drop
addr-reneg get8 1 = 1 flag? or if
+ skip-ClientHello
flush-record
begin can-output? not while
wait-co drop
repeat
100 send-warning
+ \ Put back connection in "application
+ \ data" state: it's not dead yet.
+ 1 addr-application_data set8
+ 23 addr-record_type_out set8
else
0 do-handshake
then